Skip to content

Conversation

@dcrockwell
Copy link
Contributor

Release Summary

This release improves dream_http_client recording and playback system by switching from a single-file strategy to individual files per recording, delivering better performance, concurrent test support, and improved developer experience.

Version: 3.0.1
Type: Non-breaking enhancement
Package: dream_http_client

Key Improvements

Performance

  • O(1) write performance - Eliminates read-modify-write cycles
  • Single file approach required reading all existing recordings before adding one
  • Per-file approach writes directly without reading

Concurrent Test Support

  • Multiple tests can record simultaneously without file contention
  • No more race conditions from shared file access
  • Parallel test execution works reliably

Developer Experience

  • Human-readable filenames: GET_localhost__text_7646ad.json shows what endpoint the recording is for
  • Version control friendly: Individual files show clear diffs in git
  • Easy inspection: Open and edit specific recordings directly
  • Better organization: Large test suites no longer have unwieldy single files

What Changed

Recording Storage

  • Filename format: {method}_{host}_{path}_{hash}.json
  • Hash ensures uniqueness (includes query params, headers, body based on matching config)
  • Each recording is a separate file in the directory

Example

Before (3.0.0):

mocks/
  recordings.json  (contains all recordings)

After (3.0.1):

mocks/
  GET_localhost__text_7646ad.json
  POST_localhost__users_a3f5b2.json
  GET_localhost__stream_b1110e.json

Test Improvements

  • Added 8 comprehensive tests for filename uniqueness, sanitization, overwrite behavior, etc.
  • All recordings committed to test/fixtures/recordings/ for CI without mock server
  • Eliminated nested cases throughout test suite
  • Fixed all tests to use localhost:9876 consistently

Breaking Changes

None - The user-facing recorder API is completely unchanged. Internal storage module functions now require a matching_config parameter, but most users don't call these directly.

Migration

No migration required. Existing recordings.json files still load correctly. New recordings are saved as individual files.

Testing

  • ✅ All 106 tests passing
  • ✅ Pre-commit checks passing
  • ✅ Fixtures committed and loadable
  • ✅ Playback mode works without mock server
  • ✅ CI verified

Dependencies

  • Added gleam_crypto >= 1.5.1 and < 2.0.0 for SHA256 hashing

Release Notes: release-3.0.1.md
Full Changelog: CHANGELOG.md

dcrockwell and others added 4 commits December 9, 2025 21:21
## Why This Change Was Made
- Single-file recording strategy (recordings.json) had O(n) performance - every new recording required reading ALL existing recordings, modifying the list, and writing everything back
- Concurrent tests had file contention - multiple tests writing to the same file caused race conditions and test flakiness
- Large test suites accumulated hundreds of recordings in one file, making it impossible to inspect, debug, or version control individual recordings
- Users wanted granular control - commit specific fixtures, diff individual recordings, delete obsolete ones

## What Was Changed
- storage.gleam: Rewrote to use individual files with format {method}_{host}_{path}_{hash}.json
- Added build_filename() using SHA256 hash for uniqueness while keeping human-readable base
- Added sanitize_for_filename() to handle slashes, special chars, and path truncation (50 char limit)
- load_recordings() scans directory for all .json files instead of reading single file
- save_recording_immediately() writes one file directly - O(1) instead of O(n)
- Added gleam_crypto dependency for SHA256 hashing
- Updated storage API to require matching_config parameter (internal change, recorder API unchanged)
- Added 8 comprehensive tests: filename uniqueness, sanitization, overwrite behavior, fixture-based playback, etc.
- All tests now write to test/fixtures/recordings/ (committed to git for CI without mock server)
- Fixed all tests to use localhost:9876 instead of api.example.com
- Eliminated all nested cases from test suite
- Bumped version to 3.0.1

## Note to Future Engineer
- The hash is generated from the FULL matching signature, not just visible filename parts - this means GET /text and GET /text?page=2 have different hashes even though base filename looks identical
- test/fixtures/recordings/ is COMMITTED to git - this is intentional so CI can run tests in Playback mode without spinning up the mock server
- If a recording isn't loading, check the matching config - the hash includes headers/body if match_headers/match_body are enabled
- The matching_config parameter exists because filename uniqueness depends on what parts of the request we're matching on
- Yes, accumulating fixture files in git seems weird, but it's actually the entire point - proves the recording workflow works and gives you inspectable, version-controlled test data. Embrace it or go write flaky integration tests that depend on external APIs like a savage.
…ecordings

Improve HTTP Client Recording Performance and Reliability
@dcrockwell dcrockwell merged commit 48daf40 into main Dec 10, 2025
2 checks passed
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.

2 participants