1import { assert, describe, it } from "@effect/vitest"
2import { Deferred, Effect, Latch, Layer } from "effect"
3import { DevToolsClient } from "effect/devtools"
4import { Socket } from "effect/socket"
5
6describe("DevToolsClient", () => {
7 it.effect("sends a short-lived span once in each state", () =>
8 Effect.gen(function*() {
9 const spans: Array<any> = []
10 const received = yield* Deferred.make<void>()
11 const write = (chunk: Uint8Array | string | Socket.CloseEvent) =>
12 Effect.sync(() => {
13 if (Socket.isCloseEvent(chunk)) return false
14 const text = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk)
15 for (const line of text.trim().split("\n")) {
16 if (line.length === 0) continue
17 const message = JSON.parse(line)
18 if (message._tag === "Span") spans.push(message)
19 }
20 return spans.length >= 2
21 }).pipe(
22 Effect.flatMap((done) => done ? Deferred.succeed(received, void 0) : Effect.void),
23 Effect.asVoid
24 )
25 const socket = Socket.make({
26 // sends one message, then stays idle until the reader scope closes,
27 // which fails the suspended pull as the reader contract requires
28 reader: Effect.gen(function*() {
29 const closed = Latch.makeUnsafe(false)
30 yield* Effect.addFinalizer(() => closed.open)
31 let sent = false
32 return {
33 pull: Effect.suspend(() => {
34 if (sent) {
35 return Effect.andThen(
36 closed.await,
37 Effect.fail(new Socket.SocketError({ reason: new Socket.SocketCloseError({ code: 1000 }) }))
38 )
39 }
40 sent = true
41 return Effect.succeed(["{\"_tag\":\"Pong\"}\n"] as const)
42 }),
43 upgrade: () =>
44 Effect.fail(
45 new Socket.SocketError({
46 reason: new Socket.SocketUpgradeError({})
47 })
48 )
49 }
50 }),
51 writer: Effect.succeed({
52 write,
53 writeAll: (chunks) => Effect.forEach(chunks, write, { discard: true })
54 })
55 })
56
57 yield* Effect.gen(function*() {
58 yield* Effect.void.pipe(Effect.withSpan("child"))
59 yield* Deferred.await(received)
60 }).pipe(
61 Effect.provide(
62 DevToolsClient.layerTracer.pipe(
63 Layer.provide(Layer.succeed(Socket.Socket, socket))
64 )
65 )
66 )
67
68 assert.deepStrictEqual(spans.map((span) => span.status._tag), ["Started", "Ended"])
69 }))
70})