Skip to content

test: cover the offline queue, account wipe, importers, and wear codecs#36

Merged
SiteRelEnby merged 1 commit into
mainfrom
test/coverage-pass
Jul 14, 2026
Merged

test: cover the offline queue, account wipe, importers, and wear codecs#36
SiteRelEnby merged 1 commit into
mainfrom
test/coverage-pass

Conversation

@SiteRelEnby

Copy link
Copy Markdown
Collaborator

A general test-coverage pass over the pure logic that fails silently when it breaks: serialisers whose
wrong output the server accepts as a no-op, queue codecs whose parse errors just drop a row, and
classification that quietly deletes an offline action. Went from 70 to 140 phone tests and from a handful to
45 wear tests, and it turned up two live bugs on the way.

Live bugs found (and fixed here)

AccountDataWiper skipped the queue wipe on a cache error. All three clears sat inside one
runCatching, so a throw from cache.clearAll() skipped both deleteAllSwitches() / deleteAllRemovals().
That is the precise failure the class exists to prevent: the previous accounts queued front switches surviving to replay against the new accounts credentials. Each step now runs independently.

Importing with a token that had a control char produced invalid JSON. The importers hand-build their
submit bodies (the options blob is already-encoded JSON, so a typed class would encode it twice) and escaped
only " and \. A PluralKit token pasted from a chat client routinely carries a trailing newline, which
went into the body as a literal newline inside a JSON string and the whole import 422d. Added a shared jsonEscape/jsonQuote` that covers the JSON control range and routed all eight importers through it.

Small refactors to make logic testable without an Android framework

  • SyncWorker: extracted the op ordering and the failure classification to top-level mergeQueuedOps /
    isPermanentHttpFailure (they were private inside a @HiltWorker that needs a Context).
  • Wear: isPermanentSwitchError is now a top-level function; WearSwitchQueue.parseLine widened to
    internal. No behaviour change.

What is now covered

Phone (new): the FrontUpdate tri-state adapter (omit / explicit-null / value, i.e. the "mark still
ongoing" clear), SystemTimezoneBody serialize-nulls (the "Automatic = explicit null" contract), MemberRead
display-name/initials, the offline-queue ordering + drop-vs-retry classification, AccountDataWiper step
independence, import body escaping + terminal-result decoding, TrustedDeviceCookieJar scoping (only the one
cookie, only under /v1/auth/), RevisionSafety re-auth gating, history pagination math, the
MemberDetailViewModel create-then-patch duplicate guard, and the RelationshipsEditorViewModel load-retry
latch.

Wear (new): the FrontHistory and WearSwitchQueue hand-rolled codecs, isPermanentSwitchError held to the
same verdicts as the phone, WearMember display/initials, and markdown stripping.

Not done here (tracked)

The higher-cost targets need infrastructure this PR deliberately skips: TokenAuthenticator and
WearApiClient refresh-mutex tests want MockWebServer; the SharedPreferences-backed wear helpers and the
WearAuthManager sign-out latch want Robolectric; and there is still no androidTest baseline at all. Those
are worth a follow-up if we want to add the deps.

Verification

  • :app:assemblePlayRelease :wear:assembleRelease :app:testPlayReleaseUnitTest :wear:testPlayReleaseUnitTest green.
  • 140 phone + 45 wear tests pass. Both release CI workflows already run the wear suite (from the previous PR).

…s. Adds 70

phone tests and ~40 wear tests, and fixes two live bugs the tests surfaced.

Fixes:
  runCatching, so a throw from the cache clear skipped the queue deletes and
  left the previous account's queued switches to replay under the new session.
  Each step now runs independently. This is a cross-account isolation guard, so
- The importers hand-build their JSON submit bodies and escaped only quotes and
  backslashes. A pasted token with a newline (routine when copying from a chat
  client) produced a literal newline inside a JSON string and the import 422'd.
  Added a shared jsonEscape/jsonQuote covering the control range and routed the
  eight importers through it.

Small refactors to make logic testable without an Android framework:
- Extracted SyncWorker's op ordering and failure classification to top-level
  mergeQueuedOps / isPermanentHttpFailure.
- Made wear's isPermanentSwitchError a top-level function and widened
  WearSwitchQueue.parseLine to internal.

Coverage added:
- Phone: FrontUpdate tri-state adapter, SystemTimezoneBody serialize-nulls,
  MemberRead display/initials, SyncWorker ordering + classification,
  TrustedDeviceCookieJar scoping, RevisionSafety re-auth gating, history
  pagination math, MemberDetailViewModel create-then-patch dedupe,
  RelationshipsEditorViewModel load-retry latch.
- Wear: FrontHistory codec, WearSwitchQueue codec, isPermanentSwitchError parity
  with the phone, WearMember display/initials, markdown stripping.
@SiteRelEnby
SiteRelEnby merged commit 6cfc8b2 into main Jul 14, 2026
1 check passed
@SiteRelEnby
SiteRelEnby deleted the test/coverage-pass branch July 14, 2026 23:39
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