source_monitor 0.2.0 → 0.3.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/agents/rails-concern.md +464 -0
- data/.claude/agents/rails-controller.md +424 -0
- data/.claude/agents/rails-hotwire.md +446 -0
- data/.claude/agents/rails-implement.md +374 -0
- data/.claude/agents/rails-job.md +334 -0
- data/.claude/agents/rails-lint.md +294 -0
- data/.claude/agents/rails-mailer.md +371 -0
- data/.claude/agents/rails-migration.md +449 -0
- data/.claude/agents/rails-model.md +420 -0
- data/.claude/agents/rails-policy.md +443 -0
- data/.claude/agents/rails-presenter.md +427 -0
- data/.claude/agents/rails-query.md +412 -0
- data/.claude/agents/rails-review.md +490 -0
- data/.claude/agents/rails-service.md +458 -0
- data/.claude/agents/rails-state-records.md +465 -0
- data/.claude/agents/rails-tdd.md +314 -0
- data/.claude/agents/rails-test.md +441 -0
- data/.claude/agents/rails-view-component.md +418 -0
- data/.claude/hooks/block-secrets.sh +52 -0
- data/.claude/settings.json +85 -0
- data/.claude/skills/action-cable-patterns/SKILL.md +296 -0
- data/.claude/skills/action-mailer-patterns/SKILL.md +295 -0
- data/.claude/skills/active-storage-setup/SKILL.md +311 -0
- data/.claude/skills/api-versioning/SKILL.md +294 -0
- data/.claude/skills/authentication-flow/SKILL.md +335 -0
- data/.claude/skills/authentication-flow/reference/current.md +248 -0
- data/.claude/skills/authentication-flow/reference/passwordless.md +253 -0
- data/.claude/skills/authentication-flow/reference/sessions.md +201 -0
- data/.claude/skills/authorization-pundit/SKILL.md +462 -0
- data/.claude/skills/caching-strategies/SKILL.md +350 -0
- data/.claude/skills/database-migrations/SKILL.md +354 -0
- data/.claude/skills/form-object-patterns/SKILL.md +399 -0
- data/.claude/skills/hotwire-patterns/SKILL.md +247 -0
- data/.claude/skills/hotwire-patterns/reference/stimulus.md +307 -0
- data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +112 -0
- data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +158 -0
- data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +218 -0
- data/.claude/skills/i18n-patterns/SKILL.md +320 -0
- data/.claude/skills/install/SKILL.md +367 -0
- data/.claude/skills/performance-optimization/SKILL.md +311 -0
- data/.claude/skills/rails-architecture/SKILL.md +259 -0
- data/.claude/skills/rails-architecture/reference/error-handling.md +333 -0
- data/.claude/skills/rails-architecture/reference/event-tracking.md +142 -0
- data/.claude/skills/rails-architecture/reference/layer-interactions.md +417 -0
- data/.claude/skills/rails-architecture/reference/multi-tenancy.md +152 -0
- data/.claude/skills/rails-architecture/reference/query-patterns.md +342 -0
- data/.claude/skills/rails-architecture/reference/service-patterns.md +286 -0
- data/.claude/skills/rails-architecture/reference/state-records.md +250 -0
- data/.claude/skills/rails-architecture/reference/testing-strategy.md +326 -0
- data/.claude/skills/rails-concern/SKILL.md +399 -0
- data/.claude/skills/rails-controller/SKILL.md +336 -0
- data/.claude/skills/rails-model-generator/SKILL.md +321 -0
- data/.claude/skills/rails-model-generator/reference/validations.md +298 -0
- data/.claude/skills/rails-presenter/SKILL.md +274 -0
- data/.claude/skills/rails-query-object/SKILL.md +289 -0
- data/.claude/skills/rails-service-object/SKILL.md +349 -0
- data/.claude/skills/solid-queue-setup/SKILL.md +307 -0
- data/.claude/skills/tdd-cycle/SKILL.md +359 -0
- data/.claude/skills/viewcomponent-patterns/SKILL.md +333 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -1
- data/.vbw-planning/.notification-log.jsonl +192 -0
- data/.vbw-planning/.session-log.jsonl +871 -0
- data/.vbw-planning/PROJECT.md +51 -0
- data/.vbw-planning/REQUIREMENTS.md +50 -0
- data/.vbw-planning/SHIPPED.md +28 -0
- data/.vbw-planning/codebase/ARCHITECTURE.md +147 -0
- data/.vbw-planning/codebase/CONCERNS.md +99 -0
- data/.vbw-planning/codebase/CONVENTIONS.md +97 -0
- data/.vbw-planning/codebase/DEPENDENCIES.md +100 -0
- data/.vbw-planning/codebase/INDEX.md +86 -0
- data/.vbw-planning/codebase/META.md +42 -0
- data/.vbw-planning/codebase/PATTERNS.md +262 -0
- data/.vbw-planning/codebase/STACK.md +101 -0
- data/.vbw-planning/codebase/STRUCTURE.md +324 -0
- data/.vbw-planning/codebase/TESTING.md +154 -0
- data/.vbw-planning/config.json +12 -0
- data/.vbw-planning/discovery.json +24 -0
- data/.vbw-planning/milestones/default/ROADMAP.md +115 -0
- data/.vbw-planning/milestones/default/STATE.md +83 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +56 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +187 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +64 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +137 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +67 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +142 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +64 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +138 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +85 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +147 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +63 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +129 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +74 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +154 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +303 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +510 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +61 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +161 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +66 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +132 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +59 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +171 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +56 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +152 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +33 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +42 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +119 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +52 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +195 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +79 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +130 -0
- data/CHANGELOG.md +28 -0
- data/CLAUDE.md +179 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +114 -101
- data/Rakefile +2 -0
- data/app/assets/builds/source_monitor/application.css +2076 -0
- data/app/assets/builds/source_monitor/application.js +2758 -0
- data/app/assets/builds/source_monitor/application.js.map +7 -0
- data/app/controllers/source_monitor/application_controller.rb +2 -0
- data/app/controllers/source_monitor/health_controller.rb +2 -0
- data/app/controllers/source_monitor/import_sessions/bulk_configuration.rb +106 -0
- data/app/controllers/source_monitor/import_sessions/entry_annotation.rb +187 -0
- data/app/controllers/source_monitor/import_sessions/health_check_management.rb +112 -0
- data/app/controllers/source_monitor/import_sessions/opml_parser.rb +130 -0
- data/app/controllers/source_monitor/import_sessions_controller.rb +6 -507
- data/app/controllers/source_monitor/items_controller.rb +2 -0
- data/app/controllers/source_monitor/sources_controller.rb +0 -14
- data/app/helpers/source_monitor/application_helper.rb +4 -112
- data/app/helpers/source_monitor/health_badge_helper.rb +69 -0
- data/app/helpers/source_monitor/table_sort_helper.rb +53 -0
- data/app/jobs/source_monitor/application_job.rb +2 -0
- data/app/models/source_monitor/application_record.rb +2 -0
- data/app/models/source_monitor/log_entry.rb +0 -2
- data/config/coverage_baseline.json +217 -1862
- data/config/routes.rb +2 -0
- data/db/migrate/20251009103000_add_feed_content_readability_to_sources.rb +2 -0
- data/db/migrate/20251014171659_add_performance_indexes.rb +2 -0
- data/db/migrate/20251014172525_add_fetch_status_check_constraint.rb +2 -0
- data/db/migrate/20251108120116_refresh_fetch_status_constraint.rb +2 -0
- data/db/migrate/20260210204022_add_composite_index_to_log_entries.rb +17 -0
- data/lib/source_monitor/assets/bundler.rb +2 -0
- data/lib/source_monitor/assets.rb +2 -0
- data/lib/source_monitor/configuration/authentication_settings.rb +62 -0
- data/lib/source_monitor/configuration/events.rb +60 -0
- data/lib/source_monitor/configuration/fetching_settings.rb +27 -0
- data/lib/source_monitor/configuration/health_settings.rb +27 -0
- data/lib/source_monitor/configuration/http_settings.rb +43 -0
- data/lib/source_monitor/configuration/model_definition.rb +108 -0
- data/lib/source_monitor/configuration/models.rb +36 -0
- data/lib/source_monitor/configuration/realtime_settings.rb +95 -0
- data/lib/source_monitor/configuration/retention_settings.rb +45 -0
- data/lib/source_monitor/configuration/scraper_registry.rb +67 -0
- data/lib/source_monitor/configuration/scraping_settings.rb +39 -0
- data/lib/source_monitor/configuration/validation_definition.rb +32 -0
- data/lib/source_monitor/configuration.rb +12 -579
- data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +138 -0
- data/lib/source_monitor/dashboard/queries/stats_query.rb +71 -0
- data/lib/source_monitor/dashboard/queries.rb +2 -195
- data/lib/source_monitor/engine.rb +2 -0
- data/lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb +141 -0
- data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +89 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +200 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +37 -379
- data/lib/source_monitor/items/item_creator/content_extractor.rb +113 -0
- data/lib/source_monitor/items/item_creator/entry_parser/media_extraction.rb +96 -0
- data/lib/source_monitor/items/item_creator/entry_parser.rb +294 -0
- data/lib/source_monitor/items/item_creator.rb +28 -455
- data/lib/source_monitor/setup/bundle_installer.rb +2 -0
- data/lib/source_monitor/setup/cli.rb +2 -0
- data/lib/source_monitor/setup/dependency_checker.rb +2 -0
- data/lib/source_monitor/setup/detectors.rb +2 -0
- data/lib/source_monitor/setup/gemfile_editor.rb +2 -0
- data/lib/source_monitor/setup/initializer_patcher.rb +2 -0
- data/lib/source_monitor/setup/install_generator.rb +2 -0
- data/lib/source_monitor/setup/migration_installer.rb +2 -0
- data/lib/source_monitor/setup/node_installer.rb +2 -0
- data/lib/source_monitor/setup/prompter.rb +2 -0
- data/lib/source_monitor/setup/requirements.rb +2 -0
- data/lib/source_monitor/setup/shell_runner.rb +2 -0
- data/lib/source_monitor/setup/verification/action_cable_verifier.rb +2 -0
- data/lib/source_monitor/setup/verification/printer.rb +2 -0
- data/lib/source_monitor/setup/verification/result.rb +2 -0
- data/lib/source_monitor/setup/verification/runner.rb +2 -0
- data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +2 -0
- data/lib/source_monitor/setup/verification/telemetry_logger.rb +2 -0
- data/lib/source_monitor/setup/workflow.rb +2 -0
- data/lib/source_monitor/version.rb +3 -1
- data/lib/source_monitor.rb +140 -58
- data/lib/tasks/source_monitor_assets.rake +2 -0
- data/lib/tasks/source_monitor_setup.rake +2 -0
- data/lib/tasks/source_monitor_tasks.rake +2 -0
- data/source_monitor.gemspec +3 -1
- metadata +144 -4
|
@@ -0,0 +1,142 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
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
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# PLAN-03 Summary: configuration-tests
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE
|
|
4
|
+
|
|
5
|
+
## Commit
|
|
6
|
+
|
|
7
|
+
- **Hash:** `66b8df2`
|
|
8
|
+
- **Message:** `test(dev-plan05): close coverage gaps for bulk scraper and broadcaster`
|
|
9
|
+
- **Files changed:** 1 file (configuration_test.rb), 771 insertions
|
|
10
|
+
- **Note:** Commit was mislabeled as "dev-plan05" but the code changes are configuration tests matching Plan 03 tasks exactly.
|
|
11
|
+
|
|
12
|
+
## Tasks Completed
|
|
13
|
+
|
|
14
|
+
### Task 1: Test AuthenticationSettings handlers (lines 75-130)
|
|
15
|
+
- Tested authenticate_with :symbol handler dispatches via public_send
|
|
16
|
+
- Tested authenticate_with string handler converts to symbol
|
|
17
|
+
- Tested authenticate_with callable with zero arity uses instance_exec
|
|
18
|
+
- Tested authenticate_with callable with arity passes controller
|
|
19
|
+
- Tested authenticate_with block handler
|
|
20
|
+
- Tested authorize_with symbol and callable handlers
|
|
21
|
+
- Tested Handler.call returns nil when callable is nil
|
|
22
|
+
- Tested invalid handler raises ArgumentError
|
|
23
|
+
- Tested reset! clears all handlers and methods
|
|
24
|
+
- Tested authenticate_with nil returns nil handler
|
|
25
|
+
|
|
26
|
+
### Task 2: Test ScrapingSettings and RetentionSettings edge cases (lines 132-164, 398-436)
|
|
27
|
+
- Tested ScrapingSettings defaults (max_in_flight_per_source=25, max_bulk_batch_size=100)
|
|
28
|
+
- Tested normalize_numeric: string, nil, empty string, zero, negative all handled correctly
|
|
29
|
+
- Tested ScrapingSettings reset restores defaults
|
|
30
|
+
- Tested RetentionSettings defaults (nil for days/max_items, :destroy strategy)
|
|
31
|
+
- Tested strategy accepts :soft_delete, string "destroy", rejects :archive and non-symbolizable
|
|
32
|
+
- Tested strategy normalizes nil to :destroy
|
|
33
|
+
|
|
34
|
+
### Task 3: Test RealtimeSettings adapter validation and action_cable_config (lines 166-253)
|
|
35
|
+
- Tested adapter= accepts :solid_cable, :redis, :async; rejects :websocket and nil
|
|
36
|
+
- Tested action_cable_config for solid_cable returns merged SolidCableOptions
|
|
37
|
+
- Tested action_cable_config for redis with/without url
|
|
38
|
+
- Tested action_cable_config for async returns { adapter: "async" }
|
|
39
|
+
- Tested SolidCableOptions.assign with hash, unknown keys, non-enumerable input
|
|
40
|
+
- Tested SolidCableOptions.to_h compacts nil values
|
|
41
|
+
- Tested realtime reset restores defaults
|
|
42
|
+
|
|
43
|
+
### Task 4: Test Events callbacks and item_processors (lines 438-491)
|
|
44
|
+
- Tested after_item_created with lambda and block registration
|
|
45
|
+
- Tested after_item_scraped and after_fetch_completed registration
|
|
46
|
+
- Tested multiple callbacks per event, callbacks_for unknown key returns []
|
|
47
|
+
- Tested callbacks_for returns dup preventing mutation
|
|
48
|
+
- Tested non-callable handler rejection (ArgumentError)
|
|
49
|
+
- Tested register_item_processor with lambda, block, multiple, dup protection
|
|
50
|
+
- Tested events reset! clears callbacks and item_processors
|
|
51
|
+
|
|
52
|
+
### Task 5: Test Models, ModelDefinition, ConcernDefinition, ValidationDefinition (lines 493-652)
|
|
53
|
+
- Tested Models.table_name_prefix default "sourcemon_"
|
|
54
|
+
- Tested Models exposes all model keys, for(:source) returns definition
|
|
55
|
+
- Tested Models.for(:unknown) raises ArgumentError
|
|
56
|
+
- Tested include_concern with Module, block (anonymous module), string constant
|
|
57
|
+
- Tested include_concern deduplication by signature (module, string, blocks differ)
|
|
58
|
+
- Tested include_concern with invalid string raises on resolve
|
|
59
|
+
- Tested validate with symbol, string, lambda, block; raises on invalid handler
|
|
60
|
+
- Tested ValidationDefinition.signature for symbol, string, callable handlers
|
|
61
|
+
- Tested each_concern returns Enumerator without block
|
|
62
|
+
|
|
63
|
+
## Deviations
|
|
64
|
+
|
|
65
|
+
| ID | Description | Impact |
|
|
66
|
+
|----|-------------|--------|
|
|
67
|
+
| DEVN-01 | Commit mislabeled as "dev-plan05" | None -- code changes are correct Plan 03 configuration tests |
|
|
68
|
+
| DEVN-02 | Parallel test runner segfaults on PG fork when running single file | Tests pass with PARALLEL_WORKERS=1 and in full suite; environment issue, not code defect |
|
|
69
|
+
|
|
70
|
+
## Verification Results
|
|
71
|
+
|
|
72
|
+
| Check | Result |
|
|
73
|
+
|-------|--------|
|
|
74
|
+
| `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration_test.rb` | 81 runs, 178 assertions, 0 failures, 0 errors, 0 skips |
|
|
75
|
+
| `bin/rails test` (full suite) | 760 runs, 2626 assertions, 0 failures, 0 errors, 0 skips |
|
|
76
|
+
|
|
77
|
+
## Success Criteria
|
|
78
|
+
|
|
79
|
+
- [x] 81 tests total (76 new, 5 existing), 771 lines added
|
|
80
|
+
- [x] AuthenticationSettings handlers fully tested
|
|
81
|
+
- [x] ScrapingSettings and RetentionSettings edge cases tested
|
|
82
|
+
- [x] RealtimeSettings adapter validation and action_cable_config tested
|
|
83
|
+
- [x] Events callbacks and item_processors tested
|
|
84
|
+
- [x] Models and definition classes tested
|
|
85
|
+
- [x] REQ-03 substantially satisfied
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 2
|
|
3
|
+
plan: 3
|
|
4
|
+
title: configuration-tests
|
|
5
|
+
wave: 1
|
|
6
|
+
depends_on: []
|
|
7
|
+
skills_used: []
|
|
8
|
+
must_haves:
|
|
9
|
+
truths:
|
|
10
|
+
- "Running `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0 with zero failures"
|
|
11
|
+
- "Coverage report shows lib/source_monitor/configuration.rb has fewer than 20 uncovered lines (down from 94)"
|
|
12
|
+
- "Running `bin/rails test` exits 0 with no regressions"
|
|
13
|
+
artifacts:
|
|
14
|
+
- "test/lib/source_monitor/configuration_test.rb -- extended with new test methods covering AuthenticationSettings, ScrapingSettings, RealtimeSettings, RetentionSettings, Events, Models, and ModelDefinition"
|
|
15
|
+
key_links:
|
|
16
|
+
- "REQ-03 substantially satisfied -- Configuration branch coverage above 80%"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Plan 03: configuration-tests
|
|
20
|
+
|
|
21
|
+
## Objective
|
|
22
|
+
|
|
23
|
+
Close the coverage gap in `lib/source_monitor/configuration.rb` (currently 94 uncovered lines out of 655). The existing test file has only 5 tests covering mission_control_dashboard_path, scraper registry, retention strategy default, and fetching settings. This plan targets the remaining uncovered branches across the 12 nested settings classes: AuthenticationSettings handlers, ScrapingSettings normalization, RealtimeSettings adapter validation and action_cable_config, RetentionSettings strategy validation, Events callbacks and item_processors, Models table_name_prefix and `for` method, ModelDefinition concerns and validations, ConcernDefinition resolution, and ValidationDefinition signatures.
|
|
24
|
+
|
|
25
|
+
## Context
|
|
26
|
+
|
|
27
|
+
<context>
|
|
28
|
+
@lib/source_monitor/configuration.rb -- 655 lines with ~12 nested classes
|
|
29
|
+
@test/lib/source_monitor/configuration_test.rb -- existing test file with 5 tests
|
|
30
|
+
@config/coverage_baseline.json -- lists 94 uncovered lines for configuration.rb
|
|
31
|
+
@test/test_helper.rb -- resets configuration each test
|
|
32
|
+
|
|
33
|
+
**Decomposition rationale:** Configuration has many small nested classes, each with a few uncovered branches. Rather than one mega-task, we group by logical subsystem: (1) authentication handlers, (2) scraping/retention settings with edge cases, (3) realtime adapter validation, (4) events system, (5) models and concern/validation definitions.
|
|
34
|
+
|
|
35
|
+
**Trade-offs considered:**
|
|
36
|
+
- Could create separate test files per settings class, but the existing pattern is one configuration_test.rb file.
|
|
37
|
+
- Some branches (like ConcernDefinition with constant string resolution) require careful setup to test constantize calls.
|
|
38
|
+
- RealtimeSettings action_cable_config branches test all three adapter paths.
|
|
39
|
+
|
|
40
|
+
**What constrains the structure:**
|
|
41
|
+
- Each test must call SourceMonitor.reset_configuration! in setup/teardown (already present)
|
|
42
|
+
- Tests should not leak state between settings classes
|
|
43
|
+
- ModelDefinition tests need care around Module.new blocks
|
|
44
|
+
</context>
|
|
45
|
+
|
|
46
|
+
## Tasks
|
|
47
|
+
|
|
48
|
+
### Task 1: Test AuthenticationSettings handlers
|
|
49
|
+
|
|
50
|
+
- **name:** test-authentication-settings
|
|
51
|
+
- **files:**
|
|
52
|
+
- `test/lib/source_monitor/configuration_test.rb`
|
|
53
|
+
- **action:** Add tests covering lines 75-130 (AuthenticationSettings and Handler). Specifically:
|
|
54
|
+
1. Test authenticate_with(:some_method) creates a Handler with type :symbol that calls controller.public_send (lines 80-82)
|
|
55
|
+
2. Test authenticate_with { |c| c.redirect_to "/" } creates a Handler with type :callable that calls the block with controller (lines 84-88) -- test both zero-arity and one-arity blocks
|
|
56
|
+
3. Test authorize_with works the same way (line 105-107)
|
|
57
|
+
4. Test that passing an invalid handler (not Symbol, String, or callable) raises ArgumentError (line 127)
|
|
58
|
+
5. Test reset! clears all handlers and methods (lines 109-114)
|
|
59
|
+
6. Test Handler.call returns nil when callable is nil (line 78)
|
|
60
|
+
Use a mock controller object (OpenStruct with method stubs) to verify handler dispatch.
|
|
61
|
+
- **verify:** `bin/rails test test/lib/source_monitor/configuration_test.rb -n /authentication|authorize|handler/i` exits 0
|
|
62
|
+
- **done:** Lines 75-130 covered.
|
|
63
|
+
|
|
64
|
+
### Task 2: Test ScrapingSettings and RetentionSettings edge cases
|
|
65
|
+
|
|
66
|
+
- **name:** test-scraping-and-retention-settings
|
|
67
|
+
- **files:**
|
|
68
|
+
- `test/lib/source_monitor/configuration_test.rb`
|
|
69
|
+
- **action:** Add tests covering lines 132-164 (ScrapingSettings) and lines 398-436 (RetentionSettings). Specifically:
|
|
70
|
+
1. Test ScrapingSettings defaults (max_in_flight_per_source=25, max_bulk_batch_size=100)
|
|
71
|
+
2. Test ScrapingSettings normalize_numeric: nil returns nil, "" returns nil, negative returns nil, positive returns integer (lines 157-163)
|
|
72
|
+
3. Test ScrapingSettings setter with string input (e.g., "50") normalizes to integer
|
|
73
|
+
4. Test RetentionSettings strategy= with nil defaults to :destroy (line 419)
|
|
74
|
+
5. Test RetentionSettings strategy= with invalid value raises ArgumentError (line 430)
|
|
75
|
+
6. Test RetentionSettings strategy= with string "soft_delete" normalizes to symbol
|
|
76
|
+
7. Test RetentionSettings strategy= with non-symbolizable value raises ArgumentError (line 433)
|
|
77
|
+
- **verify:** `bin/rails test test/lib/source_monitor/configuration_test.rb -n /scraping_settings|retention_settings|normalize/i` exits 0
|
|
78
|
+
- **done:** Lines 132-164 and 398-436 covered.
|
|
79
|
+
|
|
80
|
+
### Task 3: Test RealtimeSettings adapter validation and action_cable_config
|
|
81
|
+
|
|
82
|
+
- **name:** test-realtime-settings
|
|
83
|
+
- **files:**
|
|
84
|
+
- `test/lib/source_monitor/configuration_test.rb`
|
|
85
|
+
- **action:** Add tests covering lines 166-253 (RealtimeSettings, SolidCableOptions). Specifically:
|
|
86
|
+
1. Test adapter= with :solid_cable, :redis, :async all accepted (line 178)
|
|
87
|
+
2. Test adapter= with :invalid raises ArgumentError (line 179)
|
|
88
|
+
3. Test action_cable_config for :solid_cable returns hash with adapter: "solid_cable" and SolidCableOptions merged (lines 197-198)
|
|
89
|
+
4. Test action_cable_config for :redis returns hash with adapter: "redis" and redis_url when set (lines 200-202)
|
|
90
|
+
5. Test action_cable_config for :async returns { adapter: "async" } (line 204)
|
|
91
|
+
6. Test SolidCableOptions.assign with a hash of options sets the corresponding attributes (lines 223-229)
|
|
92
|
+
7. Test SolidCableOptions.to_h compacts nil values (line 251)
|
|
93
|
+
8. Test solid_cable= delegates to solid_cable.assign (line 192)
|
|
94
|
+
- **verify:** `bin/rails test test/lib/source_monitor/configuration_test.rb -n /realtime|adapter|action_cable|solid_cable/i` exits 0
|
|
95
|
+
- **done:** Lines 166-253 covered.
|
|
96
|
+
|
|
97
|
+
### Task 4: Test Events callbacks and item_processors
|
|
98
|
+
|
|
99
|
+
- **name:** test-events-system
|
|
100
|
+
- **files:**
|
|
101
|
+
- `test/lib/source_monitor/configuration_test.rb`
|
|
102
|
+
- **action:** Add tests covering lines 438-491 (Events). Specifically:
|
|
103
|
+
1. Test registering after_item_created callback with a lambda and retrieving via callbacks_for(:after_item_created) (lines 446-449)
|
|
104
|
+
2. Test registering after_item_created callback with a block
|
|
105
|
+
3. Test register_item_processor adds to item_processors list (lines 452-457)
|
|
106
|
+
4. Test that passing non-callable to register_item_processor raises ArgumentError (line 488)
|
|
107
|
+
5. Test that registering unknown event key raises ArgumentError (line 479)
|
|
108
|
+
6. Test callbacks_for returns empty array for unregistered name (line 460)
|
|
109
|
+
7. Test reset! clears all callbacks and item_processors (lines 467-470)
|
|
110
|
+
- **verify:** `bin/rails test test/lib/source_monitor/configuration_test.rb -n /events|callback|item_processor/i` exits 0
|
|
111
|
+
- **done:** Lines 438-491 covered.
|
|
112
|
+
|
|
113
|
+
### Task 5: Test Models, ModelDefinition, ConcernDefinition, and ValidationDefinition
|
|
114
|
+
|
|
115
|
+
- **name:** test-models-and-definitions
|
|
116
|
+
- **files:**
|
|
117
|
+
- `test/lib/source_monitor/configuration_test.rb`
|
|
118
|
+
- **action:** Add tests covering lines 493-652 (Models, ModelDefinition, ConcernDefinition, ValidationDefinition). Specifically:
|
|
119
|
+
1. Test Models.table_name_prefix default is "sourcemon_" (line 507)
|
|
120
|
+
2. Test Models.for(:source) returns a ModelDefinition, and Models.for(:unknown) raises ArgumentError (lines 515-521)
|
|
121
|
+
3. Test ModelDefinition.include_concern with a Module directly (line 591-592)
|
|
122
|
+
4. Test ModelDefinition.include_concern with a string constant name that resolves (lines 593-598)
|
|
123
|
+
5. Test ModelDefinition.include_concern with a block creates anonymous module (lines 588-590)
|
|
124
|
+
6. Test ModelDefinition.include_concern deduplicates by signature (line 534)
|
|
125
|
+
7. Test ModelDefinition.validate with symbol, with callable (proc), and with block (lines 549-563)
|
|
126
|
+
8. Test ModelDefinition.validate with invalid handler raises ArgumentError (line 558)
|
|
127
|
+
9. Test ValidationDefinition.signature for symbol, callable, and string handlers (lines 636-647)
|
|
128
|
+
10. Test ValidationDefinition.symbol? (lines 649-651)
|
|
129
|
+
Use real module references and string constants for concern resolution tests.
|
|
130
|
+
- **verify:** `bin/rails test test/lib/source_monitor/configuration_test.rb -n /models|model_definition|concern|validation_definition/i` exits 0
|
|
131
|
+
- **done:** Lines 493-652 covered.
|
|
132
|
+
|
|
133
|
+
## Verification
|
|
134
|
+
|
|
135
|
+
1. `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0
|
|
136
|
+
2. `COVERAGE=1 bin/rails test test/lib/source_monitor/configuration_test.rb` shows configuration.rb with >80% branch coverage
|
|
137
|
+
3. `bin/rails test` exits 0 (no regressions)
|
|
138
|
+
|
|
139
|
+
## Success Criteria
|
|
140
|
+
|
|
141
|
+
- [ ] Configuration coverage drops from 94 uncovered lines to fewer than 20
|
|
142
|
+
- [ ] AuthenticationSettings handlers fully tested
|
|
143
|
+
- [ ] ScrapingSettings and RetentionSettings edge cases tested
|
|
144
|
+
- [ ] RealtimeSettings adapter validation and action_cable_config tested
|
|
145
|
+
- [ ] Events callbacks and item_processors tested
|
|
146
|
+
- [ ] Models and definition classes tested
|
|
147
|
+
- [ ] REQ-03 substantially satisfied
|
data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# PLAN-04 Summary: dashboard-and-analytics-tests
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE
|
|
4
|
+
|
|
5
|
+
## Commits
|
|
6
|
+
|
|
7
|
+
- **Hash:** `a8f2611`
|
|
8
|
+
- **Message:** `test(dashboard-analytics): close coverage gaps for stats, activity, metrics, cache`
|
|
9
|
+
- **Files changed:** 2 files, 564 insertions (queries_test.rb + sources_index_metrics_test.rb)
|
|
10
|
+
|
|
11
|
+
- **Hash:** `2e50580` (tag commit)
|
|
12
|
+
- **Message:** `test(dev-plan04): dashboard and analytics coverage gaps`
|
|
13
|
+
- **Note:** Continuation/tagging commit for task verification.
|
|
14
|
+
|
|
15
|
+
## Tasks Completed
|
|
16
|
+
|
|
17
|
+
### Task 1: Test StatsQuery SQL branches and integer_value
|
|
18
|
+
- Tested stats returns correct counts with mixed active/inactive sources
|
|
19
|
+
- Tested failed_sources counts OR conditions (failure_count > 0, last_error, last_error_at)
|
|
20
|
+
- Tested fetches_today time boundary (started_at >= start_of_day)
|
|
21
|
+
- Tested stats with empty database returns all zeros
|
|
22
|
+
- Tested record_stats_metrics sets gauge values for all stat keys
|
|
23
|
+
|
|
24
|
+
### Task 2: Test RecentActivityQuery build_event and sub-queries
|
|
25
|
+
- Tested build_event produces Event objects for all 3 types (fetch_log, scrape_log, item)
|
|
26
|
+
- Tested fetch_log events have success based on boolean column
|
|
27
|
+
- Tested scrape_log events include scraper_adapter and source_name via JOIN
|
|
28
|
+
- Tested item events have item_title, item_url, success_flag always 1
|
|
29
|
+
- Tested events ordered by occurred_at DESC with limit
|
|
30
|
+
- Tested record_metrics for :recent_activity sets gauge values
|
|
31
|
+
|
|
32
|
+
### Task 3: Test record_metrics branches and Cache edge cases
|
|
33
|
+
- Tested record_metrics for all 4 case branches (stats, recent_activity, job_metrics, upcoming_fetch_schedule)
|
|
34
|
+
- Tested Cache.fetch returns cached value on second call
|
|
35
|
+
- Tested Cache nil/false storage, array keys, key isolation
|
|
36
|
+
|
|
37
|
+
### Task 4: Test SourcesIndexMetrics edge cases
|
|
38
|
+
- Tested fetch_interval_filter with gteq/lt/lteq combinations
|
|
39
|
+
- Tested integer_param sanitization: blank, non-numeric, valid string
|
|
40
|
+
- Tested selected_fetch_interval_bucket nil-max matching, nil when no filter
|
|
41
|
+
- Tested distribution_scope ransack delegation
|
|
42
|
+
- Tested nil search_params handling
|
|
43
|
+
|
|
44
|
+
## Deviations
|
|
45
|
+
|
|
46
|
+
None -- plan executed as specified across both target files.
|
|
47
|
+
|
|
48
|
+
## Verification Results
|
|
49
|
+
|
|
50
|
+
| Check | Result |
|
|
51
|
+
|-------|--------|
|
|
52
|
+
| `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/dashboard/queries_test.rb test/lib/source_monitor/analytics/sources_index_metrics_test.rb` | All tests pass |
|
|
53
|
+
| `bin/rails test` | 760 runs, 2626 assertions, 0 failures, 0 errors, 0 skips |
|
|
54
|
+
|
|
55
|
+
## Success Criteria
|
|
56
|
+
|
|
57
|
+
- [x] 35 new tests added (564 lines across 2 files)
|
|
58
|
+
- [x] StatsQuery SQL branches fully tested
|
|
59
|
+
- [x] RecentActivityQuery event building and sub-queries tested
|
|
60
|
+
- [x] record_metrics branches for all query types tested
|
|
61
|
+
- [x] Cache miss/hit behavior tested
|
|
62
|
+
- [x] SourcesIndexMetrics filter, sanitization, and distribution scope tested
|
|
63
|
+
- [x] REQ-04 and REQ-07 substantially satisfied
|