uto_manager/test/domain/services/template_instantiator_test....

306 lines
9.2 KiB
Dart

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.
int _counter = 0;
String nextId() => 'id-${_counter++}';
late TemplateInstantiator instantiator;
setUp(() {
_counter = 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', () {
final def = UnitDefinition(
name: 'Battalion',
echelon: 'battalion',
branch: 'infantry',
children: [
const 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', () {
final def = UnitDefinition(
name: 'Brigade',
echelon: 'brigade',
branch: 'infantry',
children: [
UnitDefinition(
name: 'Battalion',
echelon: 'battalion',
branch: 'infantry',
children: [
const 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.
int counter = 0;
final inst = TemplateInstantiator(idGenerator: () => 'uid-${counter++}');
final def = UnitDefinition(
name: 'Brigade',
echelon: 'brigade',
branch: 'infantry',
slots: const [
SlotDefinition(
positionTitle: 'Commander',
dutyMos: '11A',
authorizedRank: 'o6',
),
],
children: [
const 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);
}
});
});
}