Files
gridpilot.gg/core/racing/domain/value-objects/RaceStatus.test.ts
2026-01-16 19:46:49 +01:00

82 lines
3.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { RacingDomainValidationError } from '../errors/RacingDomainError';
import { RaceStatus, type RaceStatusValue } from './RaceStatus';
describe('RaceStatus', () => {
it('creates a status and exposes stable value/toString/props', () => {
const status = RaceStatus.create('scheduled');
expect(status.value).toBe('scheduled');
expect(status.toString()).toBe('scheduled');
expect(status.props).toEqual({ value: 'scheduled' });
});
it('rejects missing value', () => {
expect(() => RaceStatus.create('' as unknown as RaceStatusValue)).toThrow(
RacingDomainValidationError,
);
expect(() => RaceStatus.create('' as unknown as RaceStatusValue)).toThrow(
'Race status is required',
);
});
it('supports lifecycle guard helpers', () => {
expect(RaceStatus.create('scheduled').canStart()).toBe(true);
expect(RaceStatus.create('running').canStart()).toBe(false);
expect(RaceStatus.create('running').canComplete()).toBe(true);
expect(RaceStatus.create('scheduled').canComplete()).toBe(false);
expect(RaceStatus.create('scheduled').canCancel()).toBe(true);
expect(RaceStatus.create('running').canCancel()).toBe(true);
expect(RaceStatus.create('completed').canCancel()).toBe(false);
expect(RaceStatus.create('completed').canReopen()).toBe(true);
expect(RaceStatus.create('cancelled').canReopen()).toBe(true);
expect(RaceStatus.create('running').canReopen()).toBe(false);
expect(RaceStatus.create('completed').isTerminal()).toBe(true);
expect(RaceStatus.create('cancelled').isTerminal()).toBe(true);
expect(RaceStatus.create('running').isTerminal()).toBe(false);
expect(RaceStatus.create('running').isRunning()).toBe(true);
expect(RaceStatus.create('completed').isCompleted()).toBe(true);
expect(RaceStatus.create('scheduled').isScheduled()).toBe(true);
expect(RaceStatus.create('cancelled').isCancelled()).toBe(true);
});
it('validates allowed transitions', () => {
const scheduled = RaceStatus.create('scheduled');
const running = RaceStatus.create('running');
const completed = RaceStatus.create('completed');
const cancelled = RaceStatus.create('cancelled');
expect(scheduled.canTransitionTo('running')).toEqual({ valid: true });
expect(scheduled.canTransitionTo('cancelled')).toEqual({ valid: true });
expect(running.canTransitionTo('completed')).toEqual({ valid: true });
expect(running.canTransitionTo('cancelled')).toEqual({ valid: true });
expect(completed.canTransitionTo('scheduled')).toEqual({ valid: true });
expect(cancelled.canTransitionTo('scheduled')).toEqual({ valid: true });
});
it('rejects disallowed transitions with a helpful error', () => {
const scheduled = RaceStatus.create('scheduled');
const result = scheduled.canTransitionTo('completed');
expect(result.valid).toBe(false);
expect(result.error).toBe('Cannot transition from scheduled to completed');
});
it('equals compares by value', () => {
const a = RaceStatus.create('scheduled');
const b = RaceStatus.create('scheduled');
const c = RaceStatus.create('running');
expect(a.equals(b)).toBe(true);
expect(a.equals(c)).toBe(false);
});
it('only allowed status values compile (type-level)', () => {
const allowed: RaceStatusValue[] = ['scheduled', 'running', 'completed', 'cancelled'];
expect(allowed).toHaveLength(4);
});
});