storage.test.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // ---------------------------------------------------------------------------
  2. // Ordner: lib
  3. // Datei: storage.test.js
  4. // Relativer Pfad: lib/storage.test.js
  5. // ---------------------------------------------------------------------------
  6. // tests/lib/storage.test.js
  7. import {
  8. describe,
  9. it,
  10. expect,
  11. beforeAll,
  12. afterAll,
  13. beforeEach,
  14. afterEach,
  15. vi,
  16. } from "vitest";
  17. import fs from "node:fs/promises";
  18. import os from "node:os";
  19. import path from "node:path";
  20. import {
  21. listBranches,
  22. listYears,
  23. listMonths,
  24. listDays,
  25. listFiles,
  26. __clearStorageCacheForTests,
  27. } from "@/lib/storage";
  28. let tmpRoot;
  29. beforeAll(async () => {
  30. // Create a unique temporary directory for the tests
  31. tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "storage-test-"));
  32. // Point NAS_ROOT_PATH to our temp directory
  33. process.env.NAS_ROOT_PATH = tmpRoot;
  34. // Create a fake directory tree:
  35. // tmpRoot/NL01/2024/10/23/test.pdf
  36. await fs.mkdir(path.join(tmpRoot, "NL01", "2024", "10", "23"), {
  37. recursive: true,
  38. });
  39. await fs.writeFile(
  40. path.join(tmpRoot, "NL01", "2024", "10", "23", "test.pdf"),
  41. "dummy-pdf-content"
  42. );
  43. // Add snapshot folder which should be ignored
  44. await fs.mkdir(path.join(tmpRoot, "@Recently-Snapshot"));
  45. });
  46. afterAll(async () => {
  47. // Clean up the temporary directory after tests
  48. await fs.rm(tmpRoot, { recursive: true, force: true });
  49. });
  50. beforeEach(() => {
  51. // RHL-006:
  52. // Storage functions now use a TTL cache. We clear it between tests to ensure
  53. // each test sees filesystem changes deterministically.
  54. __clearStorageCacheForTests();
  55. });
  56. afterEach(() => {
  57. // Safety: if a test enables fake timers, always restore real timers afterwards
  58. // to avoid affecting unrelated tests.
  59. vi.useRealTimers();
  60. });
  61. describe("storage: listBranches", () => {
  62. it("returns branch names and filters snapshots", async () => {
  63. const branches = await listBranches();
  64. expect(branches).toEqual(["NL01"]);
  65. });
  66. });
  67. describe("storage: year/month/day helpers", () => {
  68. it("returns years for a branch", async () => {
  69. const years = await listYears("NL01");
  70. expect(years).toEqual(["2024"]);
  71. });
  72. it("returns months for a branch/year", async () => {
  73. const months = await listMonths("NL01", "2024");
  74. expect(months).toEqual(["10"]);
  75. });
  76. it("returns days for a branch/year/month", async () => {
  77. const days = await listDays("NL01", "2024", "10");
  78. expect(days).toEqual(["23"]);
  79. });
  80. });
  81. describe("storage: listFiles", () => {
  82. it("returns PDF files with relativePath", async () => {
  83. const files = await listFiles("NL01", "2024", "10", "23");
  84. expect(files).toEqual([
  85. {
  86. name: "test.pdf",
  87. relativePath: "NL01/2024/10/23/test.pdf",
  88. },
  89. ]);
  90. });
  91. });
  92. describe("storage: micro-cache (RHL-006)", () => {
  93. it("keeps results stable within TTL and refreshes after TTL expires", async () => {
  94. // Use a dedicated folder so this test doesn't interfere with other fixtures.
  95. const dayDir = path.join(tmpRoot, "NL01", "2024", "10", "24");
  96. await fs.mkdir(dayDir, { recursive: true });
  97. // Initial file
  98. await fs.writeFile(path.join(dayDir, "a.pdf"), "a");
  99. // Fake timers allow us to advance time without real waiting.
  100. vi.useFakeTimers();
  101. const t0 = new Date("2025-01-01T00:00:00.000Z");
  102. vi.setSystemTime(t0);
  103. // First call populates the cache (TTL for files: 15s)
  104. const first = await listFiles("NL01", "2024", "10", "24");
  105. expect(first.map((f) => f.name)).toEqual(["a.pdf"]);
  106. // Add a new file while the cache is still valid.
  107. await fs.writeFile(path.join(dayDir, "b.pdf"), "b");
  108. // Second call should still return the cached result (b.pdf not visible yet).
  109. const second = await listFiles("NL01", "2024", "10", "24");
  110. expect(second.map((f) => f.name)).toEqual(["a.pdf"]);
  111. // Advance time beyond the TTL (15s) so the next call re-reads the directory.
  112. vi.setSystemTime(t0.getTime() + 16_000);
  113. const third = await listFiles("NL01", "2024", "10", "24");
  114. expect(third.map((f) => f.name)).toEqual(["a.pdf", "b.pdf"]);
  115. });
  116. });