Files
insforge--insforge/backend/tests/unit/grant-internal-schema-select-defaults-migration.test.ts
wehub-resource-sync 3a28426bf4
Lint and Format Check / lint-and-format (push) Failing after 0s
Check Migrations / Check for duplicate migration numbers (push) Failing after 1s
CI Pre-merge Check / CI Pre-merge Check (push) Failing after 2m17s
chore: import upstream snapshot with attribution
2026-07-13 12:23:40 +08:00

106 lines
3.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const migrationPath = path.resolve(
currentDir,
'../../src/infra/database/migrations/055_grant-internal-schema-select-defaults.sql'
);
function readMigration(): string {
return fs.readFileSync(migrationPath, 'utf8');
}
// Schemas 055 manages. `system` is excluded (migration 054 owns its default
// privilege); `ai` is dropped here; `public` is excluded too.
const MANAGED_SCHEMAS = [
'auth',
'compute',
'deployments',
'email',
'functions',
'memory',
'payments',
'realtime',
'schedules',
'storage',
];
describe('055_grant-internal-schema-select-defaults migration', () => {
it('migration file exists', () => {
expect(fs.existsSync(migrationPath)).toBe(true);
});
it('backfills SELECT and sets default privileges for every managed schema', () => {
const sql = readMigration();
for (const schema of MANAGED_SCHEMAS) {
expect(sql).toContain(`GRANT SELECT ON ALL TABLES IN SCHEMA ${schema} TO project_admin`);
expect(sql).toContain(
`ALTER DEFAULT PRIVILEGES IN SCHEMA ${schema} GRANT SELECT ON TABLES TO project_admin`
);
}
});
it('grants schema USAGE on memory so its SELECT grant is usable', () => {
const sql = readMigration();
expect(sql).toContain('GRANT USAGE ON SCHEMA memory TO project_admin');
});
it('does not touch the system schema (owned by migration 054) or public', () => {
const sql = readMigration();
expect(sql).not.toMatch(/SCHEMA system\b/);
expect(sql).not.toMatch(/SCHEMA public\b/);
});
it('guards the role and every schema so a missing object is a no-op, not an error', () => {
const sql = readMigration();
expect(sql).toMatch(/IF NOT EXISTS \(SELECT 1 FROM pg_roles WHERE rolname = 'project_admin'\)/);
for (const schema of MANAGED_SCHEMAS) {
expect(sql).toMatch(new RegExp(`IF to_regnamespace\\('${schema}'\\) IS NOT NULL THEN`));
}
});
it('drops the deprecated ai schema safely (guarded, no CASCADE, no data loss)', () => {
const sql = readMigration();
expect(sql).toMatch(/IF to_regnamespace\('ai'\) IS NOT NULL THEN/);
expect(sql).toMatch(/DROP SCHEMA ai/);
expect(sql).not.toMatch(/DROP SCHEMA ai CASCADE/);
// A non-empty schema is left in place rather than erroring or cascading.
expect(sql).toMatch(/EXCEPTION WHEN dependent_objects_still_exist/);
// No SELECT/default grants for ai -- it is being removed.
expect(sql).not.toMatch(/ON ALL TABLES IN SCHEMA ai\b/);
});
it('default privileges are role-agnostic (no FOR ROLE) so they apply to the migration runner', () => {
const sql = readMigration();
expect(sql).not.toMatch(/DEFAULT PRIVILEGES FOR ROLE/);
});
it('is forward-only with no down migration', () => {
const sql = readMigration();
expect(sql).not.toMatch(/--\s*down migration/i);
expect(sql).not.toMatch(/REVOKE/i);
});
it('runs after migrations 045 (creates project_admin) and 050 (creates memory)', () => {
const migrationsDir = path.join(currentDir, '../../src/infra/database/migrations');
const files = fs
.readdirSync(migrationsDir)
.filter((f) => f.endsWith('.sql'))
.sort();
const idx045 = files.findIndex((f) => f.startsWith('045_'));
const idx050 = files.findIndex((f) => f.startsWith('050_'));
const idx055 = files.findIndex((f) =>
f.startsWith('055_grant-internal-schema-select-defaults')
);
expect(idx045).toBeGreaterThanOrEqual(0);
expect(idx050).toBeGreaterThanOrEqual(0);
expect(idx055).toBeGreaterThan(idx045);
expect(idx055).toBeGreaterThan(idx050);
});
});