Repository navigation
test(sync): add the P1 sync integration scenarios - #320
Merged
Merged
Conversation
mohamede1945
added this pull request to stack #326
October 14, 2026 14:05
Add LIFE-08, LIFE-10, LIFE-14 and LIFE-15. All four pass today. - LIFE-08 holds the POST at the server and the logout at token capture, so it can check that only the held POST reaches the server once the reset has begun, and that logout finishes before the POST is released. - LIFE-14 was "unknown". It passes: the engine retries after the 401, and the test refreshes the token between attempts, as a refresh would. The device uses a 100 ms retry base to leave room for that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add NOTE-05, NOTE-08, NOTE-09, NOTE-10, NOTE-11, NOTE-12 and NOTE-17. - Passing: NOTE-05, NOTE-08, NOTE-11. - NOTE-09 pins S7's 422 on the phone's UPDATE. Once S7 is fixed, it also accepts S17 keeping the phone's older edit. - NOTE-10 pins S15: the web's delete drops the phone's edit. - NOTE-12 pins S4: the device shows only the first range. Once it keeps every range, the body edit also accepts S7's 422. - NOTE-17 pins S12: the phone and the server each end with the original and the edited note. The scenario's duplicate check carries S12 too. The device makes no retries, so the run ends at the lost response and the edit lands before the next run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add TXT-02, TXT-04, TXT-05, TXT-07 and TXT-08. All five pass today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add COL-04, COL-10, COL-11, COL-12, COL-13, COL-14, COL-17 and COL-18. - Passing: COL-18. - COL-04 pins S2: the echo of the membership's collection bump reverts the local rename. - COL-10 pins S9: the rename chain fails the apply with a SQLite unique violation. - COL-11 pins S9's third scenario. The sync succeeds, but the phone keeps its unsent "Memorize" and never shows the web's Memorize or its verse, while the server still has "Notes". - COL-12 pins S16: the rename throws org.sqlite.SQLiteException. The collections stay unchanged and nothing is pushed. - COL-13 pins S17: the phone takes the tablet's older name. - COL-14 pins S15: the web's delete drops the phone's rename. - COL-17 pins S11: the rename gets 403 on every attempt. The engine reports a 403 ForbiddenError as AUTHENTICATION. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add BMK-03, BMK-06, BMK-11, BMK-12, BMK-13, BMK-14 and BMK-16. - Passing: BMK-03 and BMK-06 (each heals within one run's retries, the catalog's "eventual"), BMK-12, BMK-13, BMK-14. - BMK-11 was predicted to pass but pins a new issue, S23. A web removal from Favorites wedges the device: applying the remote BOOKMARK DELETE marks the phone's Favorites membership as a pending DELETE. The membership phase can no longer match it to the remote DELETE, so the phone pushes it and gets 404 "bookmark is not found" on every attempt. - BMK-16 was "unknown". It pins S15: the sync completes, but the web's delete drops the phone's new membership and the collection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add HL-04 and HL-07. - HL-04 passes. - HL-07 was "unknown" and pins a new issue, S24. Both devices end up showing yellow, because the highlights flow shows one color per verse. But the tablet stores the phone's green membership next to its own yellow and never removes it, so the server keeps 2:255 in both color collections. The test asserts the devices' shared color outside the known issue, then the single server color inside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add RB-02, RB-03, RB-05 and RB-07. - Passing: RB-02, RB-03, RB-05. - RB-07 confirms the suspected S18. The phone's POST is held after a GET that carries the web's slot 1. A newer local slot 1 set during the hold is lost: once the POST is released, applying the web's slot clears the phone's pending edit, and both sides end on the web's location. A pending note gives the run a POST to hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add RS-03, RS-04, RS-05, RS-07, RS-12 and RS-13. - Passing: RS-03, RS-04, RS-05. - RS-07 was "unknown". It passes: both devices and the server keep the same newest 20 sessions. - RS-12 pins S8. The phone deletes the session after the tablet's delete has synced, so its DELETE is newer by the wall clock. It wins last-write-wins and gets 404 on every attempt. - RS-13 was suspected as S19, but it passes. The server logs its DELETE of the replaced session before the moved session's UPDATE, and the phone applies them in that order. The move comes from the web, because a device merges a move onto a verse it already holds into a DELETE plus an UPDATE, which never reaches this path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add IMP-04, IMP-05 and IMP-07. - IMP-04 passes: after a restore with deleteExisting, the phone, the server and the tablet all hold exactly the backup. - IMP-05 was predicted as S1, but it passes. The second restore turns the first restore's Study into a tombstone, but the restored Study is still active and links the web's study first. The test also checks that the web's study survives later syncs and that the phone shows it once. - IMP-07 pins S11: the note on 2:300 gets 500 on every attempt and blocks the valid note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move NOTE_UPDATE_REJECTED and FAVORITES_DELETE_REJECTED into ScenarioSupport. The P1 failure and convergence scenarios meet the same two failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add ERR-04, ERR-05, ERR-06, ERR-08, ERR-10, ERR-12, ERR-15 and ERR-17 in FailureScenariosP1Test, which keeps FailureScenariosTest under ~500 lines. - Passing: ERR-15. ERR-04 and ERR-17 were "unknown" and pass: lost delete responses cause no 404 loop, and a 409 between chunks doesn't duplicate the committed chunk. - ERR-05, ERR-08 and ERR-10 pin S11: the 409 UniqueViolationError, the BE1-indexed 422, and the 500 each repeat on every attempt. - ERR-06 pins S10. In the catalog's note form, S7's 422 rejects the UPDATE before the server can answer 404, so the test accepts either signature. A collection-rename variant pins S10's 404 on its own, which confirms S10 (previously "from code"). - ERR-12 pins S21: once the retries run out during the outage, the note never reaches the server. ERR-12 uses short local-change and periodic timings, and waits three periodic intervals. - ERR-15 seeds more than 1000 rows instead of capping the page size, like ERR-14, because today's client pages by its own limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add CONV-04, CONV-05, CONV-06, CONV-08 and CONV-09 in ConvergenceScenariosP1Test. - Passing: CONV-05 and CONV-06. CONV-09 was "unknown" and passes: with no retries, the commits of all three phases each lose their response, the app relaunches each time, and nothing is duplicated. - CONV-04 pins S6, which d3's unbookmark hits first. Behind it, the test accepts S7's 422 on the note edits, and S2 reverting a rename made right after a push. Every run must succeed, so the failing run carries its signature. - CONV-08 was predicted as S7, but it pins S2. The edit made while the CREATE is in flight survives the ACK, but the next GET echoes the phone's own CREATE and reverts it before it is pushed. S7's 422 is accepted behind it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add OBS-01 and OBS-02 in a new ObservabilityScenariosTest. Both pass today. - OBS-01 checks each TextLengthLimitException's field, bound, limit and escaped length for adds and edits of notes and collection names, and that none of them changes state. - OBS-02 was "needs API". The public syncStatus flow, added in feat/sync-status, now provides it. The test collects that flow during a sync that fails for good: the flow reports exactly one Failed, carrying 422 ValidationError, not one per adapter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deletes take no timestamp. If the tablet's delete and the phone's later delete land in the same millisecond, the phone's delete is not newer, and S8 does not reproduce. Setting the fake server's clock back a minute keeps the tablet's delete strictly older. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
knownIssue now also takes a description plus several (id, matcher)
pairs. Each pair is a known issue, and they are tried in order. The
first one that accepts the failure is reported as the one that
reproduced. When the block passes, the failure names every id ("S2, S7
appear fixed"). When none accepts the failure, it is an error, as
before.
The single-issue form delegates to it, and its messages are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the last round has a failed or skipped run, syncUntilQuiescent now throws a SyncRunFailure carrying that run. A knownIssue can then pin it with syncRejectedWith or syncFailedLocally, as it can for assertSucceeded. The message is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The harness gains three helpers: - SyncDevice.syncWhileHeld(gate) starts a sync, waits until its POST reaches the gate, runs the block, then releases the gate and returns the run. It replaces the async, awaitArrived and release boilerplate in LIFE-08, RB-07, both ERR-06 tests, CONV-08 and the P0 CONV-07. - pollUntil polls a condition and returns whether it held, for checks the test then asserts itself (ERR-12). - awaitUntil polls a condition and raises HarnessTimeoutException when it never holds (LIFE-08, LIFE-14 and the P0 ERR-11). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FakeAuthRepository.refreshTokensIfNeeded now replaces the token once a test calls expireLocally(), the way OidcAuthRepository refreshes inside its expiry safety margin. It counts each refresh in `refreshes`. Tokens still never expire locally unless a test asks. getAuthHeaders now refreshes before reading the token, as OidcAuthRepository does. It used to read the token first, so a refresh in the same call sent the stale token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog's LIFE-14 passed only because the test itself rotated the token after the 401. The client has no refresh triggered by a 401. - Renamed LIFE-14 to "retries re-read the token store after a 401", which is what it proves. - Added a variant where the token has expired on both the device and the server. The client's own refresh path (refreshTokensIfNeeded) replaces it before the first request, and the sync passes. - Added a variant where the server rejects the token before its local expiry: a revocation, or a device clock running behind. Only the client's refresh path could recover, and it never runs. Every attempt gets 401 TokenExpiredError, and nothing is refreshed. This is pinned as a new issue, S25. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move NOTE-03, NOTE-09, NOTE-12, NOTE-17, CONV-04, CONV-08 and the note form of ERR-06 from one id with an OR'd matcher to one (id, matcher) pair per issue. The log now names the issue that reproduced. A fix to the visible issue no longer passes silently under its id while the issue behind it keeps the test failing. Today: - NOTE-03 and CONV-08 show S2. - NOTE-09 and ERR-06 show S7. - NOTE-12 shows S4. - NOTE-17 shows S12. - CONV-04 shows S6. Two review nits are in the same file: - NOTE-05 no longer counts POSTs. It checks that the session is among the accepted mutations. - NOTE-17 syncs until quiescent, as the catalog says. syncUntilQuiescent's failure now carries the failed run, so S7's 422 is still pinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CONV-09 relaunches after every failed run, up to six times, instead of expecting exactly three lost phases. It asserts that some response was lost. ERR-04 likewise asserts that a delete's response was lost at least once. Neither hard-codes today's three-phase POST structure. - OBS-02 asserts the whole status sequence: the login's replayed result, then Syncing, then exactly one Failed. A comment notes what StateFlow conflation still can't prove. - ERR-12's S21 pin also requires "but was:<[]>". - RS-12 freezes the server clock an hour back, instead of offsetting it, and its comment explains why the phone's later delete is newer. - COL-12 documents that it accepts IllegalArgumentException because no duplicate-name type exists yet, and that S16's fix should tighten it. - CONV-04 explains why only one device changes a color (S24). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Merge FailureScenariosP1Test into FailureScenariosTest (435 lines) and ConvergenceScenariosP1Test into ConvergenceScenariosTest (309 lines), with tests in case-id order, like the other areas. The failure file's companion now holds DROPPED, PHASES and PERIODIC as well as POISON. - Rename FAVORITES_DELETE_REJECTED to FAVORITES_DELETE_404. S6 and S23 share that signature. - Fix the import order in ImportScenariosTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The phone revisits 3:7 with a newer timestamp, so its 3:7 session is pending when the web moves 2:255 onto 3:7. S19 still doesn't surface. The revisit wins last-write-wins over the server's DELETE of the replaced session, and its UPDATE gets 404 on every attempt (S8), so the POST fails before the move is applied. The test pins S8 and accepts S19's local SQLite failure behind it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop references to the private test catalog ("the harness note for",
"the catalog predicted", "the catalog's LIFE-14") and a transient
"today" from the P1 scenario comments, keeping the explanations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove two harness path tests that a P1 scenario now repeats: - logout clears local data, keeps the server, and reports a failed revocation (LIFE-15, with LIFE-06) - a head change between GET pages is retried (ERR-15) Drop the seedNotes helper they were the last users of. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mohamede1945
force-pushed
the
test/sync-p1-scenarios
branch
from
October 14, 2026 15:36
1ab3ae6 to
dd77804
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the 61 P1 end-to-end sync scenarios: 37 pass, 24 are
knownIssue. They use the same rules as P0: only the public API plus server state, and each known issue is pinned to its exact current failure.New issues found
[401 ×6]on every run. The client only refreshes by local expiry time, and nothing reacts to a 401.Issues these tests confirm by running, previously only read from code
Harness changes
knownIssue(description, "S7" to …, "S17" to …)reports which interacting issue matched.SyncDevice.syncWhileHeld(gate) { … }, plus poll helpers.syncUntilQuiescenterrors carry the failing run.Tests
./gradlew :sync-pipelines:jvmTest --rerunpassed 3 runs in a row after the review fixes, and 7 runs before them. Each run had 255 tests, 169 of them integration tests, with 0 failures. The integration tests take about 20 s.Reviewed by a separate reviewer, who confirmed S23 and S24 against the backend code. All findings are addressed.
🤖 Generated with Claude Code
Cleanup (rebased 2026-10-14)