fix(game): recover realtime dashboard delta cloning
This commit is contained in:
@@ -2,10 +2,7 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { createEmptyRealtimeReadModelChanges } from '@sammo-ts/common';
|
||||
import {
|
||||
createMergedReadModelRefreshQueue,
|
||||
resolveDashboardRefreshPlan,
|
||||
} from '../src/utils/dashboardReadModel.ts';
|
||||
import { createMergedReadModelRefreshQueue, resolveDashboardRefreshPlan } from '../src/utils/dashboardReadModel.ts';
|
||||
|
||||
void test('last-turn-time-only events do not schedule any dashboard query', () => {
|
||||
const plan = resolveDashboardRefreshPlan(createEmptyRealtimeReadModelChanges(), {
|
||||
@@ -55,10 +52,63 @@ void test('refreshes the map only for map-projection changes', () => {
|
||||
mapGeneralIds: [7],
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).map,
|
||||
true
|
||||
assert.equal(resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).map, true);
|
||||
});
|
||||
|
||||
void test('routes defence, tax-rate, and current-city-state events to their exact dashboard slices', () => {
|
||||
const identity = { generalId: 7, cityId: 3, nationId: 2 };
|
||||
const defence = resolveDashboardRefreshPlan(
|
||||
{
|
||||
...createEmptyRealtimeReadModelChanges(),
|
||||
cityIds: [3],
|
||||
mapCityIds: [],
|
||||
},
|
||||
identity
|
||||
);
|
||||
assert.deepEqual(defence, {
|
||||
context: true,
|
||||
lobby: false,
|
||||
map: false,
|
||||
commands: true,
|
||||
contacts: false,
|
||||
boardAccess: false,
|
||||
reservedTurns: false,
|
||||
records: false,
|
||||
frontStatus: false,
|
||||
});
|
||||
|
||||
const taxRate = resolveDashboardRefreshPlan(
|
||||
{
|
||||
...createEmptyRealtimeReadModelChanges(),
|
||||
nationIds: [2],
|
||||
mapNationIds: [],
|
||||
frontStatusNationIds: [],
|
||||
},
|
||||
identity
|
||||
);
|
||||
assert.deepEqual(taxRate, {
|
||||
context: true,
|
||||
lobby: false,
|
||||
map: false,
|
||||
commands: true,
|
||||
contacts: false,
|
||||
boardAccess: true,
|
||||
reservedTurns: false,
|
||||
records: false,
|
||||
frontStatus: false,
|
||||
});
|
||||
|
||||
const cityState = resolveDashboardRefreshPlan(
|
||||
{
|
||||
...createEmptyRealtimeReadModelChanges(),
|
||||
cityIds: [3],
|
||||
mapCityIds: [3],
|
||||
},
|
||||
identity
|
||||
);
|
||||
assert.equal(cityState.context, true);
|
||||
assert.equal(cityState.commands, true);
|
||||
assert.equal(cityState.map, true);
|
||||
});
|
||||
|
||||
void test('keeps conservative map behavior for rolling-deploy payloads without projections', () => {
|
||||
@@ -74,10 +124,7 @@ void test('keeps conservative map behavior for rolling-deploy payloads without p
|
||||
contactsChanged: false,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).map,
|
||||
true
|
||||
);
|
||||
assert.equal(resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).map, true);
|
||||
});
|
||||
|
||||
void test('does not refresh front status for contact-only permission changes', () => {
|
||||
@@ -119,14 +166,8 @@ void test('targets a submitted survey projection to its own general', () => {
|
||||
frontStatusActorIds: [7],
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).frontStatus,
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
resolveDashboardRefreshPlan(changes, { generalId: 8, cityId: 3, nationId: 2 }).frontStatus,
|
||||
false
|
||||
);
|
||||
assert.equal(resolveDashboardRefreshPlan(changes, { generalId: 7, cityId: 3, nationId: 2 }).frontStatus, true);
|
||||
assert.equal(resolveDashboardRefreshPlan(changes, { generalId: 8, cityId: 3, nationId: 2 }).frontStatus, false);
|
||||
});
|
||||
|
||||
void test('merges burst payloads without losing entity ids and starts at most once per interval', async () => {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { resolveWithReadModelSnapshotFallback } from '../src/utils/readModelDeltaRecovery.ts';
|
||||
|
||||
void test('retries any local delta application failure once with a forced snapshot', async () => {
|
||||
const requests: boolean[] = [];
|
||||
const result = await resolveWithReadModelSnapshotFallback({
|
||||
request: async (forceSnapshot) => {
|
||||
requests.push(forceSnapshot);
|
||||
return forceSnapshot ? { kind: 'snapshot', value: 25 } : { kind: 'patch', value: 20 };
|
||||
},
|
||||
resolve: (response) => {
|
||||
if (response.kind === 'patch') {
|
||||
throw new DOMException('[object Object] could not be cloned.', 'DataCloneError');
|
||||
}
|
||||
return response.value;
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result, 25);
|
||||
assert.deepEqual(requests, [false, true]);
|
||||
});
|
||||
|
||||
void test('does not retry a forced snapshot application failure', async () => {
|
||||
const requests: boolean[] = [];
|
||||
|
||||
await assert.rejects(
|
||||
resolveWithReadModelSnapshotFallback({
|
||||
forceSnapshot: true,
|
||||
request: async (forceSnapshot) => {
|
||||
requests.push(forceSnapshot);
|
||||
return { kind: 'snapshot' };
|
||||
},
|
||||
resolve: () => {
|
||||
throw new Error('snapshot failure');
|
||||
},
|
||||
}),
|
||||
/snapshot failure/u
|
||||
);
|
||||
assert.deepEqual(requests, [true]);
|
||||
});
|
||||
Reference in New Issue
Block a user