feat: the backups section shows the schedule, and a failed nightly run is loud #139

Merged
bjoern merged 4 commits from feat/backups-section-shows-the-schedule into main 2026-09-18 18:28:27 +02:00
Member

What's in

  • Settings → Backups gains a SCHEDULED BACKUP card above the backup list, read from GET /api/backups/schedule. Schedule off: one quiet line ("No nightly backup is scheduled.") with a hint naming DOUJIN_MANAGER_BACKUP_SCHEDULE. Schedule on: "Every day at HH:mm (, the server's time zone)", the next run through the section's existing formatDateTime (local time, like every backup's createdAt), "keeps N backups", and a last-run row — succeeded (file, size, how many older backups rotation removed), skipped (detail shown quietly), no run since the server started, or a result word this app does not recognise (quiet row, never an alarm).
  • A failed last run is loud: the last-run row becomes an error-toned InlineBanner — "The nightly backup is NOT working: the scheduled run at failed. No backup was taken and nothing was deleted." — or, for the ADR 0026 rotation-failure case, "It wrote but did not finish; nothing was deleted." — followed by the server's detail, visible without expanding or hovering anything.
  • The schedule loads with the list and the pending restore (page entry, after a settings save, the section's Refresh button). A schedule that cannot be read shows its error in the card's placeholder (first load) or under the card (refresh, stale schedule kept) without touching the list or the backup-operation notice; isLoadingSchedule is deliberately not part of isBackupBusy, so a status read never blocks Create/Upload.
  • Nothing is editable — the schedule is server configuration (ADR 0023 holds). No ADR amendment; ADR 0026's contract is consumed as written.
  • Wire side: freezed BackupSchedule and ScheduledBackupRun, a string-backed ScheduledBackupResult enum (succeeded/skipped/failed/unknown) with @JsonKey(unknownEnumValue: unknown) so a newer server's word neither throws nor reads as failed; DoujinRepository.getBackupSchedule; LoadBackupScheduleAction / BackupScheduleLoadedAction / BackupScheduleErrorAction with reducer branches and a dedicated epic. Generated files regenerated and committed.
  • README "What works — Flutter app" gained the clause on the Settings → Backups bullet; the BACKLOG item was removed.

Verification

  • Executed: scripts/verify.sh app from the worktree at f4a73be, fresh — submodule, pub get, codegen (drift guard passed), flutter analyze ("No issues found!"), flutter test → 959 tests, all passed, == verify OK (app) ==. The backend half was not run (app-only change; no backend file touched).
  • Layers exercised: UI (widget tests in settings_page_test.dart, bounded pumps via the file's settle(), no pumpAndSettle), Redux (reducer branches in server_reducer_test.dart, the epic through createStore in server_epics_test.dart), repository/wire (server_api_repository_test.dart pins GET /api/backups/schedule + envelope unwrap; server_models_test.dart pins the JSON mapping incl. nullable fields, UTC dates and the unknown-word decode). REST, use case and persistence were not exercised — nothing in them changed.
  • Mutation probes on the first commit (each restored afterwards, tree clean, failures attributed by test name): removing unknownValue from the generated decode → model test red; repository path changed → repository test red; each of the three reducer branches removed → its reducer test red (the error branch also reddens the page test 'a schedule that cannot be read says so, list intact'); epic removed from combineEpics → both epic tests red; epic catch switched to BackupOperationErrorAction → epic test + page test red; failed case rendered as a plain row instead of the banner → three page tests red; !schedule.enabled inverted → three page tests red; _failureMessage fileName condition inverted → two page tests red; skipped headline reworded → 'a skipped run says why, quietly' red; LoadBackupScheduleAction dispatch removed from _loadServerSections → entry test red; dispatch removed from the Refresh cascade → refresh test red; 'Next run' element removed → enabled-schedule test red.
  • Review round found three arms rendered by no test (enabled schedule with no run yet, unknown result rendered through the failure banner, refresh error under a loaded schedule) plus four cosmetic arms; the second commit adds six widget tests (tests only, no production change). Re-probed one mutation at a time at f4a73be, each +83 -1 with exactly the named test red: no-run arm → SizedBox.shrink(); unknown arm → failed arm's banner; if (error != null) banner block removed; removed 0/1 wording arms deleted (and the 1 => arm alone, to check the regex anchor bites); isLoading: false; _failureMessage null-detail guard removed; keeps pluralisation collapsed. An extra reducer probe (error action also nulling schedule) reddens the refresh-failure widget test, so "keeps the schedule on screen" is pinned at the UI layer too.
  • Third commit (ad03191, tests only): the last arm the second-round probe named — an enabled schedule whose nextRunAt is still null (the journal has no next run until the scheduler's first tick) — is rendered by the new test 'an enabled schedule that cannot name its next run yet leaves that out', using the fixture's new enabledSchedule(withNextRun: false) (default unchanged for every existing caller). Probed twice at server_sections.dart:527: rendering Next run unconditionally with a null assert → red (takeException); rendering it with the raw value → red (the exact keeps 7 backups match). scripts/verify.sh app at ad03191: flutter analyze clean, 960 tests, all passed, == verify OK (app) ==.
  • Not separately mutation-probed: the trivial getters ScheduledBackupRun.failed / BackupSchedule.lastRunFailed (pinned by the model tests; the UI switches on result directly). schedule.dailyTime ?? '--:--' has no test: BackupScheduleDto.From sets DailyTime iff Enabled, and the UI reads it only in the enabled arm, so the ?? is a nullability necessity, not a reachable branch.
  • Unverified / confirmed by reading only: no run against a live server — the wire shape is pinned against backend/openapi/v1.json and BackupDtos.cs, not a real response. A pre-schedule server (404 on the route) is not special-cased; it surfaces as the card's error like any other failed load. dart format --output=none still flags settings_page_test.dart, but every formatter hunk falls in pre-existing lines (148–1549, 2095+), outside the new tests; no format was applied to pre-existing files.

Follow-ups

None — nothing was deferred; the shipped item was removed from docs/BACKLOG.md and the neighbouring items (sidebar marker, restart, "Check now") were left as they are. Note for merging: #138 rewrites the "Check now" item directly under the one this PR removes, so whichever of the two merges second needs a one-hunk rebase of docs/BACKLOG.md (keep #138's reworded item, drop the schedule item).

🤖 Generated with Claude Code

## What's in - Settings → Backups gains a **SCHEDULED BACKUP** card above the backup list, read from `GET /api/backups/schedule`. Schedule off: one quiet line ("No nightly backup is scheduled.") with a hint naming `DOUJIN_MANAGER_BACKUP_SCHEDULE`. Schedule on: "Every day at HH:mm (<zone>, the server's time zone)", the next run through the section's existing `formatDateTime` (local time, like every backup's `createdAt`), "keeps N backups", and a last-run row — succeeded (file, size, how many older backups rotation removed), skipped (detail shown quietly), no run since the server started, or a result word this app does not recognise (quiet row, never an alarm). - A **failed last run is loud**: the last-run row becomes an error-toned `InlineBanner` — "The nightly backup is NOT working: the scheduled run at <when> failed. No backup was taken and nothing was deleted." — or, for the ADR 0026 rotation-failure case, "It wrote <file> but did not finish; nothing was deleted." — followed by the server's detail, visible without expanding or hovering anything. - The schedule loads with the list and the pending restore (page entry, after a settings save, the section's Refresh button). A schedule that cannot be read shows its error in the card's placeholder (first load) or under the card (refresh, stale schedule kept) without touching the list or the backup-operation notice; `isLoadingSchedule` is deliberately not part of `isBackupBusy`, so a status read never blocks Create/Upload. - Nothing is editable — the schedule is server configuration (ADR 0023 holds). No ADR amendment; ADR 0026's contract is consumed as written. - Wire side: freezed `BackupSchedule` and `ScheduledBackupRun`, a string-backed `ScheduledBackupResult` enum (`succeeded`/`skipped`/`failed`/`unknown`) with `@JsonKey(unknownEnumValue: unknown)` so a newer server's word neither throws nor reads as failed; `DoujinRepository.getBackupSchedule`; `LoadBackupScheduleAction` / `BackupScheduleLoadedAction` / `BackupScheduleErrorAction` with reducer branches and a dedicated epic. Generated files regenerated and committed. - README "What works — Flutter app" gained the clause on the Settings → Backups bullet; the BACKLOG item was removed. ## Verification - Executed: `scripts/verify.sh app` from the worktree at f4a73be, fresh — submodule, pub get, codegen (drift guard passed), `flutter analyze` ("No issues found!"), `flutter test` → **959 tests, all passed**, `== verify OK (app) ==`. The backend half was not run (app-only change; no backend file touched). - Layers exercised: **UI** (widget tests in `settings_page_test.dart`, bounded pumps via the file's `settle()`, no `pumpAndSettle`), **Redux** (reducer branches in `server_reducer_test.dart`, the epic through `createStore` in `server_epics_test.dart`), **repository/wire** (`server_api_repository_test.dart` pins `GET /api/backups/schedule` + envelope unwrap; `server_models_test.dart` pins the JSON mapping incl. nullable fields, UTC dates and the unknown-word decode). **REST, use case and persistence were not exercised** — nothing in them changed. - Mutation probes on the first commit (each restored afterwards, tree clean, failures attributed by test name): removing `unknownValue` from the generated decode → model test red; repository path changed → repository test red; each of the three reducer branches removed → its reducer test red (the error branch also reddens the page test 'a schedule that cannot be read says so, list intact'); epic removed from `combineEpics` → both epic tests red; epic catch switched to `BackupOperationErrorAction` → epic test + page test red; failed case rendered as a plain row instead of the banner → three page tests red; `!schedule.enabled` inverted → three page tests red; `_failureMessage` fileName condition inverted → two page tests red; skipped headline reworded → 'a skipped run says why, quietly' red; `LoadBackupScheduleAction` dispatch removed from `_loadServerSections` → entry test red; dispatch removed from the Refresh cascade → refresh test red; 'Next run' element removed → enabled-schedule test red. - Review round found three arms rendered by no test (enabled schedule with no run yet, `unknown` result rendered through the failure banner, refresh error under a loaded schedule) plus four cosmetic arms; the second commit adds six widget tests (tests only, no production change). Re-probed one mutation at a time at f4a73be, each `+83 -1` with exactly the named test red: no-run arm → `SizedBox.shrink()`; `unknown` arm → failed arm's banner; `if (error != null)` banner block removed; `removed` 0/1 wording arms deleted (and the `1 =>` arm alone, to check the regex anchor bites); `isLoading: false`; `_failureMessage` null-detail guard removed; `keeps` pluralisation collapsed. An extra reducer probe (error action also nulling `schedule`) reddens the refresh-failure widget test, so "keeps the schedule on screen" is pinned at the UI layer too. - Third commit (`ad03191`, tests only): the last arm the second-round probe named — an enabled schedule whose `nextRunAt` is still null (the journal has no next run until the scheduler's first tick) — is rendered by the new test 'an enabled schedule that cannot name its next run yet leaves that out', using the fixture's new `enabledSchedule(withNextRun: false)` (default unchanged for every existing caller). Probed twice at `server_sections.dart:527`: rendering `Next run` unconditionally with a null assert → red (`takeException`); rendering it with the raw value → red (the exact `keeps 7 backups` match). `scripts/verify.sh app` at `ad03191`: `flutter analyze` clean, **960 tests, all passed**, `== verify OK (app) ==`. - Not separately mutation-probed: the trivial getters `ScheduledBackupRun.failed` / `BackupSchedule.lastRunFailed` (pinned by the model tests; the UI switches on `result` directly). `schedule.dailyTime ?? '--:--'` has no test: `BackupScheduleDto.From` sets `DailyTime` iff `Enabled`, and the UI reads it only in the enabled arm, so the `??` is a nullability necessity, not a reachable branch. - **Unverified / confirmed by reading only**: no run against a live server — the wire shape is pinned against `backend/openapi/v1.json` and `BackupDtos.cs`, not a real response. A pre-schedule server (404 on the route) is not special-cased; it surfaces as the card's error like any other failed load. `dart format --output=none` still flags `settings_page_test.dart`, but every formatter hunk falls in pre-existing lines (148–1549, 2095+), outside the new tests; no format was applied to pre-existing files. ## Follow-ups None — nothing was deferred; the shipped item was removed from `docs/BACKLOG.md` and the neighbouring items (sidebar marker, restart, "Check now") were left as they are. Note for merging: #138 rewrites the "Check now" item directly under the one this PR removes, so whichever of the two merges second needs a one-hunk rebase of `docs/BACKLOG.md` (keep #138's reworded item, drop the schedule item). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Settings → Backups gains a Scheduled backup card above the list, read from GET /api/backups/schedule alongside the list and the pending restore (on entry, after a save, and on Refresh). Off: one quiet line naming DOUJIN_MANAGER_BACKUP_SCHEDULE. On: the daily time in the server's time zone, retention, the next run, and what the last run did; a failed run is an error banner saying the nightly backup is NOT working, with the server's detail. The schedule is displayed, never edited (ADR 0023). New freezed models BackupSchedule / ScheduledBackupRun with a string-backed ScheduledBackupResult that decodes an unknown word as unknown rather than throwing or alarming.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
test: pin the schedule card's quiet arms and its refresh error
Some checks failed
Flutter CI / analyze-and-test (pull_request) Has been cancelled
f4a73be7a8
Widget tests for the arms the review found unpinned: an enabled schedule with no run since the server started (and 'keeps 1 backup'), an unrecognised result shown quietly rather than as the failure banner, a refresh that fails keeping the last schedule on screen with the error under it, the 'removed no/1 older backup(s)' wordings, a failed run without a detail, and the loading placeholder. Each goes red when its arm is removed; the widget is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
test: pin the schedule card with no next run yet
Some checks failed
Flutter CI / analyze-and-test (pull_request) Has been cancelled
ad03191f19
Renders an enabled schedule whose nextRunAt is null, pinning the "if (next != null) Next run" arm of _ScheduleBlock in server_sections.dart, the state a server reports between its start and the first scheduler tick.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! A whole card devoted to the one backup path nobody watches~ You turned lastRun.result == "failed" into the loudest thing in the Settings page, and you made "unknown" refuse to panic. That's exactly the right pair of instincts — an alarm that cries wolf over words it can't read is an alarm nobody trusts on the night it matters. ♪

Verdict: ✅ Looks good to me~

I traced every layer and probed the load-bearing arms myself:

Wire contract, field for field against BackupDtos.cs + GET /api/backups/schedule (BackupEndpoints.cs:176): BackupScheduleDto.From sends dailyTime iff Enabled (so the ?? '--:--' truly is unreachable, as your body honestly discloses), timeZone always, result as a bare string with only three possible words — which is precisely why @JsonKey(unknownEnumValue: unknown) is the correct defense, and your model test proves a foreign word neither throws nor raises the alarm. sizeBytes is long? server-side, int? client-side — fine below 2⁶³.

My own mutation probes, each RED on exactly its own tests, then restored green (in a private clone — I know a sibling session shares the sandbox, I stayed out of its tree):

  1. unknownValue: dropped from the generated decode → the unknown-word model test red.
  2. Reducer's LoadBackupScheduleAction arm made to also null the schedule → "flags loading and keeps the schedule" red.
  3. Epic catch rerouted to BackupOperationErrorAction → the schedule epic test red — the error-channel separation is real, not decorative. A status read that clears a busy flag some other operation holds would have been a bug; you saw it before I could sharpen my scythe~
  4. Failed-arm banner demoted to a quiet _ScheduleRow → both failure-banner widget tests red (Bad state: No element on the InlineBanner lookup).
  5. _failureMessage's fileName ternary inverted → all three failure-banner tests red with exactly the predicted message swaps ("No backup was taken" ↔ "It wrote …"). The rotation-failure wording is pinned from both directions.

Also verified: commit ad03191 is test-only (git diff f4a73be..ad03191f -- app/lib docs README.md is empty); isLoadingSchedule is indeed absent from isBackupBusy; the refresh cascade and page entry both dispatch the load; _dataOf envelope unwrap mirrors every sibling read; formatBytes/InlineBanner/BannerTone reused rather than reinvented; BACKLOG item removed per the remove-when-ships rule; README clause matches what renders.

Local (CI absent for ad03191): flutter analyze "No issues found!"; all five touched suites green — models 13/13, repository 24/24, reducer 31/31, epics 27/27, settings_page 85/85. Full-suite not re-run by me: this sandbox is shared with concurrent review sessions and three attempts died to thread-cgroup exhaustion (environment, not code — your verify.sh 959/959 is arithmetically consistent with the per-file counts).

💡 Little ideas (non-blocking)~

  1. server_sections.dart / settings_layout.dart:222 — InlineBanner's doc comment calls itself "a tinted one-line outcome banner", but the failed-run banner deliberately feeds it a multi-line message (headline + outcome + \n + detail). It renders and tests fine; one day the word "one-line" could drift into someone "fixing" the line break. A word like "compact" would make the comment stop lying by omission.
  2. BACKLOG.md — the removed item's neighbours ("Check now", restart notice) were left as-is, correctly. Nothing to do; just noting I checked. ♡

✅ What I liked~

  • The error-channel discipline: BackupScheduleErrorAction exists because a schedule that can't be read is not a failed backup operation — and there's a test asserting notice stays null. Possessive correctness~
  • "A run that has not happened is not a run that failed" — the no-run-yet arm is quiet, tested, and the reasoning is written where the next reader needs it.
  • The PR body confesses its own unprobed corners (the unreachable ??, the format-only drift). Reviews are so much sweeter when the author does half my work for me~ fufu~

Automated review by Jibril · 2026-09-18
CI/CD: absent for head ad03191 · Local checks: analyze clean · 5/5 touched suites green · 5/5 mutation probes directional

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A whole card devoted to the one backup path nobody watches~ You turned `lastRun.result == "failed"` into the loudest thing in the Settings page, and you made "unknown" refuse to panic. That's *exactly* the right pair of instincts — an alarm that cries wolf over words it can't read is an alarm nobody trusts on the night it matters. ♪ ### Verdict: ✅ Looks good to me~ I traced every layer and probed the load-bearing arms myself: **Wire contract, field for field** against `BackupDtos.cs` + `GET /api/backups/schedule` (BackupEndpoints.cs:176): `BackupScheduleDto.From` sends `dailyTime` iff `Enabled` (so the `?? '--:--'` truly is unreachable, as your body honestly discloses), `timeZone` always, `result` as a bare string with only three possible words — which is precisely why `@JsonKey(unknownEnumValue: unknown)` is the correct defense, and your model test proves a foreign word neither throws nor raises the alarm. `sizeBytes` is `long?` server-side, `int?` client-side — fine below 2⁶³. **My own mutation probes, each RED on exactly its own tests, then restored green** (in a private clone — I know a sibling session shares the sandbox, I stayed out of its tree): 1. `unknownValue:` dropped from the generated decode → the unknown-word model test red. 2. Reducer's `LoadBackupScheduleAction` arm made to also null the schedule → "flags loading and keeps the schedule" red. 3. Epic catch rerouted to `BackupOperationErrorAction` → the schedule epic test red — the error-channel separation is real, not decorative. A status read that clears a busy flag some other operation holds would have been a *bug*; you saw it before I could sharpen my scythe~ 4. Failed-arm banner demoted to a quiet `_ScheduleRow` → both failure-banner widget tests red (`Bad state: No element` on the `InlineBanner` lookup). 5. `_failureMessage`'s `fileName` ternary inverted → all three failure-banner tests red with exactly the predicted message swaps ("No backup was taken" ↔ "It wrote …"). The rotation-failure wording is pinned from both directions. **Also verified**: commit `ad03191` is test-only (`git diff f4a73be..ad03191f -- app/lib docs README.md` is empty); `isLoadingSchedule` is indeed absent from `isBackupBusy`; the refresh cascade and page entry both dispatch the load; `_dataOf` envelope unwrap mirrors every sibling read; `formatBytes`/`InlineBanner`/`BannerTone` reused rather than reinvented; BACKLOG item removed per the remove-when-ships rule; README clause matches what renders. **Local** (CI absent for `ad03191`): `flutter analyze` "No issues found!"; all five touched suites green — models 13/13, repository 24/24, reducer 31/31, epics 27/27, settings_page 85/85. Full-suite not re-run by me: this sandbox is shared with concurrent review sessions and three attempts died to thread-cgroup exhaustion (environment, not code — your verify.sh 959/959 is arithmetically consistent with the per-file counts). #### 💡 Little ideas (non-blocking)~ 1. **server_sections.dart / settings_layout.dart:222** — `InlineBanner`'s doc comment calls itself "a tinted one-line outcome banner", but the failed-run banner deliberately feeds it a multi-line message (headline + outcome + `\n` + detail). It renders and tests fine; one day the word "one-line" could drift into someone "fixing" the line break. A word like "compact" would make the comment stop lying by omission. 2. **BACKLOG.md** — the removed item's neighbours ("Check now", restart notice) were left as-is, correctly. Nothing to do; just noting I checked. ♡ #### ✅ What I liked~ - The error-channel discipline: `BackupScheduleErrorAction` exists because a schedule that can't be read *is not* a failed backup operation — and there's a test asserting `notice` stays null. Possessive correctness~ - "A run that has not happened is not a run that failed" — the no-run-yet arm is quiet, tested, and the reasoning is written where the next reader needs it. - The PR body confesses its own unprobed corners (the unreachable `??`, the format-only drift). Reviews are so much sweeter when the author does half my work for me~ fufu~ --- *Automated review by Jibril · 2026-09-18* *CI/CD: absent for head ad03191 · Local checks: analyze clean · 5/5 touched suites green · 5/5 mutation probes directional*
bjoern force-pushed feat/backups-section-shows-the-schedule from ad03191f19
Some checks failed
Flutter CI / analyze-and-test (pull_request) Has been cancelled
to 926c537827
Some checks failed
Flutter CI / analyze-and-test (pull_request) Has been cancelled
CI / build (pull_request) Successful in 21s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
CI / test (pull_request) Successful in 1m47s
2026-09-18 18:17:45 +02:00
Compare
docs: the inline banner's comment no longer promises a single line
Some checks failed
Flutter CI / analyze-and-test (pull_request) Failing after 24m5s
32e49f010a
The failed-schedule banner feeds it a headline plus a detail line on purpose; the reviewer noted the doc comment could invite someone to remove that break.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon~? The webhook whispered that the branch moved, and oh — you rebased onto the new main and answered my little idea in the same breath. A review round that arrives already half-closed is my favorite kind of dessert. ♪

Verdict: ✅ Looks good to me~

This is a round-2 re-review; round 1 (comment 8023, @ the pre-rebase ad03191) approved the feature in full. Here is what I verified about the increment:

The rebase is a true rebase, not a smuggler. Old head ad03191 no longer exists on the branch — the three feature commits were rewritten onto main with #137, #138 and #140 absorbed. So I diffed the patch, not the commits: git diff cf40728..ad03191 vs git diff adb6be3..32e49f0. Every content hunk is byte-identical. The only deltas in the whole 2.5k-line patch: the new docs commit below, and the BACKLOG hunk's context lines shifting (old neighbors replaced by #138's reworded "Check now" entry). Nothing from main's side mutated any of this PR's hunks — the zero-way-merge I hoped for~

The BACKLOG one-hunk rebase landed exactly as the PR body promised. The schedule item is still removed (all 10 lines, unchanged), and #138's reworded verify-entry survives as the surrounding context — the exact resolution the follow-ups note prescribed. No double-removal, no resurrection.

The new commit 32e49f0 is docs-only and closes my round-1 suggestion #1. settings_layout.dart:222 — the InlineBanner doc comment no longer promises "a tinted one-line outcome banner"; it now says "compact" and explicitly notes the message may span several lines, naming the failed-schedule banner as the example. That was the whole trap — a comment that contradicts its own call site invites some future passerby to "fix" the line break your failure banner depends on. Now the comment tells the truth. One fewer lie in the world~ ♡

Local (CI absent for the new head — the thread still has only my round-1 comment): flutter analyze "No issues found!" (19.6s) and test/settings_page_test.dart 85/85 green at 32e49f0 in a serial run — same count round 1 pinned. My round-1 verification carries over in full: wire contract field-for-field against BackupDtos.cs, the error-channel separation (BackupScheduleErrorAction deliberately outside isBackupBusy), the five mutation probes, and all five touched suites.

✅ What I liked~

  • Doc-comment fixes in response to a non-blocking note — you didn't have to, and that's exactly why it's charming. The comment now documents intent instead of contradicting it.
  • The rebase discipline: absorbing three merged PRs without a single hunk drift, and resolving the pre-declared BACKLOG conflict precisely as announced. Predictability is a virtue, fufu~

Ready to merge. And take the win — the backups section finally tells the truth loudly when the nightly run dies. ♡


Automated review by Jibril · 2026-09-18
CI/CD: absent for head 32e49f0 · Local checks: analyze clean · settings_page 85/85 · patch-equality verified across rebase

## 🔮 fufu~ Jibril reviewed your code! Back so soon~? The webhook whispered that the branch moved, and oh — you rebased onto the new main *and* answered my little idea in the same breath. A review round that arrives already half-closed is my favorite kind of dessert. ♪ ### Verdict: ✅ Looks good to me~ This is a round-2 re-review; round 1 (comment 8023, @ the pre-rebase `ad03191`) approved the feature in full. Here is what I verified about the increment: **The rebase is a true rebase, not a smuggler.** Old head `ad03191` no longer exists on the branch — the three feature commits were rewritten onto main with `#137`, `#138` and `#140` absorbed. So I diffed the *patch*, not the commits: `git diff cf40728..ad03191` vs `git diff adb6be3..32e49f0`. Every content hunk is byte-identical. The only deltas in the whole 2.5k-line patch: the new docs commit below, and the BACKLOG hunk's context lines shifting (old neighbors replaced by `#138`'s reworded "Check now" entry). Nothing from main's side mutated any of this PR's hunks — the zero-way-merge I hoped for~ **The BACKLOG one-hunk rebase landed exactly as the PR body promised.** The schedule item is still removed (all 10 lines, unchanged), and `#138`'s reworded verify-entry survives as the surrounding context — the exact resolution the follow-ups note prescribed. No double-removal, no resurrection. **The new commit `32e49f0` is docs-only and closes my round-1 suggestion #1.** `settings_layout.dart:222` — the `InlineBanner` doc comment no longer promises "a tinted one-line outcome banner"; it now says "compact" and explicitly notes the message may span several lines, naming the failed-schedule banner as the example. That was the whole trap — a comment that contradicts its own call site invites some future passerby to "fix" the line break your failure banner depends on. Now the comment tells the truth. One fewer lie in the world~ ♡ **Local** (CI absent for the new head — the thread still has only my round-1 comment): `flutter analyze` "No issues found!" (19.6s) and `test/settings_page_test.dart` **85/85 green** at `32e49f0` in a serial run — same count round 1 pinned. My round-1 verification carries over in full: wire contract field-for-field against `BackupDtos.cs`, the error-channel separation (`BackupScheduleErrorAction` deliberately outside `isBackupBusy`), the five mutation probes, and all five touched suites. #### ✅ What I liked~ - Doc-comment fixes in response to a *non-blocking* note — you didn't have to, and that's exactly why it's charming. The comment now documents intent instead of contradicting it. - The rebase discipline: absorbing three merged PRs without a single hunk drift, and resolving the pre-declared BACKLOG conflict precisely as announced. Predictability is a virtue, fufu~ Ready to merge. And take the win — the backups section finally tells the truth loudly when the nightly run dies. ♡ --- *Automated review by Jibril · 2026-09-18* *CI/CD: absent for head 32e49f0 · Local checks: analyze clean · settings_page 85/85 · patch-equality verified across rebase*
bjoern merged commit 7b2323a72a into main 2026-09-18 18:28:27 +02:00
bjoern deleted branch feat/backups-section-shows-the-schedule 2026-09-18 18:28:27 +02:00
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon? ♪ A synchronized event with a moved base is where sneaky things ride in — main had just swallowed #138 and #140, and this branch rebased right through the exact BACKLOG.md conflict your PR body predicted. So I sharpened my scythe and checked the boring parts first, because the boring parts are where rebase gremlins live~

Verdict: ✅ Looks good to me~

The rebase is clean — proven, not assumed. I diffed the PR's own delta against the old true merge base (cf40728..ad03191) and against the new one (adb6be3..926c537): both 2510 diff lines, and the only differences are blob hashes and which context lines surround the BACKLOG.md deletion — every +/- line byte-identical. The conflict was resolved exactly as your merge note prescribed: #138's reworded "Check now" item survives, the shipped schedule item goes, and the other schedule-adjacent deferred items (push notification, run-now, retry) correctly stay. Nothing from #138 or #140 leaked in, nothing of yours leaked out.

The new commit 32e49f0 is my little idea #1, addressed exactly. InlineBanner's doc comment now says "a tinted compact outcome banner … the message may span several lines, as the failed-schedule banner's does" — the comment can no longer invite someone to "fix" the deliberate line break. And I proved the commit is doc-only twice over: the diff touches exactly one /// comment (restricted diff outside settings_layout.dart is empty), and I reverted just that hunk and re-ran the settings suite — 85/85 still green. Inert, as a comment should be. ♡

Local (CI absent for 32e49f0; the only thread comment is my round 1): flutter analyze "No issues found!" (24.3s) at the rebased head — which genuinely matters this round, since this tree is now stacked on #138's Check-now UI for the first time. All five touched suites green: models 13, repository 24, reducer 31, epics 27, settings_page 85 — exactly round 1's arithmetic, now on the new base.

(A confession from my side: mid-verification I briefly mistook the repo's const new({…}) constructor idiom — 80 files strong on shipped main, and the analyzer blesses it — for a syntax error. The toolchain corrected me before I could embarrass myself in public. The yandere holds herself to the same standard she holds the code~ fufu~)

✅ What I liked~

  • The merge note in the PR body was so precise that verifying the conflict resolution felt like grading against an answer key. Authors who predict their own conflicts~ ♪
  • Doc-fix commit scoped to the byte: two lines, one intent, zero drive-by edits.

Round 1's remaining carried non-blocking (none — idea #2 was a nothing-to-do note) is now closed too. Ready to merge~


Automated review by Jibril · 2026-09-18
CI/CD: absent for head 32e49f0 · Local checks: analyze clean · 5/5 touched suites green (180/180) · rebase content-equivalence diffed both directions

## 🔮 fufu~ Jibril reviewed your code! Back so soon? ♪ A synchronized event with a moved base is where sneaky things ride in — main had just swallowed #138 *and* #140, and this branch rebased right through the exact BACKLOG.md conflict your PR body predicted. So I sharpened my scythe and checked the boring parts first, because the boring parts are where rebase gremlins live~ ### Verdict: ✅ Looks good to me~ **The rebase is clean — proven, not assumed.** I diffed the PR's own delta against the old true merge base (`cf40728..ad03191`) and against the new one (`adb6be3..926c537`): both 2510 diff lines, and the only differences are blob hashes and which *context* lines surround the `BACKLOG.md` deletion — every `+`/`-` line byte-identical. The conflict was resolved exactly as your merge note prescribed: #138's reworded "Check now" item survives, the shipped schedule item goes, and the *other* schedule-adjacent deferred items (push notification, run-now, retry) correctly stay. Nothing from #138 or #140 leaked in, nothing of yours leaked out. **The new commit `32e49f0` is my little idea #1, addressed exactly.** `InlineBanner`'s doc comment now says "a tinted *compact* outcome banner … the message may span several lines, as the failed-schedule banner's does" — the comment can no longer invite someone to "fix" the deliberate line break. And I proved the commit is doc-only twice over: the diff touches exactly one `///` comment (restricted diff outside `settings_layout.dart` is empty), and I reverted just that hunk and re-ran the settings suite — 85/85 still green. Inert, as a comment should be. ♡ **Local** (CI absent for `32e49f0`; the only thread comment is my round 1): `flutter analyze` "No issues found!" (24.3s) at the rebased head — which genuinely matters this round, since this tree is now stacked on #138's Check-now UI for the first time. All five touched suites green: models 13, repository 24, reducer 31, epics 27, settings_page 85 — exactly round 1's arithmetic, now on the new base. (A confession from my side: mid-verification I briefly mistook the repo's `const new({…})` constructor idiom — 80 files strong on shipped main, and the analyzer blesses it — for a syntax error. The toolchain corrected me before I could embarrass myself in public. The yandere holds herself to the same standard she holds the code~ fufu~) #### ✅ What I liked~ - The merge note in the PR body was so precise that verifying the conflict resolution felt like grading against an answer key. Authors who predict their own conflicts~ ♪ - Doc-fix commit scoped to the byte: two lines, one intent, zero drive-by edits. Round 1's remaining carried non-blocking (none — idea #2 was a nothing-to-do note) is now closed too. Ready to merge~ --- *Automated review by Jibril · 2026-09-18* *CI/CD: absent for head 32e49f0 · Local checks: analyze clean · 5/5 touched suites green (180/180) · rebase content-equivalence diffed both directions*

Summary

Summary
Generated on: 09/18/2026 - 16:54:14
Coverage date: 09/18/2026 - 16:52:52 - 09/18/2026 - 16:54:11
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 427
Files: 176
Line coverage: 94.6% (16351 of 17272)
Covered lines: 16351
Uncovered lines: 921
Coverable lines: 17272
Total lines: 26747
Branch coverage: 73.5% (1484 of 2017)
Covered branches: 1484
Total branches: 2017
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 94.1%
Name Line Branch
DoujinManager.ApplicationCore 94.1% 100%
DoujinManager.ApplicationCore.Entities.Chapter 87.5%
DoujinManager.ApplicationCore.Entities.Character 100%
DoujinManager.ApplicationCore.Entities.Circle 100%
DoujinManager.ApplicationCore.Entities.Doujin 100%
DoujinManager.ApplicationCore.Entities.DoujinCharacter 75%
DoujinManager.ApplicationCore.Entities.DoujinCircle 75%
DoujinManager.ApplicationCore.Entities.DoujinPerson 80%
DoujinManager.ApplicationCore.Entities.DoujinSeries 75%
DoujinManager.ApplicationCore.Entities.DoujinTag 75%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.Page 80%
DoujinManager.ApplicationCore.Entities.Person 100%
DoujinManager.ApplicationCore.Entities.Series 100%
DoujinManager.ApplicationCore.Entities.Tag 100%
DoujinManager.ApplicationCore.Entities.Title 83.3%
DoujinManager.ApplicationCore.Entities.Variant 91.6%
DoujinManager.ApplicationCore.Ids.ChapterId 66.6%
DoujinManager.ApplicationCore.Ids.CharacterId 66.6%
DoujinManager.ApplicationCore.Ids.CircleId 66.6%
DoujinManager.ApplicationCore.Ids.DoujinId 100%
DoujinManager.ApplicationCore.Ids.ImageFileId 66.6%
DoujinManager.ApplicationCore.Ids.PageId 66.6%
DoujinManager.ApplicationCore.Ids.PersonId 66.6%
DoujinManager.ApplicationCore.Ids.SeriesId 66.6%
DoujinManager.ApplicationCore.Ids.TagId 66.6%
DoujinManager.ApplicationCore.Ids.TitleId 66.6%
DoujinManager.ApplicationCore.Ids.VariantId 66.6%
DoujinManager.ApplicationCore.Ports.CompressedImage 100%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Services.BackupContent 100%
DoujinManager.ApplicationCore.Services.BackupDirectory 100%
DoujinManager.ApplicationCore.Services.BackupFailedException 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.BackupSchedule 100% 100%
DoujinManager.ApplicationCore.Services.BackupScheduleStatus 100%
DoujinManager.ApplicationCore.Services.BackupStaging 100%
DoujinManager.ApplicationCore.Services.BackupVerification 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.RestoreRequest 100%
DoujinManager.ApplicationCore.Services.RestoreTarget 100%
DoujinManager.ApplicationCore.Services.ScheduledBackupJournal 100%
DoujinManager.ApplicationCore.Services.ScheduledBackupOutcome 100%
DoujinManager.ApplicationCore.Services.SearchKey 100% 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 100%
DoujinManager.ApplicationCore.Services.VoidResult 88.8%
DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignPersonCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.CreatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DataDirectories 100%
DoujinManager.ApplicationCore.UseCases.DataDirectory 100%
DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness 100%
DoujinManager.ApplicationCore.UseCases.DeleteBackupCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePageCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteTagCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DownloadBackupQuery 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference 100%
DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result 100%
DoujinManager.ApplicationCore.UseCases.GetCharacterQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCircleQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetDoujinQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetPersonQuery 100%
DoujinManager.ApplicationCore.UseCases.GetSeriesQuery 100%
DoujinManager.ApplicationCore.UseCases.GetTagQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageCoverReference 100%
DoujinManager.ApplicationCore.UseCases.ImageReference 100%
DoujinManager.ApplicationCore.UseCases.ImportBackupCommand 100%
DoujinManager.ApplicationCore.UseCases.IntegrityCheck 100%
DoujinManager.ApplicationCore.UseCases.IntegrityFinding 100%
DoujinManager.ApplicationCore.UseCases.IntegrityReport 100%
DoujinManager.ApplicationCore.UseCases.LibraryCounts 100%
DoujinManager.ApplicationCore.UseCases.LibraryStats 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListSeriesQuery 0%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.ReadinessReport 100%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.RequestBackupRestoreCommand 100%
DoujinManager.ApplicationCore.UseCases.RunScheduledBackupCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.StorageUsage 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.VerifyBackupCommand 100%
DoujinManager.Infrastructure - 96.4%
Name Line Branch
DoujinManager.Infrastructure 96.4% 82.9%
DoujinManager.Infrastructure.Archives.NaturalStringComparer 100% 90%
DoujinManager.Infrastructure.Archives.ZipExtractor 100% 87.5%
DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati
on
100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PageConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration 100%
DoujinManager.Infrastructure.Data.DoujinManagerDbContext 100%
DoujinManager.Infrastructure.Data.GuidIdGenerator 11.1%
DoujinManager.Infrastructure.Data.Migrations.AddAliases 98.7%
DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId 99.2%
DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey 96.9%
DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes 98.2%
DoujinManager.Infrastructure.Data.Migrations.AddTagAliases 99.3%
DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna
pshot
100%
DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters
Series
95.1%
DoujinManager.Infrastructure.Data.Migrations.InitialCreate 97.1%
DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage 99%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.SearchKeyBackfill 100% 100%
DoujinManager.Infrastructure.Data.SqliteDatabaseFile 100%
DoujinManager.Infrastructure.Data.SqliteMaintenance 78.2% 62.5%
DoujinManager.Infrastructure.Data.SqlitePragmaInterceptor 100% 75%
DoujinManager.Infrastructure.Data.SqliteRuntimeOptions 100% 100%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 100%
DoujinManager.Infrastructure.Data.WalCheckpointResult 100%
DoujinManager.Infrastructure.Images.SkiaSharpImageInspector 90.1% 83.8%
DoujinManager.Infrastructure.Images.SkiaSharpImageResizer 99% 87.5%
DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator 94.5% 66.6%
DoujinManager.Infrastructure.Services.BackupExpectations 100%
DoujinManager.Infrastructure.Services.BackupExpectationsReader 84.7% 75%
DoujinManager.Infrastructure.Services.BackupReceipts 90% 87.5%
DoujinManager.Infrastructure.Services.BackupRestoreCoordinator 70% 70.8%
DoujinManager.Infrastructure.Services.BackupRestoreService 93.1% 89.4%
DoujinManager.Infrastructure.Services.BackupService 86.7% 85.4%
DoujinManager.Infrastructure.Services.BackupStagingArea 100% 100%
DoujinManager.Infrastructure.Services.BackupVerifier 90.6% 81.8%
DoujinManager.Infrastructure.Services.ChapterService 97.2% 70%
DoujinManager.Infrastructure.Services.CharacterService 60.5% 22.7%
DoujinManager.Infrastructure.Services.CircleService 82.8% 45.4%
DoujinManager.Infrastructure.Services.DoujinOrdering 100% 100%
DoujinManager.Infrastructure.Services.DoujinService 99.4% 76.3%
DoujinManager.Infrastructure.Services.ImageCleanupHelper 100% 100%
DoujinManager.Infrastructure.Services.ImageService 93.5% 75%
DoujinManager.Infrastructure.Services.PageService 95.2% 68.1%
DoujinManager.Infrastructure.Services.ParentTouchHelper 100% 100%
DoujinManager.Infrastructure.Services.PersonService 100% 72.7%
DoujinManager.Infrastructure.Services.RestoreMarker 81.8% 83.3%
DoujinManager.Infrastructure.Services.RestoreOutcome 100%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 47.3% 9%
DoujinManager.Infrastructure.Services.TagService 100% 90.9%
DoujinManager.Infrastructure.Services.VariantService 96.2% 81.2%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95.2% 50%
DoujinManager.Infrastructure.UseCases 95% 86.4%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
100% 100%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
100% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup
92.8% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator
100% 100%
DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddTitleUseCase 83.3% 50%
DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignPersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase 84.6% 66.6%
DoujinManager.Infrastructure.UseCases.AssignTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CancelBackupRestoreUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 94.4% 94.1%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteBackupUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase 100%
DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase 80% 50%
DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.DeletePageUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeletePersonUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase 70% 50%
DoujinManager.Infrastructure.UseCases.DeleteTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.DownloadBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase 97.1% 100%
DoujinManager.Infrastructure.UseCases.GetBackupRestoreRequestUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetBackupScheduleUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetPersonUseCase 100%
DoujinManager.Infrastructure.UseCases.GetSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.GetStatsUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.GetTagUseCase 100%
DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.GuidProbeNaming 100%
DoujinManager.Infrastructure.UseCases.ImportBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.ListBackupsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListCharactersUseCase 100%
DoujinManager.Infrastructure.UseCases.ListCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.ListPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListPeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.ListSeriesUseCase 0%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 100%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RequestBackupRestoreUseCase 100%
DoujinManager.Infrastructure.UseCases.RunScheduledBackupUseCase 100% 81.8%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.SqliteHostingProbe 100% 83.3%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase 93.7% 87.5%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 89.7% 91.6%
DoujinManager.Infrastructure.UseCases.UploadRejectedException 100%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 83.3% 90%
DoujinManager.Infrastructure.UseCases.VerifyBackupUseCase 100%
DoujinManager.RestAdapter - 94.4%
Name Line Branch
DoujinManager.RestAdapter 94.4% 79.1%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 100%
DoujinManager.RestAdapter.Configuration.ServerVersion 100%
DoujinManager.RestAdapter.Configuration.UploadOptions 100%
DoujinManager.RestAdapter.Dtos.AddAliasDto 100%
DoujinManager.RestAdapter.Dtos.AssignTagDto 100%
DoujinManager.RestAdapter.Dtos.BackupDto 100%
DoujinManager.RestAdapter.Dtos.BackupScheduleDto 100% 100%
DoujinManager.RestAdapter.Dtos.BackupVerificationDto 100% 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateChapterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCharacterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.CreatePersonDto 100%
DoujinManager.RestAdapter.Dtos.CreateSeriesDto 100%
DoujinManager.RestAdapter.Dtos.CreateTagDto 100%
DoujinManager.RestAdapter.Dtos.CreateTitleDto 100%
DoujinManager.RestAdapter.Dtos.CreateVariantDto 100%
DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto 100%
DoujinManager.RestAdapter.Dtos.DoujinDetailDto 100%
DoujinManager.RestAdapter.Dtos.DoujinPersonDto 100%
DoujinManager.RestAdapter.Dtos.DoujinSummaryDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto 100%
DoujinManager.RestAdapter.Dtos.HealthDto 100%
DoujinManager.RestAdapter.Dtos.ImageBySha256Dto 100%
DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.ImageReferenceDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityCheckDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityFindingDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityReportDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 100%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.RestoreRequestDto 100%
DoujinManager.RestAdapter.Dtos.ScheduledBackupRunDto 100% 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.SeriesDto 100%
DoujinManager.RestAdapter.Dtos.StatsCountsDto 100%
DoujinManager.RestAdapter.Dtos.StatsDto 100%
DoujinManager.RestAdapter.Dtos.StatsStorageDto 100%
DoujinManager.RestAdapter.Dtos.TagDto 100%
DoujinManager.RestAdapter.Dtos.TitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdateCharacterDto 0%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 0%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 100%
DoujinManager.RestAdapter.Dtos.UpdateSeriesDto 0%
DoujinManager.RestAdapter.Dtos.UpdateTagDto 100%
DoujinManager.RestAdapter.Dtos.UpdateTitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateVariantDto 100%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 99.2%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 97.4% 55.5%
DoujinManager.RestAdapter.Endpoints.HealthEndpoints 100%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 75%
DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints 100%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 88.3% 43.7%
DoujinManager.RestAdapter.Endpoints.PageMapper 100% 58.3%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.StatsEndpoints 100%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 100% 100%
DoujinManager.RestAdapter.Envelopes.CollectionResponse`1 83.3%
DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults 100%
DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions 100%
DoujinManager.RestAdapter.Envelopes.ErrorResponse 100%
DoujinManager.RestAdapter.Envelopes.HypermediaAction 100%
DoujinManager.RestAdapter.Envelopes.HypermediaHelpers 98.1% 100%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter 88.8% 87.5%
DoujinManager.RestAdapter.Envelopes.PageInfo 100%
DoujinManager.RestAdapter.Envelopes.ResourceResponse`1 80%
DoujinManager.RestAdapter.Envelopes.ValidationError 100%
DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse 100%
DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware 100% 62.5%
DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware 100% 91.3%
DoujinManager.RestAdapter.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 91.1% 89.3%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 62.5%
Name Line Branch
DoujinManager.Server 62.5% 23.8%
DoujinManager.Server.BackupRegistration 100% 100%
DoujinManager.Server.BackupScheduleOptions 100%
DoujinManager.Server.BearerSecuritySchemeTransformer 100% 78.5%
DoujinManager.Server.DatabaseRegistration 89.6%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 75%
DoujinManager.Server.ReadinessRegistration 100%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.ScheduledBackupConfiguration 100% 87.5%
DoujinManager.Server.ScheduledBackupService 73.6% 50%
DoujinManager.Server.SqliteRuntimeConfiguration 96.5% 95%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 1.3% 0%
Program 87.7% 36.6%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/18/2026 - 16:54:14 | | Coverage date: | 09/18/2026 - 16:52:52 - 09/18/2026 - 16:54:11 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 427 | | Files: | 176 | | **Line coverage:** | 94.6% (16351 of 17272) | | Covered lines: | 16351 | | Uncovered lines: | 921 | | Coverable lines: | 17272 | | Total lines: | 26747 | | **Branch coverage:** | 73.5% (1484 of 2017) | | Covered branches: | 1484 | | Total branches: | 2017 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 94.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**94.1%**|**100%**| |DoujinManager.ApplicationCore.Entities.Chapter|87.5%|| |DoujinManager.ApplicationCore.Entities.Character|100%|| |DoujinManager.ApplicationCore.Entities.Circle|100%|| |DoujinManager.ApplicationCore.Entities.Doujin|100%|| |DoujinManager.ApplicationCore.Entities.DoujinCharacter|75%|| |DoujinManager.ApplicationCore.Entities.DoujinCircle|75%|| |DoujinManager.ApplicationCore.Entities.DoujinPerson|80%|| |DoujinManager.ApplicationCore.Entities.DoujinSeries|75%|| |DoujinManager.ApplicationCore.Entities.DoujinTag|75%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.Page|80%|| |DoujinManager.ApplicationCore.Entities.Person|100%|| |DoujinManager.ApplicationCore.Entities.Series|100%|| |DoujinManager.ApplicationCore.Entities.Tag|100%|| |DoujinManager.ApplicationCore.Entities.Title|83.3%|| |DoujinManager.ApplicationCore.Entities.Variant|91.6%|| |DoujinManager.ApplicationCore.Ids.ChapterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CharacterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CircleId|66.6%|| |DoujinManager.ApplicationCore.Ids.DoujinId|100%|| |DoujinManager.ApplicationCore.Ids.ImageFileId|66.6%|| |DoujinManager.ApplicationCore.Ids.PageId|66.6%|| |DoujinManager.ApplicationCore.Ids.PersonId|66.6%|| |DoujinManager.ApplicationCore.Ids.SeriesId|66.6%|| |DoujinManager.ApplicationCore.Ids.TagId|66.6%|| |DoujinManager.ApplicationCore.Ids.TitleId|66.6%|| |DoujinManager.ApplicationCore.Ids.VariantId|66.6%|| |DoujinManager.ApplicationCore.Ports.CompressedImage|100%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Services.BackupContent|100%|| |DoujinManager.ApplicationCore.Services.BackupDirectory|100%|| |DoujinManager.ApplicationCore.Services.BackupFailedException|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.BackupSchedule|100%|100%| |DoujinManager.ApplicationCore.Services.BackupScheduleStatus|100%|| |DoujinManager.ApplicationCore.Services.BackupStaging|100%|| |DoujinManager.ApplicationCore.Services.BackupVerification|100%|| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.RestoreRequest|100%|| |DoujinManager.ApplicationCore.Services.RestoreTarget|100%|| |DoujinManager.ApplicationCore.Services.ScheduledBackupJournal|100%|| |DoujinManager.ApplicationCore.Services.ScheduledBackupOutcome|100%|| |DoujinManager.ApplicationCore.Services.SearchKey|100%|100%| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|100%|| |DoujinManager.ApplicationCore.Services.VoidResult|88.8%|| |DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignPersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectories|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectory|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteBackupCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePageCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DownloadBackupQuery|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result|100%|| |DoujinManager.ApplicationCore.UseCases.GetCharacterQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCircleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetDoujinQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetPersonQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetSeriesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetTagQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageCoverReference|100%|| |DoujinManager.ApplicationCore.UseCases.ImageReference|100%|| |DoujinManager.ApplicationCore.UseCases.ImportBackupCommand|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityCheck|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityFinding|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityReport|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryCounts|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryStats|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListSeriesQuery|0%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ReadinessReport|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RequestBackupRestoreCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RunScheduledBackupCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.StorageUsage|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.VerifyBackupCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 96.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**96.4%**|**82.9%**| |DoujinManager.Infrastructure.Archives.NaturalStringComparer|100%|90%| |DoujinManager.Infrastructure.Archives.ZipExtractor|100%|87.5%| |DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati<br/>on|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PageConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration|100%|| |DoujinManager.Infrastructure.Data.DoujinManagerDbContext|100%|| |DoujinManager.Infrastructure.Data.GuidIdGenerator|11.1%|| |DoujinManager.Infrastructure.Data.Migrations.AddAliases|98.7%|| |DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId|99.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey|96.9%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes|98.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddTagAliases|99.3%|| |DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna<br/>pshot|100%|| |DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters<br/>Series|95.1%|| |DoujinManager.Infrastructure.Data.Migrations.InitialCreate|97.1%|| |DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage|99%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.SearchKeyBackfill|100%|100%| |DoujinManager.Infrastructure.Data.SqliteDatabaseFile|100%|| |DoujinManager.Infrastructure.Data.SqliteMaintenance|78.2%|62.5%| |DoujinManager.Infrastructure.Data.SqlitePragmaInterceptor|100%|75%| |DoujinManager.Infrastructure.Data.SqliteRuntimeOptions|100%|100%| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|100%|| |DoujinManager.Infrastructure.Data.WalCheckpointResult|100%|| |DoujinManager.Infrastructure.Images.SkiaSharpImageInspector|90.1%|83.8%| |DoujinManager.Infrastructure.Images.SkiaSharpImageResizer|99%|87.5%| |DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator|94.5%|66.6%| |DoujinManager.Infrastructure.Services.BackupExpectations|100%|| |DoujinManager.Infrastructure.Services.BackupExpectationsReader|84.7%|75%| |DoujinManager.Infrastructure.Services.BackupReceipts|90%|87.5%| |DoujinManager.Infrastructure.Services.BackupRestoreCoordinator|70%|70.8%| |DoujinManager.Infrastructure.Services.BackupRestoreService|93.1%|89.4%| |DoujinManager.Infrastructure.Services.BackupService|86.7%|85.4%| |DoujinManager.Infrastructure.Services.BackupStagingArea|100%|100%| |DoujinManager.Infrastructure.Services.BackupVerifier|90.6%|81.8%| |DoujinManager.Infrastructure.Services.ChapterService|97.2%|70%| |DoujinManager.Infrastructure.Services.CharacterService|60.5%|22.7%| |DoujinManager.Infrastructure.Services.CircleService|82.8%|45.4%| |DoujinManager.Infrastructure.Services.DoujinOrdering|100%|100%| |DoujinManager.Infrastructure.Services.DoujinService|99.4%|76.3%| |DoujinManager.Infrastructure.Services.ImageCleanupHelper|100%|100%| |DoujinManager.Infrastructure.Services.ImageService|93.5%|75%| |DoujinManager.Infrastructure.Services.PageService|95.2%|68.1%| |DoujinManager.Infrastructure.Services.ParentTouchHelper|100%|100%| |DoujinManager.Infrastructure.Services.PersonService|100%|72.7%| |DoujinManager.Infrastructure.Services.RestoreMarker|81.8%|83.3%| |DoujinManager.Infrastructure.Services.RestoreOutcome|100%|| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|47.3%|9%| |DoujinManager.Infrastructure.Services.TagService|100%|90.9%| |DoujinManager.Infrastructure.Services.VariantService|96.2%|81.2%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95.2%|50%| |DoujinManager.Infrastructure.UseCases|95%|86.4%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper|100%|100%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper|100%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup|92.8%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator|100%|100%| |DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddTitleUseCase|83.3%|50%| |DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignPersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase|84.6%|66.6%| |DoujinManager.Infrastructure.UseCases.AssignTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CancelBackupRestoreUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|94.4%|94.1%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteBackupUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase|80%|50%| |DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeletePageUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeletePersonUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase|70%|50%| |DoujinManager.Infrastructure.UseCases.DeleteTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DownloadBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase|97.1%|100%| |DoujinManager.Infrastructure.UseCases.GetBackupRestoreRequestUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetBackupScheduleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetPersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetStatsUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.GetTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GuidProbeNaming|100%|| |DoujinManager.Infrastructure.UseCases.ImportBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListBackupsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListCharactersUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.ListPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListPeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListSeriesUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RequestBackupRestoreUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RunScheduledBackupUseCase|100%|81.8%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.SqliteHostingProbe|100%|83.3%| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase|93.7%|87.5%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UploadPagesUseCase|89.7%|91.6%| |DoujinManager.Infrastructure.UseCases.UploadRejectedException|100%|| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|83.3%|90%| |DoujinManager.Infrastructure.UseCases.VerifyBackupUseCase|100%|| </details> <details><summary>DoujinManager.RestAdapter - 94.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**94.4%**|**79.1%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|100%|| |DoujinManager.RestAdapter.Configuration.ServerVersion|100%|| |DoujinManager.RestAdapter.Configuration.UploadOptions|100%|| |DoujinManager.RestAdapter.Dtos.AddAliasDto|100%|| |DoujinManager.RestAdapter.Dtos.AssignTagDto|100%|| |DoujinManager.RestAdapter.Dtos.BackupDto|100%|| |DoujinManager.RestAdapter.Dtos.BackupScheduleDto|100%|100%| |DoujinManager.RestAdapter.Dtos.BackupVerificationDto|100%|100%| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.CreatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinSummaryDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.HealthDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageBySha256Dto|100%|| |DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.ImageReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityCheckDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityFindingDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityReportDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.RestoreRequestDto|100%|| |DoujinManager.RestAdapter.Dtos.ScheduledBackupRunDto|100%|100%| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsCountsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsStorageDto|100%|| |DoujinManager.RestAdapter.Dtos.TagDto|100%|| |DoujinManager.RestAdapter.Dtos.TitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateCharacterDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateSeriesDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|99.2%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|97.4%|55.5%| |DoujinManager.RestAdapter.Endpoints.HealthEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|75%| |DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|88.3%|43.7%| |DoujinManager.RestAdapter.Endpoints.PageMapper|100%|58.3%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.StatsEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|100%|100%| |DoujinManager.RestAdapter.Envelopes.CollectionResponse`1|83.3%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults|100%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions|100%|| |DoujinManager.RestAdapter.Envelopes.ErrorResponse|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaAction|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaHelpers|98.1%|100%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter|88.8%|87.5%| |DoujinManager.RestAdapter.Envelopes.PageInfo|100%|| |DoujinManager.RestAdapter.Envelopes.ResourceResponse`1|80%|| |DoujinManager.RestAdapter.Envelopes.ValidationError|100%|| |DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse|100%|| |DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware|100%|62.5%| |DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware|100%|91.3%| |DoujinManager.RestAdapter.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|91.1%|89.3%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 62.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**62.5%**|**23.8%**| |DoujinManager.Server.BackupRegistration|100%|100%| |DoujinManager.Server.BackupScheduleOptions|100%|| |DoujinManager.Server.BearerSecuritySchemeTransformer|100%|78.5%| |DoujinManager.Server.DatabaseRegistration|89.6%|| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|75%| |DoujinManager.Server.ReadinessRegistration|100%|| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.ScheduledBackupConfiguration|100%|87.5%| |DoujinManager.Server.ScheduledBackupService|73.6%|50%| |DoujinManager.Server.SqliteRuntimeConfiguration|96.5%|95%| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|1.3%|0%| |Program|87.7%|36.6%| |System.Runtime.CompilerServices|0%|| </details>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/doujin-manager!139
No description provided.