diff --git a/internal/app/mcp_integration_test.go b/internal/app/mcp_integration_test.go new file mode 100644 index 0000000..4d62f56 --- /dev/null +++ b/internal/app/mcp_integration_test.go @@ -0,0 +1,129 @@ +//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") +}