feat: Admin Area Challenges: List, Import/Export and Add/Edit Modal
This commit is contained in:
@@ -5,6 +5,7 @@ import { SeedSystemData1700000000100 } from '../../backend/src/database/migratio
|
||||
import { AddCategoryTimestampsAndUniqueAbbrev1700000000200 } from '../../backend/src/database/migrations/1700000000200-AddCategoryTimestampsAndUniqueAbbrev';
|
||||
import { UpdateSystemCategoryKeys1700000000300 } from '../../backend/src/database/migrations/1700000000300-UpdateSystemCategoryKeys';
|
||||
import { RepairCategorySchemaAndSystemCategories1700000000400 } from '../../backend/src/database/migrations/1700000000400-RepairCategorySchemaAndSystemCategories';
|
||||
import { UpgradeChallengeAdminSchema1700000000500 } from '../../backend/src/database/migrations/1700000000500-UpgradeChallengeAdminSchema';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { UserEntity } from '../../backend/src/database/entities/user.entity';
|
||||
import { SettingEntity } from '../../backend/src/database/entities/setting.entity';
|
||||
@@ -29,6 +30,7 @@ describe('Migrations', () => {
|
||||
AddCategoryTimestampsAndUniqueAbbrev1700000000200,
|
||||
UpdateSystemCategoryKeys1700000000300,
|
||||
RepairCategorySchemaAndSystemCategories1700000000400,
|
||||
UpgradeChallengeAdminSchema1700000000500,
|
||||
],
|
||||
migrationsRun: true,
|
||||
synchronize: false,
|
||||
@@ -77,10 +79,11 @@ describe('Migrations', () => {
|
||||
expect(uq).toBeDefined();
|
||||
});
|
||||
|
||||
it('enforces ON DELETE RESTRICT FK on solve', async () => {
|
||||
it('keeps ON DELETE RESTRICT on solve.user_id (Job 861)', 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);
|
||||
const userFk = fks.find((f: any) => f.table === 'user');
|
||||
expect(userFk).toBeDefined();
|
||||
expect(userFk.on_delete).toBe('RESTRICT');
|
||||
});
|
||||
|
||||
it('creates the category table with created_at and updated_at columns (Job 889)', async () => {
|
||||
@@ -100,4 +103,53 @@ describe('Migrations', () => {
|
||||
expect(Array.isArray(rows)).toBe(true);
|
||||
expect(rows.length).toBeGreaterThanOrEqual(6);
|
||||
});
|
||||
|
||||
it('upgrades challenge to canonical enums and adds enabled/updated_at columns (Job 861)', async () => {
|
||||
const cols: any[] = await dataSource.query(`PRAGMA table_info("challenge")`);
|
||||
const names = new Set(cols.map((c) => c.name));
|
||||
expect(names.has('enabled')).toBe(true);
|
||||
expect(names.has('updated_at')).toBe(true);
|
||||
expect(names.has('description_md')).toBe(true);
|
||||
});
|
||||
|
||||
it('enforces unique lower-cased challenge names (Job 861)', async () => {
|
||||
const indexes = await dataSource.query(`SELECT name, sql FROM sqlite_master WHERE type='index' AND tbl_name='challenge'`);
|
||||
const uq = indexes.find((i: any) => /UNIQUE.*LOWER\(\s*(?:"name")\s*\)/i.test(i.sql || ''));
|
||||
expect(uq).toBeDefined();
|
||||
});
|
||||
|
||||
it('cascade-deletes challenge_file rows when challenge is deleted (Job 861)', async () => {
|
||||
const fks = await dataSource.query(`PRAGMA foreign_key_list('challenge_file')`);
|
||||
const cascade = fks.find((f: any) => f.on_delete === 'CASCADE');
|
||||
expect(cascade).toBeDefined();
|
||||
});
|
||||
|
||||
it('cascade-deletes solve rows when challenge is deleted (Job 861)', async () => {
|
||||
const fks = await dataSource.query(`PRAGMA foreign_key_list('solve')`);
|
||||
const cascade = fks.find((f: any) => f.on_delete === 'CASCADE');
|
||||
expect(cascade).toBeDefined();
|
||||
});
|
||||
|
||||
it('still enforces UNIQUE(challenge_id,user_id) on solve (Job 861)', 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('adds is_first/is_second/is_third columns to solve (Job 861)', async () => {
|
||||
const cols: any[] = await dataSource.query(`PRAGMA table_info("solve")`);
|
||||
const names = new Set(cols.map((c) => c.name));
|
||||
expect(names.has('is_first')).toBe(true);
|
||||
expect(names.has('is_second')).toBe(true);
|
||||
expect(names.has('is_third')).toBe(true);
|
||||
});
|
||||
|
||||
it('adds stored_filename/mime_type/size_bytes/created_at to challenge_file (Job 861)', async () => {
|
||||
const cols: any[] = await dataSource.query(`PRAGMA table_info("challenge_file")`);
|
||||
const names = new Set(cols.map((c) => c.name));
|
||||
expect(names.has('stored_filename')).toBe(true);
|
||||
expect(names.has('mime_type')).toBe(true);
|
||||
expect(names.has('size_bytes')).toBe(true);
|
||||
expect(names.has('created_at')).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user