fix: improve integration test reliability (#13)

- Add custom types config after bd init in daemon tests
- Replace fixed sleeps with poll-based waiting in tmux tests
- Skip beads integration test for JSONL-only repos

Fixes flaky test failures in parallel execution.
This commit is contained in:
Subhrajit Makur
2026-01-08 18:55:53 +05:30
committed by Steve Yegge
parent 775af2973d
commit c8c765a239
3 changed files with 50 additions and 32 deletions

View File

@@ -127,7 +127,6 @@ func TestIntegration(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Walk up to find .beads
dir := cwd dir := cwd
for { for {
if _, err := os.Stat(filepath.Join(dir, ".beads")); err == nil { if _, err := os.Stat(filepath.Join(dir, ".beads")); err == nil {
@@ -140,6 +139,11 @@ func TestIntegration(t *testing.T) {
dir = parent dir = parent
} }
dbPath := filepath.Join(dir, ".beads", "beads.db")
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
t.Skip("no beads.db found (JSONL-only repo)")
}
b := New(dir) b := New(dir)
// Sync database with JSONL before testing to avoid "Database out of sync" errors. // Sync database with JSONL before testing to avoid "Database out of sync" errors.

View File

@@ -259,25 +259,36 @@ func writeTownJSON(t *testing.T, path string, data interface{}) {
} }
} }
// testTmuxSessionWithStubAgent tests that a tmux session runs the stub agent. func pollForOutput(t *testing.T, sessionName, expected string, timeout time.Duration) (string, bool) {
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
output := captureTmuxPane(t, sessionName, 50)
if strings.Contains(output, expected) {
return output, true
}
time.Sleep(100 * time.Millisecond)
}
return captureTmuxPane(t, sessionName, 50), false
}
func testTmuxSessionWithStubAgent(t *testing.T, tmpDir, stubAgentPath, rigName string) { func testTmuxSessionWithStubAgent(t *testing.T, tmpDir, stubAgentPath, rigName string) {
t.Helper() t.Helper()
sessionName := fmt.Sprintf("gt-test-%d", time.Now().UnixNano()) sessionName := fmt.Sprintf("gt-test-pid%d-%d", os.Getpid(), time.Now().UnixNano())
workDir := tmpDir workDir := tmpDir
// Cleanup session on exit exec.Command("tmux", "kill-session", "-t", sessionName).Run()
defer func() { defer func() {
exec.Command("tmux", "kill-session", "-t", sessionName).Run() exec.Command("tmux", "kill-session", "-t", sessionName).Run()
}() }()
// Create tmux session
cmd := exec.Command("tmux", "new-session", "-d", "-s", sessionName, "-c", workDir) cmd := exec.Command("tmux", "new-session", "-d", "-s", sessionName, "-c", workDir)
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
t.Fatalf("Failed to create tmux session: %v", err) t.Fatalf("Failed to create tmux session: %v", err)
} }
// Set environment variables
envVars := map[string]string{ envVars := map[string]string{
"GT_ROLE": "polecat", "GT_ROLE": "polecat",
"GT_POLECAT": "test-polecat", "GT_POLECAT": "test-polecat",
@@ -291,42 +302,39 @@ func testTmuxSessionWithStubAgent(t *testing.T, tmpDir, stubAgentPath, rigName s
} }
} }
// Send the stub agent command
agentCmd := fmt.Sprintf("%s --test-mode --stub", stubAgentPath) agentCmd := fmt.Sprintf("%s --test-mode --stub", stubAgentPath)
cmd = exec.Command("tmux", "send-keys", "-t", sessionName, agentCmd, "Enter") cmd = exec.Command("tmux", "send-keys", "-t", sessionName, agentCmd, "Enter")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
t.Fatalf("Failed to send keys: %v", err) t.Fatalf("Failed to send keys: %v", err)
} }
output, started := waitForTmuxOutputContains(t, sessionName, "STUB_AGENT_STARTED", 12*time.Second) output, found := pollForOutput(t, sessionName, "STUB_AGENT_STARTED", 12*time.Second)
if !started { if !found {
t.Skipf("stub agent output not detected; tmux capture unreliable. Output:\n%s", output) t.Skipf("stub agent output not detected; tmux capture unreliable. Output:
%s", output)
} }
// Verify environment variables were visible to agent
if !strings.Contains(output, "GT_ROLE: polecat") { if !strings.Contains(output, "GT_ROLE: polecat") {
t.Logf("Warning: GT_ROLE not visible in agent output (tmux env may not propagate to subshell)") t.Logf("Warning: GT_ROLE not visible in agent output (tmux env may not propagate to subshell)")
} }
// Send a question and verify response
cmd = exec.Command("tmux", "send-keys", "-t", sessionName, "ping", "Enter") cmd = exec.Command("tmux", "send-keys", "-t", sessionName, "ping", "Enter")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
t.Fatalf("Failed to send ping: %v", err) t.Fatalf("Failed to send ping: %v", err)
} }
output, pong := waitForTmuxOutputContains(t, sessionName, "STUB_AGENT_ANSWER: pong", 6*time.Second) output, found = pollForOutput(t, sessionName, "STUB_AGENT_ANSWER: pong", 6*time.Second)
if !pong { if !found {
t.Errorf("Expected 'pong' response, got:\n%s", output) t.Errorf("Expected 'pong' response, got:\n%s", output)
} }
// Send exit command
cmd = exec.Command("tmux", "send-keys", "-t", sessionName, "exit", "Enter") cmd = exec.Command("tmux", "send-keys", "-t", sessionName, "exit", "Enter")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
t.Logf("Warning: failed to send exit: %v", err) t.Logf("Warning: failed to send exit: %v", err)
} }
output, exited := waitForTmuxOutputContains(t, sessionName, "STUB_AGENT_EXITING", 3*time.Second) output, found = pollForOutput(t, sessionName, "STUB_AGENT_EXITING", 3*time.Second)
if !exited { if !found {
t.Logf("Note: Agent may have exited before capture. Output:\n%s", output) t.Logf("Note: Agent may have exited before capture. Output:\n%s", output)
} }

View File

@@ -28,7 +28,10 @@ func TestGetRoleConfigForIdentity_PrefersTownRoleBead(t *testing.T) {
townRoot := t.TempDir() townRoot := t.TempDir()
runBd(t, townRoot, "init", "--quiet", "--prefix", "hq") runBd(t, townRoot, "init", "--quiet", "--prefix", "hq")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,slot")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,event")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,event")
// Create canonical role bead. // Create canonical role bead.
runBd(t, townRoot, "create", runBd(t, townRoot, "create",
@@ -62,7 +65,10 @@ func TestGetRoleConfigForIdentity_FallsBackToLegacyRoleBead(t *testing.T) {
townRoot := t.TempDir() townRoot := t.TempDir()
runBd(t, townRoot, "init", "--quiet", "--prefix", "gt") runBd(t, townRoot, "init", "--quiet", "--prefix", "gt")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,slot")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,event")
runBd(t, townRoot, "config", "set", "types.custom", "agent,role,rig,convoy,event")
// Only legacy role bead exists. // Only legacy role bead exists.
runBd(t, townRoot, "create", runBd(t, townRoot, "create",