File size: 3,717 Bytes
b01d539
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, sep } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { prepareLocalDatabasePath } from "./database-path.js";

const temporaryDirectories: string[] = [];

afterEach(async () => {
  await Promise.all(
    temporaryDirectories
      .splice(0)
      .map((directory) => rm(directory, { recursive: true, force: true })),
  );
});

describe("prepareLocalDatabasePath", () => {
  it("rejects a live database nested beneath persistent object storage", async () => {
    const root = await mkdtemp(join(tmpdir(), "oloka-path-"));
    temporaryDirectories.push(root);

    await expect(
      prepareLocalDatabasePath({
        databasePath: join(root, "data", "database", "oloka.db"),
        objectStorageRoot: join(root, "data"),
      }),
    ).rejects.toThrow(/object storage/i);
  });

  it("rejects traversal segments instead of normalizing them silently", async () => {
    const root = await mkdtemp(join(tmpdir(), "oloka-path-"));
    temporaryDirectories.push(root);

    await expect(
      prepareLocalDatabasePath({
        databasePath: `${root}${sep}local${sep}..${sep}database${sep}oloka.db`,
        objectStorageRoot: join(root, "objects"),
      }),
    ).rejects.toThrow(/traversal/i);
  });

  it("rejects relative and empty database paths", async () => {
    await expect(
      prepareLocalDatabasePath({
        databasePath: "database/oloka.db",
        objectStorageRoot: "/data",
      }),
    ).rejects.toThrow(/absolute/i);
    await expect(
      prepareLocalDatabasePath({
        databasePath: "",
        objectStorageRoot: "/data",
      }),
    ).rejects.toThrow(/absolute/i);
  });

  it("rejects directories and zero-byte database files", async () => {
    const root = await mkdtemp(join(tmpdir(), "oloka-path-"));
    temporaryDirectories.push(root);
    const directoryPath = join(root, "directory.db");
    await mkdir(directoryPath);
    await expect(
      prepareLocalDatabasePath({
        databasePath: directoryPath,
        objectStorageRoot: join(root, "objects"),
      }),
    ).rejects.toThrow(/not a file/i);

    const zeroBytePath = join(root, "empty.db");
    await writeFile(zeroBytePath, new Uint8Array());
    await expect(
      prepareLocalDatabasePath({
        databasePath: zeroBytePath,
        objectStorageRoot: join(root, "objects"),
      }),
    ).rejects.toThrow(/zero bytes/i);
  });

  it("rejects a symbolic-link component in the database path", async () => {
    const root = await mkdtemp(join(tmpdir(), "oloka-path-"));
    temporaryDirectories.push(root);
    const target = join(root, "target");
    const alias = join(root, "alias");
    await mkdir(target);
    await symlink(target, alias, "junction");

    await expect(
      prepareLocalDatabasePath({
        databasePath: join(alias, "oloka.db"),
        objectStorageRoot: join(root, "objects"),
      }),
    ).rejects.toThrow(/symbolic link/i);
  });

  it("creates only the safe local parent and preserves a valid existing file", async () => {
    const root = await mkdtemp(join(tmpdir(), "oloka-path-"));
    temporaryDirectories.push(root);
    const databasePath = join(root, "local", "database", "oloka.db");
    const objectStorageRoot = join(root, "objects");

    await expect(
      prepareLocalDatabasePath({ databasePath, objectStorageRoot }),
    ).resolves.toEqual({ databasePath, existed: false });
    await writeFile(databasePath, "sqlite-placeholder");
    await expect(
      prepareLocalDatabasePath({ databasePath, objectStorageRoot }),
    ).resolves.toEqual({ databasePath, existed: true });
  });
});