Skip to content

Commit 1aa75c9

Browse files
committed
legacy tests
1 parent 5aafa4f commit 1aa75c9

File tree

7 files changed

+418
-0
lines changed

7 files changed

+418
-0
lines changed

__fixtures__/generated/generated.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21097,6 +21097,24 @@
2109721097
"misc/quotes_etc-28.sql": "DO $$\nBEGIN\n RAISE NOTICE 'Line one\\nLine two';\nEND;\n$$ LANGUAGE plpgsql",
2109821098
"misc/quotes_etc-29.sql": "CREATE USER MAPPING FOR local_user SERVER \"foreign_server\" OPTIONS (user 'remote_user', password 'secret123')",
2109921099
"misc/quotes_etc-30.sql": "CREATE USER MAPPING FOR local_user SERVER foreign_server OPTIONS (user 'remote_user', password 'secret123')",
21100+
"misc/pg_catalog-1.sql": "SELECT json_object('{}')",
21101+
"misc/pg_catalog-2.sql": "SELECT * FROM generate_series(1, 5)",
21102+
"misc/pg_catalog-3.sql": "SELECT get_byte(E'\\\\xDEADBEEF'::bytea, 1)",
21103+
"misc/pg_catalog-4.sql": "SELECT now()",
21104+
"misc/pg_catalog-5.sql": "SELECT clock_timestamp()",
21105+
"misc/pg_catalog-6.sql": "SELECT to_char(now(), 'YYYY-MM-DD HH24:MI:SS')",
21106+
"misc/pg_catalog-7.sql": "SELECT json_build_object('name', 'Alice', 'age', 30)",
21107+
"misc/pg_catalog-8.sql": "SELECT pg_typeof(42), pg_typeof('hello'), pg_typeof(now())",
21108+
"misc/pg_catalog-9.sql": "SELECT substring('abcdefg' FROM 2 FOR 3)",
21109+
"misc/pg_catalog-10.sql": "SELECT replace('hello world', 'l', 'L')",
21110+
"misc/pg_catalog-11.sql": "SELECT length('yolo')",
21111+
"misc/pg_catalog-12.sql": "SELECT position('G' IN 'ChatGPT')",
21112+
"misc/pg_catalog-13.sql": "SELECT trim(' padded text ')",
21113+
"misc/pg_catalog-14.sql": "SELECT ltrim('---abc', '-')",
21114+
"misc/pg_catalog-15.sql": "SELECT array_agg(id) FROM (VALUES (1), (2), (3)) AS t(id)",
21115+
"misc/pg_catalog-16.sql": "SELECT string_agg(name, ', ') FROM (VALUES ('Alice'), ('Bob'), ('Carol')) AS t(name)",
21116+
"misc/pg_catalog-17.sql": "SELECT json_agg(name) FROM (VALUES ('A'), ('B')) AS t(name)",
21117+
"misc/legacy-1.sql": "CREATE VIEW superschema.app_authorized_grants AS\n SELECT\n coalesce(nullif(s[1], ''), 'PUBLIC') as grantee,\n relname as table_name,\n nspname as table_schema,\n string_agg(s[2], ', ') as privileges,\n relkind as table_type\n FROM\n pg_class c\n join pg_namespace n on n.oid = relnamespace\n join pg_roles r on r.oid = relowner,\n unnest(coalesce(relacl::text[], format('{%%s=arwdDxt/%%s}', rolname, rolname)::text[])) acl,\n regexp_split_to_array(acl, '=|/') s\n WHERE (s[1] = 'authenticated' or s[1] is null) and nspname not in ('pg_catalog', 'information_schema', 'pg_toast')\n GROUP BY grantee, table_name, table_schema, relkind\n ORDER BY relkind != 'r', relkind != 'v', relkind != 'm', relkind != 'i', relkind, nspname, relname",
2110021118
"misc/launchql-ext-types-1.sql": "CREATE DOMAIN attachment AS jsonb CHECK ( value ?& ARRAY['url', 'mime'] AND (value->>'url') ~ '^(https?)://[^\\s/$.?#].[^\\s]*$' )",
2110121119
"misc/launchql-ext-types-2.sql": "COMMENT ON DOMAIN attachment IS E'@name launchqlInternalTypeAttachment'",
2110221120
"misc/launchql-ext-types-3.sql": "CREATE DOMAIN email AS citext CHECK ( value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$' )",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CREATE VIEW superschema.app_authorized_grants AS
2+
SELECT
3+
coalesce(nullif(s[1], ''), 'PUBLIC') as grantee,
4+
relname as table_name,
5+
nspname as table_schema,
6+
string_agg(s[2], ', ') as privileges,
7+
relkind as table_type
8+
FROM
9+
pg_class c
10+
join pg_namespace n on n.oid = relnamespace
11+
join pg_roles r on r.oid = relowner,
12+
unnest(coalesce(relacl::text[], format('{%%s=arwdDxt/%%s}', rolname, rolname)::text[])) acl,
13+
regexp_split_to_array(acl, '=|/') s
14+
WHERE (s[1] = 'authenticated' or s[1] is null) and nspname not in ('pg_catalog', 'information_schema', 'pg_toast')
15+
GROUP BY grantee, table_name, table_schema, relkind
16+
ORDER BY relkind != 'r', relkind != 'v', relkind != 'm', relkind != 'i', relkind, nspname, relname;

__fixtures__/legacy/13-legacy-check.json

Lines changed: 112 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
import { FixtureTestUtils } from '../../test-utils';
3+
const fixtures = new FixtureTestUtils();
4+
5+
it('misc-legacy', async () => {
6+
await fixtures.runFixtureTests([
7+
"misc/legacy-1.sql"
8+
]);
9+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
import { FixtureTestUtils } from '../../test-utils';
3+
const fixtures = new FixtureTestUtils();
4+
5+
it('misc-pg_catalog', async () => {
6+
await fixtures.runFixtureTests([
7+
"misc/pg_catalog-1.sql",
8+
"misc/pg_catalog-2.sql",
9+
"misc/pg_catalog-3.sql",
10+
"misc/pg_catalog-4.sql",
11+
"misc/pg_catalog-5.sql",
12+
"misc/pg_catalog-6.sql",
13+
"misc/pg_catalog-7.sql",
14+
"misc/pg_catalog-8.sql",
15+
"misc/pg_catalog-9.sql",
16+
"misc/pg_catalog-10.sql",
17+
"misc/pg_catalog-11.sql",
18+
"misc/pg_catalog-12.sql",
19+
"misc/pg_catalog-13.sql",
20+
"misc/pg_catalog-14.sql",
21+
"misc/pg_catalog-15.sql",
22+
"misc/pg_catalog-16.sql",
23+
"misc/pg_catalog-17.sql"
24+
]);
25+
});
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { Parser } from '@pgsql/parser';
2+
import {
3+
PG13ToPG17Transformer,
4+
} from '../src/transformers-direct';
5+
import { deparse } from 'pgsql-deparser';
6+
import { LegacyFixtureTestUtils } from '../test-utils';
7+
import * as PG13 from '../src/13/types';
8+
import { cleanTree } from '../test-utils/clean-tree';
9+
10+
describe('Legacy 13 to 17 Direct Transformation Tests', () => {
11+
let legacyUtils: LegacyFixtureTestUtils;
12+
let pg13Parser: any;
13+
let transformer: PG13ToPG17Transformer;
14+
15+
beforeAll(() => {
16+
legacyUtils = new LegacyFixtureTestUtils();
17+
pg13Parser = new Parser({ version: 13 });
18+
transformer = new PG13ToPG17Transformer();
19+
});
20+
21+
it('should run all legacy tests', async () => {
22+
await legacyUtils.runAllLegacyTests(async (relativePath: string, sql: string) => {
23+
let pg13Ast: any;
24+
let transformedAst: any;
25+
let deparsed: string;
26+
let pg13Ast2: any;
27+
28+
try {
29+
// Step 1: Parse with PG13
30+
console.log(`🔍 [${relativePath}] Step 1: Parsing SQL with PG13...`);
31+
console.log(`📝 SQL: ${sql.substring(0, 100)}${sql.length > 100 ? '...' : ''}`);
32+
33+
pg13Ast = await pg13Parser.parse(sql);
34+
35+
if (!pg13Ast) {
36+
throw new Error('PG13 parser returned null/undefined');
37+
}
38+
39+
console.log(`✅ [${relativePath}] Step 1 SUCCESS: PG13 AST parsed (version: ${pg13Ast.version})`);
40+
expect(pg13Ast.version).toBe(130008);
41+
42+
} catch (error) {
43+
const errorMsg = [
44+
`❌ [${relativePath}] Step 1 FAILED: PG13 Parse Error`,
45+
`📝 SQL: ${sql}`,
46+
`🔥 Error: ${error instanceof Error ? error.message : String(error)}`,
47+
`📊 Error Type: ${error instanceof Error ? error.constructor.name : typeof error}`,
48+
error instanceof Error && error.stack ? `📚 Stack: ${error.stack}` : ''
49+
].filter(Boolean).join('\n ');
50+
51+
console.error(errorMsg);
52+
throw new Error(`PG13 Parse Failed: ${error instanceof Error ? error.message : String(error)}`);
53+
}
54+
55+
try {
56+
// Step 2: Transform PG13 → PG17
57+
console.log(`🔄 [${relativePath}] Step 2: Transforming PG13 → PG17...`);
58+
console.log(`📊 Input AST type: ${typeof pg13Ast}, statements: ${pg13Ast.stmts?.length || 'unknown'}`);
59+
60+
transformedAst = transformer.transform(pg13Ast as any as PG13.ParseResult);
61+
62+
if (!transformedAst) {
63+
throw new Error('Transformer returned null/undefined');
64+
}
65+
66+
console.log(`✅ [${relativePath}] Step 2 SUCCESS: AST transformed (version: ${transformedAst.version})`);
67+
expect(transformedAst.version).toBe(170004);
68+
69+
} catch (error) {
70+
const errorMsg = [
71+
`❌ [${relativePath}] Step 2 FAILED: Transformation Error`,
72+
`📝 Original SQL: ${sql}`,
73+
`📊 PG13 AST: ${JSON.stringify(pg13Ast, null, 2).substring(0, 500)}...`,
74+
`🔥 Error: ${error instanceof Error ? error.message : String(error)}`,
75+
`📊 Error Type: ${error instanceof Error ? error.constructor.name : typeof error}`,
76+
error instanceof Error && error.stack ? `📚 Stack: ${error.stack}` : ''
77+
].filter(Boolean).join('\n ');
78+
79+
console.error(errorMsg);
80+
throw new Error(`Transformation Failed: ${error instanceof Error ? error.message : String(error)}`);
81+
}
82+
83+
try {
84+
// Step 3: Deparse transformed AST
85+
console.log(`📝 [${relativePath}] Step 3: Deparsing transformed AST...`);
86+
console.log(`📊 Transformed AST type: ${typeof transformedAst}, statements: ${transformedAst.stmts?.length || 'unknown'}`);
87+
88+
deparsed = await deparse(transformedAst);
89+
90+
if (!deparsed || typeof deparsed !== 'string') {
91+
throw new Error(`Deparser returned invalid result: ${typeof deparsed} - ${deparsed}`);
92+
}
93+
94+
console.log(`✅ [${relativePath}] Step 3 SUCCESS: SQL deparsed (${deparsed.length} chars)`);
95+
console.log(`�� Deparsed SQL: ${deparsed.substring(0, 100)}${deparsed.length > 100 ? '...' : ''}`);
96+
expect(deparsed).toBeDefined();
97+
98+
} catch (error) {
99+
const errorMsg = [
100+
`❌ [${relativePath}] Step 3 FAILED: Deparse Error`,
101+
`📝 Original SQL: ${sql}`,
102+
`📊 Transformed AST: ${JSON.stringify(transformedAst, null, 2).substring(0, 500)}...`,
103+
`🔥 Error: ${error instanceof Error ? error.message : String(error)}`,
104+
`📊 Error Type: ${error instanceof Error ? error.constructor.name : typeof error}`,
105+
error instanceof Error && error.stack ? `📚 Stack: ${error.stack}` : ''
106+
].filter(Boolean).join('\n ');
107+
108+
console.error(errorMsg);
109+
throw new Error(`Deparse Failed: ${error instanceof Error ? error.message : String(error)}`);
110+
}
111+
112+
try {
113+
// Step 4: Parse deparsed SQL with PG13 again
114+
console.log(`🔍 [${relativePath}] Step 4: Re-parsing deparsed SQL with PG13...`);
115+
console.log(`📝 Deparsed SQL to parse: ${deparsed.substring(0, 100)}${deparsed.length > 100 ? '...' : ''}`);
116+
117+
pg13Ast2 = await pg13Parser.parse(deparsed);
118+
119+
if (!pg13Ast2) {
120+
throw new Error('PG13 parser returned null/undefined for deparsed SQL');
121+
}
122+
123+
console.log(`✅ [${relativePath}] Step 4 SUCCESS: Deparsed SQL re-parsed (version: ${pg13Ast2.version})`);
124+
expect(pg13Ast2.version).toBe(130008);
125+
126+
} catch (error) {
127+
const errorMsg = [
128+
`❌ [${relativePath}] Step 4 FAILED: Re-parse Error`,
129+
`📝 Original SQL: ${sql}`,
130+
`📝 Deparsed SQL: ${deparsed}`,
131+
`📊 Transformed AST: ${JSON.stringify(transformedAst, null, 2).substring(0, 300)}...`,
132+
`🔥 Error: ${error instanceof Error ? error.message : String(error)}`,
133+
`📊 Error Type: ${error instanceof Error ? error.constructor.name : typeof error}`,
134+
error instanceof Error && error.stack ? `📚 Stack: ${error.stack}` : ''
135+
].filter(Boolean).join('\n ');
136+
137+
console.error(errorMsg);
138+
throw new Error(`Re-parse Failed: ${error instanceof Error ? error.message : String(error)}`);
139+
}
140+
141+
try {
142+
// Step 5: Compare cleaned ASTs for equality
143+
console.log(`⚖️ [${relativePath}] Step 5: Comparing AST equality...`);
144+
145+
const cleanedOriginal = cleanTree(pg13Ast);
146+
const cleanedReparsed = cleanTree(pg13Ast2);
147+
148+
console.log(`📊 Original AST statements: ${cleanedOriginal.stmts?.length || 'unknown'}`);
149+
console.log(`📊 Reparsed AST statements: ${cleanedReparsed.stmts?.length || 'unknown'}`);
150+
151+
expect(cleanedReparsed).toEqual(cleanedOriginal);
152+
153+
console.log(`✅ [${relativePath}] Step 5 SUCCESS: ASTs are equal after round-trip`);
154+
155+
} catch (error) {
156+
const cleanedOriginal = cleanTree(pg13Ast);
157+
const cleanedReparsed = cleanTree(pg13Ast2);
158+
159+
const errorMsg = [
160+
`❌ [${relativePath}] Step 5 FAILED: AST Equality Error`,
161+
`📝 Original SQL: ${sql}`,
162+
`📝 Deparsed SQL: ${deparsed}`,
163+
`📊 Original AST (cleaned): ${JSON.stringify(cleanedOriginal, null, 2).substring(0, 400)}...`,
164+
`📊 Reparsed AST (cleaned): ${JSON.stringify(cleanedReparsed, null, 2).substring(0, 400)}...`,
165+
`🔥 Error: ${error instanceof Error ? error.message : String(error)}`,
166+
`📊 Error Type: ${error instanceof Error ? error.constructor.name : typeof error}`,
167+
error instanceof Error && error.stack ? `📚 Stack: ${error.stack}` : ''
168+
].filter(Boolean).join('\n ');
169+
170+
console.error(errorMsg);
171+
throw new Error(`AST Equality Failed: ${error instanceof Error ? error.message : String(error)}`);
172+
}
173+
174+
console.log(`🎉 [${relativePath}] ALL STEPS COMPLETED SUCCESSFULLY!`);
175+
});
176+
});
177+
178+
});
179+

packages/transform/test-utils/index.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,62 @@ export class FixtureTestUtils {
216216

217217
// Re-export the full transform flow utilities
218218
export { FullTransformFlowFixture } from './full-transform-flow';
219+
220+
/**
221+
* Legacy fixture utility for testing direct transformations
222+
* Uses the legacy SQL fixtures for testing direct transformers
223+
*/
224+
export class LegacyFixtureTestUtils {
225+
private fixtures: Record<string, string>;
226+
227+
constructor() {
228+
const LEGACY_JSON = path.join(__dirname, '../../../__fixtures__/legacy/13-legacy-check.json');
229+
this.fixtures = JSON.parse(readFileSync(LEGACY_JSON, 'utf-8'));
230+
}
231+
232+
getTestEntries(filters: string[]) {
233+
if (filters.length === 0) {
234+
return Object.entries(this.fixtures);
235+
}
236+
return Object.entries(this.fixtures).filter(([relPath]) =>
237+
filters.some(filter => relPath.includes(filter))
238+
);
239+
}
240+
241+
getAllTestEntries() {
242+
return Object.entries(this.fixtures);
243+
}
244+
245+
getTestEntry(key: string): string | undefined {
246+
return this.fixtures[key];
247+
}
248+
249+
async runLegacyTests(filters: string[], testFn: (relativePath: string, sql: string) => Promise<void>) {
250+
if (filters.length === 0) {
251+
console.log('no filters provided, skipping tests.');
252+
return;
253+
}
254+
255+
const entries = this.getTestEntries(filters);
256+
for (const [relativePath, sql] of entries) {
257+
try {
258+
await testFn(relativePath, sql);
259+
} catch (error: any) {
260+
console.error(`\n❌ FAILED: ${relativePath}`);
261+
throw error;
262+
}
263+
}
264+
}
265+
266+
async runAllLegacyTests(testFn: (relativePath: string, sql: string) => Promise<void>) {
267+
const entries = this.getTestEntries([]);
268+
for (const [relativePath, sql] of entries) {
269+
try {
270+
await testFn(relativePath, sql);
271+
} catch (error: any) {
272+
console.error(`\n❌ FAILED: ${relativePath}`);
273+
throw error;
274+
}
275+
}
276+
}
277+
}

0 commit comments

Comments
 (0)