.vbw-planning/milestones/07-rails-audit-and-refactoring/06-test-infrastructure/06-01-RESEARCH.md
Framework & Setup:
test/dummy/ with Solid Queue and HotwireKey Files:
test/test_helper.rb - Main test setup (65-128 lines)test/test_prof.rb - TestProf integration (38 lines)test/support/host_app_harness.rb - Host app test generation (395 lines)test/vcr_cassettes/source_monitor/ (4 cassettes found)Test Count by Directory (estimated):
Test Count by Type:
SourceMonitor.reset_configuration! + SourceMonitor.config.http.retry_max = 0Finding: VCR is configured but usage patterns are not standardized across tests.
Current State:
test/test_helper.rb (lines 59-63):
test/vcr_cassettes/ignore_localhost = truesource_monitor/fetching/rss_success.ymlsource_monitor/fetching/atom_success.ymlsource_monitor/fetching/json_success.ymlsource_monitor/fetching/netflix_medium_rss.ymlVCR.use_cassette calls found in test files (no grep matches)Gap: Cassette strategy is not documented:
source_monitor/<module>/<descriptor>)Files Affected: test/test_helper.rb, test/vcr_cassettes/
Finding: Test helper fixtures :all loads all fixtures, creating implicit dependencies.
Current State:
test/test_helper.rb (lines 51-57):
if ActiveSupport::TestCase.respond_to?(:fixture_paths=)
fixtures_root = File.expand_path("fixtures", __dir__)
ActiveSupport::TestCase.fixture_paths = [ fixtures_root ]
ActionDispatch::IntegrationTest.fixture_paths = ActiveSupport::TestCase.fixture_paths
ActiveSupport::TestCase.file_fixture_path = fixtures_root
ActiveSupport::TestCase.fixtures :all # <-- loads ALL fixtures
end
fixtures :all loads every fixture, including users.yml if it existsfixtures :users found in grep (search returned no matches)fixtures :all implicitly couples every test to user model if fixtures existGap:
Files Affected: test/test_helper.rb, test/fixtures/ (directory)
Finding: Refactored sub-modules lack dedicated unit test files.
Current State:
AdaptiveInterval - no dedicated test file foundSourceUpdater - no dedicated test file foundEntryProcessor - test file exists: test/lib/source_monitor/fetching/feed_fetcher/entry_processor_test.rbEntryProcessor has 1 test fileGap:
AdaptiveInterval and SourceUpdater behavior don't existFiles Affected:
lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb (no test file)lib/source_monitor/fetching/feed_fetcher/source_updater.rb (no test file)lib/source_monitor/fetching/feed_fetcher/entry_processor.rb (test exists)Finding: Integration tests exist but coverage is incomplete for complex flows.
Current State:
test/integration/engine_mounting_test.rbtest/integration/host_install_flow_test.rbtest/integration/release_packaging_test.rbtest/integration/navigation_test.rb (4 total)Gap:
Estimated Test Count Needed: 8-12 additional integration tests
Finding: System tests test success flows but avoid error scenarios.
Current State:
test/system/dashboard_test.rbtest/system/items_test.rbtest/system/logs_test.rbtest/system/mission_control_test.rbtest/system/sources_test.rbtest/system/dropdown_fallback_test.rbGap:
Example Missing Test: "source deletion confirmation shows error if deletion fails"
Finding: Factory-like helpers are defined locally in each test file instead of centrally.
Current State:
test/test_helper.rb: only create_source! (lines 107-119)test/lib/source_monitor/items/retention_pruner_test.rb:
build_source (lines 147-155)create_item (lines 157-165)Pattern: Each test file that needs item/source/log creation defines its own helper, e.g.:
# In retention_pruner_test.rb
def build_source(attributes = {})
defaults = { name: "Source #{SecureRandom.hex(4)}", ... }
create_source!(defaults.merge(attributes))
end
def create_item(source:, guid:, published_at:, title:)
source.items.create!(guid:, url: "...", title:, ...)
end
Gap:
Files Likely Duplicating:
Item.create!, Source.create!, ScrapeLog.create!, FetchLog.create!, etc.Finding: Tests use 3+ different mocking styles without standardization.
Current State:
.stub):
# test/lib/source_monitor/fetching/advisory_lock_test.rb:40
ActiveRecord::Base.connection_pool.stub :with_connection, ->(&block) { block.call(fake_connection) } do
assert_raises(...)
end
# test/lib/source_monitor/fetching/advisory_lock_test.rb:30-37
fake_connection = Class.new do
def exec_query(sql)
# mock behavior
end
end.new
# test/system/items_test.rb:99
SourceMonitor::Scrapers::Readability.stub(:call, result) do
# test code
end
Gap:
Finding: Some tests create large numbers of WebMock stubs inline.
Current State:
test/test_helper.rb (lines 65): WebMock disables all external HTTP except localhostGap:
stub_request callsFinding: System tests define setup/teardown per-file instead of in a base class.
Current State:
application_system_test_case.rb (no file found yet)# test/system/dashboard_test.rb:9-23
def setup
super
SourceMonitor.reset_configuration!
SourceMonitor::Jobs::Visibility.reset!
SourceMonitor::Jobs::Visibility.setup!
purge_solid_queue_tables
SourceMonitor::Dashboard::TurboBroadcaster.setup!
end
def teardown
SourceMonitor.reset_configuration!
SourceMonitor::Jobs::Visibility.reset!
purge_solid_queue_tables
super
end
purge_solid_queue_tables (lines 167-184, dashboard_test.rb)seed_queue_activity (lines 140-165, dashboard_test.rb)apply_turbo_stream_messages (lines 186-226, dashboard_test.rb)connect_turbo_cable_stream_sources (not shown but referenced)capture_turbo_stream_broadcasts (not shown but referenced)parse_turbo_streams (lines 229-245, dashboard_test.rb)assert_item_order (test/system/items_test.rb:160-168)Gap:
Files Affected: All system test files (test/system/*.rb)
Finding: Controller tests focus on happy paths; error scenarios are light.
Current State:
test/controllers/source_monitor/application_controller_test.rb (55 lines, 4 tests)test/controllers/source_monitor/source_fetches_controller_test.rb (44 lines, 2 tests)test/controllers/source_monitor/health_controller_test.rbtest/controllers/source_monitor/logs_controller_test.rbtest/controllers/source_monitor/source_bulk_scrapes_controller_test.rbtest/controllers/source_monitor/source_health_checks_controller_test.rbtest "queues a fetch and renders turbo stream" do
source = create_source!(fetch_status: "idle")
assert_enqueued_jobs 1 do
post source_monitor.source_fetch_path(source), as: :turbo_stream
end
assert_response :success
end
Gap:
Finding: Tests use travel_to but assertions don't always validate the time-based behavior.
Current State:
test/lib/source_monitor/items/retention_pruner_test.rb uses travel_to:
test "removes items older than retention period" do
travel_to Time.zone.local(2025, 10, 1, 12, 0, 0) do
create_item(...) # old item created
end
travel_to Time.zone.local(2025, 10, 10, 12, 0, 0) do
create_item(...) # recent item created
result = RetentionPruner.call(source:)
assert_equal 1, result.removed_by_age # validates deletion
end
end
travel_back ensures (appear to be present)Gap:
ensure travel_back enforcer in base classFinding: Job testing uses both :test and :inline adapters without clear convention.
Current State:
:test (test_helper.rb:48)
ActiveJob::Base.queue_adapter = :test
with_inline_jobs when execution needed:
# test/system/items_test.rb:101
with_inline_jobs do
click_button "Manual Scrape"
end
assert_enqueued_with for :test adapterGap:
with_inline_jobs suggests it should be in test_helper but it's in test_prof.rbFinding: Counter cache fields (items_count on Source) are not validated for atomicity.
Current State:
counter_cache: true on has_many :items# test/system/items_test.rb:88
initial_item_count = Item.count
item = Item.create!(...)
# Later assertion checks the count
Gap:
reset_items_counter! testingFinding: Test names vary in style and specificity.
Current State:
Gap:
Finding: Concerns (Loggable, etc.) are tested inline; no shared test modules.
Current State:
Loggable concern used by FetchLog, ScrapeLog, HealthCheckLogtest/lib/source_monitor/logs/entry_sync_test.rb tests log behavior directlyGap:
Finding: ApplicationSystemTestCase doesn't exist or lacks Capybara wait config.
Current State:
test/dummy/app/test/application_system_test_case.rb filewait: 5, wait: 10, visible: :all, wait: 5# test/system/dashboard_test.rb:86
assert_selector "turbo-cable-stream-source", visible: :all, wait: 5
Gap:
Finding: Tests that create temp files don't have centralized cleanup.
Current State:
test/test_helper.rb includes cleanup for engine tables but not temp filestest/support/host_app_harness.rb manages temp directories for integration testsGap:
test/tmp/test/dummy/tmp/ grows unboundedCentralized in test_helper.rb:
def create_source!(attributes = {})
defaults = {
name: "Test Source",
feed_url: "https://example.com/feed-#{SecureRandom.hex(4)}.xml",
website_url: "https://example.com",
fetch_interval_minutes: 60,
scraper_adapter: "readability"
}
source = SourceMonitor::Source.new(defaults.merge(attributes))
source.save!(validate: false)
source
end
Local Patterns (per-file):
build_source(attributes) - wraps create_source! with defaultscreate_item(source:, guid:, title:, published_at:) - creates Item recordsModel.create! calls for other models (FetchLog, ScrapeLog, etc.)Minitest Stub (.stub):
Object.stub(:method, return_value) { code }
Anonymous Class Mocking:
fake = Class.new { def method; ...; end }.new
Direct Method Stubbing:
Class.stub(:method, value) { code }
stub_request(:get, "https://example.com/feed.xml")
.to_return(status: 200, body: File.read(file_fixture("feeds/rss_sample.xml")))
Standard Setup (test_helper.rb:82-91):
setup do
SourceMonitor.reset_configuration!
SourceMonitor.config.http.retry_max = 0
end
System Test Setup (per-file):
def setup
super
SourceMonitor.reset_configuration!
SourceMonitor::Jobs::Visibility.reset!
SourceMonitor::Jobs::Visibility.setup!
purge_solid_queue_tables
# dashboard-specific setup
end
Thread-Based Parallelism:
parallelize(workers: worker_count, with: :threads)
Scoped Assertions:
assert_equal 1, SourceMonitor::Item.where(source: source).count # GOOD
assert_equal 1, SourceMonitor::Item.count # BAD - counts across parallel tests
Create ApplicationSystemTestCase base class (T16)
test/dummy/app/test/application_system_test_case.rbpurge_solid_queue_tables, seed_queue_activity, apply_turbo_stream_messages, assert_item_orderCentralize Factory Helpers (T6)
test/test_helper_factories.rb or modulecreate_source!, build_source, create_item, create_fetch_log, create_scrape_logDocument VCR Strategy (T1)
test/VCR_README.mdsource_monitor/<domain>/<scenario> (existing partial convention)Add Unit Tests for Sub-Modules (T3)
test/lib/source_monitor/fetching/feed_fetcher/adaptive_interval_test.rb
test/lib/source_monitor/fetching/feed_fetcher/source_updater_test.rb
Add Integration Tests for Pipelines (T4)
test/integration/fetch_pipeline_integration_test.rb
test/integration/scrape_pipeline_integration_test.rb
Add Error Path Tests for Controllers (T10)
Add Error Scenario System Tests (T5)
Standardize Mocking Approach (T7)
stub (most concise)stubRemove Fixture Loading (T2)
fixtures :all with explicit per-test setup_fixtures for tests that need themAdd Concern Test Modules (T15)
test/support/shared_loggable_tests.rbStandardize Test Naming (T14)
Enforce Time Travel Cleanup (T11)
travel_to_at(time) { code } helper in test_helpertravel_back in ensureAdd Counter Cache Atomicity Tests (T13)
reset_items_counter! correctnessCentralize Job Testing Convention (T12)
:test vs :inline adapterwith_inline_jobs to test_helper (out of test_prof.rb)with_test_adapter helper for explicit test-mode testsAdd Temp File Cleanup Hook (T17)
test/tmp/* in teardown| Task | Effort |
|---|---|
| ApplicationSystemTestCase + helpers (T16, T9) | 2-3 hours |
| Centralize factories (T6) | 2-3 hours |
| VCR documentation (T1) | 1 hour |
| Sub-module unit tests (T3) | 3-4 hours |
| Integration tests (T4) | 4-5 hours |
| Controller error tests (T10) | 3-4 hours |
| System test error paths (T5) | 3-4 hours |
| Mocking standardization (T7) | 1-2 hours |
| Remaining consolidations (T2, T11-T15, T17) | 5-6 hours |
| Total | 25-32 hours |
Recommended Phase 06 Scope:
Future Phases:
| Metric | Current | Target |
|---|---|---|
| Test files | ~80 | ~100 (20 new) |
| Total tests | 1214 | 1350+ (136+ new) |
| Unit test coverage (sub-modules) | Partial | Complete |
| Integration test coverage | 4 scenarios | 10+ scenarios |
| System test error paths | 0% | 50%+ |
| Controller error tests | 1/controller | 5+/controller |
| Centralized factories | 1 (create_source!) | 6+ |
| ApplicationSystemTestCase | Missing | Present |
| VCR strategy doc | Missing | Present |