source_monitor 0.3.0 → 0.3.2
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/skills/sm-architecture/SKILL.md +233 -0
- data/.claude/skills/sm-architecture/reference/extraction-patterns.md +192 -0
- data/.claude/skills/sm-architecture/reference/module-map.md +194 -0
- data/.claude/skills/sm-configuration-setting/SKILL.md +264 -0
- data/.claude/skills/sm-configuration-setting/reference/settings-catalog.md +248 -0
- data/.claude/skills/sm-configuration-setting/reference/settings-pattern.md +297 -0
- data/.claude/skills/sm-configure/SKILL.md +153 -0
- data/.claude/skills/sm-configure/reference/configuration-reference.md +321 -0
- data/.claude/skills/sm-dashboard-widget/SKILL.md +344 -0
- data/.claude/skills/sm-dashboard-widget/reference/dashboard-patterns.md +304 -0
- data/.claude/skills/sm-domain-model/SKILL.md +188 -0
- data/.claude/skills/sm-domain-model/reference/model-graph.md +114 -0
- data/.claude/skills/sm-domain-model/reference/table-structure.md +348 -0
- data/.claude/skills/sm-engine-migration/SKILL.md +395 -0
- data/.claude/skills/sm-engine-migration/reference/migration-conventions.md +255 -0
- data/.claude/skills/sm-engine-test/SKILL.md +302 -0
- data/.claude/skills/sm-engine-test/reference/test-helpers.md +259 -0
- data/.claude/skills/sm-engine-test/reference/test-patterns.md +411 -0
- data/.claude/skills/sm-event-handler/SKILL.md +265 -0
- data/.claude/skills/sm-event-handler/reference/events-api.md +229 -0
- data/.claude/skills/sm-health-rule/SKILL.md +327 -0
- data/.claude/skills/sm-health-rule/reference/health-system.md +269 -0
- data/.claude/skills/sm-host-setup/SKILL.md +223 -0
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +195 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +134 -0
- data/.claude/skills/sm-job/SKILL.md +263 -0
- data/.claude/skills/sm-job/reference/job-conventions.md +245 -0
- data/.claude/skills/sm-model-extension/SKILL.md +287 -0
- data/.claude/skills/sm-model-extension/reference/extension-api.md +317 -0
- data/.claude/skills/sm-pipeline-stage/SKILL.md +254 -0
- data/.claude/skills/sm-pipeline-stage/reference/completion-handlers.md +152 -0
- data/.claude/skills/sm-pipeline-stage/reference/entry-processing.md +191 -0
- data/.claude/skills/sm-pipeline-stage/reference/feed-fetcher-architecture.md +198 -0
- data/.claude/skills/sm-scraper-adapter/SKILL.md +284 -0
- data/.claude/skills/sm-scraper-adapter/reference/adapter-contract.md +167 -0
- data/.claude/skills/sm-scraper-adapter/reference/example-adapter.md +274 -0
- data/.vbw-planning/.notification-log.jsonl +102 -0
- data/.vbw-planning/.session-log.jsonl +505 -0
- data/AGENTS.md +20 -57
- data/CHANGELOG.md +19 -0
- data/CLAUDE.md +44 -1
- data/CONTRIBUTING.md +5 -5
- data/Gemfile.lock +20 -21
- data/README.md +18 -5
- data/VERSION +1 -0
- data/docs/deployment.md +1 -1
- data/docs/setup.md +4 -4
- data/lib/source_monitor/setup/skills_installer.rb +94 -0
- data/lib/source_monitor/setup/workflow.rb +17 -2
- data/lib/source_monitor/version.rb +1 -1
- data/lib/tasks/source_monitor_setup.rake +58 -0
- data/source_monitor.gemspec +1 -0
- metadata +39 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sm-engine-test
|
|
3
|
+
description: Source Monitor engine test patterns and helpers. Use when writing or modifying tests for the Source Monitor engine, debugging test failures, setting up test isolation, or working with VCR/WebMock in this project.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Source Monitor Engine Tests
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# Minimal test file
|
|
13
|
+
require "test_helper"
|
|
14
|
+
|
|
15
|
+
module SourceMonitor
|
|
16
|
+
class MyFeatureTest < ActiveSupport::TestCase
|
|
17
|
+
test "does something" do
|
|
18
|
+
source = create_source!(name: "Test", feed_url: "https://example.com/feed.xml")
|
|
19
|
+
assert source.persisted?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Critical: Parallel Worker Caveat
|
|
26
|
+
|
|
27
|
+
**Single test files MUST use `PARALLEL_WORKERS=1`** due to a PG fork segfault bug (Ruby 3.4+ / pg 1.6+ / fork-based parallelism).
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Single file — REQUIRED
|
|
31
|
+
PARALLEL_WORKERS=1 bin/rails test test/models/source_monitor/source_test.rb
|
|
32
|
+
|
|
33
|
+
# Full suite — works fine (handles PG connections properly)
|
|
34
|
+
bin/rails test
|
|
35
|
+
|
|
36
|
+
# Coverage mode — automatically uses threads, not forks
|
|
37
|
+
COVERAGE=1 PARALLEL_WORKERS=1 bin/rails test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Test Helpers
|
|
41
|
+
|
|
42
|
+
All helpers are available in every test via `ActiveSupport::TestCase`.
|
|
43
|
+
|
|
44
|
+
| Helper | Purpose | Defined In |
|
|
45
|
+
|--------|---------|------------|
|
|
46
|
+
| `create_source!(attrs)` | Factory for Source records | `test/test_helper.rb` |
|
|
47
|
+
| `with_queue_adapter(adapter)` | Temporarily swap ActiveJob adapter | `test/test_helper.rb` |
|
|
48
|
+
| `with_inline_jobs { }` | Execute jobs inline in a block | `test/test_prof.rb` |
|
|
49
|
+
| `setup_once { }` | TestProf `before_all` for expensive setup | `test/test_prof.rb` |
|
|
50
|
+
| `clean_source_monitor_tables!` | Delete all engine records (FK-safe order) | `test/test_helper.rb` |
|
|
51
|
+
| `SourceMonitor.reset_configuration!` | Reset config to defaults (runs in setup) | automatic |
|
|
52
|
+
|
|
53
|
+
## Configuration Reset
|
|
54
|
+
|
|
55
|
+
Every test automatically calls `SourceMonitor.reset_configuration!` in setup. If you modify config in a test, it will be reset before the next test.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
test "custom config behavior" do
|
|
59
|
+
SourceMonitor.configure do |config|
|
|
60
|
+
config.fetching.min_interval_minutes = 10
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Test with custom config...
|
|
64
|
+
assert_equal 10, SourceMonitor.config.fetching.min_interval_minutes
|
|
65
|
+
end
|
|
66
|
+
# Config is automatically reset after this test
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For tests that explicitly need teardown:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
setup do
|
|
73
|
+
SourceMonitor.reset_configuration!
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
teardown do
|
|
77
|
+
SourceMonitor.reset_configuration!
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Factory Helper: create_source!
|
|
82
|
+
|
|
83
|
+
Creates a `SourceMonitor::Source` record, bypassing validations for speed.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# Defaults
|
|
87
|
+
source = create_source!
|
|
88
|
+
# => name: "Test Source"
|
|
89
|
+
# => feed_url: "https://example.com/feed-<random>.xml"
|
|
90
|
+
# => website_url: "https://example.com"
|
|
91
|
+
# => fetch_interval_minutes: 60
|
|
92
|
+
# => scraper_adapter: "readability"
|
|
93
|
+
|
|
94
|
+
# Override any attribute
|
|
95
|
+
source = create_source!(
|
|
96
|
+
name: "Custom Source",
|
|
97
|
+
feed_url: "https://custom.example.com/feed.xml",
|
|
98
|
+
active: false,
|
|
99
|
+
adaptive_fetching_enabled: false,
|
|
100
|
+
fetch_interval_minutes: 120
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Important:** `create_source!` uses `save!(validate: false)` so it skips model validations. This is intentional for test speed but means you can create records that would fail validation.
|
|
105
|
+
|
|
106
|
+
## WebMock Setup
|
|
107
|
+
|
|
108
|
+
WebMock is configured globally to block all external HTTP requests except localhost.
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# Stub a request
|
|
112
|
+
stub_request(:get, "https://example.com/feed.xml")
|
|
113
|
+
.to_return(
|
|
114
|
+
status: 200,
|
|
115
|
+
body: File.read(file_fixture("feeds/rss_sample.xml")),
|
|
116
|
+
headers: { "Content-Type" => "application/rss+xml" }
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Stub with specific headers
|
|
120
|
+
stub_request(:get, url)
|
|
121
|
+
.with(headers: { "If-None-Match" => '"etag123"' })
|
|
122
|
+
.to_return(status: 304, headers: { "ETag" => '"etag123"' })
|
|
123
|
+
|
|
124
|
+
# Stub a timeout
|
|
125
|
+
stub_request(:get, url).to_raise(Faraday::TimeoutError.new("execution expired"))
|
|
126
|
+
|
|
127
|
+
# Stub a connection failure
|
|
128
|
+
stub_request(:get, url).to_raise(Faraday::ConnectionFailed.new("connection refused"))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## VCR Cassettes
|
|
132
|
+
|
|
133
|
+
VCR is configured to hook into WebMock. Cassettes are stored in `test/vcr_cassettes/`.
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
test "fetches feed" do
|
|
137
|
+
source = create_source!(feed_url: "https://www.ruby-lang.org/en/feeds/news.rss")
|
|
138
|
+
|
|
139
|
+
VCR.use_cassette("source_monitor/fetching/rss_success") do
|
|
140
|
+
result = FeedFetcher.new(source: source, jitter: ->(_) { 0 }).call
|
|
141
|
+
assert_equal :fetched, result.status
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Naming convention:** `source_monitor/<module>/<descriptor>` (e.g., `source_monitor/fetching/rss_success`).
|
|
147
|
+
|
|
148
|
+
## Test Isolation
|
|
149
|
+
|
|
150
|
+
### Scope Queries to Specific Records
|
|
151
|
+
|
|
152
|
+
Parallel tests share the database. Always scope assertions to records you created:
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
# GOOD - scoped to specific source
|
|
156
|
+
assert_equal 3, SourceMonitor::Item.where(source: source).count
|
|
157
|
+
|
|
158
|
+
# BAD - counts all items across parallel tests
|
|
159
|
+
assert_equal 3, SourceMonitor::Item.count
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Use Unique Feed URLs
|
|
163
|
+
|
|
164
|
+
`create_source!` generates random feed URLs by default. When you need a specific URL, make it unique:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
source = create_source!(feed_url: "https://example.com/feed-#{SecureRandom.hex(4)}.xml")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Clean Tables When Needed
|
|
171
|
+
|
|
172
|
+
For tests that need a blank-slate database:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
setup do
|
|
176
|
+
clean_source_monitor_tables!
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The cleanup order respects foreign keys: LogEntry > ScrapeLog > FetchLog > HealthCheckLog > ItemContent > Item > Source.
|
|
181
|
+
|
|
182
|
+
## Job Testing
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
# Default adapter is :test (jobs are enqueued but not performed)
|
|
186
|
+
test "enqueues fetch job" do
|
|
187
|
+
source = create_source!
|
|
188
|
+
assert_enqueued_with(job: SourceMonitor::FetchSourceJob, args: [source]) do
|
|
189
|
+
source.enqueue_fetch!
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Execute jobs inline for integration tests
|
|
194
|
+
test "performs fetch end-to-end" do
|
|
195
|
+
with_inline_jobs do
|
|
196
|
+
# Jobs execute immediately when enqueued
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Switch to a specific adapter temporarily
|
|
201
|
+
test "with async adapter" do
|
|
202
|
+
with_queue_adapter(:async) do
|
|
203
|
+
# ...
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Test Types
|
|
209
|
+
|
|
210
|
+
| Type | Base Class | Location |
|
|
211
|
+
|------|-----------|----------|
|
|
212
|
+
| Model | `ActiveSupport::TestCase` | `test/models/source_monitor/` |
|
|
213
|
+
| Controller | `ActionDispatch::IntegrationTest` | `test/controllers/source_monitor/` |
|
|
214
|
+
| Library | `ActiveSupport::TestCase` | `test/lib/source_monitor/` |
|
|
215
|
+
| Generator | `Rails::Generators::TestCase` | `test/lib/generators/` |
|
|
216
|
+
|
|
217
|
+
## Common Patterns
|
|
218
|
+
|
|
219
|
+
### Testing with Notifications
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
test "emits fetch finish event" do
|
|
223
|
+
payloads = []
|
|
224
|
+
ActiveSupport::Notifications.subscribed(
|
|
225
|
+
->(_name, _start, _finish, _id, payload) { payloads << payload },
|
|
226
|
+
"source_monitor.fetch.finish"
|
|
227
|
+
) do
|
|
228
|
+
FeedFetcher.new(source: source, jitter: ->(_) { 0 }).call
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
assert payloads.last[:success]
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Testing with Time Travel
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
test "schedules next fetch" do
|
|
239
|
+
travel_to Time.zone.parse("2024-01-01 10:00:00 UTC")
|
|
240
|
+
|
|
241
|
+
# ... test logic ...
|
|
242
|
+
|
|
243
|
+
assert_equal Time.current + 45.minutes, source.next_fetch_at
|
|
244
|
+
ensure
|
|
245
|
+
travel_back
|
|
246
|
+
end
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Private Method Testing (via send)
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
test "jitter_offset returns zero for zero interval" do
|
|
253
|
+
fetcher = FeedFetcher.new(source: source)
|
|
254
|
+
assert_equal 0, fetcher.send(:jitter_offset, 0)
|
|
255
|
+
end
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Running Tests
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Full suite
|
|
262
|
+
bin/rails test
|
|
263
|
+
|
|
264
|
+
# Single file (MUST use PARALLEL_WORKERS=1)
|
|
265
|
+
PARALLEL_WORKERS=1 bin/rails test test/models/source_monitor/source_test.rb
|
|
266
|
+
|
|
267
|
+
# Verbose output
|
|
268
|
+
PARALLEL_WORKERS=1 bin/rails test test/models/source_monitor/source_test.rb --verbose
|
|
269
|
+
|
|
270
|
+
# Single test by name
|
|
271
|
+
PARALLEL_WORKERS=1 bin/rails test test/models/source_monitor/source_test.rb -n "test_is_valid_with_minimal_attributes"
|
|
272
|
+
|
|
273
|
+
# Coverage report
|
|
274
|
+
COVERAGE=1 PARALLEL_WORKERS=1 bin/rails test
|
|
275
|
+
|
|
276
|
+
# Random seed subset (via TestProf)
|
|
277
|
+
SAMPLE=10 bin/rails test
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## File Fixtures
|
|
281
|
+
|
|
282
|
+
Test fixtures (feed XML files, etc.) are in `test/fixtures/`:
|
|
283
|
+
|
|
284
|
+
```ruby
|
|
285
|
+
body = File.read(file_fixture("feeds/rss_sample.xml"))
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Testing Checklist
|
|
289
|
+
|
|
290
|
+
- [ ] Test file requires `"test_helper"`
|
|
291
|
+
- [ ] Tests wrapped in `module SourceMonitor` namespace
|
|
292
|
+
- [ ] Queries scoped to specific test records (not global counts)
|
|
293
|
+
- [ ] Feed URLs are unique per test
|
|
294
|
+
- [ ] `PARALLEL_WORKERS=1` used when running single files
|
|
295
|
+
- [ ] WebMock stubs or VCR cassettes for any HTTP calls
|
|
296
|
+
- [ ] `SourceMonitor.reset_configuration!` used if config is modified
|
|
297
|
+
- [ ] `travel_back` called in `ensure` when using `travel_to`
|
|
298
|
+
|
|
299
|
+
## References
|
|
300
|
+
|
|
301
|
+
- [reference/test-helpers.md](reference/test-helpers.md) -- Detailed helper documentation
|
|
302
|
+
- [reference/test-patterns.md](reference/test-patterns.md) -- VCR, WebMock, and isolation patterns
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Test Helpers Reference
|
|
2
|
+
|
|
3
|
+
## create_source!(attributes = {})
|
|
4
|
+
|
|
5
|
+
**File:** `test/test_helper.rb:97-109`
|
|
6
|
+
|
|
7
|
+
Creates a `SourceMonitor::Source` record with sensible defaults, bypassing model validations.
|
|
8
|
+
|
|
9
|
+
### Default Values
|
|
10
|
+
|
|
11
|
+
| Attribute | Default |
|
|
12
|
+
|-----------|---------|
|
|
13
|
+
| `name` | `"Test Source"` |
|
|
14
|
+
| `feed_url` | `"https://example.com/feed-<random_hex>.xml"` |
|
|
15
|
+
| `website_url` | `"https://example.com"` |
|
|
16
|
+
| `fetch_interval_minutes` | `60` |
|
|
17
|
+
| `scraper_adapter` | `"readability"` |
|
|
18
|
+
|
|
19
|
+
### Usage
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# All defaults
|
|
23
|
+
source = create_source!
|
|
24
|
+
|
|
25
|
+
# Override specific attributes
|
|
26
|
+
source = create_source!(
|
|
27
|
+
name: "My Feed",
|
|
28
|
+
feed_url: "https://example.com/specific-feed.xml",
|
|
29
|
+
active: false,
|
|
30
|
+
adaptive_fetching_enabled: true,
|
|
31
|
+
fetch_interval_minutes: 120,
|
|
32
|
+
scraping_enabled: true,
|
|
33
|
+
auto_scrape: true,
|
|
34
|
+
custom_headers: { "X-Api-Key" => "secret123" },
|
|
35
|
+
metadata: { "category" => "tech" }
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Implementation Detail
|
|
40
|
+
|
|
41
|
+
Uses `save!(validate: false)` intentionally. This means:
|
|
42
|
+
- Records skip URL normalization that happens during validation
|
|
43
|
+
- Records with duplicate feed_urls can be created
|
|
44
|
+
- Invalid data can be inserted (useful for edge case testing)
|
|
45
|
+
|
|
46
|
+
### Creating Related Records
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
source = create_source!
|
|
50
|
+
|
|
51
|
+
# Items
|
|
52
|
+
item = source.items.create!(
|
|
53
|
+
guid: "guid-1",
|
|
54
|
+
title: "Item Title",
|
|
55
|
+
url: "https://example.com/1",
|
|
56
|
+
published_at: Time.current
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Fetch logs
|
|
60
|
+
source.fetch_logs.create!(
|
|
61
|
+
success: true,
|
|
62
|
+
started_at: Time.current,
|
|
63
|
+
completed_at: Time.current,
|
|
64
|
+
items_created: 1
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Scrape logs
|
|
68
|
+
source.scrape_logs.create!(
|
|
69
|
+
item: item,
|
|
70
|
+
success: true,
|
|
71
|
+
started_at: Time.current,
|
|
72
|
+
completed_at: Time.current,
|
|
73
|
+
scraper_adapter: "readability"
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## with_queue_adapter(adapter)
|
|
80
|
+
|
|
81
|
+
**File:** `test/test_helper.rb:111-117`
|
|
82
|
+
|
|
83
|
+
Temporarily swaps the ActiveJob queue adapter for the duration of a block.
|
|
84
|
+
|
|
85
|
+
### Usage
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
test "enqueues with inline adapter" do
|
|
89
|
+
with_queue_adapter(:inline) do
|
|
90
|
+
# Jobs execute immediately
|
|
91
|
+
source.enqueue_fetch!
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
test "with test adapter" do
|
|
96
|
+
with_queue_adapter(:test) do
|
|
97
|
+
assert_enqueued_with(job: FetchSourceJob) do
|
|
98
|
+
source.enqueue_fetch!
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Behavior
|
|
105
|
+
|
|
106
|
+
- Saves current adapter
|
|
107
|
+
- Sets new adapter
|
|
108
|
+
- Yields to block
|
|
109
|
+
- Restores previous adapter in `ensure` (always runs, even on exception)
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## with_inline_jobs
|
|
114
|
+
|
|
115
|
+
**File:** `test/test_prof.rb:24-29`
|
|
116
|
+
|
|
117
|
+
Convenience wrapper around `with_queue_adapter(:inline)`.
|
|
118
|
+
|
|
119
|
+
### Usage
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
test "performs complete fetch pipeline" do
|
|
123
|
+
with_inline_jobs do
|
|
124
|
+
# All enqueued jobs execute immediately
|
|
125
|
+
SourceMonitor::FetchSourceJob.perform_later(source)
|
|
126
|
+
source.reload
|
|
127
|
+
assert source.last_fetched_at.present?
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## setup_once(setup_fixtures: false, &block)
|
|
135
|
+
|
|
136
|
+
**File:** `test/test_prof.rb:18-20`
|
|
137
|
+
|
|
138
|
+
Wraps TestProf's `before_all` for expensive setup that should run once per test class, not per test method.
|
|
139
|
+
|
|
140
|
+
### Usage
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
class ExpensiveSetupTest < ActiveSupport::TestCase
|
|
144
|
+
setup_once do
|
|
145
|
+
@shared_source = create_source!(name: "Shared")
|
|
146
|
+
3.times do |i|
|
|
147
|
+
@shared_source.items.create!(
|
|
148
|
+
guid: "item-#{i}",
|
|
149
|
+
url: "https://example.com/#{i}"
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
test "source has items" do
|
|
155
|
+
assert_equal 3, @shared_source.items.count
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
test "another test reuses same data" do
|
|
159
|
+
assert @shared_source.persisted?
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Caveats
|
|
165
|
+
|
|
166
|
+
- Data created in `setup_once` is rolled back after all tests in the class
|
|
167
|
+
- Use `setup_fixtures: true` if you need fixtures loaded in the `before_all` block
|
|
168
|
+
- Do NOT modify `setup_once` data in individual tests (it is shared across tests)
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## clean_source_monitor_tables!
|
|
173
|
+
|
|
174
|
+
**File:** `test/test_helper.rb:85-93`
|
|
175
|
+
|
|
176
|
+
Deletes all records from engine tables in FK-safe order.
|
|
177
|
+
|
|
178
|
+
### Deletion Order
|
|
179
|
+
|
|
180
|
+
1. `SourceMonitor::LogEntry`
|
|
181
|
+
2. `SourceMonitor::ScrapeLog`
|
|
182
|
+
3. `SourceMonitor::FetchLog`
|
|
183
|
+
4. `SourceMonitor::HealthCheckLog`
|
|
184
|
+
5. `SourceMonitor::ItemContent`
|
|
185
|
+
6. `SourceMonitor::Item`
|
|
186
|
+
7. `SourceMonitor::Source`
|
|
187
|
+
|
|
188
|
+
### Usage
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
setup do
|
|
192
|
+
clean_source_monitor_tables!
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### When to Use
|
|
197
|
+
|
|
198
|
+
- Tests that assert global counts (e.g., `Source.count`)
|
|
199
|
+
- Tests that need no pre-existing data
|
|
200
|
+
- Tests that are sensitive to data created by other tests in parallel
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## SourceMonitor.reset_configuration!
|
|
205
|
+
|
|
206
|
+
**Automatically called** in setup for every `ActiveSupport::TestCase`.
|
|
207
|
+
|
|
208
|
+
Resets the `SourceMonitor::Configuration` instance to default values. This means:
|
|
209
|
+
|
|
210
|
+
- All queue names/concurrency reset to defaults
|
|
211
|
+
- HTTP settings (timeouts, user agent) reset
|
|
212
|
+
- Fetching adaptive interval settings reset
|
|
213
|
+
- Health thresholds reset
|
|
214
|
+
- Retention settings reset
|
|
215
|
+
- Realtime adapter reset to `:solid_cable`
|
|
216
|
+
- Authentication handlers cleared
|
|
217
|
+
- Events callbacks cleared
|
|
218
|
+
- Scraping settings reset
|
|
219
|
+
- Scraper registry cleared
|
|
220
|
+
- Model definitions reset
|
|
221
|
+
|
|
222
|
+
### Manual Usage
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
setup do
|
|
226
|
+
SourceMonitor.reset_configuration!
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
teardown do
|
|
230
|
+
SourceMonitor.reset_configuration!
|
|
231
|
+
end
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Parallelization Configuration
|
|
237
|
+
|
|
238
|
+
**File:** `test/test_helper.rb:68-77`
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
# With COVERAGE env var: single-threaded
|
|
242
|
+
parallelize(workers: 1, with: :threads)
|
|
243
|
+
|
|
244
|
+
# Without COVERAGE: uses system CPU count (fork-based)
|
|
245
|
+
parallelize(workers: :number_of_processors)
|
|
246
|
+
|
|
247
|
+
# Override with SOURCE_MONITOR_TEST_WORKERS env var
|
|
248
|
+
SOURCE_MONITOR_TEST_WORKERS=4 bin/rails test
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Environment Variables
|
|
252
|
+
|
|
253
|
+
| Variable | Effect |
|
|
254
|
+
|----------|--------|
|
|
255
|
+
| `COVERAGE` | Forces `workers: 1` with threads |
|
|
256
|
+
| `SOURCE_MONITOR_TEST_WORKERS` | Override worker count |
|
|
257
|
+
| `PARALLEL_WORKERS` | Rails built-in parallelism control |
|
|
258
|
+
| `SAMPLE` | TestProf: run random subset of tests |
|
|
259
|
+
| `SAMPLE_GROUPS` | TestProf: run random subset of test groups |
|