Files
gridpilot.gg/tests/domain/StepTransitionValidator.spec.ts
2025-12-15 18:34:20 +01:00

231 lines
7.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { StepTransitionValidator } from '@gridpilot/automation/domain/services/StepTransitionValidator';
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
import { SessionState } from '@gridpilot/automation/domain/value-objects/SessionState';
describe('StepTransitionValidator Service', () => {
describe('canTransition', () => {
it('should allow sequential forward transition', () => {
const currentStep = StepId.create(1);
const nextStep = StepId.create(2);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(true);
expect(result.error).toBeUndefined();
});
it('should reject transition when not IN_PROGRESS', () => {
const currentStep = StepId.create(1);
const nextStep = StepId.create(2);
const state = SessionState.create('PENDING');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Session must be in progress to transition steps');
});
it('should reject skipping steps', () => {
const currentStep = StepId.create(1);
const nextStep = StepId.create(3);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Cannot skip steps - must progress sequentially');
});
it('should reject backward transitions', () => {
const currentStep = StepId.create(5);
const nextStep = StepId.create(4);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Cannot move backward - steps must progress forward only');
});
it('should reject same step transition', () => {
const currentStep = StepId.create(5);
const nextStep = StepId.create(5);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Already at this step');
});
it('should allow transition through modal steps', () => {
const currentStep = StepId.create(5);
const nextStep = StepId.create(6);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(true);
});
it('should allow transition from modal step to next', () => {
const currentStep = StepId.create(6);
const nextStep = StepId.create(7);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(true);
});
});
describe('validateModalStepTransition', () => {
it('should allow entering modal step 6', () => {
const currentStep = StepId.create(5);
const nextStep = StepId.create(6);
const result = StepTransitionValidator.validateModalStepTransition(currentStep, nextStep);
expect(result.isValid).toBe(true);
});
it('should allow entering modal step 9', () => {
const currentStep = StepId.create(8);
const nextStep = StepId.create(9);
const result = StepTransitionValidator.validateModalStepTransition(currentStep, nextStep);
expect(result.isValid).toBe(true);
});
it('should allow entering modal step 12', () => {
const currentStep = StepId.create(11);
const nextStep = StepId.create(12);
const result = StepTransitionValidator.validateModalStepTransition(currentStep, nextStep);
expect(result.isValid).toBe(true);
});
it('should allow exiting modal step 6', () => {
const currentStep = StepId.create(6);
const nextStep = StepId.create(7);
const result = StepTransitionValidator.validateModalStepTransition(currentStep, nextStep);
expect(result.isValid).toBe(true);
});
it('should allow non-modal transitions', () => {
const currentStep = StepId.create(1);
const nextStep = StepId.create(2);
const result = StepTransitionValidator.validateModalStepTransition(currentStep, nextStep);
expect(result.isValid).toBe(true);
});
});
describe('shouldStopAtStep18', () => {
it('should return true when transitioning to step 17 (final step)', () => {
const nextStep = StepId.create(17);
const shouldStop = StepTransitionValidator.shouldStopAtStep18(nextStep);
expect(shouldStop).toBe(true);
});
it('should return false for steps before 17', () => {
const nextStep = StepId.create(16);
const shouldStop = StepTransitionValidator.shouldStopAtStep18(nextStep);
expect(shouldStop).toBe(false);
});
it('should return false for early steps', () => {
const nextStep = StepId.create(1);
const shouldStop = StepTransitionValidator.shouldStopAtStep18(nextStep);
expect(shouldStop).toBe(false);
});
});
describe('getStepDescription', () => {
it('should return description for step 1', () => {
const step = StepId.create(1);
const description = StepTransitionValidator.getStepDescription(step);
expect(description).toBe('Navigate to Hosted Racing page');
});
it('should return description for step 6 (modal)', () => {
const step = StepId.create(6);
const description = StepTransitionValidator.getStepDescription(step);
expect(description).toBe('Add Admin (Modal)');
});
it('should return description for step 17 (final)', () => {
const step = StepId.create(17);
const description = StepTransitionValidator.getStepDescription(step);
expect(description).toBe('Track Conditions (STOP - Manual Submit Required)');
});
it('should return descriptions for all modal steps', () => {
const modalSteps = [6, 9, 12];
modalSteps.forEach(stepNum => {
const step = StepId.create(stepNum);
const description = StepTransitionValidator.getStepDescription(step);
expect(description).toContain('(Modal)');
});
});
});
describe('edge cases', () => {
it('should handle rapid sequential transitions', () => {
const state = SessionState.create('IN_PROGRESS');
let currentStep = StepId.create(1);
for (let i = 2; i <= 17; i++) {
const nextStep = StepId.create(i);
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(true);
currentStep = nextStep;
}
});
it('should prevent transitions from terminal states', () => {
const terminalStates = ['COMPLETED', 'FAILED', 'STOPPED_AT_STEP_18'] as const;
terminalStates.forEach(stateValue => {
const currentStep = StepId.create(10);
const nextStep = StepId.create(11);
const state = SessionState.create(stateValue);
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(false);
});
});
it('should allow transition from PAUSED when resumed', () => {
const currentStep = StepId.create(5);
const nextStep = StepId.create(6);
const state = SessionState.create('IN_PROGRESS');
const result = StepTransitionValidator.canTransition(currentStep, nextStep, state);
expect(result.isValid).toBe(true);
});
});
});