fix(issue-301): gate client actions from canonical phase flags
This commit is contained in:
@@ -50,11 +50,11 @@ function sessionDetailPayload(status: string, options?: { roundQuestionId?: numb
|
||||
host: {
|
||||
can_start_round: status === 'lobby',
|
||||
can_show_question: status === 'lie',
|
||||
can_mix_answers: status === 'lie',
|
||||
can_mix_answers: status === 'lie' || status === 'guess',
|
||||
can_calculate_scores: status === 'guess',
|
||||
can_reveal_scoreboard: status === 'reveal',
|
||||
can_start_next_round: status === 'scoreboard',
|
||||
can_finish_game: status === 'scoreboard',
|
||||
can_start_next_round: status === 'reveal',
|
||||
can_finish_game: status === 'reveal',
|
||||
},
|
||||
player: {
|
||||
can_join: status === 'lobby',
|
||||
@@ -116,7 +116,7 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
expect(component.loading).toBe(false);
|
||||
});
|
||||
|
||||
it('wires showQuestion, mixAnswers and calculateScores with expected request payloads', async () => {
|
||||
it('wires showQuestion, mixAnswers and calculateScores with canonical phase gating', async () => {
|
||||
const fetchMock: FetchMock = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
@@ -156,12 +156,16 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
component.sessionCode = ' abcd12 ';
|
||||
component.roundQuestionId = ' 77 ';
|
||||
|
||||
component.session = sessionDetailPayload('lie', { roundQuestionId: null }) as any;
|
||||
await component.showQuestion();
|
||||
|
||||
component.session = sessionDetailPayload('guess', { roundQuestionId: 77 }) as any;
|
||||
await component.mixAnswers();
|
||||
await component.calculateScores();
|
||||
|
||||
expect(component.error).toBe('');
|
||||
expect(component.loading).toBe(false);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(6);
|
||||
});
|
||||
|
||||
it('runs next-round transition without reload and clears scoreboard payload', async () => {
|
||||
@@ -245,27 +249,44 @@ describe('HostShellComponent gameplay wiring', () => {
|
||||
expect(component.finishError).toContain('Session code is required');
|
||||
});
|
||||
|
||||
it('blocks illegal host actions outside canonical reveal/scoreboard boundaries', async () => {
|
||||
it('blocks illegal host actions outside canonical phase permissions', async () => {
|
||||
const fetchMock: FetchMock = vi.fn();
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
|
||||
const component = new HostShellComponent();
|
||||
component.sessionCode = 'ABCD12';
|
||||
component.roundQuestionId = '77';
|
||||
|
||||
for (const status of ['lie', 'guess', 'reveal'] as const) {
|
||||
component.session = sessionDetailPayload(status) as any;
|
||||
for (const status of ['guess', 'reveal', 'scoreboard'] as const) {
|
||||
component.session = sessionDetailPayload(status, { roundQuestionId: 77 }) as any;
|
||||
await component.showQuestion();
|
||||
}
|
||||
|
||||
for (const status of ['lie', 'reveal', 'scoreboard'] as const) {
|
||||
component.session = sessionDetailPayload(status, { roundQuestionId: 77 }) as any;
|
||||
await component.calculateScores();
|
||||
}
|
||||
|
||||
for (const status of ['lie', 'guess', 'scoreboard'] as const) {
|
||||
component.session = sessionDetailPayload(status, { roundQuestionId: 77 }) as any;
|
||||
await component.loadScoreboard();
|
||||
await component.startNextRound();
|
||||
await component.finishGame();
|
||||
}
|
||||
|
||||
component.session = sessionDetailPayload('reveal') as any;
|
||||
component.session = sessionDetailPayload('guess', { roundQuestionId: 77 }) as any;
|
||||
expect(component.canShowQuestion).toBe(false);
|
||||
|
||||
component.session = sessionDetailPayload('reveal', { roundQuestionId: 77 }) as any;
|
||||
expect(component.canCalculateScores).toBe(false);
|
||||
expect(component.canLoadScoreboard).toBe(true);
|
||||
expect(component.canStartNextRound).toBe(true);
|
||||
expect(component.canFinishGame).toBe(true);
|
||||
|
||||
component.session = sessionDetailPayload('scoreboard', { roundQuestionId: 77 }) as any;
|
||||
expect(component.canLoadScoreboard).toBe(false);
|
||||
expect(component.canStartNextRound).toBe(false);
|
||||
expect(component.canFinishGame).toBe(false);
|
||||
|
||||
component.session = sessionDetailPayload('scoreboard') as any;
|
||||
await component.loadScoreboard();
|
||||
|
||||
expect(component.canLoadScoreboard).toBe(false);
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user