160 lines
6.9 KiB
Go
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)
|
|
}
|
|
}
|