@t1mmen/srtd
Version:
Supabase Repeatable Template Definitions (srtd): 🪄 Live-reloading SQL templates for Supabase DX. Make your database changes reviewable and migrations maintainable! 🚀
115 lines (114 loc) • 4.22 kB
JavaScript
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { TEST_FN_PREFIX } from '../__tests__/vitest.setup.js';
import { applyMigration } from './applyMigration.js';
import { connect } from './databaseConnection.js';
describe('applyMigration', () => {
let client;
const testContext = {
timestamp: Date.now(),
testFunctionName: '',
};
beforeAll(async () => {
client = await connect();
});
afterAll(async () => {
void client.release();
});
beforeEach(async () => {
testContext.testFunctionName = `${TEST_FN_PREFIX}${testContext.timestamp}`;
await client.query('BEGIN');
await client.query(`DROP FUNCTION IF EXISTS ${testContext.testFunctionName}()`);
await client.query('COMMIT');
});
afterEach(async () => {
await client.query('BEGIN');
await client.query(`DROP FUNCTION IF EXISTS ${testContext.testFunctionName}()`);
await client.query('COMMIT');
});
it('should successfully apply valid SQL', async () => {
const sql = `
CREATE FUNCTION ${testContext.testFunctionName}()
RETURNS void AS $$
BEGIN NULL; END;
$$ LANGUAGE plpgsql;
`;
const result = await applyMigration(sql, 'test-template');
expect(result).toBe(true);
// Verify function exists
const res = await client.query(`SELECT proname FROM pg_proc WHERE proname = $1`, [
testContext.testFunctionName,
]);
expect(res.rows).toHaveLength(1);
});
it('should fail and rollback on invalid SQL', async () => {
const sql = 'INVALID SQL SYNTAX';
const result = await applyMigration(sql, 'test-template');
expect(result).not.toBe(true);
expect(result).toMatchObject({
file: 'test-template',
error: expect.stringMatching(/syntax error/i),
});
});
it('should handle concurrent migrations with advisory locks', async () => {
const client2 = await connect();
const sql = `
DO $$
BEGIN
PERFORM pg_sleep(0.1);
RAISE NOTICE 'Executed after delay';
END $$;
`;
// Start two concurrent migrations
const [result1, result2] = await Promise.all([
applyMigration(sql, 'test-template'),
applyMigration(sql, 'test-template'),
]);
expect(result1).toBe(true);
expect(result2).toBe(true);
client2.release();
});
it('should handle transactions properly', async () => {
const sql = `
CREATE TABLE IF NOT EXISTS test_table_${testContext.timestamp} (id serial);
SELECT 1/0; -- Force error
`;
const result = await applyMigration(sql, 'test-template');
expect(result).not.toBe(true);
expect(result).toMatchObject({
error: expect.stringMatching(/division by zero/i),
});
// Verify table wasn't created (rollback worked)
const res = await client.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = $1
)`, [`test_table_${testContext.timestamp}`]);
expect(res.rows[0].exists).toBe(false);
});
it('should handle large SQL statements', async () => {
// Generate a large SQL statement
const statements = Array(1000)
.fill(0)
.map((_, i) => `SELECT ${i} as number;`)
.join('\n');
const result = await applyMigration(statements, 'test-template');
expect(result).toBe(true);
});
it('should handle errors in stored procedures', async () => {
const sql = `
CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}()
RETURNS void AS $$
BEGIN
RAISE EXCEPTION 'Custom error message';
END;
$$ LANGUAGE plpgsql;
SELECT ${testContext.testFunctionName}();
`;
const result = await applyMigration(sql, 'test-template');
expect(result).not.toBe(true);
expect(result).toMatchObject({
error: expect.stringMatching(/Custom error message/),
});
});
});
//# sourceMappingURL=applyMigrations.test.js.map