download_graph_db.test.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. const assert = require("node:assert/strict");
  2. const { spawnSync } = require("node:child_process");
  3. const fs = require("node:fs");
  4. const os = require("node:os");
  5. const path = require("node:path");
  6. const test = require("node:test");
  7. const transit = require("transit-js");
  8. const Database = require("better-sqlite3");
  9. const {
  10. parseFramedRows,
  11. parseCliArgs,
  12. sanitizeGraphIdForFilename,
  13. writeSnapshotSqlite,
  14. } = require("./download_graph_db");
  15. function runCli(args, env = {}) {
  16. return spawnSync(process.execPath, [path.join(__dirname, "download_graph_db.js"), ...args], {
  17. encoding: "utf8",
  18. env: { ...process.env, ...env },
  19. });
  20. }
  21. test("parseCliArgs accepts required args and defaults output", () => {
  22. const parsed = parseCliArgs([
  23. "--graph-id",
  24. "graph-1",
  25. "--admin-token",
  26. "admin-token-value",
  27. ]);
  28. assert.equal(parsed.graphId, "graph-1");
  29. assert.equal(parsed.baseUrl, "https://api.logseq.com");
  30. assert.equal(parsed.adminToken, "admin-token-value");
  31. assert.equal(parsed.output, path.resolve("tmp", "graph-graph-1.snapshot.sqlite"));
  32. });
  33. test("sanitizeGraphIdForFilename replaces unsafe chars", () => {
  34. assert.equal(sanitizeGraphIdForFilename("us-east-1:abc/def"), "us-east-1_abc_def");
  35. });
  36. test("CLI --help exits successfully", () => {
  37. const result = runCli(["--help"]);
  38. assert.equal(result.status, 0);
  39. assert.match(result.stdout, /Download a graph snapshot and store it as a local sqlite debug DB/);
  40. });
  41. test("CLI rejects missing --graph-id", () => {
  42. const result = runCli(["--admin-token", "admin-token-value"]);
  43. assert.equal(result.status, 1);
  44. assert.match(result.stderr, /Missing required --graph-id\./);
  45. });
  46. test("CLI rejects missing admin-token when env is absent", () => {
  47. const result = runCli(["--graph-id", "graph-1"], { DB_SYNC_ADMIN_TOKEN: "" });
  48. assert.equal(result.status, 1);
  49. assert.match(result.stderr, /Missing admin token/);
  50. });
  51. test("parseFramedRows decodes framed transit batches", () => {
  52. const writer = transit.writer("json");
  53. const rowsA = [
  54. [1, "line-1", null],
  55. [2, "line-2", null],
  56. ];
  57. const rowsB = [
  58. [1000606, "line-1000606", "{\"1\":[2,3]}"],
  59. ];
  60. const encodeFrame = (rows) => {
  61. const payload = Buffer.from(writer.write(rows), "utf8");
  62. const frame = Buffer.allocUnsafe(4 + payload.length);
  63. frame.writeUInt32BE(payload.length, 0);
  64. payload.copy(frame, 4);
  65. return frame;
  66. };
  67. const framed = Buffer.concat([encodeFrame(rowsA), encodeFrame(rowsB)]);
  68. const parsed = parseFramedRows(framed);
  69. assert.deepEqual(parsed, [...rowsA, ...rowsB]);
  70. });
  71. test("writeSnapshotSqlite writes kvs-only sqlite like local dbs", () => {
  72. const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "db-sync-download-test-"));
  73. const dbPath = path.join(tmpDir, "graph-debug.sqlite");
  74. const rows = [
  75. [1, "line-1", null],
  76. [2, "line-2", null],
  77. [1000606, "line-1000606", "{\"1\":[2,3]}"],
  78. ];
  79. writeSnapshotSqlite({
  80. outputPath: dbPath,
  81. rows,
  82. });
  83. const db = new Database(dbPath, { readonly: true });
  84. const tableNames = db
  85. .prepare("select name from sqlite_master where type = 'table' order by name")
  86. .all()
  87. .map((row) => row.name);
  88. const rowCount = db.prepare("select count(1) as count from kvs").get().count;
  89. const row1000606 = db.prepare("select addr, content, addresses from kvs where addr = 1000606").get();
  90. assert.deepEqual(tableNames, ["kvs"]);
  91. assert.equal(rowCount, 3);
  92. assert.equal(row1000606.addr, 1000606);
  93. assert.equal(row1000606.content, "line-1000606");
  94. assert.equal(row1000606.addresses, "{\"1\":[2,3]}");
  95. db.close();
  96. });