125 lines
4.4 KiB
TypeScript
125 lines
4.4 KiB
TypeScript
import { describe, it, expect } from 'bun:test';
|
|
import { World, Component, COMPONENT_KEY } from '@common/rpg/core/world';
|
|
import { component } from '@common/rpg/utils/decorators';
|
|
import { Serialization } from '@common/rpg/core/serialization';
|
|
import { Stat } from '@common/rpg/components/stat';
|
|
import { Effect } from '@common/rpg/components/effect';
|
|
import { EffectSystem } from '@common/rpg/systems/effect';
|
|
|
|
// ---------- helpers ----------
|
|
|
|
function roundtrip(world: World): World {
|
|
return Serialization.deserialize(Serialization.serialize(world)) as World;
|
|
}
|
|
|
|
function isSymbolKeyed(world: World, entityId: string, ctor: new (...a: any[]) => Component<any>): boolean {
|
|
const entity = world.getEntity(entityId)!;
|
|
for (const [, component] of entity) {
|
|
if (component instanceof ctor) {
|
|
return typeof component[COMPONENT_KEY] === 'symbol';
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ---------- named-key round-trip ----------
|
|
|
|
describe('Serialization — named key', () => {
|
|
it('preserves a string-keyed component', () => {
|
|
const w = new World();
|
|
const e = w.createEntity('player');
|
|
e.add(new Stat({ value: 42 }), 'str');
|
|
const w2 = roundtrip(w);
|
|
expect(w2.getEntity('player')!.get(Stat, 'str')!.value).toBe(42);
|
|
});
|
|
|
|
it('preserves two components with different named keys', () => {
|
|
const w = new World();
|
|
const e = w.createEntity('player');
|
|
e.add(new Stat({ value: 10 }), 'str');
|
|
e.add(new Stat({ value: 5 }), 'int');
|
|
const w2 = roundtrip(w);
|
|
const e2 = w2.getEntity('player')!;
|
|
expect(e2.get(Stat, 'str')!.value).toBe(10);
|
|
expect(e2.get(Stat, 'int')!.value).toBe(5);
|
|
});
|
|
});
|
|
|
|
// ---------- symbol-key round-trip ----------
|
|
|
|
describe('Serialization — anonymous (symbol) key', () => {
|
|
it('restores a symbol-keyed component as symbol-keyed', () => {
|
|
const w = new World();
|
|
w.createEntity('e').add(new Stat({ value: 7 })); // no key → Symbol
|
|
const w2 = roundtrip(w);
|
|
expect(isSymbolKeyed(w2, 'e', Stat)).toBeTrue();
|
|
});
|
|
|
|
it('restored symbol-keyed component is found by type lookup', () => {
|
|
const w = new World();
|
|
const e = w.createEntity('e');
|
|
e.add(new Stat({ value: 7 }));
|
|
const w2 = roundtrip(w);
|
|
expect(w2.getEntity('e')!.get(Stat)!.value).toBe(7);
|
|
});
|
|
|
|
it('two anonymous components of the same type both survive round-trip', () => {
|
|
const w = new World();
|
|
const e = w.createEntity('e');
|
|
e.add(new Stat({ value: 3 }));
|
|
e.add(new Stat({ value: 9 }));
|
|
const w2 = roundtrip(w);
|
|
const stats = w2.getEntity('e')!.getAll(Stat);
|
|
expect(stats.length).toBe(2);
|
|
expect(stats.map(s => s.value).sort()).toEqual([3, 9]);
|
|
});
|
|
|
|
it('anonymous and named component of the same type both survive round-trip', () => {
|
|
const w = new World();
|
|
const e = w.createEntity('e');
|
|
e.add(new Stat({ value: 10 }), 'str');
|
|
e.add(new Stat({ value: 20 })); // Symbol key
|
|
const w2 = roundtrip(w);
|
|
const e2 = w2.getEntity('e')!;
|
|
expect(e2.get(Stat, 'str')!.value).toBe(10);
|
|
// anonymous one still reachable by type
|
|
const all = e2.getAll(Stat);
|
|
expect(all.length).toBe(2);
|
|
});
|
|
});
|
|
|
|
// ---------- effect + stat round-trip ----------
|
|
|
|
describe('Serialization — Effect round-trip', () => {
|
|
it('multiple anonymous Effects both survive round-trip', () => {
|
|
const w = new World();
|
|
w.addSystem(new EffectSystem());
|
|
const e = w.createEntity('target');
|
|
e.add(new Stat({ value: 10 }), 'str');
|
|
e.add(new Effect({ targetKey: 'str', delta: 3 }));
|
|
e.add(new Effect({ targetKey: 'str', delta: 7 }));
|
|
|
|
const w2 = roundtrip(w);
|
|
w2.addSystem(new EffectSystem());
|
|
expect(w2.getEntity('target')!.getAll(Effect).length).toBe(2);
|
|
});
|
|
|
|
it('world globals survive round-trip', () => {
|
|
const w = new World();
|
|
w.globals.score = 99;
|
|
w.globals.level = 3;
|
|
const w2 = roundtrip(w);
|
|
expect(w2.globals.score).toBe(99);
|
|
expect(w2.globals.level).toBe(3);
|
|
});
|
|
|
|
it('entity counter continues from saved value', () => {
|
|
const w = new World();
|
|
w.createEntity();
|
|
w.createEntity();
|
|
const w2 = roundtrip(w);
|
|
const e = w2.createEntity();
|
|
expect(e.id).toBe('entity_3');
|
|
});
|
|
});
|