package mcpsrv import ( "context" "testing" "sourcecraft.dev/bigbes/sr-ht-spec/authn" "sourcecraft.dev/bigbes/sr-ht-spec/core" "sourcecraft.dev/bigbes/sr-ht-spec/service" ) // fakeWriter captures the request the tool builds and returns a canned result, // so the handler's mapping — arguments in, principal from context, result out — // is checked without a service, a repository or a database. type fakeWriter struct { got service.ProposeRequest res service.ProposeResult err error } func (f *fakeWriter) Propose(_ context.Context, req service.ProposeRequest) (service.ProposeResult, error) { f.got = req return f.res, f.err } // TestProposeHandlerForwardsPrincipalAndArgs proves the tool passes the acting // agent (resolved onto the request context by the /mcp middleware) and every // argument through to service.Propose, and maps the result — id, url, merged — // back out. func TestProposeHandlerForwardsPrincipalAndArgs(t *testing.T) { principal := authn.Principal{ Kind: authn.KindAgent, Owner: "bigbes", Agent: "claude-code/spec-writer", Session: "sess-1", } ctx := authn.WithPrincipal(context.Background(), principal) w := &fakeWriter{res: service.ProposeResult{ Proposal: service.Proposal{ ID: 7, Branch: "proposals/7", BaseRev: "1f0c1d1a1e2b3c4d5e6f708192a3b4c5d6e7f809", State: core.StateOpen, }, URL: "https://spec.srht.bigb.es/~bigbes/rfcs/p/7", Merged: false, }} out, err := proposeHandler(ctx, w, proposeInput{ Space: "~bigbes/rfcs", IfMatch: "1f0c1d1a1e2b3c4d5e6f708192a3b4c5d6e7f809", Title: "Add a note", Rationale: "because", Message: "write it", Documents: []proposeDoc{{Path: "notes/a.md", Content: "---\nid: N-1\n---\n"}}, }) if err != nil { t.Fatalf("proposeHandler: %v", err) } // The request the service saw. if w.got.Principal != principal { t.Errorf("principal = %+v, want the one on the context %+v", w.got.Principal, principal) } if w.got.Space != (core.SpaceRef{Owner: "bigbes", Name: "rfcs"}) { t.Errorf("space = %v, want ~bigbes/rfcs", w.got.Space) } if w.got.IfMatch != "1f0c1d1a1e2b3c4d5e6f708192a3b4c5d6e7f809" || w.got.Title != "Add a note" { t.Errorf("request args not forwarded: %+v", w.got) } if len(w.got.Writes) != 1 || w.got.Writes[0].Path != "notes/a.md" { t.Errorf("writes = %+v, want the one document", w.got.Writes) } // The result mapped out. if out.Proposal != 7 || out.URL != "https://spec.srht.bigb.es/~bigbes/rfcs/p/7" || out.Merged { t.Errorf("output = %+v, want id 7, the url, merged=false", out) } if out.State != "open" || out.Branch != "proposals/7" { t.Errorf("output state/branch = %q/%q, want open/proposals/7", out.State, out.Branch) } } // TestProposeHandlerRejectsEmptyDocuments refuses a call with no documents // before it reaches the service. func TestProposeHandlerRejectsEmptyDocuments(t *testing.T) { w := &fakeWriter{} if _, err := proposeHandler(context.Background(), w, proposeInput{Space: "~bigbes/rfcs"}); err == nil { t.Fatal("proposeHandler with no documents = nil, want error") } if w.got.Writes != nil || w.got.Space != (core.SpaceRef{}) { t.Fatal("service was called despite empty documents") } }