Files
GitOcean-Old/internal/app/repo_handlers_test.go
T
2026-06-08 15:03:41 -05:00

160 lines
6.9 KiB
Go

package app
import (
"database/sql"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
)
func repoRows(repo Repository) *sqlmock.Rows {
var fork any = sql.NullInt64{}
if repo.ForkedFromID != nil {
fork = sql.NullInt64{Int64: *repo.ForkedFromID, Valid: true}
}
created := repo.CreatedAt
if created.IsZero() {
created = time.Now().UTC()
}
updated := repo.UpdatedAt
if updated.IsZero() {
updated = created
}
return sqlmock.NewRows([]string{"id", "owner_user_id", "owner", "name", "visibility", "description", "default_branch", "archived", "forked_from_repository_id", "created_at", "updated_at"}).
AddRow(repo.ID, repo.OwnerUserID, repo.Owner, repo.Name, repo.Visibility, repo.Description, repo.DefaultBranch, repo.Archived, fork, created, updated)
}
func expectLoadRepo(mock sqlmock.Sqlmock, owner, name string, repo Repository) {
mock.ExpectQuery("FROM repositories r JOIN users u").WithArgs(strings.ToLower(owner), strings.ToLower(name)).WillReturnRows(repoRows(repo))
}
func TestRepoGetSearchAndUpdate(t *testing.T) {
s, mock, cleanup := newMockServer(t)
defer cleanup()
user := User{ID: 1, Email: "alice@example.com", Username: "alice"}
token := "tok"
repo := Repository{ID: 10, OwnerUserID: user.ID, Owner: "alice", Name: "demo", Visibility: "public", Description: "old", DefaultBranch: "main"}
expectLoadRepo(mock, "alice", "demo", repo)
rr := httptest.NewRecorder()
s.handleRepoGet(rr, httptest.NewRequest(http.MethodGet, "/api/repos/alice/demo", nil), "alice", "demo")
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "demo") {
t.Fatalf("get status=%d body=%s", rr.Code, rr.Body.String())
}
mock.ExpectQuery("FROM repositories r JOIN users u").WithArgs("demo", "%demo%", "%demo%").WillReturnRows(repoRows(repo))
rr = httptest.NewRecorder()
s.handleRepoSearch(rr, httptest.NewRequest(http.MethodGet, "/api/repos/search?q=demo&scope=all", nil))
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "demo") {
t.Fatalf("search status=%d body=%s", rr.Code, rr.Body.String())
}
expectBearerUser(mock, token, user)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectExec("UPDATE repositories SET description").WithArgs("new", "private", "main", false, repo.ID).WillReturnResult(sqlmock.NewResult(0, 1))
desc := "new"
body := `{"description":"` + desc + `","visibility":"private"}`
rr = httptest.NewRecorder()
req := httptest.NewRequest(http.MethodPatch, "/api/repos/alice/demo", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
s.handleRepoUpdate(rr, req, "alice", "demo")
if rr.Code != http.StatusOK {
t.Fatalf("update status=%d body=%s", rr.Code, rr.Body.String())
}
var updated Repository
if err := json.Unmarshal(rr.Body.Bytes(), &updated); err != nil {
t.Fatal(err)
}
if updated.Description != "new" || updated.Visibility != "private" {
t.Fatalf("unexpected updated repo: %#v", updated)
}
expectBearerUser(mock, token, user)
expectLoadRepo(mock, "alice", "demo", repo)
rr = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPatch, "/api/repos/alice/demo", strings.NewReader(`{"visibility":"hidden"}`))
req.Header.Set("Authorization", "Bearer "+token)
s.handleRepoUpdate(rr, req, "alice", "demo")
if rr.Code != http.StatusBadRequest {
t.Fatalf("bad visibility status=%d body=%s", rr.Code, rr.Body.String())
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}
func TestRepoDeleteAndCollaborators(t *testing.T) {
s, mock, cleanup := newMockServer(t)
defer cleanup()
owner := User{ID: 1, Email: "alice@example.com", Username: "alice"}
token := "tok"
repo := Repository{ID: 10, OwnerUserID: owner.ID, Owner: "alice", Name: "demo", Visibility: "private", DefaultBranch: "main"}
expectBearerUser(mock, token, owner)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectQuery("SELECT COUNT").WithArgs(repo.ID, repo.ID).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodDelete, "/api/repos/alice/demo", nil)
req.Header.Set("Authorization", "Bearer "+token)
s.handleRepoDelete(rr, req, "alice", "demo")
if rr.Code != http.StatusConflict {
t.Fatalf("delete open PR status=%d body=%s", rr.Code, rr.Body.String())
}
expectBearerUser(mock, token, owner)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectQuery("SELECT COUNT").WithArgs(repo.ID, repo.ID).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
mock.ExpectExec("DELETE FROM repositories").WithArgs(repo.ID).WillReturnResult(sqlmock.NewResult(0, 1))
rr = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodDelete, "/api/repos/alice/demo?force=true", nil)
req.Header.Set("Authorization", "Bearer "+token)
s.handleRepoDelete(rr, req, "alice", "demo")
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "deleted") {
t.Fatalf("delete status=%d body=%s", rr.Code, rr.Body.String())
}
expectBearerUser(mock, token, owner)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectQuery("SELECT rc.id, u.username").WithArgs(repo.ID).WillReturnRows(sqlmock.NewRows([]string{"id", "username", "role", "created_at"}).AddRow(int64(2), "bob", "write", time.Now().UTC()))
rr = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/repos/alice/demo/collaborators", nil)
req.Header.Set("Authorization", "Bearer "+token)
s.handleCollaborators(rr, req, "alice", "demo", nil)
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "bob") {
t.Fatalf("collab list status=%d body=%s", rr.Code, rr.Body.String())
}
expectBearerUser(mock, token, owner)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectQuery("SELECT id FROM users").WithArgs("bob").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(int64(2)))
mock.ExpectExec("INSERT INTO repository_collaborators").WithArgs(repo.ID, int64(2), "read").WillReturnResult(sqlmock.NewResult(3, 1))
rr = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodPost, "/api/repos/alice/demo/collaborators", strings.NewReader(`{"username":"Bob","role":"read"}`))
req.Header.Set("Authorization", "Bearer "+token)
s.handleCollaborators(rr, req, "alice", "demo", nil)
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "bob") {
t.Fatalf("collab add status=%d body=%s", rr.Code, rr.Body.String())
}
expectBearerUser(mock, token, owner)
expectLoadRepo(mock, "alice", "demo", repo)
mock.ExpectExec("DELETE rc FROM repository_collaborators").WithArgs(repo.ID, "bob").WillReturnResult(sqlmock.NewResult(0, 1))
rr = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodDelete, "/api/repos/alice/demo/collaborators/bob", nil)
req.Header.Set("Authorization", "Bearer "+token)
s.handleCollaborators(rr, req, "alice", "demo", []string{"bob"})
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "removed") {
t.Fatalf("collab remove status=%d body=%s", rr.Code, rr.Body.String())
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatal(err)
}
}