1import * as Cloudflare from "@/Cloudflare/index.ts";
2import * as Test from "@/Test/Alchemy";
3import { expect } from "alchemy-test";
4import * as ConfigProvider from "effect/ConfigProvider";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { makeTracedWorker } from "./fixtures/native-tracing/worker.ts";
9import { expectUrlContains } from "../Utils/Http.ts";
10import { startOtlpCollector } from "../Utils/OtlpCollector.ts";
11
12/**
13 * `Cloudflare.Telemetry()` under `alchemy dev`: the Layer has no
14 * deploy-time effect on a local Worker and the compatibility date is not
15 * gated. Whether local workerd records the spans is the runtime's concern;
16 * these tests pin that the fixture (KV, a Durable Object, a queue consumer,
17 * forked fibers, failing and interrupted spans) runs unchanged under local
18 * emulation with the Layer provided, and that OTLP composition still works.
19 */
20const { test } = Test.make({
21 providers: Cloudflare.providers(),
22 dev: true,
23});
24
25const logLevel = Effect.provideService(
26 MinimumLogLevel,
27 process.env.DEBUG ? "Debug" : "Info",
28);
29
30test.provider(
31 "local Cloudflare.Telemetry() does not break the Worker under emulation",
32 (stack) =>
33 Effect.gen(function* () {
34 yield* stack.destroy();
35
36 const deployed = yield* stack.deploy(
37 Effect.gen(function* () {
38 return {
39 producer: yield* makeTracedWorker("NativeTracingProducer"),
40 };
41 }),
42 );
43 expect(deployed.producer.url).toMatch(/^http:\/\/localhost:\d+$/);
44
45 // Every event path the fixture exercises works under local emulation
46 // with the Layer provided: fetch, forked fibers, failing and
47 // interrupted spans, a Durable Object RPC, and a queue round trip.
48 const url = deployed.producer.url!;
49 yield* expectUrlContains(`${url}/work?id=local-work`, "native-did-work");
50 yield* expectUrlContains(`${url}/fanout?id=local-fanout`, "local-fanout");
51 yield* expectUrlContains(`${url}/exits?id=local-exits`, "local-exits");
52 yield* expectUrlContains(
53 `${url}/rpc?id=local-rpc`,
54 "native-did-rpc:do-ok",
55 );
56 yield* expectUrlContains(`${url}/enqueue?id=local-queue`, "local-queue");
57 yield* expectUrlContains(`${url}/sampled`, "native-did-sample");
58
59 yield* stack.destroy();
60 }).pipe(logLevel),
61 {
62 tags: [
63 "provider:cloudflare",
64 "provider:cloudflare:kv",
65 "provider:cloudflare:queue",
66 "provider:cloudflare:worker",
67 "local",
68 ],
69 timeout: 180_000,
70 },
71);
72
73test.provider(
74 "local Cloudflare.Telemetry() does not gate the compatibility date",
75 (stack) =>
76 Effect.gen(function* () {
77 yield* stack.destroy();
78
79 // Live deploys reject this date; dev just runs.
80 const worker = yield* stack.deploy(
81 Effect.gen(function* () {
82 return yield* makeTracedWorker("NativeTracingLocalOldDate", {
83 compatibility: { date: "2026-03-17" },
84 });
85 }),
86 );
87 expect(worker.url).toMatch(/^http:\/\/localhost:\d+$/);
88 yield* expectUrlContains(`${worker.url}/work`, "native-did-work");
89
90 yield* stack.destroy();
91 }).pipe(logLevel),
92 {
93 tags: [
94 "provider:cloudflare",
95 "provider:cloudflare:kv",
96 "provider:cloudflare:queue",
97 "provider:cloudflare:worker",
98 "local",
99 ],
100 timeout: 120_000,
101 },
102);
103
104const compositionTest = (order: "cf-last" | "otlp-last") =>
105 test.provider(
106 `local Cloudflare.Telemetry() + logs-only OTLP compose (${order})`,
107 (stack) =>
108 Effect.gen(function* () {
109 const collector = yield* startOtlpCollector();
110 const currentConfig = yield* ConfigProvider.ConfigProvider;
111 yield* stack.destroy();
112
113 const deployed = yield* stack
114 .deploy(
115 Effect.gen(function* () {
116 return {
117 producer: yield* makeTracedWorker(
118 order === "cf-last"
119 ? "NativeTracingComposeCfLast"
120 : "NativeTracingComposeOtlpLast",
121 { compatibility: { date: "2026-08-25" } },
122 ),
123 };
124 }),
125 )
126 .pipe(
127 Effect.provideService(
128 ConfigProvider.ConfigProvider,
129 ConfigProvider.orElse(
130 ConfigProvider.fromUnknown({
131 COLLECTOR_URL: collector.url,
132 COMPOSE_ORDER: order,
133 }),
134 currentConfig,
135 ),
136 ),
137 );
138
139 yield* expectUrlContains(deployed.producer.url!, "native-tracing-ok");
140 yield* expectUrlContains(
141 `${deployed.producer.url}/work`,
142 "native-did-work",
143 );
144
145 yield* Effect.sync(() => collector.completedRequests.value).pipe(
146 Effect.repeat({
147 schedule: Schedule.spaced("200 millis"),
148 until: (posts) => posts >= 1,
149 times: 40,
150 }),
151 );
152 expect(collector.completedRequests.value).toBeGreaterThanOrEqual(1);
153
154 yield* stack.destroy();
155 }).pipe(logLevel),
156 {
157 tags: [
158 "provider:cloudflare",
159 "provider:cloudflare:kv",
160 "provider:cloudflare:queue",
161 "provider:cloudflare:worker",
162 "local",
163 ],
164 timeout: 180_000,
165 },
166 );
167
168compositionTest("cf-last");
169compositionTest("otlp-last");