85 lines · 3.3 KB · TypeScript
RawDownload
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";import { expect, it } from "@effect/vitest";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Redacted from "effect/Redacted";import * as Schema from "effect/Schema";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 type { OtlpTracer } from "effect/unstable/observability"; import * as EnvironmentConnector from "./environments/EnvironmentConnector.ts";import { makeRelayTraceLayer } from "./observability.ts"; interface ExportedRequest {  readonly authorization: string | undefined;  readonly body: string;  readonly dataset: string | undefined;} const otlpAttributeValue = (value: {  readonly stringValue?: string | null;  readonly boolValue?: boolean | null;  readonly intValue?: string | number | null;  readonly doubleValue?: number | null;}) => value.stringValue ?? value.boolValue ?? value.intValue ?? value.doubleValue; const decodeJson = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown)); it.effect("exports schema error fields as span attributes", () =>  Effect.gen(function* () {    const exportedRequest = yield* Deferred.make<ExportedRequest>();    yield* HttpServer.serveEffect(      Effect.gen(function* () {        const request = yield* HttpServerRequest.HttpServerRequest;        yield* Deferred.succeed(exportedRequest, {          authorization: request.headers.authorization,          body: yield* request.text,          dataset: request.headers["x-axiom-dataset"],        });        return HttpServerResponse.empty({ status: 204 });      }),    );     yield* Effect.fail(      new EnvironmentConnector.EnvironmentConnectNotAuthorized({        environmentId: "environment-1",        operation: "connect",        reason: "managed_endpoint_allocation_not_ready",      }),    ).pipe(      Effect.withSpan("relay.test.schema_error"),      Effect.exit,      Effect.provide(        makeRelayTraceLayer({          tracesEndpoint: "/v1/traces",          tracesDatasetName: "relay-test-traces",          ingestToken: Redacted.make("test-token"),        }),      ),    );     const request = yield* Deferred.await(exportedRequest).pipe(Effect.timeout("1 second"));    const payload = (yield* decodeJson(request.body)) as OtlpTracer.TraceData;    const span = payload.resourceSpans      .flatMap((resourceSpan) => resourceSpan.scopeSpans)      .flatMap((scopeSpan) => scopeSpan.spans)      .find((candidate) => candidate.name === "relay.test.schema_error");    const attributes = Object.fromEntries(      (span?.attributes ?? []).map((attribute) => [        attribute.key,        otlpAttributeValue(attribute.value),      ]),    );     expect(request.authorization).toBe("Bearer test-token");    expect(request.dataset).toBe("relay-test-traces");    expect(attributes).toMatchObject({      "error.type": "EnvironmentConnectNotAuthorized",      "error.environmentId": "environment-1",      "error.operation": "connect",      "error.reason": "managed_endpoint_allocation_not_ready",    });  }).pipe(Effect.provide(NodeHttpServer.layerTest), Effect.scoped),);