~bigbes/sr-ht-spec

ref: 6d7dbdb5560f52652c1ce20f153a964b5d6fc88d sr-ht-spec/mcpsrv/propose_internal_test.go -rw-r--r-- 3.3 KiB
6d7dbdb5 — Eugene Blikh ci: publish this build's own coverage and benchmarks 2 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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. Principal is no longer comparable with == —
	// it carries a grant set — so its rendering stands in.
	if w.got.Principal.String() != principal.String() {
		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")
	}
}