File size: 2,871 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * Shared settings test fixture.
 *
 * Backs onto a real isolated SQLite DB + the production
 * `updateSettings → applyRuntimeSettings` pipeline so callers exercise the
 * actual hot-reload path. Tests that need to mock `getAuthzBypassSnapshot`
 * directly defeat the integration value of AC-7 — use this helper instead.
 *
 * Usage:
 * ```ts
 * import { setupSettingsFixture, mockSettings, resetSettingsMock } from "../_mocks/settings";
 * const fixture = setupSettingsFixture("authz-bypass");
 * test.beforeEach(() => fixture.resetStorage());
 * await mockSettings({ localOnlyManageScopeBypassEnabled: false });
 * ```
 */
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import type { Settings } from "../../../src/types/settings";

let activeFixture: SettingsFixture | null = null;

export interface SettingsFixture {
  testDataDir: string;
  resetStorage(): Promise<void>;
  cleanup(): void;
}

/**
 * Allocate an isolated DATA_DIR + reset DB state per test. Must run BEFORE
 * any DB modules are imported by the test file.
 */
export function setupSettingsFixture(slug: string): SettingsFixture {
  const testDataDir = fs.mkdtempSync(path.join(os.tmpdir(), `omr-settings-mock-${slug}-`));
  process.env.DATA_DIR = testDataDir;
  if (!process.env.API_KEY_SECRET) {
    process.env.API_KEY_SECRET = `test-settings-mock-secret-${Date.now()}`;
  }

  const fixture: SettingsFixture = {
    testDataDir,
    async resetStorage() {
      const core = await import("../../../src/lib/db/core.ts");
      const runtime = await import("../../../src/lib/config/runtimeSettings.ts");
      core.resetDbInstance();
      runtime.resetRuntimeSettingsStateForTests();
      fs.rmSync(testDataDir, { recursive: true, force: true });
      fs.mkdirSync(testDataDir, { recursive: true });
    },
    cleanup() {
      fs.rmSync(testDataDir, { recursive: true, force: true });
    },
  };
  activeFixture = fixture;
  return fixture;
}

/**
 * Write a partial Settings patch through the production
 * `updateSettings → applyRuntimeSettings` pipeline. Hot-reload side effects
 * (route guard snapshot, etc.) fire exactly as they do in `PATCH /api/settings`.
 */
export async function mockSettings(partial: Partial<Settings>): Promise<Record<string, unknown>> {
  const settingsDb = await import("../../../src/lib/db/settings.ts");
  return settingsDb.updateSettings(partial as Record<string, unknown>);
}

/**
 * Reset the in-process runtime snapshot to the cold-boot default. Called by
 * test `beforeEach` hooks that need a clean slate without nuking the whole
 * fixture directory.
 */
export async function resetSettingsMock(): Promise<void> {
  const runtime = await import("../../../src/lib/config/runtimeSettings.ts");
  runtime.resetRuntimeSettingsStateForTests();
  if (activeFixture) {
    await activeFixture.resetStorage();
  }
}