187 lines
8.1 KiB
Go
187 lines
8.1 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCLIRepoMutatingHTTPCommands(t *testing.T) {
|
|
t.Setenv("GITOOCEAN_CONFIG", filepath.Join(t.TempDir(), "client.json"))
|
|
seen := map[string]int{}
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
key := r.Method + " " + r.URL.Path
|
|
seen[key]++
|
|
if r.Header.Get("Authorization") != "Bearer tok" {
|
|
t.Fatalf("missing auth for %s: %q", key, r.Header.Get("Authorization"))
|
|
}
|
|
switch key {
|
|
case "POST /api/repos":
|
|
writeJSON(w, http.StatusCreated, Repository{Owner: "alice", Name: "demo", Visibility: "public", Description: "desc", DefaultBranch: "main"})
|
|
case "PATCH /api/repos/alice/demo":
|
|
writeJSON(w, http.StatusOK, Repository{Owner: "alice", Name: "demo", Visibility: "private", Description: "new", DefaultBranch: "main", Archived: seen[key] > 1})
|
|
case "DELETE /api/repos/alice/demo":
|
|
if r.URL.Query().Get("force") != "true" {
|
|
t.Fatalf("expected force=true, got %s", r.URL.RawQuery)
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
|
case "POST /api/repos/bob/demo/fork":
|
|
writeJSON(w, http.StatusCreated, Repository{Owner: "alice", Name: "fork", Visibility: "public", DefaultBranch: "main"})
|
|
case "GET /api/repos/alice/demo/collaborators":
|
|
writeJSON(w, http.StatusOK, []Collaborator{{Username: "bob", Role: "write"}})
|
|
case "POST /api/repos/alice/demo/collaborators":
|
|
writeJSON(w, http.StatusOK, map[string]string{"username": "bob", "role": "read"})
|
|
case "DELETE /api/repos/alice/demo/collaborators/bob":
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "removed"})
|
|
default:
|
|
t.Fatalf("unexpected request %s", key)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
if err := saveConfig(Config{Server: ts.URL, Token: "tok", Username: "alice"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
checks := []struct {
|
|
name string
|
|
fn func() error
|
|
want string
|
|
}{
|
|
{"create", func() error { return cliRepoCreate([]string{"demo", "--public", "--description", "desc"}) }, "Created public repository"},
|
|
{"create json", func() error { return cliRepoCreate([]string{"demo", "--public", "--json"}) }, `"owner": "alice"`},
|
|
{"set", func() error {
|
|
return cliRepoSet([]string{"alice/demo", "--description", "new", "--visibility", "private"})
|
|
}, "Visibility: private"},
|
|
{"archive", func() error { return cliRepoArchive([]string{"alice/demo"}, true) }, "Archived: true"},
|
|
{"delete", func() error { return cliRepoDelete([]string{"alice/demo", "--force"}) }, "Deleted alice/demo"},
|
|
{"fork", func() error { return cliRepoFork([]string{"bob/demo", "--name", "fork"}) }, "Forked bob/demo to alice/fork"},
|
|
{"collaborators", func() error { return cliRepoCollaborators([]string{"alice/demo"}) }, "bob"},
|
|
{"collaborator add", func() error { return cliRepoCollaborator([]string{"add", "alice/demo", "bob", "--role", "read"}) }, "Added bob as read"},
|
|
{"collaborator remove", func() error { return cliRepoCollaborator([]string{"remove", "alice/demo", "bob"}) }, ""},
|
|
}
|
|
for _, tc := range checks {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
out := captureStdout(t, func() {
|
|
if err := tc.fn(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
if !strings.Contains(out, tc.want) {
|
|
t.Fatalf("output missing %q: %s", tc.want, out)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCLIPRMutatingHTTPCommands(t *testing.T) {
|
|
t.Setenv("GITOOCEAN_CONFIG", filepath.Join(t.TempDir(), "client.json"))
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
key := r.Method + " " + r.URL.Path
|
|
switch key {
|
|
case "POST /api/repos/alice/demo/pulls":
|
|
writeJSON(w, http.StatusCreated, PullRequest{Number: 1, Status: "open", Title: "Fix", Author: "alice", SourceOwner: "alice", SourceRepo: "demo", SourceBranch: "feature", TargetOwner: "alice", TargetRepo: "demo", TargetBranch: "main"})
|
|
case "GET /api/repos/alice/demo/pulls/1/diff":
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
_, _ = fmt.Fprint(w, "diff --git a/file b/file\n")
|
|
case "POST /api/repos/alice/demo/pulls/1/comments":
|
|
writeJSON(w, http.StatusCreated, PRComment{Author: "alice", Body: "hello"})
|
|
case "POST /api/repos/alice/demo/pulls/1/close", "POST /api/repos/alice/demo/pulls/1/merge":
|
|
writeJSON(w, http.StatusOK, PullRequest{Number: 1, Status: strings.TrimPrefix(r.URL.Path, "/api/repos/alice/demo/pulls/1/"), Title: "Fix", Author: "alice", SourceOwner: "alice", SourceRepo: "demo", SourceBranch: "feature", TargetOwner: "alice", TargetRepo: "demo", TargetBranch: "main"})
|
|
default:
|
|
t.Fatalf("unexpected request %s", key)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
if err := saveConfig(Config{Server: ts.URL, Token: "tok", Username: "alice"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checks := []struct {
|
|
name string
|
|
fn func() error
|
|
want string
|
|
}{
|
|
{"create same", func() error {
|
|
return cliPRCreate([]string{"--repo", "alice/demo", "--from", "feature", "--to", "main", "--title", "Fix"})
|
|
}, "#1 open: Fix"},
|
|
{"create cross", func() error {
|
|
return cliPRCreate([]string{"--from", "alice/demo:feature", "--to", "alice/demo:main", "--title", "Fix"})
|
|
}, "#1 open: Fix"},
|
|
{"diff", func() error { return cliPRDiff([]string{"alice/demo", "1"}) }, "diff --git"},
|
|
{"comment", func() error { return cliPRComment([]string{"alice/demo", "1", "hello"}) }, "Commented as alice"},
|
|
{"close", func() error { return cliPRAction([]string{"alice/demo", "1"}, "close") }, "#1 close: Fix"},
|
|
{"merge", func() error { return cliPRAction([]string{"alice/demo", "1"}, "merge") }, "#1 merge: Fix"},
|
|
}
|
|
for _, tc := range checks {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
out := captureStdout(t, func() {
|
|
if err := tc.fn(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
if !strings.Contains(out, tc.want) {
|
|
t.Fatalf("output missing %q: %s", tc.want, out)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCLITokenAdminAndBackupCommands(t *testing.T) {
|
|
t.Setenv("GITOOCEAN_CONFIG", filepath.Join(t.TempDir(), "client.json"))
|
|
backupCfgPath := filepath.Join(t.TempDir(), "server.json")
|
|
t.Setenv("GITOOCEAN_SERVER_CONFIG", backupCfgPath)
|
|
storage := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(storage, "repos", "alice", "demo.git"), 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := saveServerConfig(backupCfgPath, ServerConfig{Storage: storage, MySQLDSN: "u:p@tcp(127.0.0.1:3306)/gitocean"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method + " " + r.URL.Path {
|
|
case "GET /api/tokens":
|
|
writeJSON(w, http.StatusOK, []TokenInfo{{ID: 1, Active: true}})
|
|
case "DELETE /api/tokens/1", "POST /api/tokens/prune", "DELETE /api/admin/users/bob", "POST /api/admin/tokens/prune":
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
|
case "GET /api/admin/users":
|
|
writeJSON(w, http.StatusOK, []User{{Username: "alice", Email: "alice@example.com", IsAdmin: true}})
|
|
case "GET /api/admin/repos":
|
|
writeJSON(w, http.StatusOK, []Repository{{Owner: "alice", Name: "demo", Visibility: "public"}})
|
|
default:
|
|
t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
if err := saveConfig(Config{Server: ts.URL, Token: "tok", Username: "alice"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checks := []struct {
|
|
name string
|
|
fn func() error
|
|
want string
|
|
}{
|
|
{"token list", func() error { return cliToken([]string{"list"}) }, "active"},
|
|
{"token revoke", func() error { return cliToken([]string{"revoke", "1"}) }, ""},
|
|
{"token prune", func() error { return cliToken([]string{"prune"}) }, ""},
|
|
{"admin users", func() error { return cliAdmin([]string{"users", "list"}) }, "alice@example.com"},
|
|
{"admin users delete", func() error { return cliAdmin([]string{"users", "delete", "bob"}) }, ""},
|
|
{"admin repos", func() error { return cliAdmin([]string{"repos", "list"}) }, "alice/demo"},
|
|
{"admin storage", func() error { return cliAdmin([]string{"storage", "check"}) }, "demo.git"},
|
|
}
|
|
for _, tc := range checks {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
out := captureStdout(t, func() {
|
|
if err := tc.fn(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
if !strings.Contains(out, tc.want) {
|
|
t.Fatalf("output missing %q: %s", tc.want, out)
|
|
}
|
|
})
|
|
}
|
|
}
|