package main import ( "bytes" "strings" "testing" ) func TestOutputContextFunction(t *testing.T) { tests := []struct { name string mcpMode bool stealthMode bool ephemeralMode bool localOnlyMode bool expectText []string rejectText []string }{ { name: "CLI Normal (non-ephemeral)", mcpMode: false, stealthMode: false, ephemeralMode: false, localOnlyMode: false, expectText: []string{"Beads Workflow Context", "bd sync", "git push"}, rejectText: []string{"bd sync --flush-only", "--from-main"}, }, { name: "CLI Normal (ephemeral)", mcpMode: false, stealthMode: false, ephemeralMode: true, localOnlyMode: false, expectText: []string{"Beads Workflow Context", "bd sync --from-main", "ephemeral branch"}, rejectText: []string{"bd sync --flush-only", "git push"}, }, { name: "CLI Stealth", mcpMode: false, stealthMode: true, ephemeralMode: false, // stealth mode overrides ephemeral detection localOnlyMode: false, expectText: []string{"Beads Workflow Context", "bd sync --flush-only"}, rejectText: []string{"git push", "git pull", "git commit", "git status", "git add"}, }, { name: "CLI Local-only (no git remote)", mcpMode: false, stealthMode: false, ephemeralMode: false, localOnlyMode: true, expectText: []string{"Beads Workflow Context", "bd sync --flush-only", "No git remote configured"}, rejectText: []string{"git push", "git pull", "--from-main"}, }, { name: "CLI Local-only overrides ephemeral", mcpMode: false, stealthMode: false, ephemeralMode: true, // ephemeral is true but local-only takes precedence localOnlyMode: true, expectText: []string{"Beads Workflow Context", "bd sync --flush-only", "No git remote configured"}, rejectText: []string{"git push", "--from-main", "ephemeral branch"}, }, { name: "CLI Stealth overrides local-only", mcpMode: false, stealthMode: true, ephemeralMode: false, localOnlyMode: true, // local-only is true but stealth takes precedence expectText: []string{"Beads Workflow Context", "bd sync --flush-only"}, rejectText: []string{"git push", "git pull", "git commit", "git status", "git add", "No git remote configured"}, }, { name: "MCP Normal (non-ephemeral)", mcpMode: true, stealthMode: false, ephemeralMode: false, localOnlyMode: false, expectText: []string{"Beads Issue Tracker Active", "bd sync", "git push"}, rejectText: []string{"bd sync --flush-only", "--from-main"}, }, { name: "MCP Normal (ephemeral)", mcpMode: true, stealthMode: false, ephemeralMode: true, localOnlyMode: false, expectText: []string{"Beads Issue Tracker Active", "bd sync --from-main", "ephemeral branch"}, rejectText: []string{"bd sync --flush-only", "git push"}, }, { name: "MCP Stealth", mcpMode: true, stealthMode: true, ephemeralMode: false, // stealth mode overrides ephemeral detection localOnlyMode: false, expectText: []string{"Beads Issue Tracker Active", "bd sync --flush-only"}, rejectText: []string{"git push", "git pull", "git commit", "git status", "git add"}, }, { name: "MCP Local-only (no git remote)", mcpMode: true, stealthMode: false, ephemeralMode: false, localOnlyMode: true, expectText: []string{"Beads Issue Tracker Active", "bd sync --flush-only"}, rejectText: []string{"git push", "git pull", "--from-main"}, }, { name: "MCP Local-only overrides ephemeral", mcpMode: true, stealthMode: false, ephemeralMode: true, // ephemeral is true but local-only takes precedence localOnlyMode: true, expectText: []string{"Beads Issue Tracker Active", "bd sync --flush-only"}, rejectText: []string{"git push", "--from-main", "ephemeral branch"}, }, { name: "MCP Stealth overrides local-only", mcpMode: true, stealthMode: true, ephemeralMode: false, localOnlyMode: true, // local-only is true but stealth takes precedence expectText: []string{"Beads Issue Tracker Active", "bd sync --flush-only"}, rejectText: []string{"git push", "git pull", "git commit", "git status", "git add"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer stubIsEphemeralBranch(tt.ephemeralMode)() defer stubIsDaemonAutoSyncing(false)() // Default: no auto-sync in tests defer stubPrimeHasGitRemote(!tt.localOnlyMode)() // localOnly = !primeHasGitRemote var buf bytes.Buffer err := outputPrimeContext(&buf, tt.mcpMode, tt.stealthMode) if err != nil { t.Fatalf("outputPrimeContext failed: %v", err) } output := buf.String() for _, expected := range tt.expectText { if !strings.Contains(output, expected) { t.Errorf("Expected text not found: %s", expected) } } for _, rejected := range tt.rejectText { if strings.Contains(output, rejected) { t.Errorf("Unexpected text found: %s", rejected) } } }) } } // stubIsEphemeralBranch temporarily replaces isEphemeralBranch // with a stub returning returnValue. // // Returns a function to restore the original isEphemeralBranch. // Usage: // // defer stubIsEphemeralBranch(true)() func stubIsEphemeralBranch(isEphem bool) func() { original := isEphemeralBranch isEphemeralBranch = func() bool { return isEphem } return func() { isEphemeralBranch = original } } // stubIsDaemonAutoSyncing temporarily replaces isDaemonAutoSyncing // with a stub returning returnValue. func stubIsDaemonAutoSyncing(isAutoSync bool) func() { original := isDaemonAutoSyncing isDaemonAutoSyncing = func() bool { return isAutoSync } return func() { isDaemonAutoSyncing = original } } // stubPrimeHasGitRemote temporarily replaces primeHasGitRemote // with a stub returning returnValue. // // Returns a function to restore the original primeHasGitRemote. // Usage: // // defer stubPrimeHasGitRemote(true)() func stubPrimeHasGitRemote(hasRemote bool) func() { original := primeHasGitRemote primeHasGitRemote = func() bool { return hasRemote } return func() { primeHasGitRemote = original } }