293 lines · 9.4 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import type {  DesktopHostTelemetrySnapshot,  ResourceMonitorSnapshotEvent,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Stream from "effect/Stream"; import * as DesktopTelemetryReceiver from "../resourceTelemetry/DesktopTelemetryReceiver.ts";import * as NativeTelemetryClient from "../resourceTelemetry/NativeTelemetryClient.ts";import * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts";import * as ResourceTelemetry from "../resourceTelemetry/ResourceTelemetry.ts";import * as ProcessDiagnostics from "./ProcessDiagnostics.ts"; function makeNativeSnapshot(  processes: ResourceMonitorSnapshotEvent["processes"],): ResourceMonitorSnapshotEvent {  return {    version: 3,    type: "snapshot",    sequence: 1,    sampledAtUnixMs: DateTime.toEpochMillis(DateTime.makeUnsafe("2026-05-05T10:00:00.000Z")),    collectionDurationMicros: 250,    scannedProcessCount: processes.length,    retainedProcessCount: processes.length,    inaccessibleProcessCount: 0,    processes,  };} function makeTelemetryLayer(  snapshot: ResourceMonitorSnapshotEvent,  desktopSnapshot?: DesktopHostTelemetrySnapshot,) {  const nativeLayer = NativeTelemetryClient.layerTest({    sampleNow: Effect.succeed({ generation: 0, snapshot }),    health: Effect.succeed({      status: "healthy",      hello: Option.none(),      lastSampleAt: Option.some(DateTime.makeUnsafe(snapshot.sampledAtUnixMs)),      lastError: Option.none(),      restartCount: 0,      sampleIntervalMs: 1_000,    }),  });  const desktopLayer = desktopSnapshot    ? DesktopTelemetryReceiver.layerTest({        latest: Effect.succeedSome(desktopSnapshot),        health: Effect.succeed({          status: "healthy",          lastSampleAt: Option.some(DateTime.makeUnsafe(desktopSnapshot.sampledAtUnixMs)),          lastError: Option.none(),        }),      })    : DesktopTelemetryReceiver.layerTest();  return ResourceTelemetry.layer.pipe(    Layer.provide(Layer.mergeAll(nativeLayer, desktopLayer, ResourceAttribution.layer)),  );} describe("ProcessDiagnostics", () => {  it.effect("projects live process data from resource telemetry", () =>    Effect.gen(function* () {      const snapshot = makeNativeSnapshot([        {          pid: process.pid,          ppid: 1,          startTimeMs: 1_000,          runTimeMs: 60_000,          name: "node",          command: "t3 server",          status: "Running",          cpuPercent: 0,          cpuTimeMs: 100,          residentBytes: 1_024,          virtualBytes: 2_048,          ioReadBytes: 100,          ioWriteBytes: 200,          ioSemantics: "storage",        },        {          pid: 4_242,          ppid: process.pid,          startTimeMs: 2_000,          runTimeMs: 4_000,          name: "agent",          command: "codex app-server",          status: "Running",          cpuPercent: 1.5,          cpuTimeMs: 60,          residentBytes: 2_048,          virtualBytes: 4_096,          ioReadBytes: 300,          ioWriteBytes: 400,          ioSemantics: "storage",        },      ]);      const telemetryLayer = makeTelemetryLayer(snapshot);      const layer = ProcessDiagnostics.layer.pipe(Layer.provideMerge(telemetryLayer));       const diagnostics = yield* Effect.gen(function* () {        const processDiagnostics = yield* ProcessDiagnostics.ProcessDiagnostics;        return yield* processDiagnostics.read;      }).pipe(Effect.provide(layer));       expect(diagnostics.processes.map((process) => process.pid)).toEqual([4242]);      expect(diagnostics.processes[0]?.startTimeMs).toBe(2_000);      expect(diagnostics.processes[0]?.cpuPercent).toBe(1.5);      expect(diagnostics.processes[0]?.rssBytes).toBe(2_048);    }),  );   it.effect("rejects stale process identities before signaling", () =>    Effect.gen(function* () {      const snapshot = makeNativeSnapshot([]);      const telemetryLayer = makeTelemetryLayer(snapshot);      const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer));       const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe(        Effect.flatMap((processDiagnostics) =>          processDiagnostics.signal({            pid: 4_242,            startTimeMs: 2_000,            signal: "SIGINT",          }),        ),        Effect.provide(layer),      );       expect(result).toEqual({        pid: 4242,        signal: "SIGINT",        signaled: false,        message: Option.some("Process 4242 no longer matches the selected process identity."),      });    }),  );   it.effect("refuses to signal when a fresh identity check cannot be collected", () =>    Effect.gen(function* () {      const snapshot = makeNativeSnapshot([        {          pid: 4_242,          ppid: process.pid,          startTimeMs: 2_000,          runTimeMs: 4_000,          name: "agent",          command: "codex app-server",          status: "Running",          cpuPercent: 1.5,          cpuTimeMs: 60,          residentBytes: 2_048,          virtualBytes: 4_096,          ioReadBytes: 300,          ioWriteBytes: 400,          ioSemantics: "storage",        },      ]);      const staleTelemetry = yield* Effect.service(ResourceTelemetry.ResourceTelemetry).pipe(        Effect.flatMap((telemetry) => telemetry.latest),        Effect.provide(makeTelemetryLayer(snapshot)),      );      const telemetryLayer = Layer.succeed(        ResourceTelemetry.ResourceTelemetry,        ResourceTelemetry.ResourceTelemetry.of({          latest: Effect.succeed(staleTelemetry),          changes: Stream.empty,          subscribe: Effect.die("unused"),          readHistory: () => Effect.die("unused"),          refresh: Effect.fail(            new ResourceTelemetry.ResourceTelemetryRefreshFailed({              operation: "refresh",              cause: new Error("collector unavailable"),            }),          ),          validateProcessIdentity: () => Effect.die("unused"),          retry: Effect.die("unused"),        }),      );      const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer));       const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe(        Effect.flatMap((processDiagnostics) =>          processDiagnostics.signal({            pid: 4_242,            startTimeMs: 2_000,            signal: "SIGINT",          }),        ),        Effect.provide(layer),      );       expect(result).toEqual({        pid: 4_242,        signal: "SIGINT",        signaled: false,        message: Option.some(          "Could not refresh process 4242; refusing to signal a stale identity.",        ),      });    }),  );   it.effect("rejects Electron processes as signal targets", () =>    Effect.gen(function* () {      const sampledAtUnixMs = DateTime.toEpochMillis(        DateTime.makeUnsafe("2026-05-05T10:00:00.000Z"),      );      const snapshot = makeNativeSnapshot([        {          pid: 4_242,          ppid: 1,          startTimeMs: 2_000,          runTimeMs: 4_000,          name: "electron",          command: "electron",          status: "Running",          cpuPercent: 1.5,          cpuTimeMs: 60,          residentBytes: 2_048,          virtualBytes: 4_096,          ioReadBytes: 300,          ioWriteBytes: 400,          ioSemantics: "storage",        },      ]);      const sampledAt = DateTime.makeUnsafe(sampledAtUnixMs);      const telemetryLayer = makeTelemetryLayer(snapshot, {        version: 1,        type: "desktopTelemetry",        sequence: 1,        sampledAtUnixMs,        electronPid: 4_242,        power: {          source: "electron-main",          idle: "false",          idleSeconds: 0,          locked: "false",          suspended: false,          onBattery: "false",          lowPowerMode: "unknown",          thermalState: "nominal",          stale: false,          updatedAt: sampledAt,        },        speedLimitPercent: Option.none(),        electronProcesses: [          {            pid: 4_242,            creationTimeMs: 2_000,            type: "Browser",            name: "electron",            cpuPercent: 1.5,            idleWakeupsPerSecond: 0,            workingSetBytes: 2_048,            peakWorkingSetBytes: 2_048,          },        ],      });      const layer = ProcessDiagnostics.layer.pipe(Layer.provide(telemetryLayer));       const result = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe(        Effect.flatMap((processDiagnostics) =>          processDiagnostics.signal({            pid: 4_242,            startTimeMs: 2_000,            signal: "SIGKILL",          }),        ),        Effect.provide(layer),      );       expect(result).toEqual({        pid: 4_242,        signal: "SIGKILL",        signaled: false,        message: Option.some("Process 4242 is not a signalable T3 backend descendant."),      });       const diagnostics = yield* Effect.service(ProcessDiagnostics.ProcessDiagnostics).pipe(        Effect.flatMap((processDiagnostics) => processDiagnostics.read),        Effect.provide(layer),      );      expect(diagnostics.processes).toEqual([]);      expect(diagnostics.processCount).toBe(0);      expect(diagnostics.totalCpuPercent).toBe(0);      expect(diagnostics.totalRssBytes).toBe(0);    }),  );});