|
|
|
@@ -0,0 +1,70 @@
|
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
|
import { join } from 'node:path';
|
|
|
|
|
|
|
|
|
|
const schema = readFileSync(join(process.cwd(), 'prisma/schema.prisma'), 'utf8');
|
|
|
|
|
const migration = readFileSync(join(process.cwd(), 'prisma/migrations/20260621090000_init_v2_schema/migration.sql'), 'utf8');
|
|
|
|
|
const authMigration = readFileSync(join(process.cwd(), 'prisma/migrations/20260621093000_auth_sessions/migration.sql'), 'utf8');
|
|
|
|
|
|
|
|
|
|
describe('Prisma schema contract', () => {
|
|
|
|
|
it('defines every V2 core business table', () => {
|
|
|
|
|
const tables = [
|
|
|
|
|
'customers',
|
|
|
|
|
'customer_gateways',
|
|
|
|
|
'customer_gateway_policies',
|
|
|
|
|
'customer_recharges',
|
|
|
|
|
'vendors',
|
|
|
|
|
'vendor_recharges',
|
|
|
|
|
'vendor_gateways',
|
|
|
|
|
'vendor_gateway_forbidden_periods',
|
|
|
|
|
'vendor_gateway_codecs',
|
|
|
|
|
'vendor_gateway_prefix_rules',
|
|
|
|
|
'landing_line_groups',
|
|
|
|
|
'landing_line_group_items',
|
|
|
|
|
'raw_cdrs',
|
|
|
|
|
'rated_cdrs',
|
|
|
|
|
'recordings',
|
|
|
|
|
'quality_sampling_rules',
|
|
|
|
|
'quality_reviews',
|
|
|
|
|
'users',
|
|
|
|
|
'roles',
|
|
|
|
|
'permissions',
|
|
|
|
|
'user_roles',
|
|
|
|
|
'user_sessions',
|
|
|
|
|
'role_permissions',
|
|
|
|
|
'audit_logs',
|
|
|
|
|
'outbox_events',
|
|
|
|
|
'idempotency_keys'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const table of tables) {
|
|
|
|
|
expect(schema).toContain(`@@map("${table}")`);
|
|
|
|
|
expect(`${migration}\n${authMigration}`).toContain(`CREATE TABLE \`${table}\``);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('keeps money and idempotent facts constrained', () => {
|
|
|
|
|
expect(migration).toContain('DECIMAL(20, 6)');
|
|
|
|
|
expect(migration).toContain('UNIQUE INDEX `raw_cdrs_event_id_key`');
|
|
|
|
|
expect(migration).toContain('UNIQUE INDEX `raw_cdrs_call_id_ended_at_key`');
|
|
|
|
|
expect(migration).toContain('UNIQUE INDEX `customer_recharges_idempotency_key_key`');
|
|
|
|
|
expect(migration).toContain('UNIQUE INDEX `vendor_recharges_idempotency_key_key`');
|
|
|
|
|
expect(migration).toContain('UNIQUE INDEX `idempotency_keys_key_key`');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('models audit columns and outbox publishing state', () => {
|
|
|
|
|
expect(schema).toContain('createdAt');
|
|
|
|
|
expect(schema).toContain('updatedAt');
|
|
|
|
|
expect(schema).toContain('version');
|
|
|
|
|
expect(schema).toContain('model OutboxEvent');
|
|
|
|
|
expect(schema).toContain('enum OutboxStatus');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('stores refresh sessions as revocable token digests', () => {
|
|
|
|
|
expect(schema).toContain('model UserSession');
|
|
|
|
|
expect(authMigration).toContain('`refresh_token_hash` CHAR(64) NOT NULL');
|
|
|
|
|
expect(authMigration).toContain('UNIQUE INDEX `user_sessions_refresh_token_hash_key`');
|
|
|
|
|
expect(authMigration).toContain('`revoked_at` DATETIME(3) NULL');
|
|
|
|
|
expect(authMigration).toContain('`rotated_from_id` VARCHAR(40) NULL');
|
|
|
|
|
});
|
|
|
|
|
});
|