feat: export Catalog Studio assets and SQL to catalog repository
This commit is contained in:
1 parent
bfbf244141
commit
9ec9ab31ad
21 files changed
+1811
-514
No files matched your search
@@ -0,0 +1,103 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
CatalogExportQueue,
|
||||
publishCatalogFiles,
|
||||
recoverCatalogQueue,
|
||||
sqlValue,
|
||||
} from "./catalog-git-core";
|
||||
|
||||
describe("catalog export", () => {
|
||||
it("recovers queue entries owned by a terminated local process", async () => {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), "catalog-recovery-"));
|
||||
const deadPid = Number(
|
||||
execFileSync(
|
||||
process.execPath,
|
||||
["-e", "process.stdout.write(String(process.pid))"],
|
||||
{ encoding: "utf8" },
|
||||
),
|
||||
);
|
||||
const owner = JSON.stringify({ pid: deadPid, host: os.hostname() });
|
||||
await writeFile(path.join(root, "12345678.active"), owner);
|
||||
await writeFile(path.join(root, "worker.lock"), owner);
|
||||
await recoverCatalogQueue(root);
|
||||
const q = new CatalogExportQueue(root);
|
||||
expect(await q.batch()).toEqual(["12345678.pending"]);
|
||||
expect(await q.entries()).not.toContain("worker.lock");
|
||||
});
|
||||
|
||||
it("keeps new requests pending when an older snapshot completes", async () => {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), "catalog-queue-"));
|
||||
const q = new CatalogExportQueue(root);
|
||||
const finish = await q.begin();
|
||||
expect(await q.batch()).toBeNull();
|
||||
await finish();
|
||||
const batch = await q.batch();
|
||||
expect(batch).toHaveLength(1);
|
||||
await q.request();
|
||||
if (!batch) throw new Error("Expected pending batch");
|
||||
await q.complete(batch);
|
||||
expect(await q.batch()).toHaveLength(1);
|
||||
});
|
||||
it("encodes SQL strings without quote or backslash ambiguity", () => {
|
||||
expect(sqlValue("Valentine's \\ Day\n")).toBe(
|
||||
"CONVERT(X'56616c656e74696e652773205c204461790a' USING utf8mb4)",
|
||||
);
|
||||
expect(sqlValue(null)).toBe("NULL");
|
||||
expect(sqlValue(42)).toBe("42");
|
||||
expect(() => sqlValue(Number.NaN)).toThrow();
|
||||
});
|
||||
it("publishes to a bare remote, retries a failed push and avoids empty commits", async () => {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), "catalog-git-"));
|
||||
const remote = path.join(root, "remote.git");
|
||||
const checkout = path.join(root, "checkout");
|
||||
const git = (cwd: string, ...args: string[]) =>
|
||||
execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
|
||||
git(root, "init", "--bare", remote);
|
||||
git(root, "clone", remote, checkout);
|
||||
git(checkout, "checkout", "-b", "Beta-3");
|
||||
git(checkout, "config", "user.name", "Catalog test");
|
||||
git(checkout, "config", "user.email", "[email protected]");
|
||||
await writeFile(path.join(checkout, "readme.md"), "preserved");
|
||||
git(checkout, "add", "readme.md");
|
||||
git(checkout, "commit", "-m", "initial");
|
||||
git(checkout, "push", "-u", "origin", "Beta-3");
|
||||
const source = path.join(root, "chair.nitro");
|
||||
await writeFile(source, "bundle");
|
||||
const files = [
|
||||
{ source, target: "Gamedata/bundled/furniture/chair.nitro" },
|
||||
];
|
||||
const options = { checkout, remote, branch: "Beta-3", files };
|
||||
const first = await publishCatalogFiles(options);
|
||||
expect(await publishCatalogFiles(options)).toBe(first);
|
||||
await writeFile(source, "updated");
|
||||
git(
|
||||
checkout,
|
||||
"remote",
|
||||
"set-url",
|
||||
"--push",
|
||||
"origin",
|
||||
path.join(root, "missing.git"),
|
||||
);
|
||||
await expect(publishCatalogFiles(options)).rejects.toThrow();
|
||||
git(checkout, "remote", "set-url", "--push", "origin", remote);
|
||||
const retried = await publishCatalogFiles(options);
|
||||
expect(retried).not.toBe(first);
|
||||
expect(git(remote, "rev-parse", "Beta-3")).toBe(retried);
|
||||
expect(await readFile(path.join(checkout, "readme.md"), "utf8")).toBe(
|
||||
"preserved",
|
||||
);
|
||||
await expect(
|
||||
publishCatalogFiles({
|
||||
...options,
|
||||
files: [{ source, target: "../escape" }],
|
||||
}),
|
||||
).rejects.toThrow();
|
||||
await mkdir(path.join(checkout, "unrelated"));
|
||||
await writeFile(path.join(checkout, "unrelated", "local.txt"), "local");
|
||||
await expect(publishCatalogFiles(options)).rejects.toThrow(/clean/);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user