Files
Redflag/aggregator-server/internal/database/stuck_command_retry_test.go
jpetree331 f71f878a35 fix(concurrency): wire retry_count increment for stuck command re-delivery (DEV-029)
retry_count column and filter existed but counter was never
incremented. Stuck commands always had retry_count=0 and
always passed the WHERE retry_count < 5 filter, making
the cap ineffective.

Fix: Added RedeliverStuckCommandTx that sets
retry_count = retry_count + 1 on stuck->sent re-delivery.
GetCommands handler now uses MarkCommandSentTx for new
commands (retry_count stays 0) and RedeliverStuckCommandTx
for stuck re-delivery (retry_count increments).

All 77 tests pass. DEV-029 resolved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 08:16:12 -04:00

106 lines
3.4 KiB
Go

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")
}