124 lines
3.6 KiB
Go
124 lines
3.6 KiB
Go
package main
|
|
|
|
// gitlab_test.go — PAT connect + project mapping against httptest upstream.
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func newGitLabUpstream(t *testing.T) (*httptest.Server, *GitLab) {
|
|
t.Helper()
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/api/v4/user", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("Private-Token") != "pat-good" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"username":"alice"}`))
|
|
})
|
|
mux.HandleFunc("/api/v4/projects", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Header.Get("Private-Token") != "pat-good" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
q := r.URL.Query()
|
|
if q.Get("membership") != "true" || q.Get("order_by") != "last_activity_at" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`[
|
|
{"path_with_namespace":"team/b","name":"B","namespace":{"path":"team"},
|
|
"last_activity_at":"2024-02-02T00:00:00Z","web_url":"https://gl/team/b","default_branch":"main"},
|
|
{"path_with_namespace":"team/a","name":"A","namespace":{"path":"team"},
|
|
"last_activity_at":"2024-03-03T00:00:00Z","web_url":"https://gl/team/a","default_branch":"trunk"}
|
|
]`))
|
|
})
|
|
ts := httptest.NewServer(mux)
|
|
t.Cleanup(ts.Close)
|
|
store := openTestStore(t)
|
|
return ts, NewGitLab(store, ts.URL)
|
|
}
|
|
|
|
func TestGitLabConnectValidatesAndStores(t *testing.T) {
|
|
_, gl := newGitLabUpstream(t)
|
|
|
|
if status := gl.Status(); status["connected"] != false {
|
|
t.Fatalf("status before connect = %v", status)
|
|
}
|
|
|
|
if _, err := gl.Connect(context.Background(), "pat-bad"); err == nil {
|
|
t.Fatal("bad PAT should fail validation")
|
|
}
|
|
|
|
username, err := gl.Connect(context.Background(), "pat-good")
|
|
if err != nil {
|
|
t.Fatalf("connect: %v", err)
|
|
}
|
|
if username != "alice" {
|
|
t.Fatalf("username = %q", username)
|
|
}
|
|
|
|
status := gl.Status()
|
|
if status["connected"] != true || status["username"] != "alice" {
|
|
t.Fatalf("status after connect = %v", status)
|
|
}
|
|
if strings.Contains(mustJSON(t, status), "pat-good") {
|
|
t.Fatal("status must never expose the PAT")
|
|
}
|
|
|
|
if err := gl.Disconnect(); err != nil {
|
|
t.Fatalf("disconnect: %v", err)
|
|
}
|
|
if status := gl.Status(); status["connected"] != false {
|
|
t.Fatalf("status after disconnect = %v", status)
|
|
}
|
|
}
|
|
|
|
func mustJSON(t *testing.T, v any) string {
|
|
t.Helper()
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func TestGitLabReposMappingAndSorting(t *testing.T) {
|
|
_, gl := newGitLabUpstream(t)
|
|
|
|
if _, err := gl.Repos(context.Background()); err == nil || !strings.Contains(err.Error(), "not connected") {
|
|
t.Fatalf("repos without connect = %v, want not-connected error", err)
|
|
}
|
|
|
|
if _, err := gl.Connect(context.Background(), "pat-good"); err != nil {
|
|
t.Fatalf("connect: %v", err)
|
|
}
|
|
repos, err := gl.Repos(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("repos: %v", err)
|
|
}
|
|
if len(repos) != 2 {
|
|
t.Fatalf("repos = %d, want 2", len(repos))
|
|
}
|
|
want := GitLabRepo{
|
|
Path: "team/a", Name: "A", Namespace: "team",
|
|
LastActivityAt: "2024-03-03T00:00:00Z", WebURL: "https://gl/team/a", DefaultBranch: "trunk",
|
|
}
|
|
if repos[0] != want {
|
|
t.Fatalf("first repo = %+v, want %+v (sorted by activity desc)", repos[0], want)
|
|
}
|
|
encoded := mustJSON(t, repos[0])
|
|
for _, key := range []string{`"path":"team/a"`, `"namespace":"team"`, `"webUrl":"https://gl/team/a"`, `"defaultBranch":"trunk"`} {
|
|
if !strings.Contains(encoded, key) {
|
|
t.Fatalf("repo JSON %s missing %s", encoded, key)
|
|
}
|
|
}
|
|
}
|