An educational pure functional programming library in TypeScript
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add foundation tests for brand, refinement, result, option

+208
+22
tests/brand.test.ts
··· 1 + import { describe, it, expect } from "bun:test" 2 + import { brand, type Branded } from "../src/index" 3 + 4 + describe("Branded Types", () => { 5 + it("creates branded values", () => { 6 + type UserId = Branded<string, "UserId"> 7 + const id: UserId = brand("user-123") 8 + expect(id).toBe("user-123") 9 + }) 10 + 11 + it("preserves underlying value operations", () => { 12 + type Email = Branded<string, "Email"> 13 + const email: Email = brand("test@example.com") 14 + expect(email.includes("@")).toBe(true) 15 + }) 16 + 17 + it("works with number primitives", () => { 18 + type Price = Branded<number, "Price"> 19 + const price: Price = brand(99.99) 20 + expect(price + 1).toBeCloseTo(100.99) 21 + }) 22 + })
+60
tests/option.test.ts
··· 1 + import { describe, it, expect } from "bun:test" 2 + import { some, none, fromNullable, mapOption, getOrElse, flatMapOption, pipe } from "../src/index" 3 + 4 + describe("Option", () => { 5 + describe("constructors", () => { 6 + it("some creates Some variant", () => { 7 + const opt = some(42) 8 + expect(opt._tag).toBe("Some") 9 + expect(opt.value).toBe(42) 10 + }) 11 + 12 + it("none is None variant", () => { 13 + expect(none._tag).toBe("None") 14 + }) 15 + }) 16 + 17 + describe("fromNullable", () => { 18 + it("converts non-null to Some", () => { 19 + expect(fromNullable(42)).toEqual(some(42)) 20 + expect(fromNullable("")).toEqual(some("")) 21 + expect(fromNullable(0)).toEqual(some(0)) 22 + expect(fromNullable(false)).toEqual(some(false)) 23 + }) 24 + 25 + it("converts null/undefined to None", () => { 26 + expect(fromNullable(null)).toEqual(none) 27 + expect(fromNullable(undefined)).toEqual(none) 28 + }) 29 + }) 30 + 31 + describe("mapOption", () => { 32 + it("transforms Some values", () => { 33 + const result = pipe(some(5), mapOption(x => x * 2)) 34 + expect(result).toEqual(some(10)) 35 + }) 36 + 37 + it("passes through None", () => { 38 + const result = pipe(none, mapOption((x: number) => x * 2)) 39 + expect(result).toEqual(none) 40 + }) 41 + }) 42 + 43 + describe("flatMapOption", () => { 44 + it("chains Some values", () => { 45 + const safeSqrt = (x: number) => x >= 0 ? some(Math.sqrt(x)) : none 46 + const result = pipe(some(16), flatMapOption(safeSqrt)) 47 + expect(result).toEqual(some(4)) 48 + }) 49 + }) 50 + 51 + describe("getOrElse", () => { 52 + it("returns value for Some", () => { 53 + expect(pipe(some(42), getOrElse(0))).toBe(42) 54 + }) 55 + 56 + it("returns default for None", () => { 57 + expect(pipe(none, getOrElse(0))).toBe(0) 58 + }) 59 + }) 60 + })
+59
tests/refinement.test.ts
··· 1 + import { describe, it, expect } from "bun:test" 2 + import { refine, positive, nonNegative, normalized, integer } from "../src/index" 3 + 4 + describe("Refinements", () => { 5 + describe("positive", () => { 6 + it("accepts positive numbers", () => { 7 + expect(positive(5)).toBe(5) 8 + expect(positive(0.001)).toBe(0.001) 9 + }) 10 + 11 + it("rejects non-positive numbers", () => { 12 + expect(positive(0)).toBeUndefined() 13 + expect(positive(-1)).toBeUndefined() 14 + }) 15 + }) 16 + 17 + describe("nonNegative", () => { 18 + it("accepts zero and positive", () => { 19 + expect(nonNegative(0)).toBe(0) 20 + expect(nonNegative(100)).toBe(100) 21 + }) 22 + 23 + it("rejects negative numbers", () => { 24 + expect(nonNegative(-0.001)).toBeUndefined() 25 + }) 26 + }) 27 + 28 + describe("normalized", () => { 29 + it("accepts values in [0, 1]", () => { 30 + expect(normalized(0)).toBe(0) 31 + expect(normalized(0.5)).toBe(0.5) 32 + expect(normalized(1)).toBe(1) 33 + }) 34 + 35 + it("rejects values outside [0, 1]", () => { 36 + expect(normalized(-0.1)).toBeUndefined() 37 + expect(normalized(1.1)).toBeUndefined() 38 + }) 39 + }) 40 + 41 + describe("integer", () => { 42 + it("accepts integers", () => { 43 + expect(integer(42)).toBe(42) 44 + expect(integer(-10)).toBe(-10) 45 + }) 46 + 47 + it("rejects non-integers", () => { 48 + expect(integer(3.14)).toBeUndefined() 49 + }) 50 + }) 51 + 52 + describe("custom refinements", () => { 53 + it("creates custom validators", () => { 54 + const even = refine<number, "Even">(x => x % 2 === 0) 55 + expect(even(4)).toBe(4) 56 + expect(even(3)).toBeUndefined() 57 + }) 58 + }) 59 + })
+67
tests/result.test.ts
··· 1 + import { describe, it, expect } from "bun:test" 2 + import { ok, err, mapResult, chainResult, unwrapOr, tryCatch, pipe } from "../src/index" 3 + 4 + describe("Result", () => { 5 + describe("constructors", () => { 6 + it("ok creates Ok variant", () => { 7 + const result = ok(42) 8 + expect(result._tag).toBe("Ok") 9 + expect(result.value).toBe(42) 10 + }) 11 + 12 + it("err creates Err variant", () => { 13 + const result = err("failed") 14 + expect(result._tag).toBe("Err") 15 + expect(result.error).toBe("failed") 16 + }) 17 + }) 18 + 19 + describe("mapResult", () => { 20 + it("transforms Ok values", () => { 21 + const result = pipe(ok(5), mapResult(x => x * 2)) 22 + expect(result).toEqual(ok(10)) 23 + }) 24 + 25 + it("passes through Err unchanged", () => { 26 + const result = pipe(err("error"), mapResult((x: number) => x * 2)) 27 + expect(result).toEqual(err("error")) 28 + }) 29 + }) 30 + 31 + describe("chainResult", () => { 32 + it("chains Ok values", () => { 33 + const divide = (a: number, b: number) => 34 + b === 0 ? err("div by zero") : ok(a / b) 35 + 36 + const result = pipe(ok(10), chainResult(x => divide(x, 2))) 37 + expect(result).toEqual(ok(5)) 38 + }) 39 + 40 + it("short-circuits on Err", () => { 41 + const result = pipe(err("first error"), chainResult(() => ok(42))) 42 + expect(result).toEqual(err("first error")) 43 + }) 44 + }) 45 + 46 + describe("unwrapOr", () => { 47 + it("returns value for Ok", () => { 48 + expect(pipe(ok(42), unwrapOr(0))).toBe(42) 49 + }) 50 + 51 + it("returns default for Err", () => { 52 + expect(pipe(err("oops"), unwrapOr(0))).toBe(0) 53 + }) 54 + }) 55 + 56 + describe("tryCatch", () => { 57 + it("wraps successful computation", () => { 58 + const result = tryCatch(() => JSON.parse('{"a":1}')) 59 + expect(result).toEqual(ok({ a: 1 })) 60 + }) 61 + 62 + it("catches exceptions", () => { 63 + const result = tryCatch(() => JSON.parse("invalid")) 64 + expect(result._tag).toBe("Err") 65 + }) 66 + }) 67 + })