Files
gridpilot.gg/tests/e2e/workflows/full-hosted-session.workflow.e2e.test.ts
2025-11-30 17:37:03 +01:00

153 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import {
PlaywrightAutomationAdapter,
FixtureServer,
} from 'packages/infrastructure/adapters/automation';
import { InMemorySessionRepository } from 'packages/infrastructure/repositories/InMemorySessionRepository';
import { MockAutomationEngineAdapter } from 'packages/infrastructure/adapters/automation/engine/MockAutomationEngineAdapter';
import { MockBrowserAutomationAdapter } from 'packages/infrastructure/adapters/automation/engine/MockBrowserAutomationAdapter';
import { StartAutomationSessionUseCase } from 'packages/application/use-cases/StartAutomationSessionUseCase';
import { StepId } from 'packages/domain/value-objects/StepId';
describe('Workflow hosted session end-to-end (fixture-backed)', () => {
let server: FixtureServer;
let adapter: PlaywrightAutomationAdapter;
let baseUrl: string;
beforeAll(async () => {
server = new FixtureServer();
const info = await server.start();
baseUrl = info.url;
adapter = new PlaywrightAutomationAdapter(
{
headless: true,
timeout: 10_000,
baseUrl,
mode: 'mock',
},
);
const connectResult = await adapter.connect();
expect(connectResult.success).toBe(true);
});
afterAll(async () => {
await adapter.disconnect();
await server.stop();
});
function createFixtureEngine() {
const repository = new InMemorySessionRepository();
const engine = new MockAutomationEngineAdapter(adapter, repository);
const useCase = new StartAutomationSessionUseCase(engine, adapter, repository);
return { repository, engine, useCase };
}
it('runs 118 from use case to STOPPED_AT_STEP_18', async () => {
const { repository, engine, useCase } = createFixtureEngine();
const config: any = {
sessionName: 'Fixture E2E full workflow',
trackId: 'spa',
carIds: ['dallara-f3'],
};
const dto = await useCase.execute(config);
expect(dto.state).toBe('PENDING');
expect(dto.currentStep).toBe(1);
await engine.executeStep(StepId.create(1), config);
const deadline = Date.now() + 60_000;
let finalSession = null;
// Poll repository until automation loop completes
// MockAutomationEngineAdapter drives the step orchestrator internally.
// Session should end in STOPPED_AT_STEP_18 when step 18 completes.
// eslint-disable-next-line no-constant-condition
while (true) {
const sessions = await repository.findAll();
finalSession = sessions[0] ?? null;
if (finalSession && (finalSession.state.isStoppedAtStep18() || finalSession.state.isFailed())) {
break;
}
if (Date.now() > deadline) {
throw new Error('Timed out waiting for automation workflow to complete');
}
await new Promise((resolve) => setTimeout(resolve, 250));
}
expect(finalSession).not.toBeNull();
expect(finalSession!.state.isStoppedAtStep18()).toBe(true);
expect(finalSession!.currentStep.value).toBe(18);
expect(finalSession!.startedAt).toBeInstanceOf(Date);
expect(finalSession!.completedAt).toBeInstanceOf(Date);
expect(finalSession!.errorMessage).toBeUndefined();
});
it('marks session as FAILED on mid-flow automation error with diagnostics', async () => {
const repository = new InMemorySessionRepository();
const failingAdapter = new MockBrowserAutomationAdapter({
simulateFailures: true,
failureRate: 1.0,
});
await failingAdapter.connect();
const engine = new MockAutomationEngineAdapter(
failingAdapter as any,
repository,
);
const useCase = new StartAutomationSessionUseCase(
engine,
failingAdapter as any,
repository,
);
const config: any = {
sessionName: 'Fixture E2E failure workflow',
trackId: 'spa',
carIds: ['dallara-f3'],
};
const dto = await useCase.execute(config);
expect(dto.state).toBe('PENDING');
expect(dto.currentStep).toBe(1);
await engine.executeStep(StepId.create(1), config);
const deadline = Date.now() + 30_000;
let finalSession = null;
// Poll for failure state
// eslint-disable-next-line no-constant-condition
while (true) {
const sessions = await repository.findAll();
finalSession = sessions[0] ?? null;
if (finalSession && (finalSession.state.isFailed() || finalSession.state.isStoppedAtStep18())) {
break;
}
if (Date.now() > deadline) {
throw new Error('Timed out waiting for automation workflow to fail');
}
await new Promise((resolve) => setTimeout(resolve, 200));
}
await failingAdapter.disconnect();
expect(finalSession).not.toBeNull();
expect(
finalSession!.state.isFailed() || finalSession!.state.isStoppedAtStep18(),
).toBe(true);
if (finalSession!.state.isFailed()) {
expect(finalSession!.errorMessage).toBeDefined();
}
});
});