Add three new services completing the final service layer: - WikiService: CRUD operations for repository wiki pages - MiscService: markdown rendering, licence/gitignore templates, nodeinfo, version - CommitService: commit statuses (list, create, combined) and git notes - PostRaw method on Client for endpoints returning raw text (e.g. /markdown) - Remove services_stub.go (all stubs now replaced with real implementations) - Wire Commits field into Forge struct Co-Authored-By: Virgil <virgil@lethean.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
167 lines
4.8 KiB
Go
167 lines
4.8 KiB
Go
package forge
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"forge.lthn.ai/core/go-forge/types"
|
|
)
|
|
|
|
func TestCommitService_Good_ListStatuses(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
t.Errorf("expected GET, got %s", r.Method)
|
|
}
|
|
if r.URL.Path != "/api/v1/repos/core/go-forge/commits/abc123/statuses" {
|
|
t.Errorf("wrong path: %s", r.URL.Path)
|
|
}
|
|
json.NewEncoder(w).Encode([]types.CommitStatus{
|
|
{ID: 1, Context: "ci/build", Description: "Build passed"},
|
|
{ID: 2, Context: "ci/test", Description: "Tests passed"},
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
f := NewForge(srv.URL, "tok")
|
|
statuses, err := f.Commits.ListStatuses(context.Background(), "core", "go-forge", "abc123")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(statuses) != 2 {
|
|
t.Fatalf("got %d statuses, want 2", len(statuses))
|
|
}
|
|
if statuses[0].Context != "ci/build" {
|
|
t.Errorf("got context=%q, want %q", statuses[0].Context, "ci/build")
|
|
}
|
|
if statuses[1].Context != "ci/test" {
|
|
t.Errorf("got context=%q, want %q", statuses[1].Context, "ci/test")
|
|
}
|
|
}
|
|
|
|
func TestCommitService_Good_CreateStatus(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
t.Errorf("expected POST, got %s", r.Method)
|
|
}
|
|
if r.URL.Path != "/api/v1/repos/core/go-forge/statuses/abc123" {
|
|
t.Errorf("wrong path: %s", r.URL.Path)
|
|
}
|
|
var opts types.CreateStatusOption
|
|
if err := json.NewDecoder(r.Body).Decode(&opts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if opts.Context != "ci/build" {
|
|
t.Errorf("got context=%q, want %q", opts.Context, "ci/build")
|
|
}
|
|
if opts.Description != "Build passed" {
|
|
t.Errorf("got description=%q, want %q", opts.Description, "Build passed")
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(types.CommitStatus{
|
|
ID: 1,
|
|
Context: "ci/build",
|
|
Description: "Build passed",
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
f := NewForge(srv.URL, "tok")
|
|
status, err := f.Commits.CreateStatus(context.Background(), "core", "go-forge", "abc123", &types.CreateStatusOption{
|
|
Context: "ci/build",
|
|
Description: "Build passed",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if status.Context != "ci/build" {
|
|
t.Errorf("got context=%q, want %q", status.Context, "ci/build")
|
|
}
|
|
if status.ID != 1 {
|
|
t.Errorf("got id=%d, want 1", status.ID)
|
|
}
|
|
}
|
|
|
|
func TestCommitService_Good_GetNote(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
t.Errorf("expected GET, got %s", r.Method)
|
|
}
|
|
if r.URL.Path != "/api/v1/repos/core/go-forge/git/notes/abc123" {
|
|
t.Errorf("wrong path: %s", r.URL.Path)
|
|
}
|
|
json.NewEncoder(w).Encode(types.Note{
|
|
Message: "reviewed and approved",
|
|
Commit: &types.Commit{
|
|
SHA: "abc123",
|
|
},
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
f := NewForge(srv.URL, "tok")
|
|
note, err := f.Commits.GetNote(context.Background(), "core", "go-forge", "abc123")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if note.Message != "reviewed and approved" {
|
|
t.Errorf("got message=%q, want %q", note.Message, "reviewed and approved")
|
|
}
|
|
if note.Commit.SHA != "abc123" {
|
|
t.Errorf("got commit sha=%q, want %q", note.Commit.SHA, "abc123")
|
|
}
|
|
}
|
|
|
|
func TestCommitService_Good_GetCombinedStatus(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
t.Errorf("expected GET, got %s", r.Method)
|
|
}
|
|
if r.URL.Path != "/api/v1/repos/core/go-forge/statuses/main" {
|
|
t.Errorf("wrong path: %s", r.URL.Path)
|
|
}
|
|
json.NewEncoder(w).Encode(types.CombinedStatus{
|
|
SHA: "abc123",
|
|
TotalCount: 2,
|
|
Statuses: []*types.CommitStatus{
|
|
{ID: 1, Context: "ci/build"},
|
|
{ID: 2, Context: "ci/test"},
|
|
},
|
|
})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
f := NewForge(srv.URL, "tok")
|
|
cs, err := f.Commits.GetCombinedStatus(context.Background(), "core", "go-forge", "main")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cs.SHA != "abc123" {
|
|
t.Errorf("got sha=%q, want %q", cs.SHA, "abc123")
|
|
}
|
|
if cs.TotalCount != 2 {
|
|
t.Errorf("got total_count=%d, want 2", cs.TotalCount)
|
|
}
|
|
if len(cs.Statuses) != 2 {
|
|
t.Fatalf("got %d statuses, want 2", len(cs.Statuses))
|
|
}
|
|
}
|
|
|
|
func TestCommitService_Bad_NotFound(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
json.NewEncoder(w).Encode(map[string]string{"message": "not found"})
|
|
}))
|
|
defer srv.Close()
|
|
|
|
f := NewForge(srv.URL, "tok")
|
|
_, err := f.Commits.GetNote(context.Background(), "core", "go-forge", "nonexistent")
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if !IsNotFound(err) {
|
|
t.Errorf("expected not-found error, got %v", err)
|
|
}
|
|
}
|