Skip to content

Add comprehensive unit, integration, and E2E test suites - #221

Merged
einsteinx2 merged 2 commits into
mainfrom
add-tests
Jul 26, 2026
Merged

Add comprehensive unit, integration, and E2E test suites#221
einsteinx2 merged 2 commits into
mainfrom
add-tests

Conversation

@einsteinx2

Copy link
Copy Markdown
Owner

Summary

Replaces the bash smoke test with 421 xUnit v3 tests across three projects, runnable locally with plain dotnet test and wired into CI on PRs and pushes to main.

Project Tests Scope
tests/WaveBox.Core.Tests 185 Pure unit tests: extensions, User crypto, Utility, SQLiteConnectionPool, plus reflection tests asserting every ORM model / Subsonic DTO is rooted in ModelTypeRegistry/SubsonicDtoRegistry (catches NativeAOT rooting drift at test time)
tests/WaveBox.Server.Tests 217 Unit layer (UriWrapper, SubsonicMapper, XML serializer, transcoders, image resize) + serialized integration tests against a real temp SQLite DB: schema migration idempotency, repositories, playlist logic, a real TagLib folder scan of a generated MP3 fixture, ServerSettings JSONC round-trips, SubsonicAuth error codes
tests/WaveBox.E2E.Tests 19 Full port of Scripts/smoke-test.sh (all 18 sections): boots the real server binary on a random free port with an isolated root and drives /api + /rest over HTTP. WAVEBOX_E2E_BINARY selects the NativeAOT publish output (CI) or falls back to the local build (dev). Transcode test self-skips without ffmpeg

tests/WaveBox.TestFixtures provides the shared MP3 generator (C# port of make_fixture.py — python3 dependency dropped), temp-root, free-port, and conf-seeding helpers. Scripts/smoke-test.sh and tests/fixtures/make_fixture.py are retired.

Testability changes

  • WAVEBOX_ROOT / WAVEBOX_TEMP env overrides in ServerUtility.RootPath() / ServerInfo.TempFolder — enables isolation on Windows (the old HOME trick couldn't), and keeps test runs away from real user data
  • InternalsVisibleTo for the test projects; three private pure helpers widened to internal
  • The two AOT registries now record their rooted types into an internal list so tests can verify completeness

Bug fixes (each with a regression test)

  • IntExtensions.ToTimeString dropped the minutes segment when hours > 0 and minutes == 0 (3600s"1:00" instead of "1:00:00")
  • AbstractTranscoder.GetHashCode had an inverted null ternary that NRE'd exactly when Item was null
  • SQLiteConnectionPool.CloseAllConnections never re-enabled pooling after a backup
  • Utility.RandomString generated session IDs from a shared non-thread-safe Random; now Random.Shared
  • Config port was Int16, so any port above 32767 failed JSON parsing and silently fell back to 6500; now int end to end
  • StringExtensions.RemoveByteOrderMark used culture-sensitive StartsWith — under ICU, U+FEFF is zero-weight, so it stripped the first character of every string; now ordinal

A few oddities are deliberately pinned (not fixed) with commented characterization tests, notably Playlist.IndexOfMediaItem matching by ItemType only.

CI

  • Fixed the stale push: branches: [master] trigger → main
  • New fast test job (ubuntu/macos/windows) gates the six-RID AOT publish matrix, so broken PRs don't burn six AOT builds
  • NuGet caching via actions/cache
  • Smoke-test step replaced with the E2E suite against the published AOT binary — now also on win-x64 (enabled by the WAVEBOX_ROOT override)

Test plan

  • dotnet test (whole solution): 421 passed, 0 failed, stable across reruns
  • dotnet publish -c Release -r osx-arm64 + E2E against the NativeAOT binary (exactly the CI path): 19/19 passed
  • CI on this PR exercises the new test job on 3 OSes and E2E on 4 RIDs

🤖 Generated with Claude Code

https://claude.ai/code/session_01F6dsiZvEPAKYYMKBpGsypr

einsteinx2 and others added 2 commits July 26, 2026 17:00
Replace the bash smoke test with 421 xUnit v3 tests across three
projects, all runnable via plain `dotnet test` and wired into CI:

- WaveBox.Core.Tests (185, parallel): extensions, User crypto, Utility,
  SQLiteConnectionPool, and reflection tests asserting every ORM model
  and Subsonic DTO is rooted in ModelTypeRegistry/SubsonicDtoRegistry,
  catching NativeAOT rooting drift at test time.
- WaveBox.Server.Tests (217): unit layer (UriWrapper, SubsonicMapper,
  XML serializer, transcoders, image resize) plus a serialized
  integration collection against a real temp SQLite database: schema
  migration idempotency, repositories, playlist logic, a real TagLib
  folder scan of a generated MP3 fixture, ServerSettings JSONC
  round-trips, and SubsonicAuth error codes.
- WaveBox.E2E.Tests (19): full port of Scripts/smoke-test.sh. A fixture
  boots the real server binary on a random free port with an isolated
  root; WAVEBOX_E2E_BINARY selects the NativeAOT publish output in CI,
  falling back to the local build for development.
- WaveBox.TestFixtures: shared MP3 generator (C# port of
  make_fixture.py, dropping the python3 dependency), temp-root,
  free-port, and conf-seeding helpers.

Testability changes: WAVEBOX_ROOT/WAVEBOX_TEMP env overrides for
ServerUtility.RootPath()/ServerInfo.TempFolder (also enables Windows
E2E isolation), InternalsVisibleTo for the test projects, and three
private pure helpers widened to internal.

Bug fixes, each with a regression test:
- IntExtensions.ToTimeString dropped the minutes segment at H>0, M=0
  (3600s rendered "1:00" instead of "1:00:00")
- AbstractTranscoder.GetHashCode inverted null ternary NRE'd exactly
  when Item was null
- SQLiteConnectionPool.CloseAllConnections never re-enabled pooling
  after a backup
- Utility.RandomString used a shared non-thread-safe Random to generate
  session IDs; now Random.Shared
- Config "port" was Int16, so values above 32767 failed JSON parsing
  and silently fell back to 6500; now int end to end
- StringExtensions.RemoveByteOrderMark used culture-sensitive
  StartsWith, stripping the first character of every string under ICU;
  now ordinal

CI: fix the stale push trigger (master -> main), add a fast 3-OS test
job gating the AOT publish matrix, add NuGet caching, and replace the
smoke-test step with the E2E suite against the published binary,
now including win-x64. Scripts/smoke-test.sh and
tests/fixtures/make_fixture.py are retired.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6dsiZvEPAKYYMKBpGsypr
The Windows CI leg caught a real production bug: FlushSettings rendered
mediaFolders/folderArtNames with a plain quoted CSV, so backslashes in
Windows paths were written unescaped into wavebox.conf. The next reload
(or server restart) failed to parse the file and silently reset every
setting to defaults.

Lists are now rendered with JsonEncodedText so flushed values re-parse
as valid JSON strings. Adds a regression test using a folder name
containing a backslash and a quote, which exercises the escaping on
every OS rather than only on Windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6dsiZvEPAKYYMKBpGsypr
@einsteinx2
einsteinx2 merged commit 04ff8e1 into main Jul 26, 2026
9 checks passed
@einsteinx2
einsteinx2 deleted the add-tests branch July 26, 2026 22:36
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