import 'package:flutter_test/flutter_test.dart'; import 'package:uto_manager/domain/services/doctrinal_templates.dart'; import 'package:uto_manager/domain/services/template_instantiator.dart'; void main() { // Use a deterministic counter-based ID generator for tests. var counter0 = 0; String nextId() => 'id-${counter0++}'; late TemplateInstantiator instantiator; setUp(() { counter0 = 0; instantiator = TemplateInstantiator(idGenerator: nextId); }); // --------------------------------------------------------------------------- // Basic instantiation // --------------------------------------------------------------------------- group('basic instantiation', () { test('instantiates a single root unit with no children or slots', () { const def = UnitDefinition( name: 'Test Brigade', echelon: 'brigade', branch: 'infantry', ); final result = instantiator.instantiate(def); expect(result.units.length, 1); expect(result.slots, isEmpty); expect(result.units.first.name, 'Test Brigade'); expect(result.units.first.parentUnitId, isNull); }); test('root unit has no parent', () { const def = UnitDefinition( name: 'Root', echelon: 'brigade', branch: 'infantry', ); final result = instantiator.instantiate(def); expect(result.units.first.isRoot, isTrue); }); test('instantiates slots for root unit', () { const def = UnitDefinition( name: 'Test Platoon', echelon: 'platoon', branch: 'infantry', slots: [ SlotDefinition( positionTitle: 'Platoon Leader', dutyMos: '11A', authorizedRank: 'o2', isKeyLeader: true, ), SlotDefinition( positionTitle: 'Platoon Sergeant', dutyMos: '11B', authorizedRank: 'e7', isKeyLeader: true, ), ], ); final result = instantiator.instantiate(def); expect(result.units.length, 1); expect(result.slots.length, 2); expect(result.slots.first.positionTitle, 'Platoon Leader'); expect(result.slots.first.isKeyLeader, isTrue); expect(result.slots.first.unitId, result.units.first.unitId); }); test('all slots reference the correct unit ID', () { const def = UnitDefinition( name: 'Squad', echelon: 'squad', branch: 'infantry', slots: [ SlotDefinition( positionTitle: 'Squad Leader', dutyMos: '11B', authorizedRank: 'e6', ), SlotDefinition( positionTitle: 'Rifleman', dutyMos: '11B', authorizedRank: 'e3', ), ], ); final result = instantiator.instantiate(def); final unitId = result.units.first.unitId; for (final slot in result.slots) { expect(slot.unitId, unitId); } }); }); // --------------------------------------------------------------------------- // Parent-child relationships // --------------------------------------------------------------------------- group('parent-child relationships', () { test('child unit has correct parentUnitId', () { const def = UnitDefinition( name: 'Battalion', echelon: 'battalion', branch: 'infantry', children: [ UnitDefinition( name: 'Alpha Company', echelon: 'company', branch: 'infantry', ), ], ); final result = instantiator.instantiate(def); expect(result.units.length, 2); final parent = result.units.firstWhere((u) => u.name == 'Battalion'); final child = result.units.firstWhere((u) => u.name == 'Alpha Company'); expect(child.parentUnitId, parent.unitId); }); test('deeply nested tree has correct parent chain', () { const def = UnitDefinition( name: 'Brigade', echelon: 'brigade', branch: 'infantry', children: [ UnitDefinition( name: 'Battalion', echelon: 'battalion', branch: 'infantry', children: [ UnitDefinition( name: 'Company', echelon: 'company', branch: 'infantry', ), ], ), ], ); final result = instantiator.instantiate(def); expect(result.units.length, 3); final brigade = result.units.firstWhere((u) => u.name == 'Brigade'); final battalion = result.units.firstWhere((u) => u.name == 'Battalion'); final company = result.units.firstWhere((u) => u.name == 'Company'); expect(battalion.parentUnitId, brigade.unitId); expect(company.parentUnitId, battalion.unitId); }); test('all generated IDs are unique', () { // Use UUID-style generator to verify uniqueness. var counter = 0; final inst = TemplateInstantiator(idGenerator: () => 'uid-${counter++}'); const def = UnitDefinition( name: 'Brigade', echelon: 'brigade', branch: 'infantry', slots: [ SlotDefinition( positionTitle: 'Commander', dutyMos: '11A', authorizedRank: 'o6', ), ], children: [ UnitDefinition( name: 'Battalion', echelon: 'battalion', branch: 'infantry', ), ], ); final result = inst.instantiate(def); final allIds = [ ...result.units.map((u) => u.unitId), ...result.slots.map((s) => s.slotId), ]; expect(allIds.toSet().length, allIds.length); }); }); // --------------------------------------------------------------------------- // Doctrinal templates // --------------------------------------------------------------------------- group('doctrinal templates', () { test('MBCT instantiates with units and slots', () { final result = instantiator.instantiate(DoctrinalTemplates.mbct); expect(result.units, isNotEmpty); expect(result.slots, isNotEmpty); // MBCT has a brigade root expect( result.units.any((u) => u.name == 'Mobile Brigade Combat Team'), isTrue, ); }); test('MBCT root unit has no parent', () { final result = instantiator.instantiate(DoctrinalTemplates.mbct); final roots = result.units.where((u) => u.parentUnitId == null).toList(); expect(roots.length, 1); expect(roots.first.name, 'Mobile Brigade Combat Team'); }); test('SBCT instantiates with units and slots', () { final result = instantiator.instantiate(DoctrinalTemplates.sbct); expect(result.units, isNotEmpty); expect(result.slots, isNotEmpty); }); test('ABCT instantiates with units and slots', () { final result = instantiator.instantiate(DoctrinalTemplates.abct); expect(result.units, isNotEmpty); expect(result.slots, isNotEmpty); }); test('CAB Heavy instantiates with units and slots', () { final result = instantiator.instantiate(DoctrinalTemplates.cabHeavy); expect(result.units, isNotEmpty); expect(result.slots, isNotEmpty); }); test('CAB Light instantiates with units and slots', () { final result = instantiator.instantiate(DoctrinalTemplates.cabLight); expect(result.units, isNotEmpty); expect(result.slots, isNotEmpty); }); test('all template units have valid parent references', () { for (final entry in DoctrinalTemplates.all.entries) { final result = instantiator.instantiate(entry.value); final unitIds = result.units.map((u) => u.unitId).toSet(); for (final unit in result.units) { if (unit.parentUnitId != null) { expect( unitIds.contains(unit.parentUnitId), isTrue, reason: '${entry.key}: unit "${unit.name}" references unknown parent ' '${unit.parentUnitId}', ); } } } }); test('all template slots reference existing units', () { for (final entry in DoctrinalTemplates.all.entries) { final result = instantiator.instantiate(entry.value); final unitIds = result.units.map((u) => u.unitId).toSet(); for (final slot in result.slots) { expect( unitIds.contains(slot.unitId), isTrue, reason: '${entry.key}: slot "${slot.positionTitle}" references unknown ' 'unit ${slot.unitId}', ); } } }); test('MBCT has key leader slots', () { final result = instantiator.instantiate(DoctrinalTemplates.mbct); expect(result.slots.any((s) => s.isKeyLeader), isTrue); }); test('DoctrinalTemplates.all contains all five templates', () { expect( DoctrinalTemplates.all.keys, containsAll([ 'mbct', 'sbct', 'abct', 'cab_heavy', 'cab_light', ]),); }); test('DoctrinalTemplates.displayName returns non-empty strings', () { for (final key in DoctrinalTemplates.all.keys) { expect(DoctrinalTemplates.displayName(key), isNotEmpty); } }); }); }