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.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +45 -22
  3. data/.claude/skills/sm-configure/SKILL.md +10 -1
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
  5. data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
  6. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
  7. data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
  8. data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
  9. data/.gitignore +10 -0
  10. data/AGENTS.md +1 -1
  11. data/CHANGELOG.md +56 -0
  12. data/CLAUDE.md +11 -5
  13. data/Gemfile.lock +1 -1
  14. data/README.md +6 -4
  15. data/VERSION +1 -1
  16. data/app/assets/builds/source_monitor/application.css +43 -0
  17. data/app/assets/builds/source_monitor/application.js +127 -0
  18. data/app/assets/builds/source_monitor/application.js.map +3 -3
  19. data/app/assets/javascripts/source_monitor/application.js +2 -0
  20. data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
  21. data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
  22. data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
  23. data/app/controllers/source_monitor/sources_controller.rb +11 -0
  24. data/app/helpers/source_monitor/application_helper.rb +51 -0
  25. data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
  26. data/app/jobs/source_monitor/import_opml_job.rb +9 -0
  27. data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
  28. data/app/models/source_monitor/source.rb +2 -0
  29. data/app/views/layouts/source_monitor/application.html.erb +23 -2
  30. data/app/views/source_monitor/shared/_toast.html.erb +1 -0
  31. data/app/views/source_monitor/sources/_details.html.erb +34 -5
  32. data/app/views/source_monitor/sources/_row.html.erb +11 -6
  33. data/config/routes.rb +1 -0
  34. data/docs/configuration.md +1 -1
  35. data/docs/upgrade.md +22 -0
  36. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
  37. data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
  38. data/lib/source_monitor/configuration/http_settings.rb +1 -1
  39. data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
  40. data/lib/source_monitor/configuration.rb +3 -1
  41. data/lib/source_monitor/favicons/discoverer.rb +196 -0
  42. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
  43. data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
  44. data/lib/source_monitor/http.rb +5 -3
  45. data/lib/source_monitor/version.rb +1 -1
  46. data/lib/source_monitor.rb +4 -0
  47. data/lib/tasks/test_fast.rake +11 -0
  48. data/source_monitor.gemspec +1 -1
  49. metadata +7 -93
  50. data/.vbw-planning/PROJECT.md +0 -51
  51. data/.vbw-planning/ROADMAP.md +0 -32
  52. data/.vbw-planning/SHIPPED.md +0 -63
  53. data/.vbw-planning/STATE.md +0 -27
  54. data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
  55. data/.vbw-planning/codebase/CONCERNS.md +0 -99
  56. data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
  57. data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
  58. data/.vbw-planning/codebase/INDEX.md +0 -86
  59. data/.vbw-planning/codebase/META.md +0 -42
  60. data/.vbw-planning/codebase/PATTERNS.md +0 -262
  61. data/.vbw-planning/codebase/STACK.md +0 -101
  62. data/.vbw-planning/codebase/STRUCTURE.md +0 -324
  63. data/.vbw-planning/codebase/TESTING.md +0 -154
  64. data/.vbw-planning/config.json +0 -53
  65. data/.vbw-planning/discovery.json +0 -26
  66. data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
  67. data/.vbw-planning/milestones/default/STATE.md +0 -82
  68. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
  69. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
  70. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
  71. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
  72. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
  73. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
  74. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
  75. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
  76. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
  77. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
  78. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
  79. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
  80. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
  81. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
  82. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
  83. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
  84. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
  85. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
  86. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
  87. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
  88. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
  89. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
  90. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
  91. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
  92. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
  93. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
  94. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
  95. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
  96. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
  97. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
  98. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
  99. data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
  100. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
  101. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
  102. data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
  103. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
  104. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
  105. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
  106. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
  107. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
  108. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
  109. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
  110. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
  111. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
  112. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
  113. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
  114. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
  115. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
  116. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
  117. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
  118. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
  119. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
  120. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
  121. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
  122. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
  123. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
  124. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
  125. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
  126. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
  127. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
  128. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
  129. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
  130. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
  131. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
  132. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
  133. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
  134. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
  135. data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
  136. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
  137. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
  138. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
  139. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
  140. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
  141. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
@@ -1,85 +0,0 @@
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
@@ -1,147 +0,0 @@
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
@@ -1,63 +0,0 @@
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
@@ -1,129 +0,0 @@
1
- ---
2
- phase: 2
3
- plan: 4
4
- title: dashboard-and-analytics-tests
5
- wave: 1
6
- depends_on: []
7
- skills_used: []
8
- must_haves:
9
- truths:
10
- - "Running `bin/rails test test/lib/source_monitor/dashboard/queries_test.rb test/lib/source_monitor/analytics/sources_index_metrics_test.rb test/lib/source_monitor/dashboard/upcoming_fetch_schedule_test.rb` exits 0 with zero failures"
11
- - "Coverage report shows lib/source_monitor/dashboard/queries.rb has fewer than 15 uncovered lines (down from 66)"
12
- - "Coverage report shows lib/source_monitor/analytics/sources_index_metrics.rb has fewer than 10 uncovered lines (down from 34)"
13
- - "Running `bin/rails test` exits 0 with no regressions"
14
- artifacts:
15
- - "test/lib/source_monitor/dashboard/queries_test.rb -- extended with tests for StatsQuery, RecentActivityQuery, record_metrics, and Cache"
16
- - "test/lib/source_monitor/analytics/sources_index_metrics_test.rb -- extended with tests for edge cases in fetch_interval_filter, integer_param, distribution_scope, and selected_fetch_interval_bucket"
17
- key_links:
18
- - "REQ-04 substantially satisfied -- Dashboard::Queries branch coverage above 80%"
19
- - "REQ-07 substantially satisfied -- SourcesIndexMetrics branch coverage above 80%"
20
- ---
21
-
22
- # Plan 04: dashboard-and-analytics-tests
23
-
24
- ## Objective
25
-
26
- Close the coverage gaps in `lib/source_monitor/dashboard/queries.rb` (66 uncovered lines) and `lib/source_monitor/analytics/sources_index_metrics.rb` (34 uncovered lines). The existing tests cover caching, basic stats, recent_activity events, job_metrics with stub, and upcoming_fetch_schedule groups. This plan targets the remaining uncovered branches: StatsQuery SQL generation and integer_value, RecentActivityQuery's build_event and the three sub-queries, record_metrics branches for each query type, Cache miss/hit paths, SourcesIndexMetrics' fetch_interval_filter with various param combinations, integer_param sanitization, distribution_scope with ransack, and selected_fetch_interval_bucket matching logic.
27
-
28
- ## Context
29
-
30
- <context>
31
- @lib/source_monitor/dashboard/queries.rb -- 357 lines with StatsQuery, RecentActivityQuery, Cache, record_metrics
32
- @lib/source_monitor/analytics/sources_index_metrics.rb -- 93 lines with fetch_interval_filter, integer_param, distribution_scope
33
- @test/lib/source_monitor/dashboard/queries_test.rb -- existing test file with 7 tests
34
- @test/lib/source_monitor/analytics/sources_index_metrics_test.rb -- existing test file with 3 tests
35
- @lib/source_monitor/dashboard/upcoming_fetch_schedule.rb -- UpcomingFetchSchedule with Group struct
36
- @config/coverage_baseline.json -- lists uncovered lines for both files
37
-
38
- **Decomposition rationale:** Dashboard::Queries and SourcesIndexMetrics share a read-only analytics theme and can be covered in a single plan without file conflicts. Their combined gap (100 lines) is manageable in 4 tasks. The queries_test.rb file already has good infrastructure (count_sql_queries helper, setup with delete_all).
39
-
40
- **Trade-offs considered:**
41
- - StatsQuery and RecentActivityQuery use raw SQL -- tests need real database records, not mocks.
42
- - record_metrics calls SourceMonitor::Metrics.gauge -- we verify gauge values were set.
43
- - The SourcesIndexMetrics distribution_scope branch with ransack requires a scope that responds to .ransack -- the Source model does.
44
- - Some tests for integer_param can test edge cases (non-numeric, XSS-like strings) for both sanitization and type safety.
45
- </context>
46
-
47
- ## Tasks
48
-
49
- ### Task 1: Test StatsQuery SQL branches and integer_value
50
-
51
- - **name:** test-stats-query-branches
52
- - **files:**
53
- - `test/lib/source_monitor/dashboard/queries_test.rb`
54
- - **action:** Add tests covering lines 142-204 (StatsQuery). Specifically:
55
- 1. Test stats returns correct counts with mixed active/inactive sources, sources with failures (failure_count > 0, last_error present), items, and fetch logs from today vs yesterday
56
- 2. Test stats[:failed_sources] counts sources that have failure_count > 0 OR last_error IS NOT NULL OR last_error_at IS NOT NULL (the OR conditions at lines 186-190)
57
- 3. Test stats[:fetches_today] only counts fetch logs with started_at >= start_of_day (line 172)
58
- 4. Test stats with zero sources and zero items returns all zeros
59
- 5. Test record_stats_metrics sets gauge values for total_sources, active_sources, failed_sources, total_items, fetches_today (lines 105-111)
60
- Create specific database records to exercise each condition.
61
- - **verify:** `bin/rails test test/lib/source_monitor/dashboard/queries_test.rb -n /stats_query|failed_sources|fetches_today|stats_metrics/i` exits 0
62
- - **done:** Lines 142-204, 105-111 covered.
63
-
64
- ### Task 2: Test RecentActivityQuery build_event and sub-queries
65
-
66
- - **name:** test-recent-activity-query-details
67
- - **files:**
68
- - `test/lib/source_monitor/dashboard/queries_test.rb`
69
- - **action:** Add tests covering lines 206-335 (RecentActivityQuery). Specifically:
70
- 1. Test that build_event produces Event objects with correct type symbol (:fetch_log, :scrape_log, :item), correct fields (occurred_at, success, items_created, items_updated, scraper_adapter, item_title, item_url, source_name, source_id)
71
- 2. Test that fetch_log events have success based on the boolean column, items_created/items_updated from the log
72
- 3. Test that scrape_log events include scraper_adapter and source_name (via JOIN)
73
- 4. Test that item events have item_title, item_url, source_name (via JOIN), and success_flag always 1
74
- 5. Test that events are ordered by occurred_at DESC and limited correctly
75
- 6. Test record_metrics for :recent_activity sets dashboard_recent_activity_events_count and dashboard_recent_activity_limit gauges (lines 96-97)
76
- Create a mix of fetch_logs, scrape_logs, and items with specific timestamps to verify ordering and limit.
77
- - **verify:** `bin/rails test test/lib/source_monitor/dashboard/queries_test.rb -n /recent_activity_query|build_event|event_type|event_order/i` exits 0
78
- - **done:** Lines 206-335, 96-97 covered.
79
-
80
- ### Task 3: Test record_metrics branches and Cache edge cases
81
-
82
- - **name:** test-record-metrics-and-cache
83
- - **files:**
84
- - `test/lib/source_monitor/dashboard/queries_test.rb`
85
- - **action:** Add tests covering lines 75-103 (measure and record_metrics), lines 124-140 (Cache). Specifically:
86
- 1. Test record_metrics for :job_metrics sets dashboard_job_metrics_queue_count gauge (line 99)
87
- 2. Test record_metrics for :upcoming_fetch_schedule sets dashboard_fetch_schedule_group_count gauge (line 101)
88
- 3. Test that measure instruments ActiveSupport::Notifications with correct event name and payload (lines 81-83)
89
- 4. Test Cache.fetch returns cached value on second call without calling block again (lines 130-133)
90
- 5. Test Cache.fetch with different keys calls block for each (line 129 store.key? check)
91
- Use SourceMonitor::Metrics.reset! before each test and check gauge values after.
92
- - **verify:** `bin/rails test test/lib/source_monitor/dashboard/queries_test.rb -n /record_metrics|cache_behavior|measure_instrument/i` exits 0
93
- - **done:** Lines 75-103, 124-140 covered.
94
-
95
- ### Task 4: Test SourcesIndexMetrics edge cases
96
-
97
- - **name:** test-sources-index-metrics-edges
98
- - **files:**
99
- - `test/lib/source_monitor/analytics/sources_index_metrics_test.rb`
100
- - **action:** Add tests covering remaining uncovered lines in sources_index_metrics.rb. Specifically:
101
- 1. Test fetch_interval_filter returns nil when no interval params present (line 49)
102
- 2. Test fetch_interval_filter with only min param (gteq) and nil max
103
- 3. Test fetch_interval_filter prefers fetch_interval_minutes_lt over fetch_interval_minutes_lteq when both present
104
- 4. Test integer_param returns nil for blank value (line 71), returns nil for non-numeric string after sanitization (line 77-79), returns integer for valid string
105
- 5. Test selected_fetch_interval_bucket returns nil when no filter is set (line 28)
106
- 6. Test selected_fetch_interval_bucket matches bucket where min.nil? (first bucket) when filter min is nil
107
- 7. Test distribution_scope uses ransack when filtered_params are present and scope responds to ransack (lines 62-63)
108
- 8. Test distribution_scope returns base_scope when filtered_params are empty (line 65)
109
- 9. Test distribution_source_ids with a scope that responds to pluck vs one that doesn't (lines 83-88)
110
- - **verify:** `bin/rails test test/lib/source_monitor/analytics/sources_index_metrics_test.rb -n /fetch_interval_filter|integer_param|selected_bucket|distribution_scope/i` exits 0
111
- - **done:** All remaining uncovered lines in sources_index_metrics.rb covered.
112
-
113
- ## Verification
114
-
115
- 1. `bin/rails test test/lib/source_monitor/dashboard/queries_test.rb` exits 0
116
- 2. `bin/rails test test/lib/source_monitor/analytics/sources_index_metrics_test.rb` exits 0
117
- 3. `COVERAGE=1 bin/rails test test/lib/source_monitor/dashboard/queries_test.rb test/lib/source_monitor/analytics/sources_index_metrics_test.rb` shows both files with >80% branch coverage
118
- 4. `bin/rails test` exits 0 (no regressions)
119
-
120
- ## Success Criteria
121
-
122
- - [ ] Dashboard::Queries coverage drops from 66 uncovered lines to fewer than 15
123
- - [ ] SourcesIndexMetrics coverage drops from 34 uncovered lines to fewer than 10
124
- - [ ] StatsQuery SQL branches fully tested
125
- - [ ] RecentActivityQuery event building and sub-queries tested
126
- - [ ] record_metrics branches for all query types tested
127
- - [ ] Cache miss/hit behavior tested
128
- - [ ] SourcesIndexMetrics filter, sanitization, and distribution scope tested
129
- - [ ] REQ-04 and REQ-07 substantially satisfied
@@ -1,74 +0,0 @@
1
- # PLAN-05 Summary: scraping-and-broadcasting-tests
2
-
3
- ## Status: COMPLETE
4
-
5
- ## Commits
6
-
7
- - **Hash:** `e497891`
8
- - **Message:** `test(scraping-broadcasting): close coverage gaps for bulk scraper and broadcaster`
9
- - **Files changed:** 2 files, 816 insertions (broadcaster_test.rb new, bulk_source_scraper_test.rb extended)
10
-
11
- - **Hash:** `66b8df2` (tag commit)
12
- - **Message:** `test(dev-plan05): close coverage gaps for bulk scraper and broadcaster`
13
- - **Note:** This commit also contained Plan 03 (configuration-tests) work; see PLAN-03-SUMMARY.md.
14
-
15
- ## Tasks Completed
16
-
17
- ### Task 1: Test BulkSourceScraper disabled and invalid selection paths
18
- - Tested scraping_enabled: false returns error result with failure_details
19
- - Tested Result struct methods: success?, partial?, error?, rate_limited?
20
- - Tested normalize_selection with symbol, string, whitespace, uppercase, nil, invalid
21
- - Tested selection_label with valid and invalid selection values
22
-
23
- ### Task 2: Test BulkSourceScraper batch limiting and determine_status
24
- - Tested apply_batch_limit respects max_bulk_batch_size from config
25
- - Tested determine_status returns :success, :partial, :error based on counts
26
- - Tested determine_status :partial when only already_enqueued > 0
27
- - Tested without_inflight excludes items with in-flight scrape_status
28
- - Tested unknown enqueuer status handling
29
-
30
- ### Task 3: Test Broadcaster setup and broadcast_source/broadcast_item
31
- - Created new test file: test/lib/source_monitor/realtime/broadcaster_test.rb
32
- - Tested setup! registers after_fetch_completed and after_item_scraped callbacks
33
- - Tested setup! idempotent (no double-registration)
34
- - Tested broadcast_source returns early when turbo_available? is false
35
- - Tested broadcast_source returns early when source is nil after reload
36
- - Tested broadcast_source calls broadcast_source_row and broadcast_source_show
37
- - Tested broadcast_item calls Turbo::StreamsChannel with correct target/partial
38
- - Tested broadcast_item rescues errors and logs them
39
-
40
- ### Task 4: Test Broadcaster toast broadcasting and event handlers
41
- - Tested broadcast_toast returns early when turbo_available? is false or message blank
42
- - Tested broadcast_toast calls Turbo::StreamsChannel.broadcast_append_to
43
- - Tested handle_fetch_completed broadcasts source and toast for each status
44
- - Tested broadcast_fetch_toast for "fetched", "not_modified", "failed" statuses
45
- - Tested handle_item_scraped broadcasts item, source, and toast
46
- - Tested broadcast_item_toast for success and failed statuses
47
-
48
- ### Task 5: Test Broadcaster helpers
49
- - Tested reload_record returns nil for nil input, original record on reload error
50
- - Tested turbo_available? true/false based on Turbo::StreamsChannel defined
51
- - Tested register_callback no double-registration
52
- - Tested log_error swallows errors from logger itself
53
- - Tested broadcast_source_row and broadcast_source_show rescue and log errors
54
-
55
- ## Deviations
56
-
57
- | ID | Description | Impact |
58
- |----|-------------|--------|
59
- | DEVN-01 | Commit 66b8df2 bundled Plan 03 (configuration) and Plan 05 (scraping/broadcasting) work | No functional impact; both sets of tests pass independently |
60
-
61
- ## Verification Results
62
-
63
- | Check | Result |
64
- |-------|--------|
65
- | `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/scraping/bulk_source_scraper_test.rb test/lib/source_monitor/realtime/broadcaster_test.rb` | All tests pass |
66
- | `bin/rails test` | 760 runs, 2626 assertions, 0 failures, 0 errors, 0 skips |
67
-
68
- ## Success Criteria
69
-
70
- - [x] 15 new BulkSourceScraper tests (270 lines) + 35 new Broadcaster tests (546 lines)
71
- - [x] BulkSourceScraper disabled/invalid/batch/status paths tested
72
- - [x] Broadcaster setup, broadcasting, toast, and event handlers tested
73
- - [x] Broadcaster error swallowing and helper methods tested
74
- - [x] REQ-05 and REQ-06 substantially satisfied