package approval import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestNewApprovalGateManager(t *testing.T) { manager := NewApprovalGateManager() assert.NotNil(t, manager) assert.Equal(t, 0, manager.stats.TotalGates) } func TestCreateGate(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) assert.NotNil(t, gate) assert.Equal(t, "T0.1", gate.TaskID) assert.Equal(t, StatusPending, gate.Decision.Status) } func TestApproveGate(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) err := manager.ApproveGate(gate.ID, "reviewer-1", "Approved") assert.NoError(t, err) assert.True(t, manager.IsApproved(gate.ID)) } func TestRejectGate(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) err := manager.RejectGate(gate.ID, "reviewer-1", "Rejected") assert.NoError(t, err) assert.True(t, manager.IsRejected(gate.ID)) } func TestGetPendingGates(t *testing.T) { manager := NewApprovalGateManager() gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) gate2 := manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour) manager.ApproveGate(gate1.ID, "reviewer-1", "") pending := manager.GetPendingGates() assert.Equal(t, 1, len(pending)) if len(pending) > 0 { // Don't rely on map iteration order - just check it's the unapproved gate assert.Equal(t, StatusPending, pending[0].Decision.Status) assert.Equal(t, gate2.ID, pending[0].ID) // gate2 is the only pending one } } func TestGetGatesByTaskFiltering(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) gates := manager.GetGatesByTask("T0.1") assert.Equal(t, 1, len(gates)) assert.Equal(t, gate.ID, gates[0].ID) } func TestGetGatesByWorkflowFiltering(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) gates := manager.GetGatesByWorkflow("workflow-1") assert.Equal(t, 1, len(gates)) assert.Equal(t, gate.ID, gates[0].ID) } func TestIsApproved(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) assert.False(t, manager.IsApproved(gate.ID)) manager.ApproveGate(gate.ID, "reviewer-1", "") assert.True(t, manager.IsApproved(gate.ID)) } func TestIsRejected(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) assert.False(t, manager.IsRejected(gate.ID)) manager.RejectGate(gate.ID, "reviewer-1", "") assert.True(t, manager.IsRejected(gate.ID)) } func TestIsPending(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) assert.True(t, manager.IsPending(gate.ID)) manager.ApproveGate(gate.ID, "reviewer-1", "") assert.False(t, manager.IsPending(gate.ID)) } func TestGetStats(t *testing.T) { manager := NewApprovalGateManager() gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) _ = manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour) manager.ApproveGate(gate1.ID, "reviewer-1", "") stats := manager.GetStats() assert.Equal(t, 2, stats.TotalGates) assert.Equal(t, 1, stats.ApprovedGates) assert.Equal(t, 1, stats.PendingGates) } func TestGetHistory(t *testing.T) { manager := NewApprovalGateManager() gate1 := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) gate2 := manager.CreateGate("T0.2", "workflow-1", "Review", 24*time.Hour) manager.ApproveGate(gate1.ID, "reviewer-1", "") manager.RejectGate(gate2.ID, "reviewer-2", "Needs work") history := manager.GetHistory() assert.Greater(t, len(history), 0) } func TestClear(t *testing.T) { manager := NewApprovalGateManager() manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) assert.Equal(t, 1, manager.stats.TotalGates) manager.Clear() assert.Equal(t, 0, manager.stats.TotalGates) } func TestExpiredGate(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 1*time.Millisecond) time.Sleep(10 * time.Millisecond) err := manager.ApproveGate(gate.ID, "reviewer-1", "") assert.Error(t, err) } func TestMultipleApprovals(t *testing.T) { manager := NewApprovalGateManager() gate := manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) gate.RequiredApprovals = 2 err := manager.ApproveGate(gate.ID, "reviewer-1", "") assert.NoError(t, err) retrieved, _ := manager.GetGate(gate.ID) assert.Equal(t, 1, len(retrieved.Approvals)) } func BenchmarkCreateGate(b *testing.B) { manager := NewApprovalGateManager() for i := 0; i < b.N; i++ { manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) } } func BenchmarkApproveGate(b *testing.B) { manager := NewApprovalGateManager() gates := make([]*ApprovalGate, b.N) for i := 0; i < b.N; i++ { gates[i] = manager.CreateGate("T0.1", "workflow-1", "Review", 24*time.Hour) } b.ResetTimer() for i := 0; i < b.N; i++ { manager.ApproveGate(gates[i].ID, "reviewer-1", "") } }