//go:build integration // mcp_integration_test.go — through-stack integration matrix for the MCP module // (spec §8.2, 14 scenarios). // // SCOPE: This file lays out the matrix of 14 scenarios as a scaffold + sub-tests. // The suite wiring, helpers, and actual test implementations will be added in K2-K5. // // Coverage map (scenario ↔ implementation batch): // // Scenario Batch Description // ----------------------------- ------ ------------------------------------------ // StreamableHTTP_FullFlow K2 StreamableHTTP transport end-to-end // LegacySSE_FullFlow K2 SSE transport end-to-end // BearerVsQueryToken K2 Auth via header vs query param // MCPSDKVersionNegotiation K2 Protocol version negotiation // ScopeViolation_Tool K3 Token scope restricts tool access // ScopeViolation_Project K3 Token scope restricts project access // VisibilityRespect K3 Project visibility enforced // WriteToolAudit K3 Write tools produce audit records // RateLimit_PerToken K4 Per-token rate limiting // ACPSession_TokenLifecycle K4 ACP session tokens created/revoked // StartupReaper K4 Expired tokens cleaned on startup // PromptsList_FromTemplates K5 Prompts listed from templates // ResourceRead_NotFound K5 Resource read returns not-found // ResourceList_ScopeFilter K5 Resources filtered by scope // // Build/run: // go test -count=1 -tags=integration ./internal/app/... -run TestMCPIntegration package app_test import ( "testing" ) func TestMCPIntegration_Matrix(t *testing.T) { if testing.Short() { t.Skip("integration; needs docker") } suite := newMCPIntegrationSuite(t) t.Cleanup(suite.cleanup) t.Run("StreamableHTTP_FullFlow", suite.testStreamableHTTPFullFlow) t.Run("LegacySSE_FullFlow", suite.testLegacySSEFullFlow) t.Run("ScopeViolation_Tool", suite.testScopeViolationTool) t.Run("ScopeViolation_Project", suite.testScopeViolationProject) t.Run("VisibilityRespect", suite.testVisibilityRespect) t.Run("WriteToolAudit", suite.testWriteToolAudit) t.Run("RateLimit_PerToken", suite.testRateLimitPerToken) t.Run("ACPSession_TokenLifecycle", suite.testACPSessionTokenLifecycle) t.Run("StartupReaper", suite.testStartupReaper) t.Run("PromptsList_FromTemplates", suite.testPromptsListFromTemplates) t.Run("ResourceRead_NotFound", suite.testResourceReadNotFound) t.Run("ResourceList_ScopeFilter", suite.testResourceListScopeFilter) t.Run("BearerVsQueryToken", suite.testBearerVsQueryToken) t.Run("MCPSDKVersionNegotiation", suite.testMCPSDKVersionNegotiation) } type mcpIntegrationSuite struct { // Will be filled in K2-K5 cleanup func() } func newMCPIntegrationSuite(t *testing.T) *mcpIntegrationSuite { t.Helper() // Minimal scaffold — actual wiring in K2-K5 return &mcpIntegrationSuite{ cleanup: func() {}, } } // All 14 stubs — each skips until its implementation batch fills it in. func (s *mcpIntegrationSuite) testStreamableHTTPFullFlow(t *testing.T) { t.Skip("filled in K2") } func (s *mcpIntegrationSuite) testLegacySSEFullFlow(t *testing.T) { t.Skip("filled in K2") } func (s *mcpIntegrationSuite) testScopeViolationTool(t *testing.T) { t.Skip("filled in K3") } func (s *mcpIntegrationSuite) testScopeViolationProject(t *testing.T) { t.Skip("filled in K3") } func (s *mcpIntegrationSuite) testVisibilityRespect(t *testing.T) { t.Skip("filled in K3") } func (s *mcpIntegrationSuite) testWriteToolAudit(t *testing.T) { t.Skip("filled in K3") } func (s *mcpIntegrationSuite) testRateLimitPerToken(t *testing.T) { t.Skip("filled in K4") } func (s *mcpIntegrationSuite) testACPSessionTokenLifecycle(t *testing.T) { t.Skip("filled in K4") } func (s *mcpIntegrationSuite) testStartupReaper(t *testing.T) { t.Skip("filled in K4") } func (s *mcpIntegrationSuite) testPromptsListFromTemplates(t *testing.T) { t.Skip("filled in K5") } func (s *mcpIntegrationSuite) testResourceReadNotFound(t *testing.T) { t.Skip("filled in K5") } func (s *mcpIntegrationSuite) testResourceListScopeFilter(t *testing.T) { t.Skip("filled in K5") } func (s *mcpIntegrationSuite) testBearerVsQueryToken(t *testing.T) { t.Skip("filled in K2") } func (s *mcpIntegrationSuite) testMCPSDKVersionNegotiation(t *testing.T) { t.Skip("filled in K2") }