| 1 | import { describe, it } from "@effect/vitest" |
| 2 | import { assertFalse, assertTrue } from "@effect/vitest/utils" |
| 3 | import { Equivalence, pipe } from "effect" |
| 4 | |
| 5 | describe("Equivalence", () => { |
| 6 | it("String", () => { |
| 7 | assertTrue(Equivalence.String("a", "a")) |
| 8 | assertFalse(Equivalence.String("a", "b")) |
| 9 | }) |
| 10 | |
| 11 | it("Number", () => { |
| 12 | assertTrue(Equivalence.Number(1, 1)) |
| 13 | assertFalse(Equivalence.Number(1, 2)) |
| 14 | assertTrue(Equivalence.Number(NaN, NaN)) |
| 15 | assertFalse(Equivalence.Number(NaN, 1)) |
| 16 | assertFalse(Equivalence.Number(1, NaN)) |
| 17 | assertTrue(Equivalence.Number(0, -0)) |
| 18 | }) |
| 19 | |
| 20 | it("Boolean", () => { |
| 21 | assertTrue(Equivalence.Boolean(true, true)) |
| 22 | assertTrue(Equivalence.Boolean(false, false)) |
| 23 | assertFalse(Equivalence.Boolean(true, false)) |
| 24 | assertFalse(Equivalence.Boolean(false, true)) |
| 25 | }) |
| 26 | |
| 27 | it("BigInt", () => { |
| 28 | assertTrue(Equivalence.BigInt(1n, 1n)) |
| 29 | assertFalse(Equivalence.BigInt(1n, 2n)) |
| 30 | }) |
| 31 | |
| 32 | it("mapInput", () => { |
| 33 | interface Person { |
| 34 | readonly name: string |
| 35 | readonly age: number |
| 36 | } |
| 37 | const eqPerson = pipe(Equivalence.strictEqual<string>(), Equivalence.mapInput((p: Person) => p.name)) |
| 38 | assertTrue(eqPerson({ name: "a", age: 1 }, { name: "a", age: 2 })) |
| 39 | assertTrue(eqPerson({ name: "a", age: 1 }, { name: "a", age: 1 })) |
| 40 | assertFalse(eqPerson({ name: "a", age: 1 }, { name: "b", age: 1 })) |
| 41 | assertFalse(eqPerson({ name: "a", age: 1 }, { name: "b", age: 2 })) |
| 42 | }) |
| 43 | |
| 44 | it("combine", () => { |
| 45 | type T = readonly [string, number, boolean] |
| 46 | const E0: Equivalence.Equivalence<T> = Equivalence.mapInput((x: T) => x[0])(Equivalence.strictEqual<string>()) |
| 47 | const E1: Equivalence.Equivalence<T> = Equivalence.mapInput((x: T) => x[1])(Equivalence.strictEqual<number>()) |
| 48 | const eqE0E1 = Equivalence.combine(E0, E1) |
| 49 | assertTrue(eqE0E1(["a", 1, true], ["a", 1, true])) |
| 50 | assertTrue(eqE0E1(["a", 1, true], ["a", 1, false])) |
| 51 | assertFalse(eqE0E1(["a", 1, true], ["b", 1, true])) |
| 52 | assertFalse(eqE0E1(["a", 1, true], ["a", 2, false])) |
| 53 | }) |
| 54 | |
| 55 | it("combineAll", () => { |
| 56 | type T = readonly [string, number, boolean] |
| 57 | const E0: Equivalence.Equivalence<T> = Equivalence.mapInput((x: T) => x[0])(Equivalence.strictEqual<string>()) |
| 58 | const E1: Equivalence.Equivalence<T> = Equivalence.mapInput((x: T) => x[1])(Equivalence.strictEqual<number>()) |
| 59 | const E2: Equivalence.Equivalence<T> = Equivalence.mapInput((x: T) => x[2])(Equivalence.strictEqual<boolean>()) |
| 60 | const eqE0E1E2 = Equivalence.combineAll([E0, E1, E2]) |
| 61 | assertTrue(eqE0E1E2(["a", 1, true], ["a", 1, true])) |
| 62 | assertFalse(eqE0E1E2(["a", 1, true], ["b", 1, true])) |
| 63 | assertFalse(eqE0E1E2(["a", 1, true], ["a", 2, true])) |
| 64 | assertFalse(eqE0E1E2(["a", 1, true], ["a", 1, false])) |
| 65 | }) |
| 66 | |
| 67 | it("Tuple", () => { |
| 68 | const eq = Equivalence.Tuple([Equivalence.strictEqual<string>(), Equivalence.strictEqual<number>()]) |
| 69 | |
| 70 | assertTrue(eq(["a", 1], ["a", 1])) |
| 71 | assertFalse(eq(["a", 1], ["c", 1])) |
| 72 | assertFalse(eq(["a", 1], ["a", 2])) |
| 73 | }) |
| 74 | |
| 75 | it("Array", () => { |
| 76 | const eq = Equivalence.Array(Equivalence.strictEqual<number>()) |
| 77 | |
| 78 | assertTrue(eq([], [])) |
| 79 | assertTrue(eq([1, 2, 3], [1, 2, 3])) |
| 80 | assertFalse(eq([1, 2, 3], [1, 2, 4])) |
| 81 | assertFalse(eq([1, 2, 3], [1, 2])) |
| 82 | }) |
| 83 | |
| 84 | describe("Struct", () => { |
| 85 | it("string keys", () => { |
| 86 | const eq = Equivalence.Struct({ |
| 87 | a: Equivalence.strictEqual<string>(), |
| 88 | b: Equivalence.strictEqual<number>() |
| 89 | }) |
| 90 | |
| 91 | assertTrue(eq({ a: "a", b: 1 }, { a: "a", b: 1 })) |
| 92 | assertFalse(eq({ a: "a", b: 1 }, { a: "c", b: 1 })) |
| 93 | assertFalse(eq({ a: "a", b: 1 }, { a: "a", b: 2 })) |
| 94 | }) |
| 95 | |
| 96 | it("symbol keys", () => { |
| 97 | const a = Symbol.for("a") |
| 98 | const b = Symbol.for("b") |
| 99 | const eq = Equivalence.Struct({ |
| 100 | [a]: Equivalence.strictEqual<string>(), |
| 101 | [b]: Equivalence.strictEqual<number>() |
| 102 | }) |
| 103 | |
| 104 | assertTrue(eq({ [a]: "a", [b]: 1 }, { [a]: "a", [b]: 1 })) |
| 105 | assertFalse(eq({ [a]: "a", [b]: 1 }, { [a]: "c", [b]: 1 })) |
| 106 | assertFalse(eq({ [a]: "a", [b]: 1 }, { [a]: "a", [b]: 2 })) |
| 107 | }) |
| 108 | |
| 109 | it("mixed keys", () => { |
| 110 | const b = Symbol.for("b") |
| 111 | const eq = Equivalence.Struct({ |
| 112 | a: Equivalence.strictEqual<string>(), |
| 113 | [b]: Equivalence.strictEqual<number>() |
| 114 | }) |
| 115 | |
| 116 | assertTrue(eq({ a: "a", [b]: 1 }, { a: "a", [b]: 1 })) |
| 117 | assertFalse(eq({ a: "a", [b]: 1 }, { a: "c", [b]: 1 })) |
| 118 | assertFalse(eq({ a: "a", [b]: 1 }, { a: "a", [b]: 2 })) |
| 119 | }) |
| 120 | }) |
| 121 | |
| 122 | describe("Record", () => { |
| 123 | it("string keys", () => { |
| 124 | const eq = Equivalence.Record(Equivalence.strictEqual<number>()) |
| 125 | |
| 126 | assertTrue(eq({ a: 1, b: 2 }, { a: 1, b: 2 })) |
| 127 | assertFalse(eq({ a: 1, b: 2 }, { a: 1, b: 3 })) |
| 128 | assertFalse(eq({ a: 1, b: 2 }, { a: 1 })) |
| 129 | assertFalse(eq({ a: 1 }, { a: 1, b: 2 })) |
| 130 | }) |
| 131 | |
| 132 | it("symbol keys", () => { |
| 133 | const a = Symbol.for("a") |
| 134 | const b = Symbol.for("b") |
| 135 | const eq = Equivalence.Record(Equivalence.strictEqual<number>()) |
| 136 | |
| 137 | assertTrue(eq({ [a]: 1, [b]: 2 }, { [a]: 1, [b]: 2 })) |
| 138 | assertFalse(eq({ [a]: 1, [b]: 2 }, { [a]: 1, [b]: 3 })) |
| 139 | assertFalse(eq({ [a]: 1, [b]: 2 }, { [a]: 1 })) |
| 140 | assertFalse(eq({ [a]: 1 }, { [a]: 1, [b]: 2 })) |
| 141 | }) |
| 142 | |
| 143 | it("mixed keys", () => { |
| 144 | const b = Symbol.for("b") |
| 145 | const eq = Equivalence.Record(Equivalence.strictEqual<number>()) |
| 146 | |
| 147 | assertTrue(eq({ a: 1, [b]: 2 }, { a: 1, [b]: 2 })) |
| 148 | assertFalse(eq({ a: 1, [b]: 2 }, { a: 1, [b]: 3 })) |
| 149 | assertFalse(eq({ a: 1, [b]: 2 }, { a: 1 })) |
| 150 | assertFalse(eq({ a: 1 }, { a: 1, [b]: 2 })) |
| 151 | }) |
| 152 | }) |
| 153 | |
| 154 | it("Date", () => { |
| 155 | const d1 = new Date("2020-01-01T00:00:00.000Z") |
| 156 | const d2 = new Date("2020-01-01T00:00:00.000Z") |
| 157 | const d3 = new Date("2021-01-01T00:00:00.000Z") |
| 158 | |
| 159 | assertTrue(Equivalence.Date(d1, d2)) |
| 160 | assertFalse(Equivalence.Date(d1, d3)) |
| 161 | assertTrue(Equivalence.Date(d1, d1)) |
| 162 | |
| 163 | const invalid1 = new Date("foo") |
| 164 | const invalid2 = new Date("bar") |
| 165 | |
| 166 | assertTrue(Equivalence.Date(invalid1, invalid2)) |
| 167 | assertFalse(Equivalence.Date(invalid1, d1)) |
| 168 | assertFalse(Equivalence.Date(d1, invalid2)) |
| 169 | }) |
| 170 | }) |