Canonical lives at infra ci-templates/golangci.yml, distributed by the infra#309 pattern: byte-identical copies, checksum drift gate in infra. Do not edit this file here -- edit the canonical and run `task ci:sync`. Verified locally before pushing, not pushed-and-hoped: golangci-lint v2.11.4 against a Go 1.26.1 toolchain (matching CI) reports 0 issues here. Also applies gofumpt, which this config enables and the golangci-lint v2 default set does not check at all. That gap is why the drift was invisible: earlier passes ran gofmt, which is weaker. Mechanical, deterministic, build+vet verified.
153 lines
6.1 KiB
Go
153 lines
6.1 KiB
Go
package mcp_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/audit"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/brainstore"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/capture"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/capturehttp"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/classification"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/mcp"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const capStaticTok = "cap-static-tok"
|
|
|
|
type capFakeTracker struct{}
|
|
|
|
func (capFakeTracker) CreateIssue(context.Context, string, string, string) (capture.IssueRef, error) {
|
|
return capture.IssueRef{Repo: "hyperguild", Number: 1, URL: "https://git/1"}, nil
|
|
}
|
|
|
|
func (capFakeTracker) CloseIssue(context.Context, string, int, string) (capture.IssueRef, error) {
|
|
return capture.IssueRef{}, nil
|
|
}
|
|
|
|
func (capFakeTracker) CommentIssue(context.Context, string, int, string) (capture.IssueRef, error) {
|
|
return capture.IssueRef{}, nil
|
|
}
|
|
|
|
type capFakeValidator struct {
|
|
subject string
|
|
err error
|
|
}
|
|
|
|
func (v capFakeValidator) Validate(context.Context, string) (string, error) {
|
|
return v.subject, v.err
|
|
}
|
|
|
|
func captureServer(t *testing.T, validator capturehttp.Validator, sovereign []string) (*mcp.Server, string) {
|
|
t.Helper()
|
|
brainDir := t.TempDir()
|
|
cfg, err := classification.Load(brainDir)
|
|
require.NoError(t, err)
|
|
svc := capture.NewService(brainstore.New(brainDir), capFakeTracker{}, cfg, audit.NewSlogSink(nil))
|
|
srv := mcp.NewServer(brainDir, nil, nil, nil)
|
|
srv.WithCapture(svc, validator, capStaticTok, "local-cli", capturehttp.NewOriginResolver(sovereign))
|
|
return srv, brainDir
|
|
}
|
|
|
|
func captureCall(t *testing.T, srv http.Handler, authz string, args map[string]any) map[string]any {
|
|
t.Helper()
|
|
body, _ := json.Marshal(map[string]any{
|
|
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
|
"params": map[string]any{"name": "capture", "arguments": args},
|
|
})
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", bytes.NewReader(body))
|
|
if authz != "" {
|
|
req.Header.Set("Authorization", authz)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
var resp map[string]any
|
|
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
|
|
return resp
|
|
}
|
|
|
|
func TestCaptureToolListedWhenWired(t *testing.T) {
|
|
srv, _ := captureServer(t, nil, nil)
|
|
body, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", bytes.NewReader(body))
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
assert.Contains(t, rr.Body.String(), `"capture"`)
|
|
}
|
|
|
|
func TestCaptureToolNotListedByDefault(t *testing.T) {
|
|
srv := mcp.NewServer(t.TempDir(), nil, nil, nil) // no WithCapture
|
|
body, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
req := httptest.NewRequest(http.MethodPost, "/mcp", bytes.NewReader(body))
|
|
rr := httptest.NewRecorder()
|
|
srv.ServeHTTP(rr, req)
|
|
assert.NotContains(t, rr.Body.String(), `"capture"`)
|
|
}
|
|
|
|
func TestCaptureToolForwardsViaStaticPrincipal(t *testing.T) {
|
|
srv, brainDir := captureServer(t, nil, nil)
|
|
resp := captureCall(t, srv, "Bearer "+capStaticTok, map[string]any{
|
|
"context": map[string]any{"harness": "claude-code", "actor": "mathias", "classification": "internal"},
|
|
"insights": []map[string]any{{"text": "a fact", "wing": "hyperguild", "hall": "facts"}},
|
|
"tickets": []map[string]any{{"repo": "hyperguild", "action": "create", "title": "t"}},
|
|
})
|
|
require.Nil(t, resp["error"], "got error: %v", resp["error"])
|
|
text := resp["result"].(map[string]any)["content"].([]any)[0].(map[string]any)["text"].(string)
|
|
var rec capture.CaptureReceipt
|
|
require.NoError(t, json.Unmarshal([]byte(text), &rec))
|
|
assert.True(t, rec.Insights[0].OK)
|
|
assert.True(t, rec.Tickets[0].OK)
|
|
// Forwarded to the real brain store.
|
|
_, statErr := os.Stat(filepath.Join(brainDir, "wiki/hyperguild/facts"))
|
|
require.NoError(t, statErr)
|
|
}
|
|
|
|
func TestCaptureToolRefusesConfidentialViaUSNexus(t *testing.T) {
|
|
// JWT principal not in the sovereign allowlist ⇒ us-nexus origin.
|
|
srv, _ := captureServer(t, capFakeValidator{subject: "claudeai-oauth"}, nil)
|
|
resp := captureCall(t, srv, "Bearer jwt-token", map[string]any{
|
|
"context": map[string]any{"harness": "claudeai-chat", "actor": "mathias", "classification": "confidential"},
|
|
"insights": []map[string]any{{"text": "secret", "wing": "client-seb", "hall": "facts"}},
|
|
})
|
|
require.NotNil(t, resp["error"])
|
|
assert.Contains(t, resp["error"].(map[string]any)["message"].(string), "sovereignty")
|
|
}
|
|
|
|
func TestCaptureToolAllowsConfidentialViaSovereignJWT(t *testing.T) {
|
|
srv, _ := captureServer(t, capFakeValidator{subject: "koala-cli"}, []string{"koala-cli"})
|
|
resp := captureCall(t, srv, "Bearer jwt-token", map[string]any{
|
|
"context": map[string]any{"harness": "claude-code", "actor": "mathias", "classification": "confidential"},
|
|
"insights": []map[string]any{{"text": "secret", "wing": "client-seb", "hall": "facts"}},
|
|
})
|
|
assert.Nil(t, resp["error"], "sovereign JWT principal should be allowed: %v", resp["error"])
|
|
}
|
|
|
|
func TestCaptureToolRejectsUnauthenticated(t *testing.T) {
|
|
srv, _ := captureServer(t, capFakeValidator{err: errors.New("no jwt")}, nil)
|
|
resp := captureCall(t, srv, "", map[string]any{ // no Authorization
|
|
"context": map[string]any{"harness": "x", "classification": "internal"},
|
|
"insights": []map[string]any{{"text": "a", "wing": "hyperguild", "hall": "facts"}},
|
|
})
|
|
require.NotNil(t, resp["error"])
|
|
assert.Contains(t, resp["error"].(map[string]any)["message"].(string), "authenticated principal")
|
|
}
|
|
|
|
func TestCaptureToolCallerCannotForgeOrigin(t *testing.T) {
|
|
// Body asserts sovereign harness, but the us-nexus JWT principal governs.
|
|
srv, _ := captureServer(t, capFakeValidator{subject: "claudeai-oauth"}, nil)
|
|
resp := captureCall(t, srv, "Bearer jwt", map[string]any{
|
|
"context": map[string]any{"harness": "sovereign-soil", "classification": "confidential"},
|
|
"insights": []map[string]any{{"text": "secret", "wing": "client-seb", "hall": "facts"}},
|
|
})
|
|
require.NotNil(t, resp["error"])
|
|
assert.Contains(t, resp["error"].(map[string]any)["message"].(string), "sovereignty")
|
|
}
|