File size: 3,593 Bytes
b152fd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

const ORIGINAL_ENV = { ...process.env };
const CI_ENV_VARS = ["CI", "CONTINUOUS_INTEGRATION", "BUILD_NUMBER", "GITHUB_ACTIONS", "GITLAB_CI"];

function makeConfigPath(root: string, enabled: boolean): string {
  const configPath = path.join(root, ".paperclip", "config.json");
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
  fs.writeFileSync(configPath, JSON.stringify({
    $meta: {
      version: 1,
      updatedAt: "2026-03-31T00:00:00.000Z",
      source: "configure",
    },
    database: {
      mode: "embedded-postgres",
      embeddedPostgresDataDir: path.join(root, "runtime", "db"),
      embeddedPostgresPort: 54329,
      backup: {
        enabled: true,
        intervalMinutes: 60,
        retentionDays: 30,
        dir: path.join(root, "runtime", "backups"),
      },
    },
    logging: {
      mode: "file",
      logDir: path.join(root, "runtime", "logs"),
    },
    server: {
      deploymentMode: "local_trusted",
      exposure: "private",
      host: "127.0.0.1",
      port: 3100,
      allowedHostnames: [],
      serveUi: true,
    },
    auth: {
      baseUrlMode: "auto",
      disableSignUp: false,
    },
    telemetry: {
      enabled,
    },
    storage: {
      provider: "local_disk",
      localDisk: {
        baseDir: path.join(root, "runtime", "storage"),
      },
      s3: {
        bucket: "paperclip",
        region: "us-east-1",
        prefix: "",
        forcePathStyle: false,
      },
    },
    secrets: {
      provider: "local_encrypted",
      strictMode: false,
      localEncrypted: {
        keyFilePath: path.join(root, "runtime", "secrets", "master.key"),
      },
    },
  }, null, 2));
  return configPath;
}

describe("cli telemetry", () => {
  beforeEach(() => {
    process.env = { ...ORIGINAL_ENV };
    for (const key of CI_ENV_VARS) {
      delete process.env[key];
    }
    vi.stubGlobal("fetch", vi.fn(async () => ({ ok: true })));
  });

  afterEach(() => {
    process.env = { ...ORIGINAL_ENV };
    vi.unstubAllGlobals();
    vi.resetModules();
  });

  it("respects telemetry.enabled=false from the config file", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-cli-telemetry-"));
    const configPath = makeConfigPath(root, false);
    process.env.PAPERCLIP_HOME = path.join(root, "home");
    process.env.PAPERCLIP_INSTANCE_ID = "telemetry-test";

    const { initTelemetryFromConfigFile } = await import("../telemetry.js");
    const client = initTelemetryFromConfigFile(configPath);

    expect(client).toBeNull();
    expect(fs.existsSync(path.join(root, "home", "instances", "telemetry-test", "telemetry", "state.json"))).toBe(false);
  });

  it("creates telemetry state only after the first event is tracked", async () => {
    const root = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-cli-telemetry-"));
    process.env.PAPERCLIP_HOME = path.join(root, "home");
    process.env.PAPERCLIP_INSTANCE_ID = "telemetry-test";

    const { initTelemetry, flushTelemetry } = await import("../telemetry.js");
    const client = initTelemetry({ enabled: true });
    const statePath = path.join(root, "home", "instances", "telemetry-test", "telemetry", "state.json");

    expect(client).not.toBeNull();
    expect(fs.existsSync(statePath)).toBe(false);

    client!.track("install.started", { setupMode: "quickstart" });

    expect(fs.existsSync(statePath)).toBe(true);

    await flushTelemetry();
  });
});