Spaces:
Running
Running
| import { describe, it, expect } from "vitest"; | |
| import { debateReducer, createInitialDebateState } from "./engine"; | |
| import type { DebateConfig, DebateTopic, RoundData, DebateVerdict } from "./types"; | |
| const mockConfig: DebateConfig = { durationMinutes: 3, rounds: 4, timePerTurn: 45 }; | |
| const mockTopic: DebateTopic = { | |
| id: "t1", | |
| topic: "Climate Policy", | |
| question: "Should carbon taxes be mandatory?", | |
| category: "environment", | |
| heatLevel: 4, | |
| }; | |
| describe("debateReducer", () => { | |
| it("starts in idle phase", () => { | |
| const state = createInitialDebateState(); | |
| expect(state.phase).toBe("idle"); | |
| expect(state.rounds).toEqual([]); | |
| expect(state.currentRound).toBe(0); | |
| }); | |
| it("transitions to config phase on SET_CONFIG", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| expect(state.phase).toBe("config"); | |
| expect(state.config).toEqual(mockConfig); | |
| }); | |
| it("transitions to topic-select on SET_TOPICS", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| expect(state.phase).toBe("topic-select"); | |
| expect(state.topicOptions).toHaveLength(1); | |
| }); | |
| it("records vote on VOTE_TOPIC", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| state = debateReducer(state, { type: "VOTE_TOPIC", topicId: "t1" }); | |
| expect(state.myVote).toBe("t1"); | |
| }); | |
| it("transitions to round phase on SELECT_TOPIC", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| state = debateReducer(state, { type: "SELECT_TOPIC", topic: mockTopic }); | |
| expect(state.phase).toBe("round-open"); | |
| expect(state.selectedTopic).toEqual(mockTopic); | |
| expect(state.currentRound).toBe(1); | |
| }); | |
| it("tracks argument text on UPDATE_ARGUMENT", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| state = debateReducer(state, { type: "SELECT_TOPIC", topic: mockTopic }); | |
| state = debateReducer(state, { type: "UPDATE_ARGUMENT", text: "My argument" }); | |
| expect(state.myArgument).toBe("My argument"); | |
| }); | |
| it("transitions to commentary on COMPLETE_ROUND", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| state = debateReducer(state, { type: "SELECT_TOPIC", topic: mockTopic }); | |
| const round: RoundData = { | |
| roundNumber: 1, | |
| openerId: "p1", | |
| openerArgument: "arg1", | |
| rebuttalArgument: "arg2", | |
| openerFactCheck: [], | |
| rebuttalFactCheck: [], | |
| commentary: null, | |
| }; | |
| state = debateReducer(state, { type: "COMPLETE_ROUND", round }); | |
| expect(state.phase).toBe("commentary"); | |
| expect(state.rounds).toHaveLength(1); | |
| }); | |
| it("advances round on START_ROUND after complete", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "SET_TOPICS", topics: [mockTopic] }); | |
| state = debateReducer(state, { type: "SELECT_TOPIC", topic: mockTopic }); | |
| const round: RoundData = { | |
| roundNumber: 1, | |
| openerId: "p1", | |
| openerArgument: "arg1", | |
| rebuttalArgument: "arg2", | |
| openerFactCheck: [], | |
| rebuttalFactCheck: [], | |
| commentary: null, | |
| }; | |
| state = debateReducer(state, { type: "COMPLETE_ROUND", round }); | |
| state = debateReducer(state, { type: "START_ROUND" }); | |
| expect(state.phase).toBe("round-open"); | |
| expect(state.currentRound).toBe(2); | |
| expect(state.myArgument).toBe(""); | |
| }); | |
| it("transitions to results on SET_VERDICT", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| const verdict: DebateVerdict = { | |
| winnerId: "p1", | |
| margin: "clear", | |
| player1Score: 78, | |
| player2Score: 55, | |
| roundVerdicts: [ | |
| { round: 1, edge: "player1", highlight: "Player 1 framed the issue effectively." }, | |
| ], | |
| turningPoint: "Player 1's rebuttal in round 1 shifted the debate.", | |
| analysis: "Player 1 presented stronger logical arguments.", | |
| }; | |
| state = debateReducer(state, { type: "SET_VERDICT", verdict, eloChange: 16 }); | |
| expect(state.phase).toBe("verdict"); | |
| expect(state.verdict).toEqual(verdict); | |
| expect(state.eloChange).toBe(16); | |
| }); | |
| it("resets to idle on RESET", () => { | |
| let state = createInitialDebateState(); | |
| state = debateReducer(state, { type: "SET_CONFIG", config: mockConfig }); | |
| state = debateReducer(state, { type: "RESET" }); | |
| expect(state.phase).toBe("idle"); | |
| expect(state.config).toBeNull(); | |
| expect(state.rounds).toEqual([]); | |
| }); | |
| }); | |