Skip to content

Phase 3: extract pure cores + inject functions (functional refactor plan)#72

Merged
astar-development-jb merged 3 commits into
mainfrom
feature/phase-3-pure-cores
Jul 6, 2026
Merged

Phase 3: extract pure cores + inject functions (functional refactor plan)#72
astar-development-jb merged 3 commits into
mainfrom
feature/phase-3-pure-cores

Conversation

@jaybarden1

Copy link
Copy Markdown
Contributor

Summary

Phase 3 of docs/wallpaper-scrapper-functional-refactor-plan.md: extract pure, unit-testable cores from the page/workflow shells and inject effectful dependencies. Page public APIs unchanged — Result-chain conversion is Phase 4.

Pure cores (frozen behaviour, quirks preserved)

  • PageHeaderParser, SubscriptionHeaderParser, TopWallpapersHeaderParserstring? → Result<PageInfo|int, ScrapeError>; shells Match back to existing tuple returns
  • ImageLinkSelector.SelectWanted — pure href filter (non-null, contains /w/, Take 24)
  • TagRules.Evaluate — pure port of ImagePage.ProcessTheImageTagsAsync predicates via TagRuleContextTagOutcome DU (SkipImage | Accept); DB save stays in the shell
  • SearchProgress record + SearchProgressFunctions pure state transitions for SearchWorkflow

New error model

  • ScrapeError DU (7 cases) + ScrapeErrorFactory + LogFailure Serilog extension (plan §4.5)

Injected effects

  • IDelayStrategy / RandomDelayStrategy (all delay sites: category up-to-date, page navigation, already-downloaded, before-image, retry)
  • IImageRetriever / ImageRetriever (HttpClient, returns Result<byte[], ScrapeError>) — replaces deleted static ImageRetrieverHelper
  • IImageSaver / ImageSaver (IFileSystem) — replaces deleted static ImageSaveHelper
  • DirectoryHelper now takes IFileSystem; TimeProvider replaces Stopwatch in SearchWorkflow
  • DI: IDelayStrategy singleton, IImageSaver transient, AddHttpClient<IImageRetriever, ImageRetriever> (2-min timeout)

Documented deviations (reviewed & accepted)

  • Empty (non-null) header now short-circuits to PageParseFailed instead of throwing a framework exception inside the parse logic; callers' log-and-rethrow behaviour unchanged (pinned by tests)
  • Failed image download now throws InvalidOperationException (was effectively FileNotFoundException via silent-empty-array + missing file); retry-once flow unchanged (pinned by tests)
  • Latent [^50..] crash in ImagePageService logging fixed with a length guard
  • Test csproj gains a global using for the Services namespace (sibling-namespace visibility)

Test plan

  • TDD: red tests committed first (aa3e01d), implementation after
  • Full suite green: 239 Wallpaper.Scrapper (incl. all new Phase 3 tests), 118 FunctionalParadigm, 92 Utilities (1 pre-existing skip); build clean with TreatWarningsAsErrors
  • Reviewed by c-sharp-reviewer: 0 blockers; all actionable findings fixed (sequential href collection restored, var usage, ConfigureAwait(false) on new awaits, DirectoryHelper empty-list guard, factory argument validation)

🤖 Generated with Claude Code

jaybarden1 and others added 3 commits July 5, 2026 20:34
Failing-first tests (TDD red) for PageHeaderParser, SubscriptionHeaderParser,
TopWallpapersHeaderParser, ImageLinkSelector, TagRules/TagOutcome, ScrapeError DU,
PageInfo, SearchProgress functions, IDelayStrategy, IImageRetriever, IImageSaver,
IFileSystem-backed DirectoryHelper. Production types intentionally absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ScrapeError DU + factory + LogFailure extension; PageInfo record
- Pure parsers: PageHeaderParser, SubscriptionHeaderParser,
  TopWallpapersHeaderParser, ImageLinkSelector (frozen behaviour)
- TagRules.Evaluate pure core with TagRuleContext/TagOutcome DU;
  DB save stays in ImagePage shell
- SearchProgress record + SearchProgressFunctions pure transitions
- Injected effects: IDelayStrategy/RandomDelayStrategy,
  IImageRetriever/ImageRetriever (HttpClient, Result-returning),
  IImageSaver/ImageSaver (IFileSystem); DirectoryHelper takes IFileSystem
- Deleted static ImageRetrieverHelper/ImageSaveHelper
- SearchWorkflow + ImagePageService rewired to injected functions;
  TimeProvider replaces Stopwatch; DI registrations in App.axaml.cs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jaybarden1 jaybarden1 requested a review from a team July 6, 2026 00:47
@astar-development-jb astar-development-jb enabled auto-merge (squash) July 6, 2026 00:50
@astar-development-jb astar-development-jb merged commit c03dbc9 into main Jul 6, 2026
6 checks passed
@astar-development-jb astar-development-jb deleted the feature/phase-3-pure-cores branch July 6, 2026 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants