76 lines
3.6 KiB
TypeScript
76 lines
3.6 KiB
TypeScript
import * as path from 'path';
|
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
import { InitSchema1700000000000 } from '../../backend/src/database/migrations/1700000000000-InitSchema';
|
|
import { SeedSystemData1700000000100 } from '../../backend/src/database/migrations/1700000000100-SeedSystemData';
|
|
import { AddCategoryTimestampsAndUniqueAbbrev1700000000200 } from '../../backend/src/database/migrations/1700000000200-AddCategoryTimestampsAndUniqueAbbrev';
|
|
import { UpdateSystemCategoryKeys1700000000300 } from '../../backend/src/database/migrations/1700000000300-UpdateSystemCategoryKeys';
|
|
import { DataSource } from 'typeorm';
|
|
import { UserEntity } from '../../backend/src/database/entities/user.entity';
|
|
import { SettingEntity } from '../../backend/src/database/entities/setting.entity';
|
|
import { CategoryEntity } from '../../backend/src/database/entities/category.entity';
|
|
import { ChallengeEntity } from '../../backend/src/database/entities/challenge.entity';
|
|
import { ChallengeFileEntity } from '../../backend/src/database/entities/challenge-file.entity';
|
|
import { SolveEntity } from '../../backend/src/database/entities/solve.entity';
|
|
import { RefreshTokenEntity } from '../../backend/src/database/entities/refresh-token.entity';
|
|
import { BlogPostEntity } from '../../backend/src/database/entities/blog-post.entity';
|
|
|
|
describe('Migrations', () => {
|
|
let dataSource: DataSource;
|
|
|
|
beforeAll(async () => {
|
|
dataSource = new DataSource({
|
|
type: 'better-sqlite3',
|
|
database: ':memory:',
|
|
entities: [UserEntity, SettingEntity, CategoryEntity, ChallengeEntity, ChallengeFileEntity, SolveEntity, RefreshTokenEntity, BlogPostEntity],
|
|
migrations: [
|
|
InitSchema1700000000000,
|
|
SeedSystemData1700000000100,
|
|
AddCategoryTimestampsAndUniqueAbbrev1700000000200,
|
|
UpdateSystemCategoryKeys1700000000300,
|
|
],
|
|
migrationsRun: true,
|
|
synchronize: false,
|
|
logging: false,
|
|
});
|
|
await dataSource.initialize();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await dataSource.destroy();
|
|
});
|
|
|
|
it('creates all required tables', async () => {
|
|
const tables = await dataSource.query("SELECT name FROM sqlite_master WHERE type='table'");
|
|
const names = tables.map((t: any) => t.name);
|
|
expect(names).toEqual(expect.arrayContaining([
|
|
'user', 'setting', 'category', 'challenge', 'challenge_file',
|
|
'solve', 'refresh_token', 'blog_post',
|
|
]));
|
|
});
|
|
|
|
it('seeds 6 system categories with unique system_keys', async () => {
|
|
const cats = await dataSource.getRepository(CategoryEntity).find();
|
|
const systemCats = cats.filter((c) => c.systemKey);
|
|
expect(systemCats.length).toBeGreaterThanOrEqual(6);
|
|
const keys = systemCats.map((c) => c.systemKey);
|
|
expect(new Set(keys).size).toBe(keys.length);
|
|
});
|
|
|
|
it('seeds default settings', async () => {
|
|
const settings = await dataSource.getRepository(SettingEntity).find();
|
|
const keys = settings.map((s) => s.key);
|
|
expect(keys).toEqual(expect.arrayContaining(['pageTitle', 'themeKey', 'eventStartUtc', 'eventEndUtc']));
|
|
});
|
|
|
|
it('enforces UNIQUE(challengeId, userId) on solve', async () => {
|
|
const indexes = await dataSource.query("SELECT name, sql FROM sqlite_master WHERE type='index' AND tbl_name='solve'");
|
|
const uq = indexes.find((i: any) => /UNIQUE.*challenge_id.*user_id/i.test(i.sql || ''));
|
|
expect(uq).toBeDefined();
|
|
});
|
|
|
|
it('enforces ON DELETE RESTRICT FK on solve', async () => {
|
|
const fks = await dataSource.query("PRAGMA foreign_key_list('solve')");
|
|
const restrict = fks.filter((f: any) => f.on_delete === 'RESTRICT');
|
|
expect(restrict.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
}); |