JVM-only unit tests raising coverage of the lane-1 packages (data/repository, the data mappers, and pure domain/** logic). 74 new tests across 4 purely-additive test files — no production code is touched.
AccountRepositoryImplTest (new) — AccountRepositoryImpl had no test at all. Covers observeAccounts, testConnection (success + failure), addImapAccount/addOutlookAccount (happy path persists account + credential + channel + sync/backfill; rejected-LIST failure persists nothing; XOAUTH2 vs. password secret handling), deleteAccount, and resetBackfillProgress (per-account vs. all).
MailRepositoryImplCoverageTest (new) — the MailRepositoryImpl surface the existing MailRepositoryImplTest skips: observeAttachments/observeUnreadCounts/observeDrafts/observeOutbox, getMessage/getDraft/saveDraft, setStarred/deleteMessage (server-reachable vs. local-only), sendMessage + copyAttachments (staging, sanitized names, unreadable-URI skip, missing-account failure), searchServer (all-accounts vs. filtered, search-only membership), clearSearchResults/retryOutbox, and every "account/row gone mid-flight" fall-through (openMessage, prefetchMessage, inlineImages, refreshFolders, moveToFolder, expunge, downloadAttachment filename fallback, signature-disabled reply). Kept as a separate additive file so the in-flight search PR (#234) and the existing suite are left untouched.
MappersTest (new) — entity⇄domain mappers not already pinned: account/settings/message/attachment/outbox round-trips, the unknown-enum fallbacks (authType→PASSWORD_IMAP, security→SSL_TLS), toImapParams/toSmtpParams, and FetchedMessage.toEntity's composite-id / uid-parse rules.
DomainModelCoverageTest (new) — AccountSettings.signatureBlock (all three branches), Signature.plainText, default-argument constructors, and the Account.outlook / MailProvider.createAccount blank-display-name fallbacks.
Measured coverage (:app:jacocoTestReport)
In-scope target
Instruction
Line
domain/model (package)
100.0%
100.0%
domain/repository (package)
100% (no code)
100% (no code)
data/local · MappersKt (the mappers)
98.8%
100.0%
data/repository · AccountRepositoryImpl
100.0%
100.0%
data/repository · MailRepositoryImpl
94.5%
98.3%
data/repository (whole package)
82.9%
98.8%
Line coverage is ≥98.8% for every in-scope package, and instruction coverage is ≥98.8% for the mappers and 100% for all domain logic and AccountRepositoryImpl.
Why data/repositoryinstruction sits at 82.9%
82.8% of the package's missed instructions live in Kotlin-coroutine synthetic classes — the Flow.map collector continuations (…$$inlined$map$1$1, stuck at ~30%) and suspend-lambda state machines (…$openMessage$2 etc., ~60%). Their suspend/resume dispatch branches only execute when a coroutine actually suspends and resumes, which synchronous mocks never trigger — a well-known JaCoCo × coroutines limitation, not untested logic (the package's line coverage is 98.8%). The same effect keeps MailRepositoryImpl's instruction metric at 94.5% despite 98.3% line coverage; only 4 of its lines are unhit.
Excluding those coroutine synthetics (or ratcheting to the achievable level) is JaCoCo-config territory, which per the issue plan is owned by the capstone lane #251 — this PR deliberately does not touch verification/ratchet config.
Verification
assembleDebug · testDebugUnitTest · compileDebugAndroidTestKotlin · lintDebug · ktlintCheck · detekt all green locally (JDK 21). Emulator E2E not applicable — this lane adds only JVM unit tests and changes no production or androidTest code.
## What
JVM-only unit tests raising coverage of the lane-1 packages (`data/repository`, the data mappers, and pure `domain/**` logic). **74 new tests across 4 purely-additive test files — no production code is touched.**
- **`AccountRepositoryImplTest`** (new) — `AccountRepositoryImpl` had **no test at all**. Covers `observeAccounts`, `testConnection` (success + failure), `addImapAccount`/`addOutlookAccount` (happy path persists account + credential + channel + sync/backfill; rejected-LIST failure persists nothing; XOAUTH2 vs. password secret handling), `deleteAccount`, and `resetBackfillProgress` (per-account vs. all).
- **`MailRepositoryImplCoverageTest`** (new) — the `MailRepositoryImpl` surface the existing `MailRepositoryImplTest` skips: `observeAttachments`/`observeUnreadCounts`/`observeDrafts`/`observeOutbox`, `getMessage`/`getDraft`/`saveDraft`, `setStarred`/`deleteMessage` (server-reachable vs. local-only), `sendMessage` + `copyAttachments` (staging, sanitized names, unreadable-URI skip, missing-account failure), `searchServer` (all-accounts vs. filtered, search-only membership), `clearSearchResults`/`retryOutbox`, and every "account/row gone mid-flight" fall-through (`openMessage`, `prefetchMessage`, `inlineImages`, `refreshFolders`, `moveToFolder`, `expunge`, `downloadAttachment` filename fallback, signature-disabled reply). Kept as a **separate additive file** so the in-flight search PR (#234) and the existing suite are left untouched.
- **`MappersTest`** (new) — entity⇄domain mappers not already pinned: account/settings/message/attachment/outbox round-trips, the **unknown-enum fallbacks** (`authType`→`PASSWORD_IMAP`, `security`→`SSL_TLS`), `toImapParams`/`toSmtpParams`, and `FetchedMessage.toEntity`'s composite-id / uid-parse rules.
- **`DomainModelCoverageTest`** (new) — `AccountSettings.signatureBlock` (all three branches), `Signature.plainText`, default-argument constructors, and the `Account.outlook` / `MailProvider.createAccount` blank-display-name fallbacks.
## Measured coverage (`:app:jacocoTestReport`)
| In-scope target | Instruction | Line |
|---|---|---|
| `domain/model` (package) | **100.0%** | **100.0%** |
| `domain/repository` (package) | 100% (no code) | 100% (no code) |
| `data/local` · `MappersKt` (the mappers) | **98.8%** | **100.0%** |
| `data/repository` · `AccountRepositoryImpl` | **100.0%** | **100.0%** |
| `data/repository` · `MailRepositoryImpl` | 94.5% | **98.3%** |
| `data/repository` (whole package) | 82.9% | **98.8%** |
**Line coverage is ≥98.8% for every in-scope package**, and instruction coverage is ≥98.8% for the mappers and 100% for all domain logic and `AccountRepositoryImpl`.
### Why `data/repository` *instruction* sits at 82.9%
**82.8% of the package's missed instructions live in Kotlin-coroutine synthetic classes** — the `Flow.map` collector continuations (`…$$inlined$map$1$1`, stuck at ~30%) and suspend-lambda state machines (`…$openMessage$2` etc., ~60%). Their suspend/resume dispatch branches only execute when a coroutine actually suspends and resumes, which synchronous mocks never trigger — a well-known JaCoCo × coroutines limitation, not untested logic (the package's **line** coverage is 98.8%). The same effect keeps `MailRepositoryImpl`'s instruction metric at 94.5% despite 98.3% line coverage; only 4 of its lines are unhit.
Excluding those coroutine synthetics (or ratcheting to the achievable level) is JaCoCo-config territory, which per the issue plan is owned by the capstone lane **#251** — this PR deliberately does not touch verification/ratchet config.
## Verification
`assembleDebug` · `testDebugUnitTest` · `compileDebugAndroidTestKotlin` · `lintDebug` · `ktlintCheck` · `detekt` all green locally (JDK 21). Emulator E2E not applicable — this lane adds only JVM unit tests and changes no production or androidTest code.
Closes #246
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
JVM-only unit tests raising coverage of the lane-1 packages (
data/repository, the data mappers, and puredomain/**logic). 74 new tests across 4 purely-additive test files — no production code is touched.AccountRepositoryImplTest(new) —AccountRepositoryImplhad no test at all. CoversobserveAccounts,testConnection(success + failure),addImapAccount/addOutlookAccount(happy path persists account + credential + channel + sync/backfill; rejected-LIST failure persists nothing; XOAUTH2 vs. password secret handling),deleteAccount, andresetBackfillProgress(per-account vs. all).MailRepositoryImplCoverageTest(new) — theMailRepositoryImplsurface the existingMailRepositoryImplTestskips:observeAttachments/observeUnreadCounts/observeDrafts/observeOutbox,getMessage/getDraft/saveDraft,setStarred/deleteMessage(server-reachable vs. local-only),sendMessage+copyAttachments(staging, sanitized names, unreadable-URI skip, missing-account failure),searchServer(all-accounts vs. filtered, search-only membership),clearSearchResults/retryOutbox, and every "account/row gone mid-flight" fall-through (openMessage,prefetchMessage,inlineImages,refreshFolders,moveToFolder,expunge,downloadAttachmentfilename fallback, signature-disabled reply). Kept as a separate additive file so the in-flight search PR (#234) and the existing suite are left untouched.MappersTest(new) — entity⇄domain mappers not already pinned: account/settings/message/attachment/outbox round-trips, the unknown-enum fallbacks (authType→PASSWORD_IMAP,security→SSL_TLS),toImapParams/toSmtpParams, andFetchedMessage.toEntity's composite-id / uid-parse rules.DomainModelCoverageTest(new) —AccountSettings.signatureBlock(all three branches),Signature.plainText, default-argument constructors, and theAccount.outlook/MailProvider.createAccountblank-display-name fallbacks.Measured coverage (
:app:jacocoTestReport)domain/model(package)domain/repository(package)data/local·MappersKt(the mappers)data/repository·AccountRepositoryImpldata/repository·MailRepositoryImpldata/repository(whole package)Line coverage is ≥98.8% for every in-scope package, and instruction coverage is ≥98.8% for the mappers and 100% for all domain logic and
AccountRepositoryImpl.Why
data/repositoryinstruction sits at 82.9%82.8% of the package's missed instructions live in Kotlin-coroutine synthetic classes — the
Flow.mapcollector continuations (…$$inlined$map$1$1, stuck at ~30%) and suspend-lambda state machines (…$openMessage$2etc., ~60%). Their suspend/resume dispatch branches only execute when a coroutine actually suspends and resumes, which synchronous mocks never trigger — a well-known JaCoCo × coroutines limitation, not untested logic (the package's line coverage is 98.8%). The same effect keepsMailRepositoryImpl's instruction metric at 94.5% despite 98.3% line coverage; only 4 of its lines are unhit.Excluding those coroutine synthetics (or ratcheting to the achievable level) is JaCoCo-config territory, which per the issue plan is owned by the capstone lane #251 — this PR deliberately does not touch verification/ratchet config.
Verification
assembleDebug·testDebugUnitTest·compileDebugAndroidTestKotlin·lintDebug·ktlintCheck·detektall green locally (JDK 21). Emulator E2E not applicable — this lane adds only JVM unit tests and changes no production or androidTest code.Closes #246
🤖 Generated with Claude Code