merge: 최신 main을 게임 버전 표시에 재통합

This commit is contained in:
2026-08-21 01:30:34 +00:00
3 changed files with 105 additions and 0 deletions
@@ -5,6 +5,16 @@ import { describe, expect, it } from 'vitest';
import { loadScenarioDefinitionById, resolveScenarioDefaultsPath } from '../src/scenario/scenarioLoader.js';
type LoadedScenario = Awaited<ReturnType<typeof loadScenarioDefinitionById>>;
const readItemSlot = (scenario: LoadedScenario, slot: string): Record<string, number> => {
const allItems = scenario.config.const.allItems as Record<string, Record<string, number>> | undefined;
return allItems?.[slot] ?? {};
};
const readAvailableSpecialWar = (scenario: LoadedScenario): string[] =>
(scenario.config.const.availableSpecialWar as string[] | undefined) ?? [];
describe('tracked scenario resources', () => {
it('loads every scenario through its composed resource graph', async () => {
const scenarioRoot = path.dirname(resolveScenarioDefaultsPath());
@@ -35,4 +45,36 @@ describe('tracked scenario resources', () => {
]);
}
});
it('keeps the buyable war-special pack scoped to each Ref scenario contract', async () => {
const [ordinaryBlank, legacySecretBlank, mirrorBlank, multiUnitBlank, moreEffectBlank, composedAddon] =
await Promise.all(
[0, 902, 910, 912, 913, 2141].map((scenarioId) => loadScenarioDefinitionById(scenarioId))
);
expect(
Object.keys(readItemSlot(ordinaryBlank, 'item')).filter((key) => key.startsWith('event_전투특기_'))
).toEqual([]);
const legacySecretItems = readItemSlot(legacySecretBlank, 'item');
expect(Object.keys(legacySecretItems).filter((key) => key.startsWith('event_전투특기_'))).toHaveLength(19);
expect(legacySecretItems).not.toHaveProperty('event_전투특기_견고');
expect(readAvailableSpecialWar(legacySecretBlank)).not.toContain('che_견고');
const mirrorItems = readItemSlot(mirrorBlank, 'item');
expect(Object.keys(mirrorItems).filter((key) => key.startsWith('event_전투특기_'))).toHaveLength(19);
expect(mirrorItems).not.toHaveProperty('event_전투특기_척사');
expect(readAvailableSpecialWar(mirrorBlank)).not.toContain('che_척사');
const multiUnitItems = readItemSlot(multiUnitBlank, 'item');
expect(Object.keys(multiUnitItems).filter((key) => key.startsWith('event_전투특기_'))).toHaveLength(19);
expect(multiUnitItems).not.toHaveProperty('event_전투특기_견고');
const moreEffectItems = readItemSlot(moreEffectBlank, 'item');
const composedAddonItems = readItemSlot(composedAddon, 'item');
expect(Object.keys(moreEffectItems).filter((key) => key.startsWith('event_전투특기_'))).toHaveLength(20);
expect(Object.keys(composedAddonItems).filter((key) => key.startsWith('event_전투특기_'))).toHaveLength(20);
expect(readItemSlot(moreEffectBlank, 'horse').che_명마_07_백마).toBe(4);
expect(readItemSlot(composedAddon, 'horse').che_명마_07_백마).toBe(2);
});
});
@@ -93,6 +93,49 @@ const canRun = await canConnectToDatabase(databaseUrl);
const describeDb = describe.runIf(canRun);
describeDb('scenario database seed', () => {
test('persists each blank-land scenario item contract without leaking the shared addon', async () => {
const readPersistedItemContract = async (targetScenarioId: number) => {
const { applied } = await seedScenarioToDatabase({
scenarioId: targetScenarioId,
databaseUrl,
});
const connector = createGamePostgresConnector({ url: databaseUrl });
await connector.connect();
try {
const worldState = await connector.prisma.worldState.findFirstOrThrow();
const config = worldState.config as Record<string, unknown>;
const scenarioConst = (config.const ?? {}) as Record<string, unknown>;
const allItems = (scenarioConst.allItems ?? {}) as Record<string, Record<string, number>>;
const items = allItems.item ?? {};
const availableSpecialWar = (scenarioConst.availableSpecialWar ?? []) as string[];
return {
applied,
battleTraitItemCount: Object.keys(items).filter((key) => key.startsWith('event_전투특기_')).length,
availableSpecialWar,
items,
};
} finally {
await connector.disconnect();
}
};
const ordinaryBlank = await readPersistedItemContract(0);
const legacySecretBlank = await readPersistedItemContract(902);
expect(ordinaryBlank).toMatchObject({
applied: true,
battleTraitItemCount: 0,
availableSpecialWar: [],
});
expect(legacySecretBlank.applied).toBe(true);
expect(legacySecretBlank.battleTraitItemCount).toBe(19);
expect(legacySecretBlank.availableSpecialWar).toHaveLength(19);
expect(legacySecretBlank.items).not.toHaveProperty('event_전투특기_견고');
expect(legacySecretBlank.availableSpecialWar).not.toContain('che_견고');
});
test('snapshots the complete opening inheritance balance before game activity', async () => {
const serverId = 'scenario-seeder-inheritance-baseline';
const userId = 'scenario-seeder-inheritance-user';