package mcp_test import ( "bytes" "encoding/json" "log/slog" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // captureToolCallLog routes slog to a buffer for fn and returns the decoded // "mcp tool call" records. func captureToolCallLog(t *testing.T, fn func()) []map[string]any { t.Helper() var buf bytes.Buffer prev := slog.Default() slog.SetDefault(slog.New(slog.NewJSONHandler(&buf, nil))) t.Cleanup(func() { slog.SetDefault(prev) }) fn() var out []map[string]any for _, l := range strings.Split(strings.TrimSpace(buf.String()), "\n") { var rec map[string]any if json.Unmarshal([]byte(l), &rec) == nil && rec["msg"] == "mcp tool call" { out = append(out, rec) } } return out } // hyperguild#144: brain_query/brain_read left no record of who read what. func TestToolsCallLogsOneRecordWithToolPrincipalOriginAndScope(t *testing.T) { srv, _, _, _ := govServer(t) recs := captureToolCallLog(t, func() { authCall(t, srv, "Bearer "+capStaticTok, "brain_query", map[string]any{"query": "anything", "wing": "homelab", "hall": "facts"}) }) require.Len(t, recs, 1) r := recs[0] assert.Equal(t, "brain_query", r["tool"]) assert.Equal(t, "local-cli", r["principal"]) assert.Equal(t, "static", r["origin"]) assert.Equal(t, "homelab", r["wing"]) assert.Equal(t, "facts", r["hall"]) assert.Equal(t, "ok", r["outcome"]) assert.Contains(t, r, "duration_ms") } func TestToolsCallWithoutDerivablePrincipalLogsOriginUnknown(t *testing.T) { srv, _, _, _ := govServer(t) recs := captureToolCallLog(t, func() { authCall(t, srv, "", "brain_query", map[string]any{"query": "x"}) }) require.Len(t, recs, 1) assert.Equal(t, "unknown", recs[0]["origin"]) assert.Equal(t, "", recs[0]["principal"]) } func TestToolsCallLogsErrorOutcome(t *testing.T) { srv, _, _, _ := govServer(t) recs := captureToolCallLog(t, func() { authCall(t, srv, "Bearer "+capStaticTok, "brain_query", map[string]any{}) }) require.Len(t, recs, 1) assert.Equal(t, "error", recs[0]["outcome"]) } // The audit line must not become a second copy of brain content. func TestToolsCallLogOmitsContentButRecordsItsSize(t *testing.T) { const sentinel = "SENTINEL-BRAIN-CONTENT-91c2" srv, _, _, _ := govServer(t) recs := captureToolCallLog(t, func() { authCall(t, srv, "Bearer "+capStaticTok, "brain_write", map[string]any{ "content": "# t\n\n" + sentinel, "wing": "testing", "hall": "facts", "filename": "audit-sentinel", }) }) require.Len(t, recs, 1) raw, _ := json.Marshal(recs[0]) assert.NotContains(t, string(raw), sentinel) assert.EqualValues(t, len("# t\n\n"+sentinel), recs[0]["content_bytes"]) }