source_monitor 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/commands/release.md +45 -22
- data/.claude/skills/sm-configure/SKILL.md +10 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
- data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
- data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
- data/.gitignore +10 -0
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +56 -0
- data/CLAUDE.md +11 -5
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/VERSION +1 -1
- data/app/assets/builds/source_monitor/application.css +43 -0
- data/app/assets/builds/source_monitor/application.js +127 -0
- data/app/assets/builds/source_monitor/application.js.map +3 -3
- data/app/assets/javascripts/source_monitor/application.js +2 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
- data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
- data/app/controllers/source_monitor/sources_controller.rb +11 -0
- data/app/helpers/source_monitor/application_helper.rb +51 -0
- data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
- data/app/jobs/source_monitor/import_opml_job.rb +9 -0
- data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
- data/app/models/source_monitor/source.rb +2 -0
- data/app/views/layouts/source_monitor/application.html.erb +23 -2
- data/app/views/source_monitor/shared/_toast.html.erb +1 -0
- data/app/views/source_monitor/sources/_details.html.erb +34 -5
- data/app/views/source_monitor/sources/_row.html.erb +11 -6
- data/config/routes.rb +1 -0
- data/docs/configuration.md +1 -1
- data/docs/upgrade.md +22 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
- data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
- data/lib/source_monitor/configuration/http_settings.rb +1 -1
- data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
- data/lib/source_monitor/configuration.rb +3 -1
- data/lib/source_monitor/favicons/discoverer.rb +196 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
- data/lib/source_monitor/http.rb +5 -3
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +4 -0
- data/lib/tasks/test_fast.rake +11 -0
- data/source_monitor.gemspec +1 -1
- metadata +7 -93
- data/.vbw-planning/PROJECT.md +0 -51
- data/.vbw-planning/ROADMAP.md +0 -32
- data/.vbw-planning/SHIPPED.md +0 -63
- data/.vbw-planning/STATE.md +0 -27
- data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
- data/.vbw-planning/codebase/CONCERNS.md +0 -99
- data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
- data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
- data/.vbw-planning/codebase/INDEX.md +0 -86
- data/.vbw-planning/codebase/META.md +0 -42
- data/.vbw-planning/codebase/PATTERNS.md +0 -262
- data/.vbw-planning/codebase/STACK.md +0 -101
- data/.vbw-planning/codebase/STRUCTURE.md +0 -324
- data/.vbw-planning/codebase/TESTING.md +0 -154
- data/.vbw-planning/config.json +0 -53
- data/.vbw-planning/discovery.json +0 -26
- data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
- data/.vbw-planning/milestones/default/STATE.md +0 -82
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
- data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
- data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
- data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
- data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
- data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
- data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
- data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
- data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Plan 02 Summary: rubocop-audit-and-fix
|
|
2
|
-
|
|
3
|
-
## Status: complete
|
|
4
|
-
|
|
5
|
-
## Result
|
|
6
|
-
|
|
7
|
-
The codebase already passes RuboCop with zero offenses against the `rubocop-rails-omakase` ruleset. No code changes were required.
|
|
8
|
-
|
|
9
|
-
## Tasks Completed
|
|
10
|
-
|
|
11
|
-
| Task | Name | Result |
|
|
12
|
-
|------|------|--------|
|
|
13
|
-
| 1 | rubocop-audit-categorize-violations | 341 files inspected, 0 offenses |
|
|
14
|
-
| 2 | apply-safe-rubocop-autocorrect | No-op (no violations to fix) |
|
|
15
|
-
| 3 | fix-remaining-rubocop-violations | No-op (no violations to fix) |
|
|
16
|
-
| 4 | configure-rubocop-exclusions-for-phase3 | No-op (Metrics cops disabled in omakase) |
|
|
17
|
-
| 5 | validate-rubocop-zero-offenses | All checks pass |
|
|
18
|
-
|
|
19
|
-
## Violation Counts
|
|
20
|
-
|
|
21
|
-
- **Before:** 0 offenses (341 files inspected)
|
|
22
|
-
- **After:** 0 offenses (341 files inspected)
|
|
23
|
-
|
|
24
|
-
## Commit
|
|
25
|
-
|
|
26
|
-
No commit created -- no code changes were needed.
|
|
27
|
-
|
|
28
|
-
## Verification Results
|
|
29
|
-
|
|
30
|
-
| Check | Result |
|
|
31
|
-
|-------|--------|
|
|
32
|
-
| `bin/rubocop -f simple` | 341 files inspected, no offenses detected |
|
|
33
|
-
| `bin/rubocop -f github` | Exit 0 |
|
|
34
|
-
| `bin/rails test` | 473 runs, 1927 assertions, 0 failures, 0 errors, 0 skips |
|
|
35
|
-
| Coverage baseline (`config/coverage_baseline.json`) | 2328 lines (unchanged) |
|
|
36
|
-
|
|
37
|
-
## Key Findings
|
|
38
|
-
|
|
39
|
-
1. **Omakase config is minimal by design.** The `rubocop-rails-omakase` gem enables only 45 cops (out of 775 available). It focuses on essential layout and style rules, not metrics or complex analysis.
|
|
40
|
-
|
|
41
|
-
2. **Metrics cops are all disabled.** `Metrics/ClassLength`, `Metrics/MethodLength`, `Metrics/BlockLength`, and all other Metrics cops are set to `Enabled: false` in the omakase config. This means the large files (FeedFetcher 627 lines, Configuration 655 lines, ImportSessionsController 792 lines) do not trigger any violations. No `.rubocop.yml` exclusions were needed.
|
|
42
|
-
|
|
43
|
-
3. **Plan 01 was the key enabler.** The frozen_string_literal pragma work (Plan 01, commit 5f02db8, 113 files) was likely the primary source of violations. After that was completed, the remaining code was already compliant with the 45 enabled cops.
|
|
44
|
-
|
|
45
|
-
4. **Enabled cop categories:**
|
|
46
|
-
- Layout (27 cops): indentation, spacing, whitespace
|
|
47
|
-
- Style (12 cops): string literals, hash syntax, parentheses, semicolons
|
|
48
|
-
- Lint (4 cops): string coercion, require parentheses, syntax, URI escape
|
|
49
|
-
- Performance (1 cop): flat_map
|
|
50
|
-
- Migration (1 cop): department name
|
|
51
|
-
|
|
52
|
-
## Deviations
|
|
53
|
-
|
|
54
|
-
| ID | Description | Impact |
|
|
55
|
-
|----|-------------|--------|
|
|
56
|
-
| DEVN-01 | Tasks 2-4 were no-ops because no violations existed | None -- plan designed for worst case, actual state was already compliant |
|
|
57
|
-
| DEVN-01 | No git commit created (no code changes) | None -- the success criteria (zero offenses, tests pass) are satisfied without changes |
|
|
58
|
-
| DEVN-01 | No `.rubocop.yml` exclusions added for Phase 3 files | None -- Metrics cops are disabled in omakase, so exclusions would be meaningless |
|
|
59
|
-
|
|
60
|
-
## REQ-14 Status
|
|
61
|
-
|
|
62
|
-
**REQ-14 (Audit and fix any RuboCop violations against omakase ruleset): SATISFIED**
|
|
63
|
-
|
|
64
|
-
The audit confirms zero violations exist. The CI lint job (`bin/rubocop -f github`) exits 0.
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 1
|
|
3
|
-
plan: 2
|
|
4
|
-
title: rubocop-audit-and-fix
|
|
5
|
-
wave: 2
|
|
6
|
-
depends_on: [1]
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "`bin/rubocop` exits 0 with zero offenses (verified by `bin/rubocop -f simple` output showing `no offenses detected`)"
|
|
11
|
-
- "All existing tests pass (`bin/rails test` exits 0 with no new failures)"
|
|
12
|
-
- "CI lint job would pass (`bin/rubocop -f github` exits 0)"
|
|
13
|
-
artifacts:
|
|
14
|
-
- "All Ruby files in app/, lib/, test/, config/, db/ comply with rubocop-rails-omakase rules"
|
|
15
|
-
- "`.rubocop.yml` may have additional targeted exclusions if auto-generated files legitimately violate rules"
|
|
16
|
-
key_links:
|
|
17
|
-
- "REQ-14 fully satisfied by this plan"
|
|
18
|
-
- "Depends on Plan 01 (frozen_string_literal already resolved -- largest category of violations removed)"
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
# Plan 02: rubocop-audit-and-fix
|
|
22
|
-
|
|
23
|
-
## Objective
|
|
24
|
-
|
|
25
|
-
Audit the entire codebase with RuboCop using the `rubocop-rails-omakase` configuration and fix all violations, achieving zero offenses. This satisfies REQ-14 and ensures the CI lint job passes cleanly.
|
|
26
|
-
|
|
27
|
-
## Context
|
|
28
|
-
|
|
29
|
-
<context>
|
|
30
|
-
@.vbw-planning/REQUIREMENTS.md -- REQ-14: RuboCop audit against omakase ruleset
|
|
31
|
-
@.vbw-planning/codebase/CONVENTIONS.md -- Rails omakase style guide, existing patterns
|
|
32
|
-
@.rubocop.yml -- inherits from rubocop-rails-omakase, excludes test/dummy/db/schema.rb
|
|
33
|
-
@Gemfile -- rubocop-rails-omakase gem included
|
|
34
|
-
@bin/rubocop -- wrapper script that forces config path
|
|
35
|
-
@.github/workflows/ci.yml -- lint job runs `bin/rubocop -f github`
|
|
36
|
-
|
|
37
|
-
**Decomposition rationale:** This plan depends on Plan 01 because `Style/FrozenStringLiteralComment` is typically the most voluminous cop violation. By completing Plan 01 first, this plan deals with a much smaller and more varied set of violations that require more careful, contextual fixes.
|
|
38
|
-
|
|
39
|
-
**Current state:**
|
|
40
|
-
- The project uses `rubocop-rails-omakase` as its base ruleset (inherits the gem's rubocop.yml)
|
|
41
|
-
- Only one exclusion exists: `test/dummy/db/schema.rb`
|
|
42
|
-
- After Plan 01 completes, `Style/FrozenStringLiteralComment` violations will be resolved
|
|
43
|
-
- Remaining violations are unknown until the audit is run, but likely categories include:
|
|
44
|
-
- `Style/StringLiterals` (single vs double quotes)
|
|
45
|
-
- `Layout/*` (indentation, spacing, line length)
|
|
46
|
-
- `Metrics/*` (method/class length -- may need exclusions for large files targeted in Phase 3)
|
|
47
|
-
- `Naming/*` (variable/method naming conventions)
|
|
48
|
-
- `Rails/*` cops from the omakase set
|
|
49
|
-
- Large files (FeedFetcher 627 lines, Configuration 655 lines, ImportSessionsController 792 lines) may trigger `Metrics/ClassLength` or `Metrics/MethodLength` -- these should be addressed with targeted `.rubocop.yml` exclusions since Phase 3 handles the actual refactoring
|
|
50
|
-
|
|
51
|
-
**Constraints:**
|
|
52
|
-
- Fix violations using RuboCop auto-correct where safe (`rubocop -a` for safe corrections, `-A` for aggressive only when reviewed)
|
|
53
|
-
- Do NOT refactor large files to satisfy Metrics cops -- instead exclude them in `.rubocop.yml` with a comment referencing Phase 3
|
|
54
|
-
- Preserve all existing behavior -- style-only changes
|
|
55
|
-
- The `test/dummy/db/schema.rb` exclusion must remain (Rails-generated)
|
|
56
|
-
- Test files in `test/tmp/` are not git-tracked and not subject to RuboCop
|
|
57
|
-
</context>
|
|
58
|
-
|
|
59
|
-
## Tasks
|
|
60
|
-
|
|
61
|
-
### Task 1: Run RuboCop audit and categorize violations
|
|
62
|
-
|
|
63
|
-
- **name:** rubocop-audit-categorize-violations
|
|
64
|
-
- **files:** (no files modified -- analysis only)
|
|
65
|
-
- **action:** Run `bin/rubocop -f json -o tmp/rubocop_report.json` and `bin/rubocop -f simple` to get a complete picture of all violations. Categorize them by: (a) auto-correctable with `-a` (safe), (b) auto-correctable with `-A` (unsafe, needs review), (c) manual fix required, (d) should be excluded (Metrics cops on large files destined for Phase 3 refactoring). Document the count and category of each cop violation type.
|
|
66
|
-
- **verify:** The audit report exists and lists all violations. Categorization is complete.
|
|
67
|
-
- **done:** Full understanding of the violation landscape. Clear plan for which files need which type of fix.
|
|
68
|
-
|
|
69
|
-
### Task 2: Apply safe auto-corrections
|
|
70
|
-
|
|
71
|
-
- **name:** apply-safe-rubocop-autocorrect
|
|
72
|
-
- **files:** All Ruby files flagged by RuboCop safe auto-correct
|
|
73
|
-
- **action:** Run `bin/rubocop -a` to apply all safe auto-corrections across the codebase. This handles cops like `Style/StringLiterals`, `Layout/TrailingWhitespace`, `Layout/EmptyLineAfterMagicComment`, `Layout/SpaceInsideBlockBraces`, etc. Review the diff to confirm no behavioral changes -- only formatting/style changes. If any auto-correction looks wrong, revert that specific change and handle it manually in Task 3.
|
|
74
|
-
- **verify:** Run `git diff --stat` to see scope of changes. Run `bin/rails test` to confirm no test regressions. Run `bin/rubocop -f simple` to see remaining violations after safe auto-correct.
|
|
75
|
-
- **done:** All safe auto-correctable violations are fixed. Test suite still passes.
|
|
76
|
-
|
|
77
|
-
### Task 3: Fix remaining violations manually
|
|
78
|
-
|
|
79
|
-
- **name:** fix-remaining-rubocop-violations
|
|
80
|
-
- **files:** Files with violations that were not auto-correctable or were unsafe auto-corrections
|
|
81
|
-
- **action:** For each remaining violation:
|
|
82
|
-
- **Style cops**: Fix manually following the omakase conventions (single quotes for simple strings, double quotes when interpolation needed, etc.)
|
|
83
|
-
- **Layout cops**: Fix indentation, spacing, alignment manually
|
|
84
|
-
- **Naming cops**: Rename variables/methods to comply (ensure test references are updated)
|
|
85
|
-
- **Rails cops**: Fix any Rails-specific violations (e.g., `Rails/HttpPositionalArguments`)
|
|
86
|
-
- If a violation is in a file that is inherently non-compliant due to its nature (e.g., a migration with unusual structure), add a targeted inline `# rubocop:disable` comment with an explanation
|
|
87
|
-
- **verify:** Run `bin/rubocop -f simple` after each batch of fixes. The violation count should decrease monotonically. Run `bin/rails test` after all manual fixes.
|
|
88
|
-
- **done:** All non-Metrics violations are resolved either by code changes or justified inline disables.
|
|
89
|
-
|
|
90
|
-
### Task 4: Configure exclusions for large files (Phase 3 targets)
|
|
91
|
-
|
|
92
|
-
- **name:** configure-rubocop-exclusions-for-phase3
|
|
93
|
-
- **files:**
|
|
94
|
-
- `.rubocop.yml`
|
|
95
|
-
- **action:** If `Metrics/ClassLength`, `Metrics/MethodLength`, or `Metrics/BlockLength` violations remain for the three large files targeted for refactoring in Phase 3, add targeted exclusions to `.rubocop.yml`:
|
|
96
|
-
```yaml
|
|
97
|
-
# Phase 3 refactoring targets -- remove exclusions after extraction
|
|
98
|
-
Metrics/ClassLength:
|
|
99
|
-
Exclude:
|
|
100
|
-
- "lib/source_monitor/fetching/feed_fetcher.rb"
|
|
101
|
-
- "lib/source_monitor/configuration.rb"
|
|
102
|
-
- "app/controllers/source_monitor/import_sessions_controller.rb"
|
|
103
|
-
```
|
|
104
|
-
Add a comment explaining these are temporary exclusions that will be removed in Phase 3. Do NOT exclude any other files -- only the three identified large files.
|
|
105
|
-
- **verify:** Run `bin/rubocop` and confirm it exits 0 with zero offenses. The exclusions should only cover the Phase 3 target files.
|
|
106
|
-
- **done:** `.rubocop.yml` has targeted, documented exclusions. Zero RuboCop offenses across the entire codebase.
|
|
107
|
-
|
|
108
|
-
### Task 5: Final validation and CI readiness check
|
|
109
|
-
|
|
110
|
-
- **name:** validate-rubocop-zero-offenses
|
|
111
|
-
- **files:** (no files modified -- validation only)
|
|
112
|
-
- **action:** Run the full validation suite: (1) `bin/rubocop -f simple` -- must show `no offenses detected`. (2) `bin/rubocop -f github` -- must exit 0 (this is what CI runs). (3) `bin/rails test` -- full test suite must pass. (4) Verify the coverage baseline has not grown (run `wc -l config/coverage_baseline.json` and confirm it is still approximately 2328 lines -- style changes should not affect coverage).
|
|
113
|
-
- **verify:**
|
|
114
|
-
- `bin/rubocop` exits 0
|
|
115
|
-
- `bin/rubocop -f github` exits 0
|
|
116
|
-
- `bin/rails test` exits 0
|
|
117
|
-
- Coverage baseline line count has not increased
|
|
118
|
-
- **done:** Zero RuboCop violations. CI-ready. REQ-14 satisfied.
|
|
119
|
-
|
|
120
|
-
## Verification
|
|
121
|
-
|
|
122
|
-
1. `bin/rubocop -f simple` outputs `no offenses detected`
|
|
123
|
-
2. `bin/rubocop -f github` exits 0 (CI lint format)
|
|
124
|
-
3. `bin/rails test` exits 0 with no regressions
|
|
125
|
-
4. Coverage baseline (`config/coverage_baseline.json`) has not grown in line count
|
|
126
|
-
|
|
127
|
-
## Success Criteria
|
|
128
|
-
|
|
129
|
-
- [x] Zero RuboCop violations against the omakase ruleset (REQ-14)
|
|
130
|
-
- [x] Any `.rubocop.yml` exclusions are limited to Phase 3 target files with documenting comments
|
|
131
|
-
- [x] CI lint job (`bin/rubocop -f github`) passes
|
|
132
|
-
- [x] No test regressions
|
|
133
|
-
- [x] No behavioral changes -- all fixes are style/formatting only
|
|
134
|
-
|
|
135
|
-
## Phase 1 Coverage Note
|
|
136
|
-
|
|
137
|
-
Phase 1 success criterion #3 ("Coverage baseline shrinks by at least 10%") is not directly addressed by Plans 01 or 02, which focus on code quality (REQ-13, REQ-14). The coverage baseline may shrink slightly if RuboCop fixes remove dead branches or simplify code paths. After Plan 02 completes, regenerate the baseline with `bin/update-coverage-baseline` and measure the delta. The 10% reduction target (from 2328 uncovered lines to ~2095 or fewer) will primarily be achieved in Phase 2 when dedicated test coverage plans execute.
|
data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# PLAN-01 Summary: feed-fetcher-tests
|
|
2
|
-
|
|
3
|
-
## Status: COMPLETE
|
|
4
|
-
|
|
5
|
-
## Commit
|
|
6
|
-
|
|
7
|
-
- **Hash:** `8d4e8d3`
|
|
8
|
-
- **Message:** `test(feed-fetcher): close coverage gaps for retry, errors, headers, entries, helpers [dev-plan01]`
|
|
9
|
-
- **Files changed:** 1 file, 734 insertions
|
|
10
|
-
|
|
11
|
-
## Tasks Completed
|
|
12
|
-
|
|
13
|
-
### Task 1: Test retry strategy and circuit breaker transitions
|
|
14
|
-
- Added tests for reset_retry_state!, apply_retry_strategy!, circuit breaker open/close
|
|
15
|
-
- Verified first timeout sets fetch_retry_attempt to 1
|
|
16
|
-
- Verified exhausting retries opens circuit (sets fetch_circuit_opened_at, fetch_circuit_until)
|
|
17
|
-
- Verified successful fetch resets retry state
|
|
18
|
-
- Tested RetryPolicy error handling fallback
|
|
19
|
-
|
|
20
|
-
### Task 2: Test Faraday error wrapping and connection failures
|
|
21
|
-
- Tested Faraday::ConnectionFailed raises ConnectionError with original_error preserved
|
|
22
|
-
- Tested Faraday::SSLError raises ConnectionError
|
|
23
|
-
- Tested Faraday::ClientError with response hash builds HTTPError via build_http_error_from_faraday
|
|
24
|
-
- Tested generic Faraday::Error raises FetchError
|
|
25
|
-
- Tested non-Faraday StandardError wrapped in UnexpectedResponseError
|
|
26
|
-
|
|
27
|
-
### Task 3: Test Last-Modified header handling and request headers
|
|
28
|
-
- Tested If-Modified-Since header sent when source.last_modified is set
|
|
29
|
-
- Tested Last-Modified response header parsed and stored on source
|
|
30
|
-
- Tested malformed Last-Modified headers silently ignored
|
|
31
|
-
- Tested custom_headers from source passed through to request
|
|
32
|
-
- Tested ETag and Last-Modified preserved on 304 responses
|
|
33
|
-
|
|
34
|
-
### Task 4: Test entry processing edge cases and error normalization
|
|
35
|
-
- Tested feed without entries returns zero counts
|
|
36
|
-
- Tested Events.run_item_processors called for each entry
|
|
37
|
-
- Tested Events.after_item_created called only for created items
|
|
38
|
-
- Tested normalize_item_error extracts guid via entry_id/id fallbacks
|
|
39
|
-
- Tested safe_entry_title returns nil for entries without title
|
|
40
|
-
|
|
41
|
-
### Task 5: Test jitter, interval helpers, and metadata management
|
|
42
|
-
- Tested jitter_offset returns 0 when interval_seconds <= 0
|
|
43
|
-
- Tested jitter_offset uses jitter_proc when provided
|
|
44
|
-
- Tested body_digest returns nil for blank body, SHA256 for non-blank
|
|
45
|
-
- Tested updated_metadata preserves existing metadata
|
|
46
|
-
- Tested configured_seconds, extract_numeric edge cases
|
|
47
|
-
|
|
48
|
-
## Deviations
|
|
49
|
-
|
|
50
|
-
None -- plan executed as specified.
|
|
51
|
-
|
|
52
|
-
## Verification Results
|
|
53
|
-
|
|
54
|
-
| Check | Result |
|
|
55
|
-
|-------|--------|
|
|
56
|
-
| `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` | All tests pass |
|
|
57
|
-
| `bin/rails test` | 760 runs, 2626 assertions, 0 failures, 0 errors, 0 skips |
|
|
58
|
-
|
|
59
|
-
## Success Criteria
|
|
60
|
-
|
|
61
|
-
- [x] 48 new tests added (734 lines)
|
|
62
|
-
- [x] All retry/circuit breaker branches tested
|
|
63
|
-
- [x] All Faraday error wrapping branches tested
|
|
64
|
-
- [x] All header handling branches tested
|
|
65
|
-
- [x] Entry processing and error normalization branches tested
|
|
66
|
-
- [x] Jitter, interval helpers, and metadata management tested
|
|
67
|
-
- [x] REQ-01 substantially satisfied
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 2
|
|
3
|
-
plan: 1
|
|
4
|
-
title: feed-fetcher-tests
|
|
5
|
-
wave: 1
|
|
6
|
-
depends_on: []
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "Running `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with zero failures"
|
|
11
|
-
- "Coverage report shows lib/source_monitor/fetching/feed_fetcher.rb has fewer than 50 uncovered lines (down from 245)"
|
|
12
|
-
- "All new tests use WebMock stubs or VCR cassettes -- no real HTTP requests"
|
|
13
|
-
- "Running `bin/rails test` exits 0 with no regressions"
|
|
14
|
-
artifacts:
|
|
15
|
-
- "test/lib/source_monitor/fetching/feed_fetcher_test.rb -- extended with new test methods covering retry strategy, error handling, header management, entry processing, and helper methods"
|
|
16
|
-
key_links:
|
|
17
|
-
- "REQ-01 substantially satisfied -- FeedFetcher branch coverage above 80%"
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
# Plan 01: feed-fetcher-tests
|
|
21
|
-
|
|
22
|
-
## Objective
|
|
23
|
-
|
|
24
|
-
Close the coverage gap in `lib/source_monitor/fetching/feed_fetcher.rb` (currently 245 uncovered lines out of 627). The existing test file covers basic RSS/Atom/JSON fetching, 304 handling, timeout/HTTP/parsing failures, and adaptive interval mechanics. This plan targets the remaining uncovered branches: retry strategy application, circuit breaker state transitions, connection error wrapping, last_modified header handling, entry processing edge cases, jitter computation, and private helper methods.
|
|
25
|
-
|
|
26
|
-
## Context
|
|
27
|
-
|
|
28
|
-
<context>
|
|
29
|
-
@lib/source_monitor/fetching/feed_fetcher.rb -- 627 lines, the core fetch pipeline
|
|
30
|
-
@lib/source_monitor/fetching/retry_policy.rb -- RetryPolicy with Decision struct
|
|
31
|
-
@lib/source_monitor/fetching/fetch_error.rb -- error hierarchy (TimeoutError, ConnectionError, HTTPError, ParsingError, UnexpectedResponseError)
|
|
32
|
-
@test/lib/source_monitor/fetching/feed_fetcher_test.rb -- existing test file with 12 tests covering success, 304, timeout, HTTP 404, parsing failure, adaptive intervals
|
|
33
|
-
@config/coverage_baseline.json -- lists 245 uncovered lines for feed_fetcher.rb
|
|
34
|
-
@test/test_helper.rb -- test infrastructure (WebMock, VCR, create_source!, with_queue_adapter)
|
|
35
|
-
|
|
36
|
-
**Decomposition rationale:** FeedFetcher is the single largest coverage gap (245 lines). It is tested in isolation from ItemCreator (which has its own plan). The uncovered code falls into distinct categories that can each be addressed as a focused task: (1) retry/circuit breaker logic, (2) Faraday error wrapping and connection failures, (3) header and metadata management, (4) entry processing edge cases, (5) jitter and interval helpers. Each task adds test methods to the existing test file.
|
|
37
|
-
|
|
38
|
-
**Trade-offs considered:**
|
|
39
|
-
- Could split FeedFetcher tests across multiple test files (e.g., feed_fetcher_retry_test.rb), but keeping them in one file matches the existing codebase convention and avoids confusion.
|
|
40
|
-
- Could use mocks for RetryPolicy, but testing the integration between FeedFetcher and RetryPolicy provides higher confidence.
|
|
41
|
-
|
|
42
|
-
**What constrains the structure:**
|
|
43
|
-
- Tests must use WebMock stubs (no real HTTP)
|
|
44
|
-
- All tests go in the existing test file to avoid file proliferation
|
|
45
|
-
- The `jitter: ->(_) { 0 }` pattern from existing tests should be reused to make interval assertions deterministic
|
|
46
|
-
- Tests need `travel_to` for time-sensitive assertions
|
|
47
|
-
</context>
|
|
48
|
-
|
|
49
|
-
## Tasks
|
|
50
|
-
|
|
51
|
-
### Task 1: Test retry strategy and circuit breaker transitions
|
|
52
|
-
|
|
53
|
-
- **name:** test-retry-and-circuit-breaker
|
|
54
|
-
- **files:**
|
|
55
|
-
- `test/lib/source_monitor/fetching/feed_fetcher_test.rb`
|
|
56
|
-
- **action:** Add tests covering lines 262-298 (reset_retry_state!, apply_retry_strategy!) and lines 267-290 (retry/circuit decisions). Specifically:
|
|
57
|
-
1. Test that a first timeout failure sets fetch_retry_attempt to 1 (retry decision with wait)
|
|
58
|
-
2. Test that exhausting retry attempts opens the circuit (sets fetch_circuit_opened_at, fetch_circuit_until, next_fetch_at)
|
|
59
|
-
3. Test that a successful fetch after retries resets retry state (fetch_retry_attempt=0, circuit fields nil)
|
|
60
|
-
4. Test that apply_retry_strategy! handles StandardError by logging and setting defaults (lines 291-298) -- simulate by stubbing RetryPolicy to raise
|
|
61
|
-
5. Test that retry decision adjusts next_fetch_at to the minimum of current and retry time (line 283)
|
|
62
|
-
Use WebMock stubs: first stub raises Faraday::TimeoutError to trigger retries, then stub success for recovery tests. Set source.fetch_retry_attempt manually to simulate multiple failures.
|
|
63
|
-
- **verify:** `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n /retry|circuit/` exits 0
|
|
64
|
-
- **done:** Lines 262-298 covered. Source retry fields verified in assertions.
|
|
65
|
-
|
|
66
|
-
### Task 2: Test Faraday error wrapping and connection failures
|
|
67
|
-
|
|
68
|
-
- **name:** test-faraday-error-wrapping
|
|
69
|
-
- **files:**
|
|
70
|
-
- `test/lib/source_monitor/fetching/feed_fetcher_test.rb`
|
|
71
|
-
- **action:** Add tests covering lines 77-86 (perform_fetch error wrapping) and lines 405-417 (build_http_error_from_faraday). Specifically:
|
|
72
|
-
1. Test that Faraday::ConnectionFailed raises SourceMonitor::Fetching::ConnectionError with original_error preserved
|
|
73
|
-
2. Test that Faraday::SSLError raises ConnectionError
|
|
74
|
-
3. Test that Faraday::ClientError with response hash builds HTTPError via build_http_error_from_faraday (lines 405-417) with correct status, message, and ResponseWrapper
|
|
75
|
-
4. Test that generic Faraday::Error raises FetchError
|
|
76
|
-
5. Test that a non-Faraday StandardError is wrapped in UnexpectedResponseError (lines 52-54)
|
|
77
|
-
Use WebMock's `to_raise` for each error type. Assert the result.error class, message, and original_error.
|
|
78
|
-
- **verify:** `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n /connection|ssl|client_error|unexpected|faraday/i` exits 0
|
|
79
|
-
- **done:** Lines 77-86 and 405-417 covered. All Faraday error types correctly wrapped.
|
|
80
|
-
|
|
81
|
-
### Task 3: Test Last-Modified header handling and request headers
|
|
82
|
-
|
|
83
|
-
- **name:** test-last-modified-and-headers
|
|
84
|
-
- **files:**
|
|
85
|
-
- `test/lib/source_monitor/fetching/feed_fetcher_test.rb`
|
|
86
|
-
- **action:** Add tests covering lines 97-104 (request_headers with custom_headers, etag, last_modified), lines 203-215 and 228-240 (response Last-Modified parsing and storage), and lines 349-355 (parse_http_time). Specifically:
|
|
87
|
-
1. Test that If-Modified-Since header is sent when source.last_modified is set
|
|
88
|
-
2. Test that Last-Modified response header is parsed and stored on source
|
|
89
|
-
3. Test that malformed Last-Modified headers are silently ignored (parse_http_time returns nil for invalid dates, line 353)
|
|
90
|
-
4. Test that custom_headers from source are passed through to the request (lines 98)
|
|
91
|
-
5. Test that both ETag and Last-Modified are preserved on 304 not_modified responses (lines 228-234)
|
|
92
|
-
Use WebMock to verify request headers via `.with(headers: {...})` and return specific response headers.
|
|
93
|
-
- **verify:** `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n /last_modified|custom_header|if_modified/i` exits 0
|
|
94
|
-
- **done:** Lines 97-104, 203-215, 228-240, 349-355 covered.
|
|
95
|
-
|
|
96
|
-
### Task 4: Test entry processing edge cases and error normalization
|
|
97
|
-
|
|
98
|
-
- **name:** test-entry-processing-edges
|
|
99
|
-
- **files:**
|
|
100
|
-
- `test/lib/source_monitor/fetching/feed_fetcher_test.rb`
|
|
101
|
-
- **action:** Add tests covering lines 520-567 (process_feed_entries) and lines 603-624 (normalize_item_error, safe_entry_guid, safe_entry_title). Specifically:
|
|
102
|
-
1. Test that a feed without entries (feed that doesn't respond_to :entries) returns zero counts (line 529)
|
|
103
|
-
2. Test that Events.run_item_processors is called for each entry (line 542)
|
|
104
|
-
3. Test that Events.after_item_created is called only for created items (line 547), not updated ones
|
|
105
|
-
4. Test that normalize_item_error extracts guid via entry_id (line 615-616), falls back to id (line 617-618), and handles entries without either
|
|
106
|
-
5. Test that safe_entry_title returns nil when entry doesn't respond_to :title (line 623)
|
|
107
|
-
Use a simple XML feed fixture with known entries. Mock ItemCreator to control created vs updated results. Use Notifications subscriptions to verify event dispatch.
|
|
108
|
-
- **verify:** `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n /entry_processing|item_processor|normalize_error|safe_entry/i` exits 0
|
|
109
|
-
- **done:** Lines 520-567, 603-624 covered.
|
|
110
|
-
|
|
111
|
-
### Task 5: Test jitter, interval helpers, and metadata management
|
|
112
|
-
|
|
113
|
-
- **name:** test-jitter-and-interval-helpers
|
|
114
|
-
- **files:**
|
|
115
|
-
- `test/lib/source_monitor/fetching/feed_fetcher_test.rb`
|
|
116
|
-
- **action:** Add tests covering lines 490-518 (jitter_offset, adjusted_interval_with_jitter, body_digest, updated_metadata) and lines 569-600 (configured_seconds, configured_positive, configured_non_negative, extract_numeric, fetching_config). Specifically:
|
|
117
|
-
1. Test jitter_offset returns 0 when interval_seconds <= 0 (line 505)
|
|
118
|
-
2. Test jitter_offset uses jitter_proc when provided (line 506)
|
|
119
|
-
3. Test jitter_offset computes random jitter within expected range when no proc given
|
|
120
|
-
4. Test body_digest returns nil for blank body (line 515), returns SHA256 for non-blank
|
|
121
|
-
5. Test updated_metadata preserves existing metadata, removes dynamic_fetch_interval_seconds key, adds last_feed_signature
|
|
122
|
-
6. Test configured_seconds returns default when minutes_value is nil or non-positive (lines 570-571)
|
|
123
|
-
7. Test extract_numeric handles Numeric, responds_to :to_f, and non-numeric values (lines 590-596)
|
|
124
|
-
These are private methods -- test them through the public `call` method by configuring specific fetching settings and verifying the resulting source state. Alternatively, use `send` for the pure-function helpers.
|
|
125
|
-
- **verify:** `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n /jitter|body_digest|metadata|configured_|extract_numeric/i` exits 0
|
|
126
|
-
- **done:** Lines 490-518 and 569-600 covered.
|
|
127
|
-
|
|
128
|
-
## Verification
|
|
129
|
-
|
|
130
|
-
1. `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0
|
|
131
|
-
2. `COVERAGE=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` shows feed_fetcher.rb with >80% branch coverage
|
|
132
|
-
3. `bin/rails test` exits 0 (no regressions)
|
|
133
|
-
|
|
134
|
-
## Success Criteria
|
|
135
|
-
|
|
136
|
-
- [ ] FeedFetcher coverage drops from 245 uncovered lines to fewer than 50
|
|
137
|
-
- [ ] All retry/circuit breaker branches tested
|
|
138
|
-
- [ ] All Faraday error wrapping branches tested
|
|
139
|
-
- [ ] All header handling branches tested
|
|
140
|
-
- [ ] Entry processing and error normalization branches tested
|
|
141
|
-
- [ ] Jitter, interval helpers, and metadata management tested
|
|
142
|
-
- [ ] REQ-01 substantially satisfied
|
data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# PLAN-02 Summary: item-creator-tests
|
|
2
|
-
|
|
3
|
-
## Status: COMPLETE
|
|
4
|
-
|
|
5
|
-
## Commit
|
|
6
|
-
|
|
7
|
-
- **Hash:** `ce8ede4`
|
|
8
|
-
- **Message:** `test(item-creator): close coverage gaps for URL/content, dupes, authors, errors, utils`
|
|
9
|
-
- **Files changed:** 1 file, 941 insertions
|
|
10
|
-
|
|
11
|
-
## Tasks Completed
|
|
12
|
-
|
|
13
|
-
### Task 1: Test URL extraction fallbacks and content extraction chain
|
|
14
|
-
- Tested extract_url with link_nodes alternate link, links array fallback, nil handling
|
|
15
|
-
- Tested extract_content priority: content > content_encoded > summary
|
|
16
|
-
- Tested extract_updated_timestamp returns entry.updated when present
|
|
17
|
-
|
|
18
|
-
### Task 2: Test concurrent duplicate handling (RecordNotUnique)
|
|
19
|
-
- Tested RecordNotUnique for guid conflict finds and updates existing item
|
|
20
|
-
- Tested RecordNotUnique for fingerprint conflict finds by fingerprint
|
|
21
|
-
- Tested handle_concurrent_duplicate returns Result with status: :updated
|
|
22
|
-
|
|
23
|
-
### Task 3: Test multi-format author, enclosure, and media extraction
|
|
24
|
-
- Tested extract_authors from dc_creators, author_nodes (email/uri), deduplication
|
|
25
|
-
- Tested extract_enclosures from RSS enclosure_nodes, Atom link_nodes, JSON attachments
|
|
26
|
-
- Tested extract_media_content with url, type, medium, dimensions
|
|
27
|
-
- Tested extract_media_thumbnail_url fallback from nodes to entry.image
|
|
28
|
-
- Tested blank URL filtering in enclosures
|
|
29
|
-
|
|
30
|
-
### Task 4: Test feed content processing error paths and readability edge cases
|
|
31
|
-
- Tested parser error produces item with raw content and error metadata
|
|
32
|
-
- Tested should_process_feed_content? returns false for blank/non-HTML content
|
|
33
|
-
- Tested deep_copy handles Hash, Array, deep_dup, TypeError rescue
|
|
34
|
-
- Tested build_feed_content_metadata includes readability_text_length
|
|
35
|
-
- Tested html_fragment? detection
|
|
36
|
-
|
|
37
|
-
### Task 5: Test utility methods
|
|
38
|
-
- Tested safe_integer: nil, Integer, string "42", non-numeric string
|
|
39
|
-
- Tested split_keywords: commas, semicolons, whitespace stripping
|
|
40
|
-
- Tested extract_guid dedup when entry_id equals URL
|
|
41
|
-
- Tested extract_language from json_entry and entry.language paths
|
|
42
|
-
- Tested normalize_metadata JSON roundtrip and unparseable values
|
|
43
|
-
- Tested extract_comments_count slash_comments_raw/comments_count fallback
|
|
44
|
-
|
|
45
|
-
## Deviations
|
|
46
|
-
|
|
47
|
-
None -- plan executed as specified.
|
|
48
|
-
|
|
49
|
-
## Verification Results
|
|
50
|
-
|
|
51
|
-
| Check | Result |
|
|
52
|
-
|-------|--------|
|
|
53
|
-
| `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` | All tests pass |
|
|
54
|
-
| `bin/rails test` | 760 runs, 2626 assertions, 0 failures, 0 errors, 0 skips |
|
|
55
|
-
|
|
56
|
-
## Success Criteria
|
|
57
|
-
|
|
58
|
-
- [x] 56 new tests added (78 total, 941 lines)
|
|
59
|
-
- [x] URL extraction fallback branches tested
|
|
60
|
-
- [x] Concurrent duplicate handling tested
|
|
61
|
-
- [x] Multi-format author/enclosure/media extraction tested
|
|
62
|
-
- [x] Feed content processing error paths tested
|
|
63
|
-
- [x] All utility methods tested
|
|
64
|
-
- [x] REQ-02 substantially satisfied
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 2
|
|
3
|
-
plan: 2
|
|
4
|
-
title: item-creator-tests
|
|
5
|
-
wave: 1
|
|
6
|
-
depends_on: []
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "Running `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0 with zero failures"
|
|
11
|
-
- "Coverage report shows lib/source_monitor/items/item_creator.rb has fewer than 40 uncovered lines (down from 228)"
|
|
12
|
-
- "Running `bin/rails test` exits 0 with no regressions"
|
|
13
|
-
artifacts:
|
|
14
|
-
- "test/lib/source_monitor/items/item_creator_test.rb -- extended with new test methods covering URL extraction, content extraction, concurrent duplicate handling, author/enclosure/media extraction, feed content processing, and utility methods"
|
|
15
|
-
key_links:
|
|
16
|
-
- "REQ-02 substantially satisfied -- ItemCreator branch coverage above 80%"
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
# Plan 02: item-creator-tests
|
|
20
|
-
|
|
21
|
-
## Objective
|
|
22
|
-
|
|
23
|
-
Close the coverage gap in `lib/source_monitor/items/item_creator.rb` (currently 228 uncovered lines out of 601). The existing test file covers basic RSS/Atom/JSON creation, fingerprint generation, guid fallback, readability processing, metadata extraction, and guid/fingerprint deduplication. This plan targets the remaining uncovered branches: URL extraction from link_nodes and links arrays, content extraction fallback chain, concurrent duplicate handling (RecordNotUnique), author extraction from multiple sources, enclosure extraction from Atom link_nodes and JSON attachments, media content extraction, language/copyright/comments extraction, keyword splitting, safe_integer edge cases, and the deep_copy utility.
|
|
24
|
-
|
|
25
|
-
## Context
|
|
26
|
-
|
|
27
|
-
<context>
|
|
28
|
-
@lib/source_monitor/items/item_creator.rb -- 601 lines, item creation with dedup logic
|
|
29
|
-
@test/lib/source_monitor/items/item_creator_test.rb -- existing test file with 8 tests
|
|
30
|
-
@config/coverage_baseline.json -- lists 228 uncovered lines for item_creator.rb
|
|
31
|
-
@test/fixtures/feeds/ -- RSS, Atom, JSON feed fixtures
|
|
32
|
-
@test/test_helper.rb -- test infrastructure
|
|
33
|
-
|
|
34
|
-
**Decomposition rationale:** ItemCreator is the second largest coverage gap. Its uncovered lines cluster into: (1) URL/content/timestamp extraction branches, (2) concurrent duplicate handling, (3) multi-format author/enclosure/media extraction, (4) feed content processing error paths, (5) utility methods. Each task targets a distinct cluster.
|
|
35
|
-
|
|
36
|
-
**Trade-offs considered:**
|
|
37
|
-
- Many branches are triggered by specific Feedjira entry types (AtomEntry, JSONFeedItem). Tests need mock entries or real parsed fixtures to exercise these.
|
|
38
|
-
- The concurrent duplicate test (RecordNotUnique) requires careful setup to simulate a race condition without actually racing.
|
|
39
|
-
- Testing private methods through the public `call` interface keeps tests realistic, but some deep utility methods (deep_copy, safe_integer, split_keywords) are easier to verify directly.
|
|
40
|
-
|
|
41
|
-
**What constrains the structure:**
|
|
42
|
-
- Must use Feedjira-parsed entries (not plain hashes) to exercise respond_to? checks
|
|
43
|
-
- Atom and JSON-specific branches need entries of the correct type
|
|
44
|
-
- Tests extend the existing test file
|
|
45
|
-
</context>
|
|
46
|
-
|
|
47
|
-
## Tasks
|
|
48
|
-
|
|
49
|
-
### Task 1: Test URL extraction fallbacks and content extraction chain
|
|
50
|
-
|
|
51
|
-
- **name:** test-url-and-content-extraction
|
|
52
|
-
- **files:**
|
|
53
|
-
- `lib/source_monitor/items/item_creator.rb` -- (read-only reference)
|
|
54
|
-
- `test/lib/source_monitor/items/item_creator_test.rb`
|
|
55
|
-
- **action:** Add tests covering lines 288-310 (extract_url with link_nodes and links arrays), lines 318-336 (extract_content with CONTENT_METHODS fallback chain), and lines 328-342 (extract_timestamp, extract_updated_timestamp). Specifically:
|
|
56
|
-
1. Test extract_url when entry.url is blank but entry.link_nodes has an alternate link with href -- use an Atom entry fixture
|
|
57
|
-
2. Test extract_url when entry.url is blank and link_nodes are empty but entry.links has a URL string
|
|
58
|
-
3. Test extract_url returns nil when no URL source is available (mock entry with no url/link_nodes/links)
|
|
59
|
-
4. Test extract_content tries :content, then :content_encoded, then :summary in order -- create mock entries that respond to different method subsets
|
|
60
|
-
5. Test extract_updated_timestamp returns entry.updated when present, nil otherwise
|
|
61
|
-
Use OpenStruct or Minitest::Mock to create entries with specific respond_to? patterns for edge cases not covered by real feed fixtures.
|
|
62
|
-
- **verify:** `bin/rails test test/lib/source_monitor/items/item_creator_test.rb -n /url_extraction|content_extraction|updated_timestamp/i` exits 0
|
|
63
|
-
- **done:** Lines 288-310, 318-342 covered.
|
|
64
|
-
|
|
65
|
-
### Task 2: Test concurrent duplicate handling (RecordNotUnique)
|
|
66
|
-
|
|
67
|
-
- **name:** test-concurrent-duplicate-handling
|
|
68
|
-
- **files:**
|
|
69
|
-
- `test/lib/source_monitor/items/item_creator_test.rb`
|
|
70
|
-
- **action:** Add tests covering lines 104-128 (create_new_item rescue RecordNotUnique, handle_concurrent_duplicate, find_conflicting_item). Specifically:
|
|
71
|
-
1. Test that when create_new_item raises ActiveRecord::RecordNotUnique for a guid conflict, the item is found and updated instead
|
|
72
|
-
2. Test that when RecordNotUnique fires for a fingerprint conflict (no raw guid), the item is found by fingerprint and updated
|
|
73
|
-
3. Test that handle_concurrent_duplicate returns a Result with status: :updated and the correct matched_by
|
|
74
|
-
Simulate RecordNotUnique by: (a) creating an item first, (b) stubbing source.items.new to return an item that raises RecordNotUnique on save!, then verifying the fallback path finds and updates the existing item.
|
|
75
|
-
- **verify:** `bin/rails test test/lib/source_monitor/items/item_creator_test.rb -n /concurrent_duplicate|record_not_unique/i` exits 0
|
|
76
|
-
- **done:** Lines 104-128 covered.
|
|
77
|
-
|
|
78
|
-
### Task 3: Test multi-format author, enclosure, and media extraction
|
|
79
|
-
|
|
80
|
-
- **name:** test-author-enclosure-media-extraction
|
|
81
|
-
- **files:**
|
|
82
|
-
- `test/lib/source_monitor/items/item_creator_test.rb`
|
|
83
|
-
- **action:** Add tests covering lines 348-383 (extract_authors with rss_authors, dc_creators, author_nodes, json authors), lines 416-466 (extract_enclosures from rss enclosure_nodes, atom link_nodes with rel=enclosure, json attachments), and lines 477-499 (extract_media_content). Specifically:
|
|
84
|
-
1. Test extract_authors collects from rss_authors, dc_creators, dc_creator, and author_nodes (name/email/uri) -- deduplicates and compacts
|
|
85
|
-
2. Test extract_enclosures from Atom link_nodes with rel="enclosure" produces entries with source: "atom_link"
|
|
86
|
-
3. Test extract_media_content builds array from media_content_nodes with url, type, medium, height, width, file_size, duration, expression -- compacted
|
|
87
|
-
4. Test extract_media_thumbnail_url falls back from media_thumbnail_nodes to entry.image
|
|
88
|
-
5. Test extract_enclosures skips entries with blank URLs
|
|
89
|
-
Build mock entries using Struct or OpenStruct to simulate the various node types. The JSON-specific paths are already partially tested; focus on Atom and RSS edge cases.
|
|
90
|
-
- **verify:** `bin/rails test test/lib/source_monitor/items/item_creator_test.rb -n /authors|enclosure|media_content|media_thumbnail/i` exits 0
|
|
91
|
-
- **done:** Lines 348-383, 416-466, 477-499 covered.
|
|
92
|
-
|
|
93
|
-
### Task 4: Test feed content processing error path and readability edge cases
|
|
94
|
-
|
|
95
|
-
- **name:** test-feed-content-processing-errors
|
|
96
|
-
- **files:**
|
|
97
|
-
- `test/lib/source_monitor/items/item_creator_test.rb`
|
|
98
|
-
- **action:** Add tests covering lines 137-158 (process_feed_content error rescue), lines 160-165 (should_process_feed_content?), lines 187-208 (default_feed_readability_options, build_feed_content_metadata), and lines 210-231 (html_fragment?, deep_copy). Specifically:
|
|
99
|
-
1. Test that when readability parser raises an error, the item is created with raw content and error metadata (lines 148-157): metadata has "status"=>"failed", "error_class", "error_message"
|
|
100
|
-
2. Test should_process_feed_content? returns false when content is blank or when content has no HTML tags (html_fragment? returns false)
|
|
101
|
-
3. Test deep_copy handles Hash, Array, and objects that support deep_dup, plus TypeError rescue
|
|
102
|
-
4. Test build_feed_content_metadata includes readability_text_length and title when present
|
|
103
|
-
5. Test html_fragment? returns true for `<p>text</p>` and false for plain text
|
|
104
|
-
Stub the parser class to raise for the error path test. Use source with feed_content_readability_enabled: true.
|
|
105
|
-
- **verify:** `bin/rails test test/lib/source_monitor/items/item_creator_test.rb -n /processing_error|html_fragment|deep_copy|readability_metadata/i` exits 0
|
|
106
|
-
- **done:** Lines 137-165, 187-231 covered.
|
|
107
|
-
|
|
108
|
-
### Task 5: Test utility methods: safe_integer, split_keywords, string_or_nil, normalize_metadata, extract_guid edge cases
|
|
109
|
-
|
|
110
|
-
- **name:** test-utility-methods
|
|
111
|
-
- **files:**
|
|
112
|
-
- `test/lib/source_monitor/items/item_creator_test.rb`
|
|
113
|
-
- **action:** Add tests covering lines 501-534 (extract_language, extract_copyright, extract_comments_url, extract_comments_count), lines 536-543 (extract_metadata with normalize_metadata), lines 555-597 (string_or_nil, sanitize_string_array, split_keywords, safe_integer, json_entry?, atom_entry?, normalize_metadata). Specifically:
|
|
114
|
-
1. Test safe_integer returns nil for nil, returns Integer for Integer, parses string "42", returns nil for non-numeric string (lines 574-584)
|
|
115
|
-
2. Test split_keywords splits on commas and semicolons, strips whitespace, removes blank entries (lines 565-572)
|
|
116
|
-
3. Test extract_guid returns nil when entry_id equals URL (dedup logic at line 283-285)
|
|
117
|
-
4. Test extract_language from json_entry? path (line 506-507) and from entry.language (line 502-503)
|
|
118
|
-
5. Test normalize_metadata returns empty hash for unparseable values (JSON roundtrip at lines 594-597)
|
|
119
|
-
6. Test extract_comments_count tries slash_comments_raw then comments_count (lines 529-533)
|
|
120
|
-
Use mock entries with targeted respond_to? patterns. For json_entry? and atom_entry? tests, use actual Feedjira-parsed entries from fixtures.
|
|
121
|
-
- **verify:** `bin/rails test test/lib/source_monitor/items/item_creator_test.rb -n /safe_integer|split_keywords|extract_guid_edge|language|copyright|normalize_metadata|comments_count/i` exits 0
|
|
122
|
-
- **done:** Lines 501-597 covered.
|
|
123
|
-
|
|
124
|
-
## Verification
|
|
125
|
-
|
|
126
|
-
1. `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0
|
|
127
|
-
2. `COVERAGE=1 bin/rails test test/lib/source_monitor/items/item_creator_test.rb` shows item_creator.rb with >80% branch coverage
|
|
128
|
-
3. `bin/rails test` exits 0 (no regressions)
|
|
129
|
-
|
|
130
|
-
## Success Criteria
|
|
131
|
-
|
|
132
|
-
- [ ] ItemCreator coverage drops from 228 uncovered lines to fewer than 40
|
|
133
|
-
- [ ] URL extraction fallback branches tested
|
|
134
|
-
- [ ] Concurrent duplicate handling tested
|
|
135
|
-
- [ ] Multi-format author/enclosure/media extraction tested
|
|
136
|
-
- [ ] Feed content processing error paths tested
|
|
137
|
-
- [ ] All utility methods tested
|
|
138
|
-
- [ ] REQ-02 substantially satisfied
|