MCPcopy Create free account
hub / github.com/SethGammon/Citadel / buildFixture

Function buildFixture

scripts/test-state-hygiene.js:74–128  ·  view source on GitHub ↗

* Build a complete fixture. Returns paths to everything created.

()

Source from the content-addressed store, hash-verified

72 * Build a complete fixture. Returns paths to everything created.
73 */
74function buildFixture() {
75 const root = fs.mkdtempSync(path.join(os.tmpdir(), 'citadel-hygiene-'));
76 const pluginDataDir = path.join(root, 'plugin-data');
77 const planningDir = path.join(root, '.planning');
78 fs.mkdirSync(pluginDataDir, { recursive: true });
79 fs.mkdirSync(path.join(planningDir, 'telemetry'), { recursive: true });
80
81 const fx = { root, pluginDataDir, planningDir };
82
83 // Consent markers: one fresh, one expired (6h TTL)
84 fx.sessionFresh = path.join(pluginDataDir, 'consent-session-externalActions.json');
85 fx.sessionExpired = path.join(pluginDataDir, 'consent-session-daemonSpend.json');
86 writeMarker(fx.sessionFresh, 1 * HOUR);
87 writeMarker(fx.sessionExpired, 7 * HOUR);
88
89 // One-time tokens: one fresh, one expired (120s TTL)
90 fx.onetimeFresh = path.join(pluginDataDir, 'consent-onetime-fleetSpawn.json');
91 fx.onetimeExpired = path.join(pluginDataDir, 'consent-onetime-externalActions.json');
92 writeMarker(fx.onetimeFresh, 30 * 1000);
93 writeMarker(fx.onetimeExpired, 5 * MINUTE);
94
95 // Decoys in plugin data dir: must never be touched
96 fx.decoyConfig = path.join(pluginDataDir, 'harness.json');
97 fs.writeFileSync(fx.decoyConfig, '{}');
98 fx.decoyWrongExt = path.join(pluginDataDir, 'consent-session-externalActions.txt');
99 fs.writeFileSync(fx.decoyWrongExt, 'not a marker');
100
101 // Lockdirs: stale top-level, stale nested, fresh top-level (10 min cutoff)
102 fx.lockStale = path.join(planningDir, 'watch-state.json.lock');
103 fx.lockStaleNested = path.join(planningDir, 'telemetry', 'some-state.json.lock');
104 fx.lockFresh = path.join(planningDir, 'fresh-state.json.lock');
105 makeLockdir(fx.lockStale, 30 * MINUTE);
106 makeLockdir(fx.lockStaleNested, 30 * MINUTE);
107 makeLockdir(fx.lockFresh, 0);
108
109 // Decoy: session-end.lock is a regular FILE (idempotency marker written by
110 // hooks_src/session-end.js). Stale mtime, but files are never removed.
111 fx.lockFileDecoy = path.join(planningDir, 'telemetry', 'session-end.lock');
112 fs.writeFileSync(fx.lockFileDecoy, 'session-id-123');
113 const old = (Date.now() - 2 * HOUR) / 1000;
114 fs.utimesSync(fx.lockFileDecoy, old, old);
115
116 // Decoy: symlinked .lock entry must be skipped, never followed or deleted.
117 // Junction type works on Windows without elevation. Best effort.
118 fx.symlinkTarget = path.join(root, 'symlink-target');
119 fx.lockSymlink = path.join(planningDir, 'evil.lock');
120 fx.hasSymlink = false;
121 try {
122 makeLockdir(fx.symlinkTarget, 30 * MINUTE);
123 fs.symlinkSync(fx.symlinkTarget, fx.lockSymlink, 'junction');
124 fx.hasSymlink = true;
125 } catch { /* symlink unsupported on this system, sub-asserts skipped */ }
126
127 return fx;
128}
129
130function destroyFixture(fx) {
131 try { fs.rmSync(fx.root, { recursive: true, force: true }); } catch { /* best effort */ }

Callers 1

mainFunction · 0.85

Calls 2

writeMarkerFunction · 0.85
makeLockdirFunction · 0.85

Tested by

no test coverage detected