Skip to content

test: property-based tests for Result/Option/Arrays/pipe laws#10

Merged
kelsos merged 1 commit intomainfrom
test/property-based
Apr 18, 2026
Merged

test: property-based tests for Result/Option/Arrays/pipe laws#10
kelsos merged 1 commit intomainfrom
test/property-based

Conversation

@kelsos
Copy link
Copy Markdown
Owner

@kelsos kelsos commented Apr 18, 2026

Summary

Batch #4 of the post-launch polish. Add fast-check properties that verify the algebraic laws the library implicitly claims. Each property runs ~100 random cases by default, so this PR adds roughly 2,500 automated check cases on top of the existing example-based tests — cheap insurance against entire classes of regressions.

What gets verified

Result (`result/laws.prop.test.ts`)

  • Functor identity: `map(r, id) === r`
  • Functor composition: `map(map(r, f), g) === map(r, g ∘ f)`
  • Monad left identity: `flatMap(ok(x), f) === f(x)`
  • Monad right identity: `flatMap(r, ok) === r`
  • Monad associativity: `flatMap(flatMap(r, f), g) === flatMap(r, x => flatMap(f(x), g))`

Option (`option/laws.prop.test.ts`)

Same five laws with `flatMap` + `some`.

Arrays (`arrays/transform.prop.test.ts`)

  • Functor identity + composition for `map`
  • `filter` length never exceeds input
  • `filter` idempotence — applying the same predicate twice is the same as once
  • Identity and absorption: `filter(true) === xs`, `filter(false) === []`
  • `flatMap` singleton lift: `flatMap(xs, x => [x]) === xs`
  • `flatMap` annihilation: `flatMap(xs, () => []) === []`
  • `reduce(xs, 0, +)` agrees with native `xs.reduce`

pipe + flow (`pipe.prop.test.ts`)

  • Identity: `pipe(x) === x`
  • Single-arg application: `pipe(x, f) === f(x)`
  • Two-step composition: `pipe(x, f, g) === g(f(x))`
  • Associativity: `pipe(pipe(x, f), g) === pipe(x, f, g)`
  • flow/pipe equivalence: `flow(f, g)(x) === pipe(x, f, g)`
  • `flow(id)` is the identity function

Implementation

  • `fast-check@4.6.0` added as a plainfp devDep (41 days old, past the 7-day minimumReleaseAge wall; no allowlist entry needed).
  • Tests named `.prop.test.ts` — still picked up by the existing `src/**/.test.ts` vitest include pattern.
  • 25 new test blocks, ~2,500 generated cases per run.

Results

  • 249 total tests passing (was 224)
  • Typecheck clean
  • No runtime code touched

Test plan

  • All required CI checks pass
  • Coverage job stays within threshold (property tests exercise code paths too, so coverage should tick up)
  • Test job reports the new files in its run output

Add fast-check properties that verify the algebraic laws the library
implicitly claims. Each property runs ~100 random cases (fast-check's
default), so this is ~2500 additional check cases on top of the existing
example-based tests — cheap insurance against whole classes of future
regressions.

Covered:

- `result/laws.prop.test.ts`
  - Functor identity: map(r, id) === r
  - Functor composition: map(map(r, f), g) === map(r, g ∘ f)
  - Monad left identity: flatMap(ok(x), f) === f(x)
  - Monad right identity: flatMap(r, ok) === r
  - Monad associativity

- `option/laws.prop.test.ts` — same five laws for Option/flatMap/some

- `arrays/transform.prop.test.ts`
  - Functor identity + composition for Arrays.map
  - filter length never exceeds input
  - filter idempotence: filter(p)(filter(p)(xs)) === filter(p)(xs)
  - filter(true) === xs, filter(false) === []
  - flatMap(xs, x => [x]) === xs (singleton lift)
  - flatMap(xs, x => []) === []
  - reduce(xs, 0, +) matches xs.reduce

- `pipe.prop.test.ts`
  - Identity: pipe(x) === x
  - Single-arg: pipe(x, f) === f(x)
  - Two-step composition: pipe(x, f, g) === g(f(x))
  - Associativity: pipe(pipe(x, f), g) === pipe(x, f, g)
  - flow/pipe equivalence: flow(f, g)(x) === pipe(x, f, g)
  - flow(id) is the identity function

Adds `fast-check@4.6.0` as a plainfp devDep. No runtime impact — these
are test-only. All 249 tests still pass, typecheck clean.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.16%. Comparing base (a4b6d4f) to head (5153437).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #10      +/-   ##
==========================================
+ Coverage   88.41%   89.16%   +0.75%     
==========================================
  Files          23       23              
  Lines         397      397              
  Branches       90       90              
==========================================
+ Hits          351      354       +3     
+ Misses         15       13       -2     
+ Partials       31       30       -1     
Flag Coverage Δ
unittests 89.16% <ø> (+0.75%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 18, 2026

Merging this PR will not alter performance

✅ 48 untouched benchmarks


Comparing test/property-based (5153437) with main (a4b6d4f)

Open in CodSpeed

@kelsos kelsos merged commit 5153437 into main Apr 18, 2026
11 checks passed
@kelsos kelsos deleted the test/property-based branch April 18, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant