import 'package:test/test.dart'; class SmokeFixtureBoundary { final String name; final String target; final bool startsExternalRepo; final List requiredInputs; final List expectedEvidence; final List forbiddenDependencies; const SmokeFixtureBoundary({ required this.name, required this.target, required this.startsExternalRepo, required this.requiredInputs, required this.expectedEvidence, required this.forbiddenDependencies, }); } class SmokeMigrationStep { final String id; final String description; const SmokeMigrationStep(this.id, this.description); } class OtoServerSmokeMigrationPlan { final SmokeFixtureBoundary legacySource; final SmokeFixtureBoundary otoServerTarget; final List steps; const OtoServerSmokeMigrationPlan({ required this.legacySource, required this.otoServerTarget, required this.steps, }); } const migrationPlan = OtoServerSmokeMigrationPlan( legacySource: SmokeFixtureBoundary( name: 'legacy-iop-edge-online-smoke', target: 'apps/runner/test/oto_iop_connection_smoke_test.dart', startsExternalRepo: true, requiredInputs: [ 'IOP_REPO_ROOT or workspace sibling iop checkout', 'iop Edge config fixture', 'iop runtime RegisterRequest/RegisterResponse protobuf', ], expectedEvidence: [ 'registration accepted by iop Edge', 'first heartbeat transitions the iop Edge node record to online', ], forbiddenDependencies: [], ), otoServerTarget: SmokeFixtureBoundary( name: 'oto-server-runner-online-smoke', target: 'apps/runner/test/oto_server_connection_smoke_test.dart', startsExternalRepo: false, requiredInputs: [ 'loopback OTO Core listener with dynamic port', 'temporary runner install/workspace/log directories', 'runner enrollment token fixture owned by services/core', 'OTO Server registration and heartbeat contract fixture', ], expectedEvidence: [ 'runner registration accepted by OTO Server', 'runner id and alias recorded in OTO Server registry', 'first heartbeat or equivalent online signal marks runner online', ], forbiddenDependencies: [ 'IOP_REPO_ROOT', '../iop checkout', 'iop Edge process', 'iop runtime protobuf as the acceptance contract', ], ), steps: [ SmokeMigrationStep( 'freeze-legacy', 'Keep the iop smoke as migration source evidence until the OTO Server ' 'target fixture reaches parity.', ), SmokeMigrationStep( 'define-core-fixture', 'Add a services/core test fixture that can issue enrollment tokens, ' 'accept runner registration, and expose runner online state.', ), SmokeMigrationStep( 'define-runner-fixture', 'Start OTO Core on loopback from the runner test, create temporary ' 'runtime directories, and point AgentConfig at the OTO Server target.', ), SmokeMigrationStep( 'replace-contract', 'Replace iop RegisterRequest/RegisterResponse acceptance with an OTO ' 'Server owned registration and heartbeat contract.', ), SmokeMigrationStep( 'retire-iop-smoke', 'Remove the iop smoke from required acceptance once the OTO Server smoke ' 'proves registration accepted and online evidence.', ), ], ); void main() { group('OTO Server migration smoke plan', () { test('keeps the existing iop smoke as legacy source evidence only', () { expect(migrationPlan.legacySource.name, 'legacy-iop-edge-online-smoke'); expect(migrationPlan.legacySource.startsExternalRepo, isTrue); expect( migrationPlan.legacySource.requiredInputs, contains('IOP_REPO_ROOT or workspace sibling iop checkout'), ); expect( migrationPlan.legacySource.expectedEvidence, contains('registration accepted by iop Edge'), ); }); test('defines an OTO Server target fixture without iop dependencies', () { final target = migrationPlan.otoServerTarget; expect(target.name, 'oto-server-runner-online-smoke'); expect(target.startsExternalRepo, isFalse); expect( target.requiredInputs, contains('loopback OTO Core listener with dynamic port'), ); expect( target.requiredInputs, contains('runner enrollment token fixture owned by services/core'), ); expect( target.expectedEvidence, contains('runner registration accepted by OTO Server'), ); expect(target.forbiddenDependencies, contains('IOP_REPO_ROOT')); expect(target.forbiddenDependencies, contains('../iop checkout')); expect(target.forbiddenDependencies, contains('iop Edge process')); expect( target.forbiddenDependencies, contains('iop runtime protobuf as the acceptance contract'), ); }); test('orders the handoff before retiring the iop smoke', () { final stepIds = migrationPlan.steps.map((step) => step.id).toList(); expect(stepIds, [ 'freeze-legacy', 'define-core-fixture', 'define-runner-fixture', 'replace-contract', 'retire-iop-smoke', ]); expect( migrationPlan.steps.last.description, contains('Remove the iop smoke from required acceptance'), ); }); }); }