1import * as Cause from "effect/Cause"
2import * as Context from "effect/Context"
3import * as Effect from "effect/Effect"
4import * as Equal from "effect/Equal"
5import * as Exit from "effect/Exit"
6import { pipe } from "effect/Function"
7import * as Hash from "effect/Hash"
8import * as Option from "effect/Option"
9import * as Result from "effect/Result"
10import assert from "node:assert/strict"
11import { describe, it } from "vitest"
12
13describe("Cause", () => {
14 describe("TypeId", () => {
15 it("is a string constant", () => {
16 assert.strictEqual(Cause.TypeId, "~effect/Cause")
17 })
18 })
19
20 describe("ReasonTypeId", () => {
21 it("is a string constant", () => {
22 assert.strictEqual(Cause.ReasonTypeId, "~effect/Cause/Reason")
23 })
24 })
25
26 describe("isCause", () => {
27 it("returns true for Cause values", () => {
28 assert.strictEqual(Cause.isCause(Cause.empty), true)
29 assert.strictEqual(Cause.isCause(Cause.fail("error")), true)
30 assert.strictEqual(Cause.isCause(Cause.die("defect")), true)
31 assert.strictEqual(Cause.isCause(Cause.interrupt(1)), true)
32 })
33
34 it("returns false for non-Cause values", () => {
35 assert.strictEqual(Cause.isCause(null), false)
36 assert.strictEqual(Cause.isCause(undefined), false)
37 assert.strictEqual(Cause.isCause("string"), false)
38 assert.strictEqual(Cause.isCause(42), false)
39 assert.strictEqual(Cause.isCause({}), false)
40 assert.strictEqual(Cause.isCause([]), false)
41 })
42 })
43
44 describe("isReason", () => {
45 it("returns true for Reason values", () => {
46 assert.strictEqual(Cause.isReason(Cause.makeFailReason("error")), true)
47 assert.strictEqual(Cause.isReason(Cause.makeDieReason("defect")), true)
48 assert.strictEqual(Cause.isReason(Cause.makeInterruptReason(1)), true)
49 })
50
51 it("returns true for reasons extracted from a cause", () => {
52 const reason = Cause.fail("error").reasons[0]
53 assert.strictEqual(Cause.isReason(reason), true)
54 })
55
56 it("returns false for non-Reason values", () => {
57 assert.strictEqual(Cause.isReason(null), false)
58 assert.strictEqual(Cause.isReason("string"), false)
59 assert.strictEqual(Cause.isReason(Cause.fail("error")), false)
60 })
61 })
62
63 describe("isFailReason", () => {
64 it("narrows Fail reasons", () => {
65 const reason = Cause.fail("error").reasons[0]
66 assert.strictEqual(Cause.isFailReason(reason), true)
67 })
68
69 it("rejects Die and Interrupt reasons", () => {
70 const die = Cause.die("defect").reasons[0]
71 const interrupt = Cause.interrupt(1).reasons[0]
72 assert.strictEqual(Cause.isFailReason(die), false)
73 assert.strictEqual(Cause.isFailReason(interrupt), false)
74 })
75 })
76
77 describe("isDieReason", () => {
78 it("narrows Die reasons", () => {
79 const reason = Cause.die("defect").reasons[0]
80 assert.strictEqual(Cause.isDieReason(reason), true)
81 })
82
83 it("rejects Fail and Interrupt reasons", () => {
84 const fail = Cause.fail("error").reasons[0]
85 const interrupt = Cause.interrupt(1).reasons[0]
86 assert.strictEqual(Cause.isDieReason(fail), false)
87 assert.strictEqual(Cause.isDieReason(interrupt), false)
88 })
89 })
90
91 describe("isInterruptReason", () => {
92 it("narrows Interrupt reasons", () => {
93 const reason = Cause.interrupt(1).reasons[0]
94 assert.strictEqual(Cause.isInterruptReason(reason), true)
95 })
96
97 it("rejects Fail and Die reasons", () => {
98 const fail = Cause.fail("error").reasons[0]
99 const die = Cause.die("defect").reasons[0]
100 assert.strictEqual(Cause.isInterruptReason(fail), false)
101 assert.strictEqual(Cause.isInterruptReason(die), false)
102 })
103 })
104
105 describe("empty", () => {
106 it("has no reasons", () => {
107 assert.strictEqual(Cause.empty.reasons.length, 0)
108 })
109
110 it("is a Cause", () => {
111 assert.strictEqual(Cause.isCause(Cause.empty), true)
112 })
113 })
114
115 describe("fail", () => {
116 it("creates a cause with a single Fail reason", () => {
117 const cause = Cause.fail("error")
118 assert.strictEqual(cause.reasons.length, 1)
119 assert.strictEqual(Cause.isFailReason(cause.reasons[0]), true)
120 })
121
122 it("preserves the error value", () => {
123 const cause = Cause.fail("error")
124 const reason = cause.reasons[0]
125 if (Cause.isFailReason(reason)) {
126 assert.strictEqual(reason.error, "error")
127 }
128 })
129
130 it("works with various error types", () => {
131 const obj = { key: "value" }
132 const cause = Cause.fail(obj)
133 const reason = cause.reasons[0]
134 if (Cause.isFailReason(reason)) {
135 assert.strictEqual(reason.error, obj)
136 }
137 })
138 })
139
140 describe("die", () => {
141 it("creates a cause with a single Die reason", () => {
142 const cause = Cause.die("defect")
143 assert.strictEqual(cause.reasons.length, 1)
144 assert.strictEqual(Cause.isDieReason(cause.reasons[0]), true)
145 })
146
147 it("preserves the defect value", () => {
148 const cause = Cause.die("defect")
149 const reason = cause.reasons[0]
150 if (Cause.isDieReason(reason)) {
151 assert.strictEqual(reason.defect, "defect")
152 }
153 })
154
155 it("works with Error instances", () => {
156 const err = new Error("boom")
157 const cause = Cause.die(err)
158 const reason = cause.reasons[0]
159 if (Cause.isDieReason(reason)) {
160 assert.strictEqual(reason.defect, err)
161 }
162 })
163 })
164
165 describe("interrupt", () => {
166 it("creates a cause with a single Interrupt reason", () => {
167 const cause = Cause.interrupt(123)
168 assert.strictEqual(cause.reasons.length, 1)
169 assert.strictEqual(Cause.isInterruptReason(cause.reasons[0]), true)
170 })
171
172 it("preserves the fiber ID", () => {
173 const cause = Cause.interrupt(42)
174 const reason = cause.reasons[0]
175 if (Cause.isInterruptReason(reason)) {
176 assert.strictEqual(reason.fiberId, 42)
177 }
178 })
179
180 it("allows undefined fiber ID", () => {
181 const cause = Cause.interrupt()
182 const reason = cause.reasons[0]
183 if (Cause.isInterruptReason(reason)) {
184 assert.strictEqual(reason.fiberId, undefined)
185 }
186 })
187 })
188
189 describe("fromReasons", () => {
190 it("creates a cause from an array of reasons", () => {
191 const reasons = [
192 Cause.makeFailReason("err1"),
193 Cause.makeFailReason("err2")
194 ]
195 const cause = Cause.fromReasons(reasons)
196 assert.strictEqual(cause.reasons.length, 2)
197 })
198
199 it("creates empty cause from empty array", () => {
200 const cause = Cause.fromReasons([])
201 assert.strictEqual(cause.reasons.length, 0)
202 })
203
204 it("supports mixed reason types", () => {
205 const reasons = [
206 Cause.makeFailReason("error"),
207 Cause.makeDieReason("defect"),
208 Cause.makeInterruptReason(1)
209 ]
210 const cause = Cause.fromReasons(reasons)
211 assert.strictEqual(cause.reasons.length, 3)
212 })
213 })
214
215 describe("makeFailReason", () => {
216 it("creates a Fail reason with _tag and error", () => {
217 const reason = Cause.makeFailReason("error")
218 assert.strictEqual(reason._tag, "Fail")
219 assert.strictEqual(reason.error, "error")
220 })
221 })
222
223 describe("makeDieReason", () => {
224 it("creates a Die reason with _tag and defect", () => {
225 const reason = Cause.makeDieReason("defect")
226 assert.strictEqual(reason._tag, "Die")
227 assert.strictEqual(reason.defect, "defect")
228 })
229 })
230
231 describe("makeInterruptReason", () => {
232 it("creates an Interrupt reason with _tag and fiberId", () => {
233 const reason = Cause.makeInterruptReason(42)
234 assert.strictEqual(reason._tag, "Interrupt")
235 assert.strictEqual(reason.fiberId, 42)
236 })
237
238 it("allows undefined fiberId", () => {
239 const reason = Cause.makeInterruptReason()
240 assert.strictEqual(reason._tag, "Interrupt")
241 assert.strictEqual(reason.fiberId, undefined)
242 })
243 })
244
245 describe("hasFails", () => {
246 it("returns true when cause has Fail reasons", () => {
247 assert.strictEqual(Cause.hasFails(Cause.fail("error")), true)
248 })
249
250 it("returns false when cause has no Fail reasons", () => {
251 assert.strictEqual(Cause.hasFails(Cause.die("defect")), false)
252 assert.strictEqual(Cause.hasFails(Cause.interrupt(1)), false)
253 assert.strictEqual(Cause.hasFails(Cause.empty), false)
254 })
255
256 it("returns true for combined cause with at least one Fail", () => {
257 const combined = Cause.combine(Cause.die("defect"), Cause.fail("error"))
258 assert.strictEqual(Cause.hasFails(combined), true)
259 })
260 })
261
262 describe("hasDies", () => {
263 it("returns true when cause has Die reasons", () => {
264 assert.strictEqual(Cause.hasDies(Cause.die("defect")), true)
265 })
266
267 it("returns false when cause has no Die reasons", () => {
268 assert.strictEqual(Cause.hasDies(Cause.fail("error")), false)
269 assert.strictEqual(Cause.hasDies(Cause.interrupt(1)), false)
270 assert.strictEqual(Cause.hasDies(Cause.empty), false)
271 })
272 })
273
274 describe("hasInterrupts", () => {
275 it("returns true when cause has Interrupt reasons", () => {
276 assert.strictEqual(Cause.hasInterrupts(Cause.interrupt(1)), true)
277 })
278
279 it("returns false when cause has no Interrupt reasons", () => {
280 assert.strictEqual(Cause.hasInterrupts(Cause.fail("error")), false)
281 assert.strictEqual(Cause.hasInterrupts(Cause.die("defect")), false)
282 assert.strictEqual(Cause.hasInterrupts(Cause.empty), false)
283 })
284 })
285
286 describe("hasInterruptsOnly", () => {
287 it("returns true when all reasons are Interrupts", () => {
288 assert.strictEqual(Cause.hasInterruptsOnly(Cause.interrupt(1)), true)
289 const combined = Cause.combine(Cause.interrupt(1), Cause.interrupt(2))
290 assert.strictEqual(Cause.hasInterruptsOnly(combined), true)
291 })
292
293 it("returns false for empty cause", () => {
294 assert.strictEqual(Cause.hasInterruptsOnly(Cause.empty), false)
295 })
296
297 it("returns false when mixed with other reason types", () => {
298 const combined = Cause.combine(Cause.interrupt(1), Cause.fail("error"))
299 assert.strictEqual(Cause.hasInterruptsOnly(combined), false)
300 })
301 })
302
303 describe("squash", () => {
304 it("returns the first Fail even after an interrupt and a defect", () => {
305 const cause = Cause.fromReasons([
306 Cause.makeInterruptReason(1),
307 Cause.makeDieReason("defect"),
308 Cause.makeFailReason("first"),
309 Cause.makeFailReason("second")
310 ])
311 assert.strictEqual(Cause.squash(cause), "first")
312 })
313
314 it("returns the first Die when there are no Fails", () => {
315 const cause = Cause.fromReasons([
316 Cause.makeInterruptReason(1),
317 Cause.makeDieReason("first"),
318 Cause.makeDieReason("second")
319 ])
320 assert.strictEqual(Cause.squash(cause), "first")
321 })
322
323 it("reports an interrupt-only cause", () => {
324 const result = Cause.squash(Cause.interrupt(1))
325 assert.ok(result instanceof Error)
326 assert.strictEqual(result.message, "All fibers interrupted without error")
327 })
328
329 it("reports an empty cause", () => {
330 const result = Cause.squash(Cause.empty)
331 assert.ok(result instanceof Error)
332 assert.strictEqual(result.message, "Empty cause")
333 })
334 })
335
336 describe("findFail", () => {
337 it("returns success with the first Fail reason", () => {
338 const result = Cause.findFail(Cause.fail("error"))
339 assert.strictEqual(Result.isSuccess(result), true)
340 if (Result.isSuccess(result)) {
341 assert.strictEqual(result.success.error, "error")
342 assert.strictEqual(result.success._tag, "Fail")
343 }
344 })
345
346 it("returns failure when no Fail reason exists", () => {
347 const result = Cause.findFail(Cause.die("defect"))
348 assert.strictEqual(Result.isFailure(result), true)
349 })
350
351 it("returns failure for empty cause", () => {
352 const result = Cause.findFail(Cause.empty)
353 assert.strictEqual(Result.isFailure(result), true)
354 })
355 })
356
357 describe("findError", () => {
358 it("returns success with the error value", () => {
359 const result = Cause.findError(Cause.fail("error"))
360 assert.strictEqual(Result.isSuccess(result), true)
361 if (Result.isSuccess(result)) {
362 assert.strictEqual(result.success, "error")
363 }
364 })
365
366 it("returns failure when no Fail reason exists", () => {
367 const result = Cause.findError(Cause.die("defect"))
368 assert.strictEqual(Result.isFailure(result), true)
369 })
370 })
371
372 describe("findErrorOption", () => {
373 it("returns Some with the error value", () => {
374 const result = Cause.findErrorOption(Cause.fail("error"))
375 assert.strictEqual(Option.isSome(result), true)
376 if (Option.isSome(result)) {
377 assert.strictEqual(result.value, "error")
378 }
379 })
380
381 it("returns None when no Fail reason exists", () => {
382 assert.strictEqual(Option.isNone(Cause.findErrorOption(Cause.die("defect"))), true)
383 assert.strictEqual(Option.isNone(Cause.findErrorOption(Cause.empty)), true)
384 })
385 })
386
387 describe("findDie", () => {
388 it("returns success with the first Die reason", () => {
389 const result = Cause.findDie(Cause.die("defect"))
390 assert.strictEqual(Result.isSuccess(result), true)
391 if (Result.isSuccess(result)) {
392 assert.strictEqual(result.success.defect, "defect")
393 assert.strictEqual(result.success._tag, "Die")
394 }
395 })
396
397 it("returns failure when no Die reason exists", () => {
398 const result = Cause.findDie(Cause.fail("error"))
399 assert.strictEqual(Result.isFailure(result), true)
400 })
401 })
402
403 describe("findDefect", () => {
404 it("returns success with the defect value", () => {
405 const result = Cause.findDefect(Cause.die("defect"))
406 assert.strictEqual(Result.isSuccess(result), true)
407 if (Result.isSuccess(result)) {
408 assert.strictEqual(result.success, "defect")
409 }
410 })
411
412 it("returns failure when no Die reason exists", () => {
413 const result = Cause.findDefect(Cause.fail("error"))
414 assert.strictEqual(Result.isFailure(result), true)
415 })
416 })
417
418 describe("findInterrupt", () => {
419 it("returns success with the first Interrupt reason", () => {
420 const result = Cause.findInterrupt(Cause.interrupt(42))
421 assert.strictEqual(Result.isSuccess(result), true)
422 if (Result.isSuccess(result)) {
423 assert.strictEqual(result.success.fiberId, 42)
424 assert.strictEqual(result.success._tag, "Interrupt")
425 }
426 })
427
428 it("returns failure when no Interrupt reason exists", () => {
429 const result = Cause.findInterrupt(Cause.fail("error"))
430 assert.strictEqual(Result.isFailure(result), true)
431 })
432 })
433
434 describe("interruptors", () => {
435 it("returns a set of fiber IDs", () => {
436 const cause = Cause.combine(Cause.interrupt(1), Cause.interrupt(2))
437 const ids = Cause.interruptors(cause)
438 assert.strictEqual(ids.has(1), true)
439 assert.strictEqual(ids.has(2), true)
440 assert.strictEqual(ids.size, 2)
441 })
442
443 it("returns empty set for non-interrupt causes", () => {
444 assert.strictEqual(Cause.interruptors(Cause.fail("error")).size, 0)
445 assert.strictEqual(Cause.interruptors(Cause.empty).size, 0)
446 })
447
448 it("excludes undefined fiber IDs from set", () => {
449 const cause = Cause.interrupt()
450 const ids = Cause.interruptors(cause)
451 assert.strictEqual(ids.has(undefined as any), false)
452 })
453 })
454
455 describe("filterInterruptors", () => {
456 it("returns success with fiber ID set when interrupts exist", () => {
457 const result = Cause.filterInterruptors(Cause.interrupt(1))
458 assert.strictEqual(Result.isSuccess(result), true)
459 if (Result.isSuccess(result)) {
460 assert.strictEqual(result.success.has(1), true)
461 }
462 })
463
464 it("returns failure when no interrupts exist", () => {
465 const result = Cause.filterInterruptors(Cause.fail("error"))
466 assert.strictEqual(Result.isFailure(result), true)
467 })
468 })
469
470 describe("map", () => {
471 it("transforms error values in Fail reasons (data-first)", () => {
472 const cause = Cause.fail("error")
473 const mapped = Cause.map(cause, (e) => e.toUpperCase())
474 const reason = mapped.reasons[0]
475 if (Cause.isFailReason(reason)) {
476 assert.strictEqual(reason.error, "ERROR")
477 }
478 })
479
480 it("works in data-last (pipeable) form", () => {
481 const mapped = pipe(Cause.fail(1), Cause.map((n) => n + 1))
482 const reason = mapped.reasons[0]
483 if (Cause.isFailReason(reason)) {
484 assert.strictEqual(reason.error, 2)
485 }
486 })
487
488 it("does not affect Die reasons", () => {
489 const cause = Cause.die("defect")
490 const mapped = Cause.map(cause, () => "should not appear")
491 assert.strictEqual(mapped.reasons.length, 1)
492 assert.strictEqual(Cause.isDieReason(mapped.reasons[0]), true)
493 })
494
495 it("does not affect Interrupt reasons", () => {
496 const cause = Cause.interrupt(1)
497 const mapped = Cause.map(cause, () => "should not appear")
498 assert.strictEqual(mapped.reasons.length, 1)
499 assert.strictEqual(Cause.isInterruptReason(mapped.reasons[0]), true)
500 })
501
502 it("maps empty cause to empty cause", () => {
503 const mapped = Cause.map(Cause.empty, () => "x")
504 assert.strictEqual(mapped.reasons.length, 0)
505 })
506 })
507
508 describe("combine", () => {
509 it("preserves unequal reasons with colliding hashes while removing duplicates", () => {
510 class Collision implements Equal.Equal {
511 constructor(readonly value: string) {}
512 [Hash.symbol](): number {
513 return 0
514 }
515 [Equal.symbol](that: Equal.Equal): boolean {
516 return that instanceof Collision && this.value === that.value
517 }
518 }
519
520 const first = Cause.makeFailReason(new Collision("first"))
521 const second = Cause.makeFailReason(new Collision("second"))
522 const duplicate = Cause.makeFailReason(new Collision("first"))
523 assert.strictEqual(Hash.hash(first), Hash.hash(second))
524 assert.strictEqual(Equal.equals(first, second), false)
525
526 const combined = Cause.combine(
527 Cause.fromReasons([first, second, first]),
528 Cause.fromReasons([duplicate, second])
529 )
530 assert.strictEqual(combined.reasons.length, 2)
531 assert.strictEqual(combined.reasons[0], first)
532 assert.strictEqual(combined.reasons[1], second)
533 assert.strictEqual(Cause.combine(combined, Cause.fromReasons([duplicate])), combined)
534 })
535
536 it("merges two causes (data-first)", () => {
537 const combined = Cause.combine(Cause.fail("a"), Cause.fail("b"))
538 assert.strictEqual(combined.reasons.length, 2)
539 })
540
541 it("works in data-last (pipeable) form", () => {
542 const combined = pipe(Cause.fail("a"), Cause.combine(Cause.fail("b")))
543 assert.strictEqual(combined.reasons.length, 2)
544 })
545
546 it("combining with empty returns the other cause", () => {
547 const cause = Cause.fail("error")
548 const combined1 = Cause.combine(cause, Cause.empty)
549 const combined2 = Cause.combine(Cause.empty, cause)
550 assert.strictEqual(combined1.reasons.length, 1)
551 assert.strictEqual(combined2.reasons.length, 1)
552 })
553
554 it("combines mixed reason types", () => {
555 const combined = Cause.combine(
556 Cause.fail("error"),
557 Cause.combine(Cause.die("defect"), Cause.interrupt(1))
558 )
559 assert.strictEqual(combined.reasons.length, 3)
560 })
561 })
562
563 describe("prettyErrors", () => {
564 it("converts Fail with Error to array of Errors", () => {
565 const cause = Cause.fail(new Error("boom"))
566 const errors = Cause.prettyErrors(cause)
567 assert.strictEqual(errors.length, 1)
568 assert.ok(errors[0] instanceof Error)
569 assert.ok(errors[0].message.includes("boom"))
570 })
571
572 it("converts Fail with string to Error", () => {
573 const cause = Cause.fail("string error")
574 const errors = Cause.prettyErrors(cause)
575 assert.strictEqual(errors.length, 1)
576 assert.ok(errors[0] instanceof Error)
577 })
578
579 it("converts Die to Error", () => {
580 const cause = Cause.die("defect")
581 const errors = Cause.prettyErrors(cause)
582 assert.strictEqual(errors.length, 1)
583 assert.ok(errors[0] instanceof Error)
584 })
585
586 it("returns InterruptError for interrupt-only cause", () => {
587 const cause = Cause.interrupt(1)
588 const errors = Cause.prettyErrors(cause)
589 assert.strictEqual(errors.length, 1)
590 assert.ok(errors[0] instanceof Error)
591 })
592
593 it("handles empty cause", () => {
594 const errors = Cause.prettyErrors(Cause.empty)
595 assert.ok(Array.isArray(errors))
596 })
597
598 it("restores Error.stackTraceLimit when formatting a reason throws", () => {
599 const limit = Error.stackTraceLimit
600 const error = new Error("boom")
601 Object.defineProperty(error, "stack", {
602 get() {
603 throw new Error("stack getter")
604 }
605 })
606 assert.throws(() => Cause.prettyErrors(Cause.fail(error)), /stack getter/)
607 assert.strictEqual(Error.stackTraceLimit, limit)
608 })
609 })
610
611 describe("pretty", () => {
612 it("renders a Fail cause as a string", () => {
613 const cause = Cause.fail("something went wrong")
614 const rendered = Cause.pretty(cause)
615 assert.strictEqual(typeof rendered, "string")
616 assert.ok(rendered.includes("something went wrong"))
617 })
618
619 it("renders a Die cause as a string", () => {
620 const cause = Cause.die(new Error("unexpected"))
621 const rendered = Cause.pretty(cause)
622 assert.strictEqual(typeof rendered, "string")
623 assert.ok(rendered.includes("unexpected"))
624 })
625
626 it("returns a string for empty cause", () => {
627 const rendered = Cause.pretty(Cause.empty)
628 assert.strictEqual(typeof rendered, "string")
629 })
630
631 it("cuts the stack of a defect at the internal frame that called user code", async () => {
632 const render = async (effect: Effect.Effect<unknown, unknown>): Promise<string> => {
633 const exit = await Effect.runPromiseExit(effect)
634 assert.ok(Exit.isFailure(exit))
635 return Cause.pretty(exit.cause)
636 }
637 for (
638 const effect of [
639 Effect.try({ try: userCode, catch: userCode }),
640 // the catcher runs outside the run loop, so it relies on the marker frame
641 Effect.tryPromise({ try: () => Promise.reject(1), catch: userCode })
642 ]
643 ) {
644 const rendered = await render(effect)
645 assert.match(rendered, /at userCode /)
646 assert.doesNotMatch(rendered, /~effect\/Utils\/internal/)
647 }
648 })
649
650 const userCode = (): never => {
651 throw new Error("boom")
652 }
653 const assertCleanStack = (effect: Effect.Effect<unknown, unknown>): void => {
654 const exit = Effect.runSyncExit(effect)
655 assert.ok(Exit.isFailure(exit))
656 const rendered = Cause.pretty(exit.cause)
657 assert.match(rendered, /\buserCode \(/)
658 assert.doesNotMatch(rendered, /\/src\/internal\/|~effect\/Utils\/internal/)
659 }
660
661 it("does not render internal frames from match and matchCause handlers", () => {
662 for (const source of [Effect.succeed(1), Effect.fail("error")]) {
663 assertCleanStack(Effect.match(source, { onSuccess: userCode, onFailure: userCode }))
664 assertCleanStack(Effect.matchCause(source, { onSuccess: userCode, onFailure: userCode }))
665 }
666 })
667
668 it("does not render internal frames from map", () => {
669 assertCleanStack(Effect.map(Effect.succeed(1), userCode))
670 })
671 })
672
673 describe("annotate", () => {
674 it("attaches annotations to a cause (data-first)", () => {
675 const cause = Cause.fail("error")
676 const annotated = Cause.annotate(cause, Context.empty())
677 assert.strictEqual(Cause.isCause(annotated), true)
678 assert.strictEqual(annotated.reasons.length, 1)
679 })
680
681 it("works in data-last (pipeable) form", () => {
682 const annotated = pipe(Cause.fail("error"), Cause.annotate(Context.empty()))
683 assert.strictEqual(Cause.isCause(annotated), true)
684 })
685
686 it("does not mutate the original cause", () => {
687 const original = Cause.fail("error")
688 Cause.annotate(original, Context.empty())
689 assert.strictEqual(original.reasons.length, 1)
690 })
691 })
692
693 describe("reasonAnnotations", () => {
694 it("returns annotations from a reason", () => {
695 const reason = Cause.makeFailReason("error")
696 const anns = Cause.reasonAnnotations(reason)
697 assert.ok(anns !== undefined)
698 })
699 })
700
701 describe("annotations", () => {
702 it("returns merged annotations from a cause", () => {
703 const cause = Cause.fail("error")
704 const anns = Cause.annotations(cause)
705 assert.ok(anns !== undefined)
706 })
707 })
708
709 describe("StackTrace", () => {
710 it("is a Context.Service", () => {
711 assert.ok(Cause.StackTrace !== undefined)
712 })
713 })
714
715 describe("InterruptorStackTrace", () => {
716 it("is a Context.Service", () => {
717 assert.ok(Cause.InterruptorStackTrace !== undefined)
718 })
719 })
720
721 describe("NoSuchElementError", () => {
722 it("creates an error with _tag and message", () => {
723 const error = new Cause.NoSuchElementError("not found")
724 assert.strictEqual(error._tag, "NoSuchElementError")
725 assert.strictEqual(error.message, "not found")
726 })
727
728 it("creates an error without message", () => {
729 const error = new Cause.NoSuchElementError()
730 assert.strictEqual(error._tag, "NoSuchElementError")
731 })
732
733 it("is an instance of Error", () => {
734 const error = new Cause.NoSuchElementError()
735 assert.ok(error instanceof Error)
736 })
737 })
738
739 describe("isNoSuchElementError", () => {
740 it("returns true for NoSuchElementError instances", () => {
741 assert.strictEqual(Cause.isNoSuchElementError(new Cause.NoSuchElementError()), true)
742 })
743
744 it("returns false for other values", () => {
745 assert.strictEqual(Cause.isNoSuchElementError("nope"), false)
746 assert.strictEqual(Cause.isNoSuchElementError(new Error()), false)
747 assert.strictEqual(Cause.isNoSuchElementError(null), false)
748 })
749 })
750
751 describe("NoSuchElementErrorTypeId", () => {
752 it("is a string constant", () => {
753 assert.strictEqual(Cause.NoSuchElementErrorTypeId, "~effect/Cause/NoSuchElementError")
754 })
755 })
756
757 describe("Done", () => {
758 it("creates a Done signal without value", () => {
759 const d = Cause.Done()
760 assert.strictEqual(d._tag, "Done")
761 assert.strictEqual(d.value, undefined)
762 })
763
764 it("creates a Done signal with a value", () => {
765 const d = Cause.Done(42)
766 assert.strictEqual(d._tag, "Done")
767 assert.strictEqual(d.value, 42)
768 })
769 })
770
771 describe("isDone", () => {
772 it("returns true for Done values", () => {
773 assert.strictEqual(Cause.isDone(Cause.Done()), true)
774 assert.strictEqual(Cause.isDone(Cause.Done(42)), true)
775 })
776
777 it("returns false for other values", () => {
778 assert.strictEqual(Cause.isDone("not done"), false)
779 assert.strictEqual(Cause.isDone(null), false)
780 assert.strictEqual(Cause.isDone(new Cause.NoSuchElementError()), false)
781 })
782 })
783
784 describe("DoneTypeId", () => {
785 it("is a string constant", () => {
786 assert.strictEqual(Cause.DoneTypeId, "~effect/Cause/Done")
787 })
788 })
789
790 describe("TimeoutError", () => {
791 it("creates an error with _tag and message", () => {
792 const error = new Cause.TimeoutError("timed out")
793 assert.strictEqual(error._tag, "TimeoutError")
794 assert.strictEqual(error.message, "timed out")
795 })
796
797 it("creates an error without message", () => {
798 const error = new Cause.TimeoutError()
799 assert.strictEqual(error._tag, "TimeoutError")
800 })
801
802 it("is an instance of Error", () => {
803 assert.ok(new Cause.TimeoutError() instanceof Error)
804 })
805 })
806
807 describe("isTimeoutError", () => {
808 it("returns true for TimeoutError instances", () => {
809 assert.strictEqual(Cause.isTimeoutError(new Cause.TimeoutError()), true)
810 })
811
812 it("returns false for other values", () => {
813 assert.strictEqual(Cause.isTimeoutError("nope"), false)
814 assert.strictEqual(Cause.isTimeoutError(new Error()), false)
815 })
816 })
817
818 describe("TimeoutErrorTypeId", () => {
819 it("is a string constant", () => {
820 assert.strictEqual(Cause.TimeoutErrorTypeId, "~effect/Cause/TimeoutError")
821 })
822 })
823
824 describe("IllegalArgumentError", () => {
825 it("creates an error with _tag and message", () => {
826 const error = new Cause.IllegalArgumentError("bad arg")
827 assert.strictEqual(error._tag, "IllegalArgumentError")
828 assert.strictEqual(error.message, "bad arg")
829 })
830
831 it("creates an error without message", () => {
832 const error = new Cause.IllegalArgumentError()
833 assert.strictEqual(error._tag, "IllegalArgumentError")
834 })
835
836 it("is an instance of Error", () => {
837 assert.ok(new Cause.IllegalArgumentError() instanceof Error)
838 })
839 })
840
841 describe("isIllegalArgumentError", () => {
842 it("returns true for IllegalArgumentError instances", () => {
843 assert.strictEqual(Cause.isIllegalArgumentError(new Cause.IllegalArgumentError()), true)
844 })
845
846 it("returns false for other values", () => {
847 assert.strictEqual(Cause.isIllegalArgumentError("nope"), false)
848 assert.strictEqual(Cause.isIllegalArgumentError(new Error()), false)
849 })
850 })
851
852 describe("IllegalArgumentErrorTypeId", () => {
853 it("is a string constant", () => {
854 assert.strictEqual(Cause.IllegalArgumentErrorTypeId, "~effect/Cause/IllegalArgumentError")
855 })
856 })
857
858 describe("ExceededCapacityError", () => {
859 it("creates an error with _tag and message", () => {
860 const error = new Cause.ExceededCapacityError("queue full")
861 assert.strictEqual(error._tag, "ExceededCapacityError")
862 assert.strictEqual(error.message, "queue full")
863 })
864
865 it("creates an error without message", () => {
866 const error = new Cause.ExceededCapacityError()
867 assert.strictEqual(error._tag, "ExceededCapacityError")
868 })
869
870 it("is an instance of Error", () => {
871 assert.ok(new Cause.ExceededCapacityError() instanceof Error)
872 })
873 })
874
875 describe("isExceededCapacityError", () => {
876 it("returns true for ExceededCapacityError instances", () => {
877 assert.strictEqual(Cause.isExceededCapacityError(new Cause.ExceededCapacityError()), true)
878 })
879
880 it("returns false for other values", () => {
881 assert.strictEqual(Cause.isExceededCapacityError("nope"), false)
882 assert.strictEqual(Cause.isExceededCapacityError(new Error()), false)
883 })
884 })
885
886 describe("ExceededCapacityErrorTypeId", () => {
887 it("is a string constant", () => {
888 assert.strictEqual(Cause.ExceededCapacityErrorTypeId, "~effect/Cause/ExceededCapacityError")
889 })
890 })
891
892 describe("UnknownError", () => {
893 it("creates an error with cause and message", () => {
894 const error = new Cause.UnknownError("original", "wrapper message")
895 assert.strictEqual(error._tag, "UnknownError")
896 assert.strictEqual(error.message, "wrapper message")
897 })
898
899 it("stores the original cause", () => {
900 const original = { raw: true }
901 const error = new Cause.UnknownError(original)
902 assert.strictEqual(error._tag, "UnknownError")
903 })
904
905 it("is an instance of Error", () => {
906 assert.ok(new Cause.UnknownError("x") instanceof Error)
907 })
908 })
909
910 describe("isUnknownError", () => {
911 it("returns true for UnknownError instances", () => {
912 assert.strictEqual(Cause.isUnknownError(new Cause.UnknownError("x")), true)
913 })
914
915 it("returns false for other values", () => {
916 assert.strictEqual(Cause.isUnknownError("nope"), false)
917 assert.strictEqual(Cause.isUnknownError(new Error()), false)
918 })
919 })
920
921 describe("UnknownErrorTypeId", () => {
922 it("is a string constant", () => {
923 assert.strictEqual(Cause.UnknownErrorTypeId, "~effect/Cause/UnknownError")
924 })
925 })
926})