import { describe, it, beforeEach, afterEach } from "node:test"; import assert from "node:assert"; import { initTurnOrder, checkTurn, onSpeakerDone, advanceTurn, resetTurn, getTurnDebugInfo, onNewMessage } from "../plugin/turn-manager.ts"; import { setChannelShuffling, getChannelShuffling } from "../plugin/core/channel-modes.ts"; describe("Shuffle Mode Tests", () => { const channelId = "test-channel"; beforeEach(() => { resetTurn(channelId); }); afterEach(() => { resetTurn(channelId); }); describe("/turn-shuffling command functionality", () => { it("should enable shuffle mode", () => { setChannelShuffling(channelId, true); assert.strictEqual(getChannelShuffling(channelId), true); }); it("should disable shuffle mode", () => { setChannelShuffling(channelId, false); assert.strictEqual(getChannelShuffling(channelId), false); }); it("should start with shuffle mode disabled by default", () => { assert.strictEqual(getChannelShuffling(channelId), false); }); }); describe("shuffle mode behavior", () => { it("should not reshuffle when shuffling is disabled", () => { const botIds = ["agent-a", "agent-b", "agent-c"]; initTurnOrder(channelId, botIds); // Disable shuffling (should be default anyway) setChannelShuffling(channelId, false); // Simulate a full cycle without reshuffling const initialOrder = getTurnDebugInfo(channelId).turnOrder as string[]; const firstSpeaker = initialOrder[0]; // Have first speaker finish their turn onSpeakerDone(channelId, firstSpeaker, false); // Check that the order didn't change (since shuffling is disabled) const orderAfterOneTurn = getTurnDebugInfo(channelId).turnOrder as string[]; // The order should remain the same when shuffling is disabled assert.deepStrictEqual(initialOrder, orderAfterOneTurn); }); it("should reshuffle when shuffling is enabled after a full cycle", () => { const botIds = ["agent-a", "agent-b", "agent-c"]; initTurnOrder(channelId, botIds); // Enable shuffling setChannelShuffling(channelId, true); // Get initial order const initialOrder = getTurnDebugInfo(channelId).turnOrder as string[]; const firstSpeaker = initialOrder[0]; // Complete a full cycle by having each agent speak once for (const agent of initialOrder) { const turnResult = checkTurn(channelId, agent); if (turnResult.allowed) { onSpeakerDone(channelId, agent, false); } } // After a full cycle, the order should have potentially changed if shuffling is enabled const orderAfterCycle = getTurnDebugInfo(channelId).turnOrder as string[]; // The order might be different due to shuffling, or it might be the same by chance // But the important thing is that the shuffling mechanism was called assert(Array.isArray(orderAfterCycle)); assert.strictEqual(orderAfterCycle.length, 3); }); it("should ensure last speaker doesn't become first in next round when shuffling", () => { const botIds = ["agent-a", "agent-b"]; initTurnOrder(channelId, botIds); // Enable shuffling setChannelShuffling(channelId, true); // Get initial order const initialOrder = getTurnDebugInfo(channelId).turnOrder as string[]; assert.strictEqual(initialOrder.length, 2); // Have first agent speak const firstSpeaker = initialOrder[0]; const secondSpeaker = initialOrder[1]; // Have first speaker finish onSpeakerDone(channelId, firstSpeaker, false); // Have second speaker finish (completing a full cycle) onSpeakerDone(channelId, secondSpeaker, false); // The turn order should be reshuffled but with constraints const orderAfterReshuffle = getTurnDebugInfo(channelId).turnOrder as string[]; // Verify the order is still valid assert.strictEqual(orderAfterReshuffle.length, 2); assert.ok(orderAfterReshuffle.includes("agent-a")); assert.ok(orderAfterReshuffle.includes("agent-b")); }); it("should handle single agent scenario gracefully", () => { const botIds = ["agent-a"]; initTurnOrder(channelId, botIds); // Enable shuffling setChannelShuffling(channelId, true); // Single agent should work fine const turnResult = checkTurn(channelId, "agent-a"); assert.strictEqual(turnResult.allowed, true); onSpeakerDone(channelId, "agent-a", false); // Should still work with single agent after reshuffle attempt const turnResultAfter = checkTurn(channelId, "agent-a"); assert.strictEqual(turnResultAfter.allowed, true); }); it("should handle double agent scenario properly", () => { const botIds = ["agent-a", "agent-b"]; initTurnOrder(channelId, botIds); // Enable shuffling setChannelShuffling(channelId, true); const initialOrder = getTurnDebugInfo(channelId).turnOrder as string[]; const firstSpeaker = initialOrder[0]; const secondSpeaker = initialOrder[1]; // Have first speaker finish onSpeakerDone(channelId, firstSpeaker, false); // Have second speaker finish (this completes a cycle) onSpeakerDone(channelId, secondSpeaker, false); // The order might be reshuffled, but it should be valid const newOrder = getTurnDebugInfo(channelId).turnOrder as string[]; assert.strictEqual(newOrder.length, 2); assert.ok(newOrder.includes("agent-a")); assert.ok(newOrder.includes("agent-b")); // Next speaker should be determined by the new order const nextSpeaker = advanceTurn(channelId); assert.ok(["agent-a", "agent-b"].includes(nextSpeaker as string)); }); }); describe("compatibility with other modes", () => { it("should work with dormant state", () => { const botIds = ["agent-a", "agent-b"]; initTurnOrder(channelId, botIds); setChannelShuffling(channelId, true); // Start with dormant state resetTurn(channelId); const dormantState = getTurnDebugInfo(channelId); assert.strictEqual(dormantState.dormant, true); // Activate with new message onNewMessage(channelId, "agent-a", false); const activeState = getTurnDebugInfo(channelId); assert.strictEqual(activeState.dormant, false); assert.ok(activeState.currentSpeaker); }); }); });