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,441 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-test
|
|
3
|
+
description: Generates comprehensive Minitest tests with fixtures for all layers. Use when writing tests, creating test files, adding test coverage, debugging test failures, or when the user mentions testing, minitest, fixtures, test helpers, or test organization.
|
|
4
|
+
tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Comprehensive Minitest Testing
|
|
8
|
+
|
|
9
|
+
## Project Conventions
|
|
10
|
+
- **Testing:** Minitest + fixtures (NEVER RSpec or FactoryBot)
|
|
11
|
+
- **Components:** ViewComponents for reusable UI (partials OK for simple one-offs)
|
|
12
|
+
- **Authorization:** Pundit policies (deny by default)
|
|
13
|
+
- **Jobs:** Solid Queue, shallow jobs, `_later`/`_now` naming
|
|
14
|
+
- **Frontend:** Hotwire (Turbo + Stimulus) + Tailwind CSS
|
|
15
|
+
- **State:** State-as-records for business state (booleans only for technical flags)
|
|
16
|
+
- **Architecture:** Rich models first, service objects for multi-model orchestration
|
|
17
|
+
- **Routing:** Everything-is-CRUD (new resource over new action)
|
|
18
|
+
- **Quality:** RuboCop (omakase) + Brakeman
|
|
19
|
+
|
|
20
|
+
## Test Directory Structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
test/
|
|
24
|
+
├── test_helper.rb
|
|
25
|
+
├── application_system_test_case.rb
|
|
26
|
+
├── components/ # ViewComponent tests
|
|
27
|
+
├── controllers/ # Integration tests
|
|
28
|
+
├── fixtures/ # Test data (YAML)
|
|
29
|
+
├── jobs/ # ActiveJob tests
|
|
30
|
+
├── mailers/ # ActionMailer tests
|
|
31
|
+
│ └── previews/ # Mailer previews
|
|
32
|
+
├── models/ # Unit tests
|
|
33
|
+
│ └── concerns/ # Concern tests
|
|
34
|
+
├── policies/ # Pundit policy tests
|
|
35
|
+
├── presenters/ # Presenter tests
|
|
36
|
+
├── queries/ # Query object tests
|
|
37
|
+
├── services/ # Service object tests
|
|
38
|
+
└── system/ # Browser tests (Capybara)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Test Helper Setup
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# test/test_helper.rb
|
|
45
|
+
ENV["RAILS_ENV"] ||= "test"
|
|
46
|
+
require_relative "../config/environment"
|
|
47
|
+
require "rails/test_help"
|
|
48
|
+
|
|
49
|
+
module ActiveSupport
|
|
50
|
+
class TestCase
|
|
51
|
+
fixtures :all
|
|
52
|
+
parallelize(workers: :number_of_processors)
|
|
53
|
+
|
|
54
|
+
def sign_in_as(user)
|
|
55
|
+
post session_url, params: { email_address: user.email_address, password: "password" }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def sign_out
|
|
59
|
+
delete session_url
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def assert_success(result)
|
|
63
|
+
assert result.success?, "Expected success but got failure: #{result.error}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def assert_failure(result, code: nil)
|
|
67
|
+
assert result.failure?, "Expected failure but got success"
|
|
68
|
+
assert_equal code, result.code if code
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# test/application_system_test_case.rb
|
|
76
|
+
require "test_helper"
|
|
77
|
+
|
|
78
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|
79
|
+
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 900]
|
|
80
|
+
|
|
81
|
+
def sign_in_as(user)
|
|
82
|
+
visit new_session_url
|
|
83
|
+
fill_in "Email", with: user.email_address
|
|
84
|
+
fill_in "Password", with: "password"
|
|
85
|
+
click_button "Sign in"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Fixture Best Practices
|
|
91
|
+
|
|
92
|
+
Name fixtures after their state. Use ERB sparingly. Reference other fixtures by label.
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
# test/fixtures/users.yml
|
|
96
|
+
admin:
|
|
97
|
+
name: Admin User
|
|
98
|
+
email_address: admin@example.com
|
|
99
|
+
password_digest: <%= BCrypt::Password.create("password") %>
|
|
100
|
+
role: admin
|
|
101
|
+
|
|
102
|
+
regular:
|
|
103
|
+
name: Regular User
|
|
104
|
+
email_address: user@example.com
|
|
105
|
+
password_digest: <%= BCrypt::Password.create("password") %>
|
|
106
|
+
account: one
|
|
107
|
+
|
|
108
|
+
# test/fixtures/events.yml
|
|
109
|
+
upcoming:
|
|
110
|
+
name: Upcoming Conference
|
|
111
|
+
event_date: <%= 2.weeks.from_now.to_date %>
|
|
112
|
+
account: one
|
|
113
|
+
|
|
114
|
+
past:
|
|
115
|
+
name: Past Workshop
|
|
116
|
+
event_date: <%= 1.month.ago.to_date %>
|
|
117
|
+
account: one
|
|
118
|
+
|
|
119
|
+
other_account_event:
|
|
120
|
+
name: Other Event
|
|
121
|
+
event_date: <%= 1.week.from_now.to_date %>
|
|
122
|
+
account: two
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Model Tests
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# test/models/event_test.rb
|
|
129
|
+
require "test_helper"
|
|
130
|
+
|
|
131
|
+
class EventTest < ActiveSupport::TestCase
|
|
132
|
+
test "requires name" do
|
|
133
|
+
event = Event.new(name: nil)
|
|
134
|
+
assert_not event.valid?
|
|
135
|
+
assert_includes event.errors[:name], "can't be blank"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
test "requires event_date" do
|
|
139
|
+
event = Event.new(event_date: nil)
|
|
140
|
+
assert_not event.valid?
|
|
141
|
+
assert_includes event.errors[:event_date], "can't be blank"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
test ".upcoming returns only future events" do
|
|
145
|
+
assert_includes Event.upcoming, events(:upcoming)
|
|
146
|
+
assert_not_includes Event.upcoming, events(:past)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
test "#days_until returns days until event" do
|
|
150
|
+
event = Event.new(event_date: 5.days.from_now.to_date)
|
|
151
|
+
assert_equal 5, event.days_until
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Controller / Integration Tests
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
# test/controllers/events_controller_test.rb
|
|
160
|
+
require "test_helper"
|
|
161
|
+
|
|
162
|
+
class EventsControllerTest < ActionDispatch::IntegrationTest
|
|
163
|
+
setup do
|
|
164
|
+
@user = users(:regular)
|
|
165
|
+
@event = events(:upcoming)
|
|
166
|
+
sign_in_as @user
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
test "requires authentication" do
|
|
170
|
+
sign_out
|
|
171
|
+
get events_url
|
|
172
|
+
assert_redirected_to new_session_url
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
test "should get index" do
|
|
176
|
+
get events_url
|
|
177
|
+
assert_response :success
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
test "index only shows own account events" do
|
|
181
|
+
get events_url
|
|
182
|
+
assert_no_match events(:other_account_event).name, response.body
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
test "should create event" do
|
|
186
|
+
assert_difference("Event.count") do
|
|
187
|
+
post events_url, params: {
|
|
188
|
+
event: { name: "New Event", event_date: 1.week.from_now.to_date }
|
|
189
|
+
}
|
|
190
|
+
end
|
|
191
|
+
assert_redirected_to event_url(Event.last)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
test "renders errors for invalid event" do
|
|
195
|
+
post events_url, params: { event: { name: "" } }
|
|
196
|
+
assert_response :unprocessable_entity
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
test "should update event" do
|
|
200
|
+
patch event_url(@event), params: { event: { name: "Updated" } }
|
|
201
|
+
assert_redirected_to event_url(@event)
|
|
202
|
+
assert_equal "Updated", @event.reload.name
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
test "should destroy event" do
|
|
206
|
+
assert_difference("Event.count", -1) do
|
|
207
|
+
delete event_url(@event)
|
|
208
|
+
end
|
|
209
|
+
assert_redirected_to events_url
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## System Tests
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
# test/system/events_test.rb
|
|
218
|
+
require "application_system_test_case"
|
|
219
|
+
|
|
220
|
+
class EventsTest < ApplicationSystemTestCase
|
|
221
|
+
setup do
|
|
222
|
+
sign_in_as users(:regular)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
test "creating an event" do
|
|
226
|
+
visit new_event_url
|
|
227
|
+
fill_in "Name", with: "Team Offsite"
|
|
228
|
+
fill_in "Event date", with: 1.month.from_now.to_date
|
|
229
|
+
click_button "Create Event"
|
|
230
|
+
|
|
231
|
+
assert_text "Event was successfully created"
|
|
232
|
+
assert_text "Team Offsite"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
test "shows validation errors" do
|
|
236
|
+
visit new_event_url
|
|
237
|
+
click_button "Create Event"
|
|
238
|
+
assert_text "can't be blank"
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Service Tests
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# test/services/orders/create_service_test.rb
|
|
247
|
+
require "test_helper"
|
|
248
|
+
|
|
249
|
+
class Orders::CreateServiceTest < ActiveSupport::TestCase
|
|
250
|
+
setup do
|
|
251
|
+
@user = users(:regular)
|
|
252
|
+
@product = products(:widget)
|
|
253
|
+
@service = Orders::CreateService.new
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
test "returns success with valid params" do
|
|
257
|
+
result = @service.call(user: @user, items: [{ product_id: @product.id, quantity: 2 }])
|
|
258
|
+
assert result.success?
|
|
259
|
+
assert_kind_of Order, result.data
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
test "creates order and line items" do
|
|
263
|
+
assert_difference ["Order.count", "LineItem.count"], 1 do
|
|
264
|
+
@service.call(user: @user, items: [{ product_id: @product.id, quantity: 1 }])
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
test "returns failure with empty items" do
|
|
269
|
+
result = @service.call(user: @user, items: [])
|
|
270
|
+
assert result.failure?
|
|
271
|
+
assert_equal :empty_cart, result.code
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
test "rolls back transaction on error" do
|
|
275
|
+
assert_no_difference "Order.count" do
|
|
276
|
+
@service.call(user: @user, items: [{ product_id: 0, quantity: 1 }])
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Query, Presenter, and Component Tests
|
|
283
|
+
|
|
284
|
+
```ruby
|
|
285
|
+
# test/queries/active_events_query_test.rb
|
|
286
|
+
require "test_helper"
|
|
287
|
+
|
|
288
|
+
class ActiveEventsQueryTest < ActiveSupport::TestCase
|
|
289
|
+
setup do
|
|
290
|
+
@query = ActiveEventsQuery.new(account: accounts(:one))
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
test "returns active events for account" do
|
|
294
|
+
assert_includes @query.call, events(:upcoming)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
test "excludes other account events" do
|
|
298
|
+
assert_not_includes @query.call, events(:other_account_event)
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# test/presenters/event_presenter_test.rb
|
|
303
|
+
require "test_helper"
|
|
304
|
+
|
|
305
|
+
class EventPresenterTest < ActiveSupport::TestCase
|
|
306
|
+
test "#status_badge returns HTML-safe string" do
|
|
307
|
+
presenter = EventPresenter.new(events(:upcoming))
|
|
308
|
+
assert_predicate presenter.status_badge, :html_safe?
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
test "#formatted_date with nil date returns TBD" do
|
|
312
|
+
presenter = EventPresenter.new(Event.new(event_date: nil))
|
|
313
|
+
assert_match "TBD", presenter.formatted_date
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# test/components/event_card_component_test.rb
|
|
318
|
+
require "test_helper"
|
|
319
|
+
|
|
320
|
+
class EventCardComponentTest < ViewComponent::TestCase
|
|
321
|
+
test "renders event name" do
|
|
322
|
+
event = events(:upcoming)
|
|
323
|
+
render_inline(EventCardComponent.new(event: event))
|
|
324
|
+
assert_text event.name
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
test "renders status badge" do
|
|
328
|
+
render_inline(EventCardComponent.new(event: events(:upcoming)))
|
|
329
|
+
assert_selector ".badge"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Policy Tests
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
# test/policies/event_policy_test.rb
|
|
338
|
+
require "test_helper"
|
|
339
|
+
|
|
340
|
+
class EventPolicyTest < ActiveSupport::TestCase
|
|
341
|
+
setup do
|
|
342
|
+
@owner = users(:regular)
|
|
343
|
+
@other = users(:other_account)
|
|
344
|
+
@event = events(:upcoming)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
test "owner can show" do
|
|
348
|
+
assert EventPolicy.new(@owner, @event).show?
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
test "non-owner cannot show" do
|
|
352
|
+
assert_not EventPolicy.new(@other, @event).show?
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
test "scope returns only own account events" do
|
|
356
|
+
scope = EventPolicy::Scope.new(@owner, Event).resolve
|
|
357
|
+
assert_includes scope, @event
|
|
358
|
+
assert_not_includes scope, events(:other_account_event)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Job and Mailer Tests
|
|
364
|
+
|
|
365
|
+
```ruby
|
|
366
|
+
# test/jobs/fulfill_order_job_test.rb
|
|
367
|
+
require "test_helper"
|
|
368
|
+
|
|
369
|
+
class FulfillOrderJobTest < ActiveJob::TestCase
|
|
370
|
+
test "fulfills the order" do
|
|
371
|
+
order = orders(:pending)
|
|
372
|
+
FulfillOrderJob.perform_now(order.id)
|
|
373
|
+
assert_not_nil order.reload.fulfilled_at
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# test/mailers/user_mailer_test.rb
|
|
378
|
+
require "test_helper"
|
|
379
|
+
|
|
380
|
+
class UserMailerTest < ActionMailer::TestCase
|
|
381
|
+
test "welcome email" do
|
|
382
|
+
user = users(:regular)
|
|
383
|
+
email = UserMailer.welcome(user)
|
|
384
|
+
|
|
385
|
+
assert_emails 1 do
|
|
386
|
+
email.deliver_now
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
assert_equal [user.email_address], email.to
|
|
390
|
+
assert_match "Welcome", email.subject
|
|
391
|
+
assert_match user.name, email.body.encoded
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Performance Tips
|
|
397
|
+
|
|
398
|
+
| Test Type | Speed | Write First? |
|
|
399
|
+
|-----------|-------|--------------|
|
|
400
|
+
| Model / Service / Query / Policy | Fast | Yes |
|
|
401
|
+
| Component / Controller | Medium | For key features |
|
|
402
|
+
| System | Slow | Critical paths only |
|
|
403
|
+
|
|
404
|
+
- Prefer model/service tests over system tests (10-100x faster)
|
|
405
|
+
- Use `parallelize(workers: :number_of_processors)` for multi-core
|
|
406
|
+
- Use `assert_no_difference` instead of checking count before/after
|
|
407
|
+
- Avoid `sleep` in tests; use Capybara's built-in waiting
|
|
408
|
+
|
|
409
|
+
## Running Tests
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
bin/rails test # All tests
|
|
413
|
+
bin/rails test test/models/event_test.rb # Single file
|
|
414
|
+
bin/rails test test/models/event_test.rb:15 # Single test by line
|
|
415
|
+
bin/rails test -n "test_requires_name" # By name
|
|
416
|
+
bin/rails test --verbose # Verbose output
|
|
417
|
+
bin/rails test:system # System tests only
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Test Generation Checklist
|
|
421
|
+
|
|
422
|
+
- [ ] Model tests: validations, scopes, associations, methods
|
|
423
|
+
- [ ] Service tests: success path, failure path, edge cases
|
|
424
|
+
- [ ] Controller tests: auth, CRUD actions, error responses
|
|
425
|
+
- [ ] Policy tests: all actions, scope filtering
|
|
426
|
+
- [ ] Fixtures with meaningful names and minimal data
|
|
427
|
+
- [ ] System tests for 1-2 critical user paths
|
|
428
|
+
- [ ] Component tests if ViewComponents used
|
|
429
|
+
- [ ] Job tests for background processing
|
|
430
|
+
- [ ] Mailer tests for email content
|
|
431
|
+
|
|
432
|
+
## Anti-Patterns
|
|
433
|
+
|
|
434
|
+
| Anti-Pattern | Problem | Solution |
|
|
435
|
+
|--------------|---------|----------|
|
|
436
|
+
| Using FactoryBot / RSpec | Not our convention | Use Minitest + fixtures |
|
|
437
|
+
| Testing implementation | Brittle, breaks on refactor | Test behavior and outcomes |
|
|
438
|
+
| Mystery guest | Unclear fixture references | Use descriptive fixture names |
|
|
439
|
+
| Too many system tests | Slow test suite | Prefer unit tests |
|
|
440
|
+
| No assertions | Test passes but verifies nothing | Every test needs an assertion |
|
|
441
|
+
| Hardcoded IDs | Breaks when fixtures change | Reference fixtures by name |
|