| 1 | import { Cause, Equal, Option, Predicate, Result, SchemaIssue } from "effect" |
| 2 | import * as Exit from "effect/Exit" |
| 3 | import * as assert from "node:assert" |
| 4 | import { assert as vassert } from "vitest" |
| 5 | |
| 6 | // ---------------------------- |
| 7 | // Primitives |
| 8 | // ---------------------------- |
| 9 | |
| 10 | /** |
| 11 | * Throws an `AssertionError` with the provided error message. |
| 12 | */ |
| 13 | export function fail(message: string) { |
| 14 | assert.fail(message) |
| 15 | } |
| 16 | |
| 17 | export function deepStrictEqual<A>(actual: A, expected: A, message?: string, ..._: Array<never>) { |
| 18 | if (message === undefined) { |
| 19 | assert.deepStrictEqual(actual, expected) |
| 20 | } else { |
| 21 | assert.deepStrictEqual(actual, expected, message) |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | export function notDeepStrictEqual<A>(actual: A, expected: A, message?: string, ..._: Array<never>) { |
| 26 | if (message !== undefined) { |
| 27 | assert.notDeepStrictEqual(actual, expected, message) |
| 28 | } else { |
| 29 | assert.notDeepStrictEqual(actual, expected) |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | export function strictEqual<A>(actual: A, expected: A, message?: string, ..._: Array<never>) { |
| 34 | if (message !== undefined) { |
| 35 | assert.strictEqual(actual, expected, message) |
| 36 | } else { |
| 37 | assert.strictEqual(actual, expected) |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Asserts that `actual` is equal to `expected` using the `Equal.equals` trait. |
| 43 | */ |
| 44 | export function assertEquals<A>(actual: A, expected: A, message?: string, ..._: Array<never>) { |
| 45 | if (!Equal.equals(actual, expected)) { |
| 46 | deepStrictEqual(actual, expected, message) // show diff |
| 47 | fail(message ?? "Expected values to be Equal.equals") |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | export function doesNotThrow(thunk: () => void, message?: string, ..._: Array<never>) { |
| 52 | assert.doesNotThrow(thunk, message) |
| 53 | } |
| 54 | |
| 55 | // ---------------------------- |
| 56 | // Derived |
| 57 | // ---------------------------- |
| 58 | |
| 59 | /** |
| 60 | * Asserts that `value` is an instance of `constructor`. |
| 61 | */ |
| 62 | export function assertInstanceOf<C extends new(...args: any) => any>( |
| 63 | value: unknown, |
| 64 | constructor: C, |
| 65 | message?: string, |
| 66 | ..._: Array<never> |
| 67 | ): asserts value is InstanceType<C> { |
| 68 | vassert.instanceOf(value, constructor, message) |
| 69 | } |
| 70 | |
| 71 | export function assertTrue(self: unknown, message?: string, ..._: Array<never>): asserts self { |
| 72 | strictEqual(self, true, message) |
| 73 | } |
| 74 | |
| 75 | export function assertFalse(self: boolean, message?: string, ..._: Array<never>) { |
| 76 | strictEqual(self, false, message) |
| 77 | } |
| 78 | |
| 79 | export function assertSchemaIssueError( |
| 80 | self: unknown, |
| 81 | expectedIssueMessage: string, |
| 82 | ..._: Array<never> |
| 83 | ): asserts self is Error & { readonly cause: SchemaIssue.Issue } { |
| 84 | assertInstanceOf(self, Error) |
| 85 | strictEqual(self.message, "Schema validation failed") |
| 86 | assertTrue(SchemaIssue.isIssue(self.cause)) |
| 87 | strictEqual(SchemaIssue.defaultFormatter(self.cause), expectedIssueMessage) |
| 88 | } |
| 89 | |
| 90 | export function assertInclude(actual: string | undefined, expected: string, ..._: Array<never>) { |
| 91 | if (Predicate.isString(expected)) { |
| 92 | if (!actual?.includes(expected)) { |
| 93 | fail(`Expected\n\n${actual}\n\nto include\n\n${expected}`) |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | export function assertMatch(actual: string, regExp: RegExp, ..._: Array<never>) { |
| 99 | if (!regExp.test(actual)) { |
| 100 | fail(`Expected\n\n${actual}\n\nto match\n\n${regExp}`) |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | export function throws(thunk: () => void, error?: string | Error | ((u: unknown) => undefined), ..._: Array<never>) { |
| 105 | try { |
| 106 | thunk() |
| 107 | } catch (e) { |
| 108 | if (error !== undefined) { |
| 109 | if (Predicate.isString(error)) { |
| 110 | assertInstanceOf(e, Error) |
| 111 | strictEqual(e.message, error) |
| 112 | } else if (Predicate.isError(error)) { |
| 113 | deepStrictEqual(e, error) |
| 114 | } else { |
| 115 | error(e) |
| 116 | } |
| 117 | } |
| 118 | return |
| 119 | } |
| 120 | fail("Expected to throw an error") |
| 121 | } |
| 122 | |
| 123 | export async function throwsAsync( |
| 124 | thunk: () => Promise<void>, |
| 125 | error?: Error | ((u: unknown) => undefined), |
| 126 | ..._: Array<never> |
| 127 | ) { |
| 128 | try { |
| 129 | await thunk() |
| 130 | } catch (e) { |
| 131 | if (error !== undefined) { |
| 132 | if (Predicate.isFunction(error)) { |
| 133 | error(e) |
| 134 | } else { |
| 135 | deepStrictEqual(e, error) |
| 136 | } |
| 137 | } |
| 138 | return |
| 139 | } |
| 140 | fail("Expected to throw an error") |
| 141 | } |
| 142 | |
| 143 | // ---------------------------- |
| 144 | // Option |
| 145 | // ---------------------------- |
| 146 | |
| 147 | export function assertNone<A>(option: Option.Option<A>, ..._: Array<never>): asserts option is Option.None<never> { |
| 148 | deepStrictEqual(option, Option.none()) |
| 149 | } |
| 150 | |
| 151 | export function assertSome<A>( |
| 152 | option: Option.Option<A>, |
| 153 | expected: A, |
| 154 | ..._: Array<never> |
| 155 | ): asserts option is Option.Some<A> { |
| 156 | deepStrictEqual(option, Option.some(expected)) |
| 157 | } |
| 158 | |
| 159 | // ---------------------------- |
| 160 | // Result |
| 161 | // ---------------------------- |
| 162 | |
| 163 | export function assertFailure<A, E>( |
| 164 | result: Result.Result<A, E>, |
| 165 | expected: E, |
| 166 | ..._: Array<never> |
| 167 | ): asserts result is Result.Failure<never, E> { |
| 168 | deepStrictEqual(result, Result.fail(expected)) |
| 169 | } |
| 170 | |
| 171 | export function assertSuccess<A, E>( |
| 172 | result: Result.Result<A, E>, |
| 173 | expected: A, |
| 174 | ..._: Array<never> |
| 175 | ): asserts result is Result.Success<A, never> { |
| 176 | deepStrictEqual(result, Result.succeed(expected)) |
| 177 | } |
| 178 | |
| 179 | // ---------------------------- |
| 180 | // Exit |
| 181 | // ---------------------------- |
| 182 | |
| 183 | export function assertExitFailure<A, E>( |
| 184 | exit: Exit.Exit<A, E>, |
| 185 | expected: Cause.Cause<E>, |
| 186 | ..._: Array<never> |
| 187 | ): asserts exit is Exit.Failure<never, E> { |
| 188 | deepStrictEqual(exit, Exit.failCause(expected)) |
| 189 | } |
| 190 | |
| 191 | export function assertExitSuccess<A, E>( |
| 192 | exit: Exit.Exit<A, E>, |
| 193 | expected: A, |
| 194 | ..._: Array<never> |
| 195 | ): asserts exit is Exit.Success<A, never> { |
| 196 | deepStrictEqual(exit, Exit.succeed(expected)) |
| 197 | } |
| 198 | |
| 199 | export function assertCauseFail<E>( |
| 200 | cause: Cause.Cause<E>, |
| 201 | expected: E, |
| 202 | ..._: Array<never> |
| 203 | ) { |
| 204 | deepStrictEqual(cause, Cause.fail(expected)) |
| 205 | } |