Skip to content

test(repo): tighten throw assertions in tests - #2972

Open
VelikovPetar wants to merge 1 commit into
masterfrom
claude/should-throw-tests-assertion-bd4a61
Open

VelikovPetar wants to merge 1 commit into
masterfrom
claude/should-throw-tests-assertion-bd4a61

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-794

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

packages/stream_chat/test/ asserted thrown errors with a bare try/catch and no fail() on the fall-through path:

test('should throw if trying to set `name`', () {
  try {
    channel.name = 'New name';
  } catch (e) {
    expect(e, isA<StateError>());   // never runs if nothing throws
  }
});

When the production guard goes away the catch body never runs, so the test stays green. grep -rn '\bfail(' packages/*/test/ returned zero hits repo-wide, so no instance of this pattern anywhere closed the hole.

This converts all 56 such blocks to the throwsA idiom the suite already uses ~29 times (e.g. channel_test.dart:6153), and adds a TESTING.md rule so the pattern does not come back.

Scale

58 try/catch blocks in packages/stream_chat/test/; 56 are vacuous with respect to the throw. Two were already correct — they capture the error and assert outside the try — and are left alone.

File Blocks Pre-fix, mutated Post-fix, mutated
channel_test.dart 25 all PASS all FAIL
stream_http_client_test.dart 10 all PASS all FAIL
client_test.dart 8 all PASS all FAIL
auth_interceptor_test.dart 4 all PASS all FAIL
general_api_test.dart 3 all PASS all FAIL
token_manager_test.dart 2 all PASS all FAIL
websocket_test.dart 2 all PASS all FAIL
requests_test.dart, token_test.dart 2 PASS FAIL

How this was verified

Every block was mutation-tested in both directions: neutralise the throw source, run that single test before the fix (must pass — proving it was vacuous) and after (must fail — proving the assertion is live). 56/56 in each direction.

Tier-1 blocks were mutated by neutralising the production guard: the channel.dart setters, the requests.dart / general_api.dart / token.dart / token_manager.dart asserts, the websocket.dart re-entrancy guard, the AuthInterceptor reject paths, and StreamHttpClient's _parseError wrapping. The rest were mutated at the production seam — replacing each rethrow in channel.dart with a normal return — which leaves the surrounding state-transition assertions intact and isolates the error contract exactly.

Two mutations had to be deepened to mean anything. Deleting the general_api.dart asserts leaves an unstubbed mock returning null, so the test fails with a TypeError — that is "a different throw appeared", not "the throw was removed"; a faithful returns-normally simulation shows all three blocks are vacuous. token_manager_test:54 is the same story: a second, same-typed assert downstream in loadToken keeps it green for the wrong reason until both are removed.

Production code is untouched — every mutation was reverted, and git diff HEAD -- packages/stream_chat/lib is empty.

Two real defects this surfaced

  1. token_test.dart had zero coverage of the guard it names. `.fromRawValue` should throw if does not contain `user_id` passed 'bad-token-without-a-user-id', which is not a JWT at all — jose throws ArgumentError while parsing, and the actual missing-claim guard (the assert at token.dart:41, an AssertionError) was never reached. The assertion happened to match the wrong error. Split into two tests, one per path.
  2. Two synchronous-throw traps. WebSocket.connect and both pinMessage overloads return Future but are not async, so they throw while expectLater's argument is being evaluated and the matcher never sees the error. These use the closure form; the old try/catch hid the distinction entirely.

Two deviations from the fix proposed in FLU-794

  • Plain throwsA(isA<T>()) would have dropped assertions. Several catch bodies assert more than the type (networkError.code, err.message, equality against StreamChatNetworkError.fromDioException(error)). Those are preserved with .having(...) / allOf(...).
  • auth_interceptor_test keeps a try/catch. Those four blocks unwrap a private dio type via (e as dynamic).data. They use capture-and-assert-outside instead — the form the stream_chat_flutter / stream_chat_flutter_core suites already use — which closes the hole without forcing a dynamic access into a matcher.

So six try/catch blocks remain in the package by design: the two that were already correct, plus those four.

Scope

repo rather than llc: this is test-only, changes no package behavior, and therefore takes no CHANGELOG.md entry per the changelog policy. The semantic_changelog_update job keys that requirement off the PR scope.

Testing

  • packages/stream_chat: 1922 tests green (1921 before, +1 from the token_test split).
  • melos run analyze: clean across all packages.
  • dart format: clean on every changed file.

Follow-up, deliberately not in this PR

general_api.dart:44, channel.dart:1207, client.dart:2172 and requests.dart:32 validate caller input inside asserts, which STYLE_GUIDE.md explicitly forbids ("Do not use asserts to validate user input or network data (those must throw at runtime)"). Those "should throw" contracts therefore do not exist in release builds at all. Fixing it is a behavior change and needs its own ticket.

Screenshots / Videos

Not applicable — test-only change.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Updated error-handling tests to use clearer synchronous and asynchronous exception matchers.
    • Expanded coverage for malformed tokens, connection failures, invalid arguments, and WebSocket errors.
    • Added detailed validation of error types, messages, codes, and other relevant fields.
  • Documentation
    • Added guidance for asserting synchronous and asynchronous errors, including when to use closures, futures, and captured exceptions.

…atch

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 9372f416-b7bc-41fa-a8fa-52b29cca448c

📥 Commits

Reviewing files that changed from the base of the PR and between 8a4145c and 6d489ba.

📒 Files selected for processing (10)
  • TESTING.md
  • packages/stream_chat/test/src/client/channel/channel_test.dart
  • packages/stream_chat/test/src/client/client_test.dart
  • packages/stream_chat/test/src/core/api/general_api_test.dart
  • packages/stream_chat/test/src/core/api/requests_test.dart
  • packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart
  • packages/stream_chat/test/src/core/http/stream_http_client_test.dart
  • packages/stream_chat/test/src/core/http/token_manager_test.dart
  • packages/stream_chat/test/src/core/http/token_test.dart
  • packages/stream_chat/test/src/ws/websocket_test.dart

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The pull request updates Dart test error assertions across client, channel, API, HTTP, token, and WebSocket tests. It adds guidance for synchronous and asynchronous throwsA usage. Production code and public declarations are unchanged.

Changes

Exception Assertion Refactor

Layer / File(s) Summary
Testing guidance
TESTING.md
Documents throwsA, closures for synchronous failures, futures for asynchronous failures, field matching with having, and external assertions for captured errors.
Channel error assertions
packages/stream_chat/test/src/client/channel/channel_test.dart
Replaces channel test try/catch assertions with matcher-based checks for error types, codes, retriability, and synchronous validation.
Client and WebSocket assertions
packages/stream_chat/test/src/client/client_test.dart, packages/stream_chat/test/src/ws/websocket_test.dart
Uses asynchronous and closure-based expectations for connection, WebSocket, and invalid pinMessage errors.
API validation assertions
packages/stream_chat/test/src/core/api/general_api_test.dart, packages/stream_chat/test/src/core/api/requests_test.dart
Directly asserts ArgumentError and AssertionError for invalid API arguments.
HTTP error assertions
packages/stream_chat/test/src/core/http/stream_http_client_test.dart
Uses composed matchers for network error types, messages, converted errors, and carried errors.
Auth and token assertions
packages/stream_chat/test/src/core/http/interceptor/auth_interceptor_test.dart, packages/stream_chat/test/src/core/http/token_manager_test.dart, packages/stream_chat/test/src/core/http/token_test.dart
Moves interceptor assertions outside catches and directly asserts token configuration and JWT validation errors.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Other

Suggested reviewers: xsahil03x

Merge Risk: ⚪ Minimal · up to 6d489

The test refactor strengthens error assertions without changing production behavior. The updated tests and guidance are ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: strengthening throw assertions across repository tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/should-throw-tests-assertion-bd4a61

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@VelikovPetar VelikovPetar changed the title test(repo): assert thrown errors with throwsA instead of a bare try/catch test(repo): tighten throw assertions in tests Sep 17, 2026
@VelikovPetar
VelikovPetar requested a review from a team September 17, 2026 10:06
@VelikovPetar
VelikovPetar marked this pull request as ready for review September 17, 2026 10:06
@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.95%. Comparing base (8a4145c) to head (6d489ba).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2972   +/-   ##
=======================================
  Coverage   75.95%   75.95%           
=======================================
  Files         447      447           
  Lines       28870    28870           
=======================================
  Hits        21928    21928           
  Misses       6942     6942           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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