import { describe, it } from "@effect/vitest" import { assertFailure as vassertFailure, assertFalse, assertNone, assertSome, assertSuccess as vassertSuccess, assertTrue, strictEqual, throws } from "@effect/vitest/utils" import { Brand, Result, Schema } from "effect" function assertSuccess>(ctor: Brand.Constructor, value: Brand.Brand.Unbranded) { vassertSuccess(ctor.result(value), value as T) } function assertFailure>( ctor: Brand.Constructor, value: Brand.Brand.Unbranded, message: string ) { vassertFailure(ctor.result(value).pipe(Result.mapError((e) => e.message)), message) } describe("Brand", () => { it("formats validation failures as BrandError", () => { type Int = number & Brand.Brand<"Int"> const Int = Brand.check(Schema.isInt()) const result = Int.result(1.1) assertTrue(Result.isFailure(result)) strictEqual(String(result.failure), "BrandError(Expected an integer, got 1.1)") }) it("creates nominal brands without runtime validation", () => { type MyNumber = number & Brand.Brand<"MyNumber"> const MyNumber = Brand.nominal() strictEqual(MyNumber(1), 1) strictEqual(MyNumber(1.1), 1.1) strictEqual(MyNumber(-1), -1) assertTrue(MyNumber.is(1)) assertTrue(MyNumber.is(1.1)) assertTrue(MyNumber.is(-1)) assertSome(MyNumber.option(1), 1 as MyNumber) assertSome(MyNumber.option(1.1), 1.1 as MyNumber) assertSome(MyNumber.option(-1), -1 as MyNumber) assertSuccess(MyNumber, 1) assertSuccess(MyNumber, 1.1) assertSuccess(MyNumber, -1) }) it("validates custom predicates created with make", () => { type Int = number & Brand.Brand<"Int"> const Int = Brand.make( (n) => Number.isInteger(n) || `Expected ${n} to be an integer` ) strictEqual(Int(1), 1) throws(() => Int(1.1)) strictEqual(Int(-1), -1) assertTrue(Int.is(1)) assertFalse(Int.is(1.1)) assertTrue(Int.is(-1)) assertSome(Int.option(1), 1 as Int) assertNone(Int.option(1.1)) assertSome(Int.option(-1), -1 as Int) assertSuccess(Int, 1) assertFailure(Int, 1.1, "Expected 1.1 to be an integer") assertSuccess(Int, -1) }) describe("check", () => { it("validates a single schema check", () => { type Int = number & Brand.Brand<"Int"> const Int = Brand.check(Schema.isInt()) strictEqual(Int(1), 1) throws(() => Int(1.1)) strictEqual(Int(-1), -1) assertTrue(Int.is(1)) assertFalse(Int.is(1.1)) assertTrue(Int.is(-1)) assertSome(Int.option(1), 1 as Int) assertNone(Int.option(1.1)) assertSome(Int.option(-1), -1 as Int) assertSuccess(Int, 1) assertFailure(Int, 1.1, "Expected an integer, got 1.1") assertSuccess(Int, -1) }) it("accumulates failures from multiple schema checks", () => { type PositiveInt = number & Brand.Brand<"PositiveInt"> const PositiveInt = Brand.check(Schema.isInt(), Schema.isGreaterThan(0)) assertSuccess(PositiveInt, 1) assertFailure(PositiveInt, 1.1, "Expected an integer, got 1.1") assertFailure(PositiveInt, -1, "Expected a value greater than 0, got -1") assertFailure( PositiveInt, -1.1, `Expected an integer, got -1.1 Expected a value greater than 0, got -1.1` ) }) it("stops after an aborted failed schema check", () => { type PositiveInt = number & Brand.Brand<"PositiveInt"> const PositiveInt = Brand.check( Schema.isInt().abort(), // abort the first check Schema.isGreaterThan(0) ) assertSuccess(PositiveInt, 1) assertFailure(PositiveInt, 1.1, "Expected an integer, got 1.1") assertFailure(PositiveInt, -1, "Expected a value greater than 0, got -1") assertFailure(PositiveInt, -1.1, `Expected an integer, got -1.1`) }) }) it("combines existing brand constructors with all", () => { type Int = number & Brand.Brand<"Int"> const Int = Brand.check(Schema.isInt()) type Positive = number & Brand.Brand<"Positive"> const Positive = Brand.check(Schema.isGreaterThan(0)) const PositiveInt = Brand.all(Int, Positive) strictEqual(PositiveInt(1), 1) throws(() => PositiveInt(1.1)) throws(() => PositiveInt(-1)) assertTrue(PositiveInt.is(1)) assertFalse(PositiveInt.is(1.1)) assertFalse(PositiveInt.is(-1)) assertSome(PositiveInt.option(1), 1 as any) assertNone(PositiveInt.option(1.1)) assertNone(PositiveInt.option(-1)) assertSuccess(PositiveInt, 1) assertFailure(PositiveInt, 1.1, "Expected an integer, got 1.1") assertFailure( PositiveInt, -1.1, `Expected an integer, got -1.1 Expected a value greater than 0, got -1.1` ) }) })