package swoosh import ( "bytes" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "time" ) func TestHandleTransition(t *testing.T) { // Setup executor with in-memory stores wal := &mockWAL{} snap := &mockSnapshotStore{} initialState := OrchestratorState{ Meta: struct { Version string SchemaHash string }{ Version: "1.0.0", SchemaHash: "test", }, } hash, _ := computeStateHash(initialState) initialState.StateHash = hash snapshot := Snapshot{ State: initialState, LastAppliedHLC: "0-0-0", LastAppliedIndex: 0, } executor := NewExecutor(wal, snap, nil, snapshot) // Create test proposal proposal := TransitionProposal{ CurrentStateHash: initialState.StateHash, TransitionName: "LICENSE_GRANTED", InputsHash: "test-input", Signer: "test-signer", IdemKey: "test-idem-1", HLC: "1-0-0000000000000001", WindowID: "window-1", Evidence: []string{"test-evidence"}, } body, _ := json.Marshal(proposal) req := httptest.NewRequest(http.MethodPost, "/transition", bytes.NewReader(body)) w := httptest.NewRecorder() handler := handleTransition(executor) handler(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } var response map[string]interface{} if err := json.NewDecoder(w.Body).Decode(&response); err != nil { t.Fatalf("failed to decode response: %v", err) } if success, ok := response["success"].(bool); !ok || !success { t.Errorf("expected success=true, got %v", response) } if _, ok := response["state_hash"].(string); !ok { t.Errorf("expected state_hash in response") } } func TestHandleState(t *testing.T) { wal := &mockWAL{} snap := &mockSnapshotStore{} initialState := OrchestratorState{ HLCLast: "1-0-0000000000000001", } hash, _ := computeStateHash(initialState) initialState.StateHash = hash snapshot := Snapshot{ State: initialState, LastAppliedHLC: initialState.HLCLast, LastAppliedIndex: 1, } executor := NewExecutor(wal, snap, nil, snapshot) req := httptest.NewRequest(http.MethodGet, "/state", nil) w := httptest.NewRecorder() handler := handleState(executor) handler(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } var response map[string]interface{} if err := json.NewDecoder(w.Body).Decode(&response); err != nil { t.Fatalf("failed to decode response: %v", err) } if stateHash, ok := response["state_hash"].(string); !ok || stateHash == "" { t.Errorf("expected state_hash, got %v", response) } if hlcLast, ok := response["hlc_last"].(string); !ok || hlcLast != "1-0-0000000000000001" { t.Errorf("expected hlc_last=1-0-0000000000000001, got %v", response) } } func TestHandleHealth(t *testing.T) { wal := &mockWAL{} snap := &mockSnapshotStore{} initialState := OrchestratorState{ Boot: struct { Licensed bool LicenseExpiry time.Time NodeID string }{ Licensed: true, NodeID: "test-node", }, Control: struct { Paused bool Degraded bool Recovering bool }{ Degraded: false, }, Policy: struct { Quarantined bool Rationale string }{ Quarantined: false, }, HLCLast: "5-0-0000000000000005", } hash, _ := computeStateHash(initialState) initialState.StateHash = hash snapshot := Snapshot{ State: initialState, LastAppliedHLC: "5-0-0000000000000005", LastAppliedIndex: 5, } executor := NewExecutor(wal, snap, nil, snapshot) req := httptest.NewRequest(http.MethodGet, "/health", nil) w := httptest.NewRecorder() handler := handleHealth(executor) handler(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } var response map[string]interface{} if err := json.NewDecoder(w.Body).Decode(&response); err != nil { t.Fatalf("failed to decode response: %v", err) } if licensed, ok := response["licensed"].(bool); !ok || !licensed { t.Errorf("expected licensed=true, got %v", response) } if quarantined, ok := response["quarantined"].(bool); !ok || quarantined { t.Errorf("expected quarantined=false, got %v", response) } if degraded, ok := response["degraded"].(bool); !ok || degraded { t.Errorf("expected degraded=false, got %v", response) } } func TestHandleCouncilOpportunity_NotImplemented(t *testing.T) { wal := &mockWAL{} snap := &mockSnapshotStore{} snapshot := Snapshot{State: OrchestratorState{}} executor := NewExecutor(wal, snap, nil, snapshot) payload := map[string]interface{}{ "council_id": "test-council", "roles": []string{"developer", "reviewer"}, "window_id": "window-1", "hlc": "1-0-0000000000000001", } body, _ := json.Marshal(payload) req := httptest.NewRequest(http.MethodPost, "/api/v1/opportunities/council", bytes.NewReader(body)) w := httptest.NewRecorder() handler := handleCouncilOpportunity(executor) handler(w, req) // Should return 501 Not Implemented per spec if w.Code != http.StatusNotImplemented { t.Errorf("expected status 501, got %d", w.Code) } var response map[string]interface{} if err := json.NewDecoder(w.Body).Decode(&response); err != nil { t.Fatalf("failed to decode response: %v", err) } if _, ok := response["error"].(string); !ok { t.Errorf("expected error message in response") } } func TestHandleTasks_NotImplemented(t *testing.T) { wal := &mockWAL{} snap := &mockSnapshotStore{} snapshot := Snapshot{State: OrchestratorState{}} executor := NewExecutor(wal, snap, nil, snapshot) req := httptest.NewRequest(http.MethodGet, "/api/v1/tasks", nil) w := httptest.NewRecorder() handler := handleTasks(executor) handler(w, req) // Should return 501 Not Implemented per spec if w.Code != http.StatusNotImplemented { t.Errorf("expected status 501, got %d", w.Code) } var response map[string]interface{} if err := json.NewDecoder(w.Body).Decode(&response); err != nil { t.Fatalf("failed to decode response: %v", err) } if _, ok := response["error"].(string); !ok { t.Errorf("expected error message in response") } } // Mock implementations for testing type mockWAL struct { records []WALRecord } func (m *mockWAL) Append(record WALRecord) error { m.records = append(m.records, record) return nil } func (m *mockWAL) Replay(fromIndex uint64) ([]WALRecord, error) { return []WALRecord{}, nil } func (m *mockWAL) Sync() error { return nil } func (m *mockWAL) LastIndex() uint64 { if len(m.records) == 0 { return 0 } return m.records[len(m.records)-1].Index } type mockSnapshotStore struct { latest *Snapshot } func (m *mockSnapshotStore) Save(s Snapshot) error { m.latest = &s return nil } func (m *mockSnapshotStore) LoadLatest() (Snapshot, error) { if m.latest == nil { return Snapshot{}, fmt.Errorf("no snapshot") } return *m.latest, nil }