refactor(linkedin-studio): M0-2 — storage.ts getDataRoot + external default (ANALYTICS_ROOT alias kept)

This commit is contained in:
Kjell Tore Guttormsen 2026-06-18 11:12:11 +02:00
commit 760d4c9101
2 changed files with 91 additions and 42 deletions

View file

@ -3,25 +3,34 @@ import assert from "node:assert/strict";
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync } from "node:fs";
import { join, resolve } from "node:path";
import { tmpdir } from "node:os";
import { getAnalyticsRoot, findPluginRoot } from "../src/utils/storage.js";
import { getAnalyticsRoot, getDataRoot, findPluginRoot } from "../src/utils/storage.js";
// Regression lock for the fresh-clone / foreign-CWD analytics root resolution.
// The root must anchor on the .claude-plugin/plugin.json marker (correct by
// construction), NOT on a fragile count of "../" segments that silently breaks
// if the source layout is ever moved.
describe("analytics root resolution", () => {
// M0: the data root moved OUT of the plugin tree to the external per-user dir
// (~/.claude/linkedin-studio), behind getDataRoot(). getAnalyticsRoot() is now a
// thin alias = getDataRoot("analytics") that still honors the deprecated
// ANALYTICS_ROOT env for back-compat (D4). findPluginRoot stays — it now locates
// bundled read-only assets only (commit 798484b, the fresh-clone crash fix).
//
// The default must be stubbed via HOME + LINKEDIN_STUDIO_DATA save/restore so it
// never couples to the dev's real $HOME. Mirrors the .mjs twin test
// (hooks/scripts/__tests__/data-root.test.mjs) — keep them in sync.
describe("data root resolution", () => {
let tempDir: string | undefined;
const savedEnv = process.env.ANALYTICS_ROOT;
const saved = {
ANALYTICS_ROOT: process.env.ANALYTICS_ROOT,
LINKEDIN_STUDIO_DATA: process.env.LINKEDIN_STUDIO_DATA,
HOME: process.env.HOME,
USERPROFILE: process.env.USERPROFILE,
};
afterEach(() => {
if (tempDir && existsSync(tempDir)) {
rmSync(tempDir, { recursive: true, force: true });
}
tempDir = undefined;
if (savedEnv === undefined) {
delete process.env.ANALYTICS_ROOT;
} else {
process.env.ANALYTICS_ROOT = savedEnv;
for (const [k, v] of Object.entries(saved)) {
if (v === undefined) delete process.env[k];
else process.env[k] = v;
}
});
@ -46,32 +55,50 @@ describe("analytics root resolution", () => {
});
});
describe("getDataRoot", () => {
test("external default (HOME-stubbed): getDataRoot('analytics') = HOME/.claude/linkedin-studio/analytics", () => {
process.env.HOME = "/home/tester";
delete process.env.USERPROFILE;
delete process.env.LINKEDIN_STUDIO_DATA;
assert.equal(
getDataRoot("analytics"),
join("/home/tester", ".claude", "linkedin-studio", "analytics"),
);
});
test("LINKEDIN_STUDIO_DATA override wins", () => {
process.env.LINKEDIN_STUDIO_DATA = "/tmp/lis-data";
assert.equal(getDataRoot("analytics"), join("/tmp/lis-data", "analytics"));
});
});
describe("getAnalyticsRoot", () => {
test("honors ANALYTICS_ROOT override (resolved env path)", () => {
test("default (no env) now resolves to the EXTERNAL data dir, not in-plugin assets/analytics", () => {
process.env.HOME = "/home/tester";
delete process.env.USERPROFILE;
delete process.env.LINKEDIN_STUDIO_DATA;
delete process.env.ANALYTICS_ROOT;
assert.equal(
getAnalyticsRoot(),
join("/home/tester", ".claude", "linkedin-studio", "analytics"),
);
});
test("honors deprecated ANALYTICS_ROOT alias (resolved env path) for back-compat", () => {
tempDir = mkdtempSync(join(tmpdir(), "analytics-root-"));
process.env.ANALYTICS_ROOT = tempDir;
assert.equal(getAnalyticsRoot(), resolve(tempDir));
});
test("default (no env) anchors on the plugin dir, not scripts/analytics/assets", () => {
test("LINKEDIN_STUDIO_DATA drives the analytics root when ANALYTICS_ROOT is unset", () => {
delete process.env.ANALYTICS_ROOT;
process.env.LINKEDIN_STUDIO_DATA = "/tmp/lis-data";
const root = getAnalyticsRoot();
const suffix = join("assets", "analytics");
assert.ok(root.endsWith(suffix), `expected to end with ${suffix}, got ${root}`);
// The parent of assets/analytics must be the real plugin root (holds the marker),
// proving the root is NOT scripts/analytics/assets/analytics.
const pluginRoot = root.slice(0, root.length - (suffix.length + 1));
assert.ok(
existsSync(join(pluginRoot, ".claude-plugin", "plugin.json")),
`plugin marker missing under resolved root parent: ${pluginRoot}`,
);
assert.ok(
!pluginRoot.endsWith(join("scripts", "analytics")),
`root wrongly anchored under scripts/analytics: ${pluginRoot}`,
);
assert.equal(getAnalyticsRoot(), join("/tmp/lis-data", "analytics"));
});
});
});