feat(#54): non-military takeovers — automatic regime rename (TurnResult.takeovers)
This commit is contained in:
+305
-20
@@ -35,6 +35,7 @@ import {
|
||||
validateMarkerOps,
|
||||
validateNationOps,
|
||||
validateRegionChanges,
|
||||
validateTakeovers,
|
||||
validateTurnResult,
|
||||
} from './engine';
|
||||
|
||||
@@ -542,7 +543,7 @@ describe('validateNationOps (v0.23 #16 P4a)', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [{ kind: 'rename', nationRef: 'France', to: 'French Republic' }], markers: { add: [], remove: [] },
|
||||
nationOps: [{ kind: 'rename', nationRef: 'France', to: 'French Republic' }], takeovers: [], markers: { add: [], remove: [] },
|
||||
});
|
||||
expect(withOps).not.toBeNull();
|
||||
expect(withOps!.nationOps).toEqual([{ kind: 'rename', nationRef: 'France', to: 'French Republic' }]);
|
||||
@@ -564,7 +565,7 @@ describe('applyTurnResult (nation-op proposals, v0.23 #16 P4a)', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], markers: { add: [], remove: [] },
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], takeovers: [], markers: { add: [], remove: [] },
|
||||
});
|
||||
expect(next.nations.de.name).toBe('DE'); // still the old name
|
||||
expect(next.pendingNationOps).toEqual([{ kind: 'rename', nationRef: 'de', to: 'Reich' }]);
|
||||
@@ -585,7 +586,7 @@ describe('applyTurnResult (nation-op proposals, v0.23 #16 P4a)', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [{ kind: 'rename', nationRef: 'us', to: 'USA' }], markers: { add: [], remove: [] }, // new proposal this turn
|
||||
nationOps: [{ kind: 'rename', nationRef: 'us', to: 'USA' }], takeovers: [], markers: { add: [], remove: [] }, // new proposal this turn
|
||||
});
|
||||
expect(next.pendingNationOps).toEqual([
|
||||
{ kind: 'rename', nationRef: 'de', to: 'Reich' }, // the old one survived
|
||||
@@ -604,7 +605,7 @@ describe('applyTurnResult (nation-op proposals, v0.23 #16 P4a)', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], markers: { add: [], remove: [] },
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], takeovers: [], markers: { add: [], remove: [] },
|
||||
});
|
||||
expect(next.pendingNationOps).toEqual([{ kind: 'rename', nationRef: 'de', to: 'Reich' }]);
|
||||
});
|
||||
@@ -1061,7 +1062,7 @@ describe('applyTurnResult', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
applyTurnResult(state, result);
|
||||
expect(JSON.stringify(state)).toBe(snapshot);
|
||||
@@ -1078,7 +1079,7 @@ describe('applyTurnResult', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.pending).toEqual([]);
|
||||
@@ -1101,7 +1102,7 @@ describe('applyTurnResult', () => {
|
||||
// is not a real key at all. Both are proposed as a gain by DE.
|
||||
territoryChanges: [{ nation: 'de', gain: ['wales-bbbb', 'not-a-region-xyz'], lose: [] }],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result, new Set(['wales-bbbb']));
|
||||
// the valid key was gained (taken from its sole previous owner); the bogus one was ignored
|
||||
@@ -1127,7 +1128,7 @@ describe('applyTurnResult', () => {
|
||||
// released (not in state.unowned), so it does not enter GameState.unowned.
|
||||
territoryChanges: [{ nation: 'de', gain: ['POL', 'wales-bbbb'], lose: [] }],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result); // no validKeys -> nothing filtered
|
||||
expect(next.nations.de.territory).toContain('wales-bbbb');
|
||||
@@ -1145,7 +1146,7 @@ describe('applyTurnResult (sole-ownership invariants, v0.21 #16 P2)', () => {
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
|
||||
it('a double claim (two different gainers, one key) is rejected: the key stays with its original owner', () => {
|
||||
@@ -1235,7 +1236,7 @@ describe('applyTurnResult (no man\'s land, v0.22 #16 P3)', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [], markers: { add: [], remove: [] },
|
||||
nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
};
|
||||
|
||||
it('a release removes the key from its sole owner and marks it in GameState.unowned', () => {
|
||||
@@ -1350,7 +1351,7 @@ describe('applyTurnResult (no man\'s land, v0.22 #16 P3)', () => {
|
||||
|
||||
it('a lone release is meaningful (it survives the retry/nudge path)', () => {
|
||||
expect(
|
||||
isMeaningful({ date: 'x', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: ['GBR'], nationOps: [], markers: { add: [], remove: [] } }),
|
||||
isMeaningful({ date: 'x', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: ['GBR'], nationOps: [], takeovers: [], markers: { add: [], remove: [] } }),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1657,9 +1658,9 @@ describe('buildSystemPrompt (tool mode, v0.21 #16 P2)', () => {
|
||||
|
||||
describe('isMeaningful', () => {
|
||||
it('is true with an event, false when everything is empty', () => {
|
||||
expect(isMeaningful({ date: 'x', events: [{ nation: 'uk', type: 'military', title: 't', text: 'b', outcome: 'neutral' }], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], markers: { add: [], remove: [] } })).toBe(true);
|
||||
expect(isMeaningful({ date: 'x', events: [], stateChanges: [{ nation: 'uk', stat: 'military', op: 'set', value: 5 }], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], markers: { add: [], remove: [] } })).toBe(true);
|
||||
expect(isMeaningful({ date: 'x', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], markers: { add: [], remove: [] } })).toBe(false);
|
||||
expect(isMeaningful({ date: 'x', events: [{ nation: 'uk', type: 'military', title: 't', text: 'b', outcome: 'neutral' }], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], takeovers: [], markers: { add: [], remove: [] } })).toBe(true);
|
||||
expect(isMeaningful({ date: 'x', events: [], stateChanges: [{ nation: 'uk', stat: 'military', op: 'set', value: 5 }], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], takeovers: [], markers: { add: [], remove: [] } })).toBe(true);
|
||||
expect(isMeaningful({ date: 'x', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [], unowned: [], nationOps: [], takeovers: [], markers: { add: [], remove: [] } })).toBe(false);
|
||||
});
|
||||
|
||||
it('(v0.23 #16 P4a) a turn that only proposes a nation-op is meaningful (it survives the retry/nudge path)', () => {
|
||||
@@ -1672,7 +1673,7 @@ describe('isMeaningful', () => {
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], markers: { add: [], remove: [] },
|
||||
nationOps: [{ kind: 'rename', nationRef: 'de', to: 'Reich' }], takeovers: [], markers: { add: [], remove: [] },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -1688,10 +1689,202 @@ describe('isMeaningful', () => {
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [],
|
||||
markers: { add: [{ type: 'city', name: 'Lyon', regionKey: 'rhone-rhne' }], remove: [] },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('(v0.32.0 #54) a turn that only performs a takeover is meaningful (it survives the retry/nudge path)', () => {
|
||||
expect(
|
||||
isMeaningful({
|
||||
date: 'x',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'de', newRegimeName: 'The Reich' }],
|
||||
markers: { add: [], remove: [] },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('takeovers (regime change, v0.32.0 #54)', () => {
|
||||
it('a takeover renames the target nation; id, territory, relations, and stats are preserved', () => {
|
||||
const state = baseState();
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'de', newRegimeName: 'The Reich' }],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.nations.de.name).toBe('The Reich');
|
||||
// id is preserved
|
||||
expect(next.nations.de.id).toBe('de');
|
||||
// territory is preserved
|
||||
expect(next.nations.de.territory).toEqual(['DEU']);
|
||||
// relations are preserved (keyed by id, not name)
|
||||
expect(next.nations.de.relations).toEqual({});
|
||||
// stats are preserved
|
||||
expect(next.nations.de.stats.military).toBe(95);
|
||||
// other nations are untouched
|
||||
expect(next.nations.uk.name).toBe('UK');
|
||||
expect(next.nations.us.name).toBe('US');
|
||||
});
|
||||
|
||||
it('a takeover of the player\'s own nation preserves playerNationId (player keeps leading it)', () => {
|
||||
const state = baseState(); // playerNationId is 'uk'
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'uk', newRegimeName: 'The Commonwealth' }],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.playerNationId).toBe('uk'); // player still leads it
|
||||
expect(next.nations.uk.name).toBe('The Commonwealth');
|
||||
expect(next.nations.uk.id).toBe('uk');
|
||||
});
|
||||
|
||||
it('an unknown nationRef → no-op (nation unchanged, no throw)', () => {
|
||||
const state = baseState();
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'nonexistent', newRegimeName: 'New Name' }],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
// no nation was renamed
|
||||
expect(next.nations.uk.name).toBe('UK');
|
||||
expect(next.nations.us.name).toBe('US');
|
||||
expect(next.nations.de.name).toBe('DE');
|
||||
});
|
||||
|
||||
it('an ambiguous nationRef (two nations share the name) → no-op', () => {
|
||||
const state = baseState();
|
||||
// make two nations share the same name
|
||||
state.nations.us.name = 'UK'; // now both 'uk' and 'us' are named 'UK'
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'UK', newRegimeName: 'The Empire' }],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
// neither was renamed (ambiguous)
|
||||
expect(next.nations.uk.name).toBe('UK');
|
||||
expect(next.nations.us.name).toBe('UK');
|
||||
});
|
||||
|
||||
it('an empty/whitespace newRegimeName → no-op', () => {
|
||||
const state = baseState();
|
||||
// validateTakeovers drops entries with empty newRegimeName, so the validated
|
||||
// result has no takeovers → applyTurnResult is a no-op.
|
||||
const validated = validateTurnResult({
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'de', newRegimeName: ' ' }],
|
||||
markers: { add: [], remove: [] },
|
||||
});
|
||||
expect(validated!.takeovers).toEqual([]);
|
||||
const next = applyTurnResult(state, validated!);
|
||||
expect(next.nations.de.name).toBe('DE');
|
||||
});
|
||||
|
||||
it('multiple takeovers in one turn all apply', () => {
|
||||
const state = baseState();
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [
|
||||
{ nationRef: 'uk', newRegimeName: 'The Commonwealth' },
|
||||
{ nationRef: 'de', newRegimeName: 'The Reich' },
|
||||
],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.nations.uk.name).toBe('The Commonwealth');
|
||||
expect(next.nations.de.name).toBe('The Reich');
|
||||
expect(next.nations.us.name).toBe('US'); // untouched
|
||||
});
|
||||
|
||||
it('a takeover turn bumps the version once (same as any other turn)', () => {
|
||||
const state = baseState(); // version 1
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'de', newRegimeName: 'The Reich' }],
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.version).toBe(2); // exactly one bump for the whole turn
|
||||
});
|
||||
|
||||
it('a takeover via case-insensitive name resolution works', () => {
|
||||
const state = baseState();
|
||||
const result: TurnResult = {
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'De', newRegimeName: 'The Reich' }], // 'De' → resolves to 'de' by name
|
||||
markers: { add: [], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result);
|
||||
expect(next.nations.de.name).toBe('The Reich');
|
||||
});
|
||||
});
|
||||
|
||||
describe('markers (point placement, v0.27 #44)', () => {
|
||||
@@ -1716,6 +1909,7 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [],
|
||||
markers: { add: [mk('city', 'Strasbourg', 'moselle-rygy')], remove: [] },
|
||||
};
|
||||
const next = applyTurnResult(state, result, undefined, centroidOf);
|
||||
@@ -1740,13 +1934,13 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
const add = [mk('city', 'Strasbourg', 'moselle-rygy')];
|
||||
const first = applyTurnResult(state, {
|
||||
date: '1939-09-03', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [],
|
||||
unowned: [], nationOps: [], markers: { add, remove: [] },
|
||||
unowned: [], nationOps: [], takeovers: [], markers: { add, remove: [] },
|
||||
}, undefined, centroidOf);
|
||||
expect(first.markers).toHaveLength(1);
|
||||
// a second turn proposes the exact same marker again
|
||||
const second = applyTurnResult(first, {
|
||||
date: '1939-09-05', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [],
|
||||
unowned: [], nationOps: [], markers: { add, remove: [] },
|
||||
unowned: [], nationOps: [], takeovers: [], markers: { add, remove: [] },
|
||||
}, undefined, centroidOf);
|
||||
expect(second.markers).toHaveLength(1);
|
||||
expect(second.markers[0].id).toBe(first.markers[0].id); // stable id, no second copy
|
||||
@@ -1768,6 +1962,7 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [],
|
||||
markers: {
|
||||
add: [
|
||||
{ type: 'city', name: 'Good', regionKey: 'moselle-rygy' }, // valid + centroid → kept
|
||||
@@ -1810,6 +2005,7 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [],
|
||||
markers: {
|
||||
add: [
|
||||
{ type: 'city', name: 'NewA', regionKey: 'new-a' },
|
||||
@@ -1835,7 +2031,7 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
];
|
||||
const next = applyTurnResult(state, {
|
||||
date: '1939-09-03', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [],
|
||||
unowned: [], nationOps: [], markers: { add: [], remove: [idA, 'zzzz-not-there'] },
|
||||
unowned: [], nationOps: [], takeovers: [], markers: { add: [], remove: [idA, 'zzzz-not-there'] },
|
||||
}, undefined, centroidOf);
|
||||
expect(next.markers).toHaveLength(1);
|
||||
expect(next.markers[0].id).toBe(idB); // only A removed; the bogus id was a no-op
|
||||
@@ -1851,14 +2047,14 @@ describe('markers (point placement, v0.27 #44)', () => {
|
||||
// 1) uk releases the region → the marker becomes ownerless
|
||||
const released = applyTurnResult(state, {
|
||||
date: '1939-09-03', events: [], stateChanges: [], regionChanges: [], relations: [], territoryChanges: [],
|
||||
unowned: ['moselle-rygy'], nationOps: [], markers: { add: [], remove: [] },
|
||||
unowned: ['moselle-rygy'], nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
}, undefined, centroidOf);
|
||||
expect(released.markers[0].ownerNationId).toBeNull();
|
||||
// 2) de re-claims the region → the marker's owner follows to de
|
||||
const reclaimed = applyTurnResult(released, {
|
||||
date: '1939-09-05', events: [], stateChanges: [], regionChanges: [], relations: [],
|
||||
territoryChanges: [{ nation: 'de', gain: ['moselle-rygy'], lose: [] }],
|
||||
unowned: [], nationOps: [], markers: { add: [], remove: [] },
|
||||
unowned: [], nationOps: [], takeovers: [], markers: { add: [], remove: [] },
|
||||
}, undefined, centroidOf);
|
||||
expect(reclaimed.markers[0].ownerNationId).toBe('de');
|
||||
});
|
||||
@@ -2006,6 +2202,7 @@ function mkTurnResult(partial: Partial<TurnResult>): TurnResult {
|
||||
territoryChanges: partial.territoryChanges ?? [],
|
||||
unowned: partial.unowned ?? [],
|
||||
nationOps: partial.nationOps ?? [],
|
||||
takeovers: partial.takeovers ?? [],
|
||||
markers: partial.markers ?? { add: [], remove: [] },
|
||||
};
|
||||
}
|
||||
@@ -2670,3 +2867,91 @@ describe('buildUserPrompt — frontier balance + takeover risks (v0.30 #50)', ()
|
||||
expect(prompt).not.toMatch(/takeoverRisks/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSystemPrompt — takeovers contract (v0.32.0 #54)', () => {
|
||||
it('both modes document the takeovers field and the automatic (NOT player-confirmed) rule', () => {
|
||||
for (const p of [buildSystemPrompt(preset), buildSystemPrompt(preset, true)]) {
|
||||
// the JSON shape carries the field in both modes
|
||||
expect(p).toMatch(
|
||||
/"takeovers": \[\{"nationRef":"<nation id or name>","newRegimeName":"<name of the new regime>"\}\]/,
|
||||
);
|
||||
// the rule: a takeover is an automatic in-world regime change, not a proposal
|
||||
expect(p).toMatch(/Takeover \(automatic, NOT player-confirmed\)/);
|
||||
// and it is tied to the stability + gathering mechanic
|
||||
expect(p).toMatch(/low-stability nation/);
|
||||
expect(p).toMatch(/gathering/);
|
||||
// and it is distinguished from the player-confirmed nationOps rename
|
||||
expect(p).toMatch(/use "nationOps" rename only for a diplomatic\/political name change/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateTakeovers (v0.32.0 #54)', () => {
|
||||
it('parses valid entries (trimmed, capped) and drops invalid ones', () => {
|
||||
const takeovers = validateTakeovers([
|
||||
{ nationRef: ' de ', newRegimeName: 'The Reich' },
|
||||
{ nationRef: 'uk', newRegimeName: ' ' }, // empty name -> dropped
|
||||
{ nationRef: '', newRegimeName: 'X' }, // empty ref -> dropped
|
||||
{ newRegimeName: 'no ref' }, // missing nationRef -> dropped
|
||||
{ nationRef: 'us' }, // missing newRegimeName -> dropped
|
||||
'not-an-object',
|
||||
null,
|
||||
42,
|
||||
]);
|
||||
expect(takeovers).toEqual([{ nationRef: 'de', newRegimeName: 'The Reich' }]);
|
||||
});
|
||||
|
||||
it('caps both fields at 200 characters', () => {
|
||||
const long = 'x'.repeat(250);
|
||||
const takeovers = validateTakeovers([{ nationRef: long, newRegimeName: long }]);
|
||||
expect(takeovers).toEqual([{ nationRef: 'x'.repeat(200), newRegimeName: 'x'.repeat(200) }]);
|
||||
});
|
||||
|
||||
it('returns [] for non-array input and is never thrown by', () => {
|
||||
expect(validateTakeovers(undefined)).toEqual([]);
|
||||
expect(validateTakeovers('nope')).toEqual([]);
|
||||
expect(validateTakeovers({})).toEqual([]);
|
||||
});
|
||||
|
||||
it('validateTurnResult carries validated takeovers through (and defaults to [] when absent)', () => {
|
||||
const withTakeovers = validateTurnResult({
|
||||
date: '1939-09-03',
|
||||
events: [],
|
||||
stateChanges: [],
|
||||
regionChanges: [],
|
||||
relations: [],
|
||||
territoryChanges: [],
|
||||
unowned: [],
|
||||
nationOps: [],
|
||||
takeovers: [{ nationRef: 'de', newRegimeName: 'The Reich' }, { nationRef: 'bad', newRegimeName: '' }],
|
||||
markers: { add: [], remove: [] },
|
||||
});
|
||||
expect(withTakeovers).not.toBeNull();
|
||||
// the valid one survives, the empty-name one is dropped
|
||||
expect(withTakeovers!.takeovers).toEqual([{ nationRef: 'de', newRegimeName: 'The Reich' }]);
|
||||
|
||||
const without = validateTurnResult({ date: '1939-09-03', events: [], stateChanges: [] });
|
||||
expect(without).not.toBeNull();
|
||||
expect(without!.takeovers).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stub — takeovers (v0.32.0 #54)', () => {
|
||||
it('the deterministic stub returns takeovers: [] (it never performs a takeover)', () => {
|
||||
const payload = {
|
||||
state: {
|
||||
turn: 1,
|
||||
date: '1939-09-01',
|
||||
playerNationId: 'uk',
|
||||
nations: {
|
||||
uk: { stats: { military: 50, economy: 50, stability: 50, population: 50 }, territory: ['a'] },
|
||||
de: { stats: { military: 60, economy: 60, stability: 60, population: 60 }, territory: ['b'] },
|
||||
},
|
||||
},
|
||||
pending: [{ kind: 'action' as const, text: 'test' }],
|
||||
};
|
||||
const result = deterministicTurn(payload);
|
||||
expect(result.takeovers).toEqual([]);
|
||||
expect(result).toHaveProperty('takeovers');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user