194 lines · 6.9 KB · TypeScript
RawDownload
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import * as ConfigProvider from "effect/ConfigProvider";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as HttpServer from "effect/unstable/http/HttpServer";import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";import { HostProcessArchitecture, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import * as ServerConfig from "../config.ts";import { getTelemetryIdentifier } from "./Identify.ts";import * as AnalyticsService from "./AnalyticsService.ts"; interface RecordedBatchRequest {  readonly path: string;  readonly body: {    readonly batch?: ReadonlyArray<{      readonly event?: string;      readonly properties?: {        readonly index?: number;        readonly clientType?: string;        readonly serverOs?: string;        readonly serverArch?: string;        readonly serverAppVersion?: string;        readonly serverMode?: string;        readonly t3CodeVersion?: string;      };    }>;  } | null;} interface RecordedBatchBody {  readonly batch: ReadonlyArray<{    readonly event?: string;    readonly properties?: {      readonly index?: number;      readonly clientType?: string;      readonly serverOs?: string;      readonly serverArch?: string;      readonly serverAppVersion?: string;      readonly serverMode?: string;      readonly t3CodeVersion?: string;    };  }>;} it.layer(NodeServices.layer)("AnalyticsService test", (it) => {  it.effect("flush drains all buffered events across multiple batches", () =>    Effect.gen(function* () {      const capturedRequests: Array<RecordedBatchRequest> = [];      const serverConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), {        prefix: "t3-telemetry-base-",      });       const telemetryLayer = AnalyticsService.layer.pipe(Layer.provideMerge(serverConfigLayer));      const configLayer = ConfigProvider.layer(        ConfigProvider.fromUnknown({          T3CODE_TELEMETRY_ENABLED: true,          T3CODE_POSTHOG_KEY: "phc_test_key",          T3CODE_POSTHOG_HOST: "http://localhost",          T3CODE_TELEMETRY_FLUSH_BATCH_SIZE: 20,        }),      );      const batchServerLayer = HttpServer.serve(        Effect.gen(function* () {          const request = yield* HttpServerRequest.HttpServerRequest;          if (request.method !== "POST") {            return HttpServerResponse.empty({ status: 404 });          }           const payload = yield* request.json.pipe(            Effect.map((body) => body as RecordedBatchRequest["body"]),            Effect.orElseSucceed(() => null),          );           capturedRequests.push({ path: request.url, body: payload });           return HttpServerResponse.jsonUnsafe({});        }),      );      const runtimeLayer = telemetryLayer.pipe(        Layer.provide(configLayer),        Layer.provide(          Layer.mergeAll(            Layer.succeed(HostProcessPlatform, "linux"),            Layer.succeed(HostProcessArchitecture, "arm64"),          ),        ),        Layer.provideMerge(NodeHttpServer.layerTest),      );       yield* Effect.gen(function* () {        yield* Layer.launch(batchServerLayer).pipe(Effect.forkScoped);        const telemetryIdentifier = yield* getTelemetryIdentifier;        assert.equal(telemetryIdentifier !== null, true);        const analytics = yield* AnalyticsService.AnalyticsService;         for (let index = 0; index < 45; index += 1) {          yield* analytics.record("test.flush.drain", { index });        }         yield* analytics.flush;      }).pipe(Effect.provide(runtimeLayer));       const batchRequests = capturedRequests.filter(        (request): request is RecordedBatchRequest & { readonly body: RecordedBatchBody } =>          Array.isArray(request.body?.batch),      );      assert.equal(batchRequests.length, 3);      assert.equal(        batchRequests.every(          (request) => request.path.endsWith("/batch/") || request.path.endsWith("/batch"),        ),        true,      );      const deliveredIndexes = batchRequests.flatMap((request) =>        request.body.batch          .filter((event) => event.event === "test.flush.drain")          .map((event) => event.properties?.index)          .filter((index): index is number => typeof index === "number"),      );       const sorted = deliveredIndexes.toSorted((a, b) => a - b);      assert.equal(sorted.length, 45);      assert.deepEqual(        sorted,        Array.from({ length: 45 }, (_, index) => index),      );      assert.equal(        batchRequests.every((request) =>          request.body.batch.every((event) => event.properties?.clientType === "cli-web-client"),        ),        true,      );      assert.equal(        batchRequests.every((request) =>          request.body.batch.every(            (event) =>              event.properties?.serverOs === "Linux" &&              event.properties.serverArch === "arm64" &&              event.properties.serverAppVersion === event.properties.t3CodeVersion &&              event.properties.serverMode === "web",          ),        ),        true,      );    }),  );   it.effect("does not send batch requests when telemetry is disabled", () =>    Effect.gen(function* () {      const capturedPaths: Array<string> = [];      const serverConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), {        prefix: "t3-telemetry-disabled-",      });      const telemetryLayer = AnalyticsService.layer.pipe(Layer.provideMerge(serverConfigLayer));      const configLayer = ConfigProvider.layer(        ConfigProvider.fromUnknown({          T3CODE_TELEMETRY_ENABLED: false,          T3CODE_POSTHOG_KEY: "phc_test_key",          T3CODE_POSTHOG_HOST: "http://localhost",        }),      );      const batchServerLayer = HttpServer.serve(        Effect.gen(function* () {          const request = yield* HttpServerRequest.HttpServerRequest;          capturedPaths.push(request.url);          return HttpServerResponse.jsonUnsafe({});        }),      );      const runtimeLayer = telemetryLayer.pipe(        Layer.provide(configLayer),        Layer.provide(          Layer.mergeAll(            Layer.succeed(HostProcessPlatform, "linux"),            Layer.succeed(HostProcessArchitecture, "arm64"),          ),        ),        Layer.provideMerge(NodeHttpServer.layerTest),      );       yield* Effect.gen(function* () {        yield* Layer.launch(batchServerLayer).pipe(Effect.forkScoped);        const analytics = yield* AnalyticsService.AnalyticsService;        yield* analytics.record("test.disabled", { index: 1 });        yield* analytics.flush;      }).pipe(Effect.provide(runtimeLayer));       assert.deepEqual(capturedPaths, []);    }),  );});