package database_test // stuck_command_retry_test.go — Tests for stuck command retry limit. // // F-B2-10 FIXED: retry_count column added (migration 029). // GetStuckCommands filters retry_count < 5. // RedeliverStuckCommandTx increments retry_count on re-delivery (DEV-029 fix). import ( "os" "path/filepath" "strings" "testing" ) func TestStuckCommandHasNoMaxRetryCount(t *testing.T) { // POST-FIX: retry_count column exists, filter in query, and increment wired. migrationsDir := filepath.Join("migrations") files, err := os.ReadDir(migrationsDir) if err != nil { t.Fatalf("failed to read migrations directory: %v", err) } hasRetryCount := false for _, f := range files { if !strings.HasSuffix(f.Name(), ".up.sql") { continue } content, err := os.ReadFile(filepath.Join(migrationsDir, f.Name())) if err != nil { continue } src := strings.ToLower(string(content)) if strings.Contains(src, "agent_commands") && strings.Contains(src, "retry_count") { hasRetryCount = true } } if !hasRetryCount { t.Error("[ERROR] [server] [database] F-B2-10 NOT FIXED: no retry_count column") } // Check GetStuckCommands for retry limit cmdPath := filepath.Join("queries", "commands.go") content, err := os.ReadFile(cmdPath) if err != nil { t.Fatalf("failed to read commands.go: %v", err) } src := string(content) // Verify filter exists in GetStuckCommands stuckIdx := strings.Index(src, "func (q *CommandQueries) GetStuckCommands") if stuckIdx == -1 { t.Fatal("[ERROR] [server] [database] GetStuckCommands function not found") } stuckBody := src[stuckIdx:] if len(stuckBody) > 500 { stuckBody = stuckBody[:500] } if !strings.Contains(strings.ToLower(stuckBody), "retry_count") { t.Error("[ERROR] [server] [database] F-B2-10 NOT FIXED: GetStuckCommands has no retry filter") } // DEV-029: Verify RedeliverStuckCommandTx increments retry_count if !strings.Contains(src, "RedeliverStuckCommandTx") { t.Error("[ERROR] [server] [database] DEV-029 NOT FIXED: no RedeliverStuckCommandTx function") } redeliverIdx := strings.Index(src, "func (q *CommandQueries) RedeliverStuckCommandTx") if redeliverIdx != -1 { redeliverBody := src[redeliverIdx:] if len(redeliverBody) > 300 { redeliverBody = redeliverBody[:300] } if !strings.Contains(redeliverBody, "retry_count = retry_count + 1") { t.Error("[ERROR] [server] [database] DEV-029 NOT FIXED: RedeliverStuckCommandTx does not increment retry_count") } } t.Log("[INFO] [server] [database] F-B2-10 + DEV-029 FIXED: retry count wired end-to-end") } func TestStuckCommandHasMaxRetryCount(t *testing.T) { // Verify: column exists, filter in query, and increment function exists cmdPath := filepath.Join("queries", "commands.go") content, err := os.ReadFile(cmdPath) if err != nil { t.Fatalf("failed to read commands.go: %v", err) } src := string(content) // Must have RedeliverStuckCommandTx with retry_count increment if !strings.Contains(src, "retry_count = retry_count + 1") { t.Errorf("[ERROR] [server] [database] retry_count is never incremented.\n" + "DEV-029: RedeliverStuckCommandTx must increment retry_count on re-delivery.") } // Must have retry_count < 5 filter if !strings.Contains(src, "retry_count < 5") { t.Errorf("[ERROR] [server] [database] no retry_count < 5 filter in stuck command queries") } t.Log("[INFO] [server] [database] F-B2-10 + DEV-029 FIXED: retry count capped at 5") }