package provider import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "net/http" "net/http/httptest" "orchestra/internal/domain" "os" "path/filepath" "testing" ) func TestGiteaSourceNameDefaultsToRepo(t *testing.T) { if got := (Gitea{Repo: "orchestra"}).SourceName(); got != "gitea" { t.Fatalf("expected legacy unnamespaced source, got %q", got) } if got := (Gitea{Repo: "orchestra", Project: "correx"}).SourceName(); got != "gitea:correx" { t.Fatalf("expected namespaced source, got %q", got) } } func TestGiteaIngestWebhookTagsProject(t *testing.T) { g := Gitea{Owner: "kami", Repo: "correx-repo", Project: "correx", WebhookSecret: "s3cret"} body, _ := json.Marshal(map[string]any{ "action": "opened", "issue": map[string]any{"number": 42, "title": "fix thing", "body": "", "state": "open"}, }) mac := hmac.New(sha256.New, []byte("s3cret")) mac.Write(body) sig := hex.EncodeToString(mac.Sum(nil)) sk := &sink{} if err := g.IngestWebhook(body, sig, sk); err != nil { t.Fatalf("ingest: %v", err) } if len(sk.events) != 1 { t.Fatalf("expected 1 event, got %d", len(sk.events)) } var p struct { Project string `json:"project"` Source string `json:"source"` ExternalID string `json:"external_id"` } if err := json.Unmarshal(sk.events[0].Payload, &p); err != nil { t.Fatalf("unmarshal payload: %v", err) } if p.Project != "correx" || p.Source != "gitea:correx" || p.ExternalID != "42" { t.Fatalf("unexpected payload: %+v", p) } } func TestGiteaIngestWebhookRejectsBadSignature(t *testing.T) { g := Gitea{Owner: "kami", Repo: "r", Project: "p", WebhookSecret: "s3cret"} if err := g.IngestWebhook([]byte(`{"action":"opened","issue":{"number":1}}`), "wrong", &sink{}); err == nil { t.Fatal("expected signature rejection") } } func TestMultiGiteaReflectDispatchesByTaskSource(t *testing.T) { var hitCorrex, hitMaven bool correxSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { hitCorrex = true w.WriteHeader(200) })) defer correxSrv.Close() mavenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { hitMaven = true w.WriteHeader(200) })) defer mavenSrv.Close() m := MultiGitea{Sources: map[string]Gitea{ "gitea:correx": {BaseURL: correxSrv.URL, Owner: "kami", Repo: "correx-repo", Project: "correx"}, "gitea:maven": {BaseURL: mavenSrv.URL, Owner: "kami", Repo: "maven-repo", Project: "maven"}, }} if err := m.ReflectTask(domain.Task{Source: "gitea:correx", ExternalID: "7"}, domain.Event{Type: "TaskCompleted"}); err != nil { t.Fatalf("reflect to correx: %v", err) } if !hitCorrex || hitMaven { t.Fatalf("expected only correx server hit, got correx=%v maven=%v", hitCorrex, hitMaven) } hitCorrex, hitMaven = false, false if err := m.ReflectTask(domain.Task{Source: "gitea:maven", ExternalID: "3"}, domain.Event{Type: "TaskFailed"}); err != nil { t.Fatalf("reflect to maven: %v", err) } if hitCorrex || !hitMaven { t.Fatalf("expected only maven server hit, got correx=%v maven=%v", hitCorrex, hitMaven) } // A task from a non-Gitea source (or an unregistered Gitea project) must // be a silent no-op, not an error. if err := m.ReflectTask(domain.Task{Source: "jsonl"}, domain.Event{Type: "TaskCompleted"}); err != nil { t.Fatalf("unmatched source should no-op, got %v", err) } } func TestLoadGiteaConfigsValidatesAndRejectsDuplicates(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "gitea.json") good := `[ {"project":"correx","base_url":"https://gitea.internal","owner":"kami","repo":"correx"}, {"project":"maven","base_url":"https://gitea.internal","owner":"kami","repo":"maven"} ]` if err := os.WriteFile(path, []byte(good), 0644); err != nil { t.Fatal(err) } cfgs, err := LoadGiteaConfigs(path) if err != nil || len(cfgs) != 2 { t.Fatalf("cfgs=%d err=%v", len(cfgs), err) } dupPath := filepath.Join(dir, "dup.json") dup := `[ {"project":"correx","base_url":"https://gitea.internal","owner":"kami","repo":"correx"}, {"project":"correx","base_url":"https://gitea.internal","owner":"kami","repo":"other"} ]` if err := os.WriteFile(dupPath, []byte(dup), 0644); err != nil { t.Fatal(err) } if _, err := LoadGiteaConfigs(dupPath); err == nil { t.Fatal("expected duplicate project rejection") } incompletePath := filepath.Join(dir, "incomplete.json") if err := os.WriteFile(incompletePath, []byte(`[{"project":"x"}]`), 0644); err != nil { t.Fatal(err) } if _, err := LoadGiteaConfigs(incompletePath); err == nil { t.Fatal("expected missing-field rejection") } }