Commit 77db789
authored
fix(core-backend): control randomness to fix flaky test (#6936)
## Explanation
### Current State
The `BackendWebSocketService` tests were experiencing flakiness after
introducing the new ExponentialBackoff logic using Cockatiel. The random
jitter in the exponential backoff algorithm was causing
non-deterministic test behavior, particularly in tests that relied on
precise timing control with `jest.advanceTimersByTime()`.
**Symptoms:**
- Tests would randomly fail with state mismatches (e.g., expecting
"error" but receiving "connecting")
- Reconnection attempt counts would be inconsistent
- Coverage would fluctuate between runs (sometimes 99.39%, sometimes
100%)
- Specific lines (456, 552-553) would intermittently miss coverage
### Solution
This PR fixes all flaky tests by making the exponential backoff behavior
deterministic and adjusting test timing to prevent race conditions.
#### 1. Added `Math.random()` Mock to 9 Tests
Mocked `Math.random()` to return `0` in tests that involve reconnection
logic. This ensures Cockatiel's exponential backoff delays are
predictable and consistent across test runs:
- `should handle abnormal WebSocket close by triggering reconnection`
- `should reset reconnect attempts after stable connection`
- `should skip connect when reconnect timer is already scheduled`
- `should handle WebSocket onclose during connection phase`
- `should clear connection timeout in handleClose when timeout occurs
then close fires`
- `should not schedule multiple reconnects when scheduleReconnect called
multiple times`
- `should force reconnection and schedule connect`
- `should skip forceReconnection when reconnect timer is already
scheduled`
- `should stop reconnection when isEnabled returns false during
scheduled reconnect`
#### 2. Timing Adjustments to Prevent Race Conditions
Changed `completeAsyncOperations(10)` to `completeAsyncOperations(0)` in
6 tests to check state immediately after operations without allowing
timers to execute prematurely:
- `should handle WebSocket onclose during connection phase` - After
simulateClose
- `should clear connection timeout in handleClose when timeout occurs
then close fires` - After manual close trigger
- `should not schedule multiple reconnects when scheduleReconnect called
multiple times` - After both close events
- `should force reconnection and schedule connect` - After
forceReconnection call
- `should skip forceReconnection when reconnect timer is already
scheduled` - After simulateError
- `should skip connect when reconnect timer is already scheduled` -
After simulateClose
#### 3. Coverage Stability
Fixed intermittent coverage issues for:
- **Line 456**: Early return in `connect()` when reconnect timer is
already scheduled
- **Lines 552-553**: Early return in `forceReconnection()` when
reconnect timer is already scheduled
These lines are now consistently covered by ensuring reconnect timers
remain scheduled during assertions.
### Technical Details
**Exponential Backoff with Jitter:**
- Cockatiel uses `Math.random()` to add jitter to prevent thundering
herd problems
- Without mocking, delays vary unpredictably: e.g., 500ms could become
250-750ms
- With `Math.random() = 0`, delays are deterministic and tests can
advance time precisely
**Timing Philosophy:**
- `completeAsyncOperations(0)`: Flush promises only, no time advancement
- Used when we want to check state immediately after scheduling timers
- Prevents race conditions where timers execute before assertions run
### Testing
Verified stability with extensive testing:
- Individual flaky tests run 100 times each: ✅ All passed
- Full test suite run 100 times: ✅ Consistent 100% coverage
- All tests pass reliably without flakiness
## References
- Related to ExponentialBackoff implementation in
BackendWebSocketService
- Ensures reliable CI/CD test execution
- Fixes intermittent test failures that were blocking development
## Checklist
- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs),
highlighting breaking changes as necessary
- [ ] I've prepared draft pull requests for clients and consumer
packages to resolve any breaking changes
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Stabilizes BackendWebSocketService reconnection tests by mocking
Math.random jitter and using immediate async flushing to avoid timer
races, with minor state/idempotency assertions added.
>
> - **Tests
(`packages/core-backend/src/BackendWebSocketService.test.ts`)**
> - Mock `Math.random()` to `0` in reconnection-related tests to make
Cockatiel backoff jitter deterministic.
> - Replace `completeAsyncOperations(10)` with
`completeAsyncOperations(0)` where assertions must occur before timers
run (e.g., after `simulateClose`/`simulateError`,
post-`forceReconnection`).
> - Add targeted assertions and checks:
> - Verify `CONNECTING` state during connection-phase close handling.
> - Ensure early-return/idempotency when reconnect timer exists
(connect/forceReconnection, multiple `scheduleReconnect` calls).
> - Result: deterministic timing, eliminated flakiness, stable reconnect
attempt counts and coverage.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6333eda. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 78fc4c3 commit 77db789
1 file changed
+31
-8
lines changedLines changed: 31 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
817 | 817 | | |
818 | 818 | | |
819 | 819 | | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
820 | 823 | | |
821 | 824 | | |
822 | 825 | | |
823 | 826 | | |
824 | 827 | | |
825 | 828 | | |
826 | 829 | | |
827 | | - | |
| 830 | + | |
828 | 831 | | |
829 | 832 | | |
830 | 833 | | |
| |||
894 | 897 | | |
895 | 898 | | |
896 | 899 | | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
897 | 903 | | |
898 | 904 | | |
899 | 905 | | |
900 | 906 | | |
901 | | - | |
| 907 | + | |
902 | 908 | | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
903 | 914 | | |
904 | | - | |
| 915 | + | |
905 | 916 | | |
906 | 917 | | |
907 | 918 | | |
| |||
916 | 927 | | |
917 | 928 | | |
918 | 929 | | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
919 | 933 | | |
920 | 934 | | |
921 | 935 | | |
| |||
939 | 953 | | |
940 | 954 | | |
941 | 955 | | |
942 | | - | |
| 956 | + | |
943 | 957 | | |
944 | 958 | | |
945 | 959 | | |
| |||
952 | 966 | | |
953 | 967 | | |
954 | 968 | | |
| 969 | + | |
| 970 | + | |
| 971 | + | |
955 | 972 | | |
956 | 973 | | |
957 | 974 | | |
| |||
961 | 978 | | |
962 | 979 | | |
963 | 980 | | |
964 | | - | |
| 981 | + | |
965 | 982 | | |
966 | 983 | | |
967 | 984 | | |
968 | 985 | | |
969 | 986 | | |
970 | 987 | | |
971 | 988 | | |
972 | | - | |
| 989 | + | |
973 | 990 | | |
974 | 991 | | |
975 | 992 | | |
| |||
987 | 1004 | | |
988 | 1005 | | |
989 | 1006 | | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
990 | 1010 | | |
991 | 1011 | | |
992 | 1012 | | |
| |||
1001 | 1021 | | |
1002 | 1022 | | |
1003 | 1023 | | |
1004 | | - | |
| 1024 | + | |
1005 | 1025 | | |
1006 | 1026 | | |
1007 | 1027 | | |
| |||
1018 | 1038 | | |
1019 | 1039 | | |
1020 | 1040 | | |
| 1041 | + | |
| 1042 | + | |
| 1043 | + | |
1021 | 1044 | | |
1022 | 1045 | | |
1023 | 1046 | | |
1024 | 1047 | | |
1025 | 1048 | | |
1026 | 1049 | | |
1027 | 1050 | | |
1028 | | - | |
| 1051 | + | |
1029 | 1052 | | |
1030 | 1053 | | |
1031 | 1054 | | |
| |||
0 commit comments