test(slice-0.4): assert migration SQL and top-bar silver/XP rendering
Adds a real-SQL-inspection test for CreateLootAndRewards1788600000000 (mirroring the visible-vertical-slice pattern) so the one-reward-per-combat unique index and other DB-level invariants can't be silently deleted without failing a test, and asserts the TopBar renders character.silver and character.experience values already present in the app.spec.ts fixture. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'reflect-metadata';
|
||||
import { getMetadataArgsStorage } from 'typeorm';
|
||||
import { getMetadataArgsStorage, QueryRunner } from 'typeorm';
|
||||
import { CreateLootAndRewards1788600000000 } from './1788600000000-CreateLootAndRewards';
|
||||
import { Character } from '../../characters/entities/character.entity';
|
||||
import { CharacterItem } from '../../items/entities/character-item.entity';
|
||||
import { ItemDefinition } from '../../items/entities/item-definition.entity';
|
||||
@@ -94,4 +95,53 @@ describe('loot and rewards schema', () => {
|
||||
expect(dropChance?.options.precision).toBe(5);
|
||||
expect(dropChance?.options.scale).toBe(4);
|
||||
});
|
||||
|
||||
it('emits the real SQL that enforces the schema invariants, not just entity decorators', async () => {
|
||||
// synchronize: false means entity decorators never touch the real database -
|
||||
// only the raw SQL emitted by the migration itself does. Assert on that SQL
|
||||
// directly so deleting a constraint here would fail this test.
|
||||
const query = jest.fn().mockResolvedValue(undefined);
|
||||
const queryRunner = { query } as unknown as QueryRunner;
|
||||
const migration = new CreateLootAndRewards1788600000000();
|
||||
|
||||
await migration.up(queryRunner);
|
||||
|
||||
const upQueries = query.mock.calls.map(([sql]) => sql as string);
|
||||
|
||||
expect(upQueries).toEqual(
|
||||
expect.arrayContaining([
|
||||
// The database half of the "one reward per combat" invariant (spec §7, §37).
|
||||
expect.stringContaining('CREATE UNIQUE INDEX "IDX_combat_rewards_combat"'),
|
||||
expect.stringContaining('CREATE UNIQUE INDEX "IDX_character_items_character_item"'),
|
||||
expect.stringContaining('CREATE UNIQUE INDEX "IDX_combat_reward_items_reward_item"'),
|
||||
expect.stringContaining('ALTER TABLE "characters" ADD COLUMN "silver"'),
|
||||
expect.stringContaining('ALTER TABLE "monster_definitions" ADD COLUMN "loot_table_id"'),
|
||||
]),
|
||||
);
|
||||
|
||||
const checkConstraints = upQueries.filter((sql) => sql.includes('CHECK ('));
|
||||
expect(checkConstraints.length).toBeGreaterThan(0);
|
||||
expect(
|
||||
checkConstraints.some(
|
||||
(sql) =>
|
||||
sql.includes('CHK_loot_table_entries_drop_chance') ||
|
||||
sql.includes('CHK_character_items_quantity'),
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
await migration.down(queryRunner);
|
||||
|
||||
const downQueries = query.mock.calls
|
||||
.slice(upQueries.length)
|
||||
.map(([sql]) => sql as string);
|
||||
|
||||
// Proves down() is real and reverses the up() migration, not a no-op.
|
||||
expect(downQueries).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.stringContaining('DROP TABLE "combat_rewards"'),
|
||||
expect.stringContaining('DROP TABLE "character_items"'),
|
||||
'ALTER TABLE "characters" DROP COLUMN "silver"',
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,6 +100,8 @@ describe('App', () => {
|
||||
);
|
||||
expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('Stufe 7');
|
||||
expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('52 / 80');
|
||||
expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('150');
|
||||
expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('320');
|
||||
});
|
||||
|
||||
it('redirects root and unknown routes to the world shell', () => {
|
||||
|
||||
Reference in New Issue
Block a user