diff --git a/src/lib/generators/python.ts b/src/lib/generators/python.ts index 2f92a9e..564193f 100644 --- a/src/lib/generators/python.ts +++ b/src/lib/generators/python.ts @@ -319,10 +319,10 @@ export class PythonGenerator extends BaseGenerator { const filename = `${timestamp}_${name}.py`; const upStatements = upSql - .map((sql) => ` op.execute("""${sql}""")`) + .map((sql) => ` op.execute(${JSON.stringify(sql)})`) .join("\n"); const downStatements = (downSql || []) - .map((sql) => ` op.execute("""${sql}""")`) + .map((sql) => ` op.execute(${JSON.stringify(sql)})`) .join("\n"); const content = `"""${name}""" diff --git a/test/lib/generators/python.test.ts b/test/lib/generators/python.test.ts index 4f37da3..dc5fbb4 100644 --- a/test/lib/generators/python.test.ts +++ b/test/lib/generators/python.test.ts @@ -1,4 +1,5 @@ import { expect, describe, test, beforeAll } from "@jest/globals"; +import { spawnSync } from "child_process"; import { PythonGenerator } from "../../../src/lib/generators/python"; import { GeneratorConfig, Entity, Relationship } from "../../../src/lib/types"; @@ -567,4 +568,50 @@ describe("PythonGenerator", () => { expect(files).toEqual([]); }); }); + + describe("migration generation (cli#148)", () => { + const hasPython = spawnSync("python3", ["--version"]).status === 0; + + // Run the migration under a stub alembic.op that records each statement. + const runMigration = ` +import json, sys, types +calls = [] +alembic = types.ModuleType("alembic") +alembic.op = types.SimpleNamespace(execute=calls.append) +sys.modules["alembic"] = alembic +exec(sys.stdin.read()) +upgrade() +downgrade() +print(json.dumps(calls)) +`; + + (hasPython ? test : test.skip)( + "writes a migration Python runs with the SQL unchanged", + async () => { + const upSql = [ + // ends in a quoted identifier + 'ALTER TABLE "task" DROP COLUMN "title"', + // backslash + String.raw`ALTER TABLE "task" ALTER COLUMN "path" SET DEFAULT 'C:\tmp'`, + // newlines + 'CREATE TABLE "note" (\n "id" SERIAL NOT NULL\n)', + ]; + const downSql = ['DROP TABLE "user"']; + const files = await generator.generateMigration({ + entities: [], + relationshipMap: {}, + upSql, + downSql, + }); + + expect(files).toHaveLength(1); + const result = spawnSync("python3", ["-c", runMigration], { + input: files[0].content, + encoding: "utf8", + }); + expect(result.stderr).toBe(""); + expect(JSON.parse(result.stdout)).toEqual([...upSql, ...downSql]); + } + ); + }); });