rails_claude_skills 0.1.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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
  5. data/.github/ISSUE_TEMPLATE/question.yml +90 -0
  6. data/.github/dependabot.yml +19 -0
  7. data/.github/workflows/ci.yml +77 -0
  8. data/.github/workflows/release.yml +66 -0
  9. data/.rubocop.yml +52 -0
  10. data/CHANGELOG.md +94 -0
  11. data/CLAUDE.md +332 -0
  12. data/CODE_OF_CONDUCT.md +134 -0
  13. data/CONTRIBUTING.md +580 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +544 -0
  16. data/Rakefile +8 -0
  17. data/lib/generators/claude/agent/agent_generator.rb +71 -0
  18. data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
  19. data/lib/generators/claude/command/command_generator.rb +50 -0
  20. data/lib/generators/claude/command/templates/command.md.tt +28 -0
  21. data/lib/generators/claude/commands_library/create-pr.md +27 -0
  22. data/lib/generators/claude/commands_library/dbchange.md +19 -0
  23. data/lib/generators/claude/commands_library/quality.md +20 -0
  24. data/lib/generators/claude/commands_library/stimulus.md +19 -0
  25. data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
  26. data/lib/generators/claude/install/install_generator.rb +211 -0
  27. data/lib/generators/claude/install/templates/README.md.tt +59 -0
  28. data/lib/generators/claude/install/templates/USAGE +28 -0
  29. data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
  30. data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
  31. data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
  32. data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
  33. data/lib/generators/claude/rule/rule_generator.rb +175 -0
  34. data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
  35. data/lib/generators/claude/rules_library/code-style.md +37 -0
  36. data/lib/generators/claude/rules_library/database.md +47 -0
  37. data/lib/generators/claude/rules_library/hotwire.md +56 -0
  38. data/lib/generators/claude/rules_library/security.md +54 -0
  39. data/lib/generators/claude/rules_library/testing.md +47 -0
  40. data/lib/generators/claude/skill/skill_generator.rb +196 -0
  41. data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
  42. data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
  43. data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
  44. data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
  45. data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
  46. data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
  47. data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
  48. data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
  49. data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
  50. data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
  51. data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
  52. data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
  53. data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
  54. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
  55. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
  56. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
  57. data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
  58. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
  59. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
  60. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
  61. data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
  62. data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
  63. data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
  64. data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
  65. data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
  66. data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
  67. data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
  68. data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
  69. data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
  70. data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
  71. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
  72. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
  73. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
  74. data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
  75. data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
  76. data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
  77. data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
  78. data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
  79. data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
  80. data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
  81. data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
  82. data/lib/generators/claude/views/views_generator.rb +113 -0
  83. data/lib/rails_claude_skills/railtie.rb +16 -0
  84. data/lib/rails_claude_skills/version.rb +5 -0
  85. data/lib/rails_claude_skills.rb +27 -0
  86. data/sig/rails_claude_skills.rbs +4 -0
  87. metadata +199 -0
@@ -0,0 +1,273 @@
1
+ # Better Specs - RSpec Best Practices
2
+
3
+ ## Describe Blocks
4
+
5
+ Use Ruby documentation conventions when naming describe blocks:
6
+ - `.method_name` for class methods
7
+ - `#method_name` for instance methods
8
+
9
+ **Example:**
10
+ ```ruby
11
+ describe '.authenticate' do
12
+ # tests for class method
13
+ end
14
+
15
+ describe '#admin?' do
16
+ # tests for instance method
17
+ end
18
+ ```
19
+
20
+ ## Context Blocks
21
+
22
+ Organize tests with contexts using descriptive language:
23
+ - Start descriptions with "when," "with," or "without"
24
+ - Groups related behaviors and improves readability
25
+
26
+ **Example:**
27
+ ```ruby
28
+ context 'when logged in' do
29
+ it { is_expected.to respond_with 200 }
30
+ end
31
+
32
+ context 'with valid parameters' do
33
+ # tests for valid scenarios
34
+ end
35
+
36
+ context 'without authentication' do
37
+ # tests for unauthorized scenarios
38
+ end
39
+ ```
40
+
41
+ ## It Blocks
42
+
43
+ Keep test descriptions concise—ideally under 40 characters. Split longer descriptions into contexts instead.
44
+
45
+ Use third-person present tense without "should":
46
+
47
+ **Good:**
48
+ ```ruby
49
+ it 'does not change timings' do
50
+ it 'creates a new project' do
51
+ it 'redirects to the dashboard' do
52
+ ```
53
+
54
+ **Bad:**
55
+ ```ruby
56
+ it 'should not change timings' do
57
+ it 'should create a new project' do
58
+ ```
59
+
60
+ ## Single Expectations
61
+
62
+ Isolated unit tests should contain one expectation per test. This makes tests:
63
+ - Easier to understand
64
+ - Easier to debug when they fail
65
+ - More maintainable
66
+
67
+ For slower, non-isolated tests (database, external services), multiple expectations are acceptable for performance reasons.
68
+
69
+ **Good (unit test):**
70
+ ```ruby
71
+ it 'validates presence of name' do
72
+ project = Project.new(name: nil)
73
+ expect(project).not_to be_valid
74
+ end
75
+
76
+ it 'adds error message for missing name' do
77
+ project = Project.new(name: nil)
78
+ project.valid?
79
+ expect(project.errors[:name]).to include("can't be blank")
80
+ end
81
+ ```
82
+
83
+ **Acceptable (integration/system test):**
84
+ ```ruby
85
+ it 'creates a project and redirects' do
86
+ expect do
87
+ post :create, params: {project: valid_attributes}
88
+ end.to change(Project, :count).by(1)
89
+
90
+ expect(response).to redirect_to(Project.last)
91
+ expect(flash[:notice]).to eq('Project created successfully')
92
+ end
93
+ ```
94
+
95
+ ## Test All Cases
96
+
97
+ Cover valid, edge, and invalid scenarios. Test "all the possible inputs."
98
+
99
+ **Example:**
100
+ ```ruby
101
+ describe 'validations' do
102
+ it 'validates presence of name'
103
+ it 'validates length of name'
104
+ it 'validates uniqueness of name'
105
+ it 'allows valid names'
106
+ end
107
+ ```
108
+
109
+ ## Expect vs Should Syntax
110
+
111
+ Always use `expect()` syntax on new projects (not `should`):
112
+
113
+ **Good:**
114
+ ```ruby
115
+ expect(response).to respond_with_content_type(:json)
116
+ expect(user).to be_valid
117
+ ```
118
+
119
+ **Bad (deprecated):**
120
+ ```ruby
121
+ response.should respond_with_content_type(:json)
122
+ user.should be_valid
123
+ ```
124
+
125
+ For one-line expectations, use `is_expected.to`:
126
+
127
+ **Good:**
128
+ ```ruby
129
+ it { is_expected.to be_valid }
130
+ it { is_expected.to respond_with 422 }
131
+ ```
132
+
133
+ ## Subject Usage
134
+
135
+ Use `subject {}` to DRY up multiple related tests:
136
+
137
+ **Good:**
138
+ ```ruby
139
+ subject { assigns('message') }
140
+
141
+ it { is_expected.to match /pattern/ }
142
+ it { is_expected.to be_present }
143
+ ```
144
+
145
+ **When not to use subject:**
146
+ - Avoid using `subject` explicitly inside `it` blocks
147
+ - If you need to name it, use `let` instead
148
+
149
+ ## Let vs Before
150
+
151
+ Prefer `let` over `before` blocks for variable assignment. Variables defined with `let`:
152
+ - Are lazy loaded (only evaluated when referenced)
153
+ - Are cached during each test
154
+ - Make dependencies explicit
155
+
156
+ Use `let!` when you need immediate evaluation (before the test runs).
157
+
158
+ **Good:**
159
+ ```ruby
160
+ let(:resource) { create :device }
161
+ let(:user) { create :user }
162
+ ```
163
+
164
+ **When to use before:**
165
+ - Setting up global test state
166
+ - Configuring mocks/stubs
167
+ - Database cleanup
168
+
169
+ **Example:**
170
+ ```ruby
171
+ before do
172
+ # Freeze time for consistent test results
173
+ freeze_time
174
+ end
175
+ ```
176
+
177
+ ## Mocking Strategy
178
+
179
+ "Do not (over)use mocks and test real behavior when possible."
180
+
181
+ Test actual application flow rather than stubbed interactions when feasible. Mocks are useful for:
182
+ - External services
183
+ - Slow operations
184
+ - Testing error conditions
185
+
186
+ But prefer real objects for:
187
+ - Simple collaborators
188
+ - Fast operations
189
+ - Core business logic
190
+
191
+ ## Data Creation
192
+
193
+ Create only necessary test data. Use `create_list` sparingly.
194
+
195
+ **Good:**
196
+ ```ruby
197
+ let(:project) { create(:project) }
198
+ ```
199
+
200
+ **Avoid:**
201
+ ```ruby
202
+ let(:projects) { create_list(:project, 50) } # Usually unnecessary
203
+ ```
204
+
205
+ ## Factories Over Fixtures
206
+
207
+ Use FactoryBot instead of fixtures. Factories:
208
+ - Are easier to understand and maintain
209
+ - Reduce coupling between tests
210
+ - Make test data explicit
211
+ - Are easier to modify
212
+
213
+ **Example:**
214
+ ```ruby
215
+ # spec/factories/projects.rb
216
+ FactoryBot.define do
217
+ factory :project do
218
+ name { "Heart Rate Monitor" }
219
+ device_description { "A medical device..." }
220
+
221
+ trait :class_ii do
222
+ fda_class { :class_ii_confirmed }
223
+ end
224
+ end
225
+ end
226
+ ```
227
+
228
+ ## Shared Examples
229
+
230
+ Eliminate test duplication using shared examples, particularly for controller tests:
231
+
232
+ **Definition:**
233
+ ```ruby
234
+ RSpec.shared_examples 'a listable resource' do
235
+ it 'returns success' do
236
+ expect(response).to have_http_status(:success)
237
+ end
238
+
239
+ it 'assigns resources' do
240
+ expect(assigns(:resources)).to be_present
241
+ end
242
+ end
243
+ ```
244
+
245
+ **Usage:**
246
+ ```ruby
247
+ describe 'GET #index' do
248
+ it_behaves_like 'a listable resource'
249
+ it_behaves_like 'a paginable resource'
250
+ end
251
+ ```
252
+
253
+ ## Integration Testing
254
+
255
+ Focus on integration and model tests rather than controller tests. "Test what you see" using Capybara and RSpec.
256
+
257
+ Integration tests:
258
+ - Cover all use cases
259
+ - Run fast with proper setup
260
+ - Test actual user flows
261
+ - Catch more real bugs
262
+
263
+ ## HTTP Stubbing
264
+
265
+ Stub external API calls using WebMock or VCR rather than relying on real services.
266
+
267
+ **Example:**
268
+ ```ruby
269
+ before do
270
+ stub_request(:get, "https://api.example.com/data")
271
+ .to_return(status: 200, body: '{"status":"ok"}')
272
+ end
273
+ ```
@@ -0,0 +1,407 @@
1
+ # Thoughtbot RSpec Patterns
2
+
3
+ ## Syntax & Expectations
4
+
5
+ ### Use Modern RSpec Syntax
6
+ - Use RSpec's `expect` syntax (not `should`)
7
+ - Use RSpec's `allow` syntax for method stubs (not `stub`)
8
+ - Prefer `eq` over `==` in RSpec assertions
9
+ - Use `not_to` instead of `to_not` in expectations
10
+
11
+ **Examples:**
12
+ ```ruby
13
+ # Good
14
+ expect(user.name).to eq('John')
15
+ expect(response).not_to be_nil
16
+ allow(service).to receive(:call).and_return(result)
17
+
18
+ # Bad
19
+ user.name.should == 'John'
20
+ response.should_not be_nil
21
+ service.stub(:call).and_return(result)
22
+ ```
23
+
24
+ ### Capybara Matchers
25
+ Prefer the `have_css` matcher to the `have_selector` matcher in Capybara assertions:
26
+
27
+ ```ruby
28
+ # Good
29
+ expect(page).to have_css('.success-message')
30
+
31
+ # Less preferred
32
+ expect(page).to have_selector('.success-message')
33
+ ```
34
+
35
+ ## Test Structure
36
+
37
+ ### Separate Test Phases
38
+ Separate setup, exercise, verification, and teardown phases with newlines:
39
+
40
+ ```ruby
41
+ it 'creates a new project' do
42
+ # Setup
43
+ user = create(:user)
44
+ attributes = {name: 'Test Project'}
45
+
46
+ # Exercise
47
+ project = Project.create(attributes)
48
+
49
+ # Verification
50
+ expect(project).to be_persisted
51
+ expect(project.name).to eq('Test Project')
52
+ end
53
+ ```
54
+
55
+ ### Single Level of Abstraction
56
+ Use a single level of abstraction within `it` examples:
57
+
58
+ ```ruby
59
+ # Good
60
+ it 'notifies the user' do
61
+ perform_action
62
+ expect_notification_sent
63
+ end
64
+
65
+ # Bad - mixing abstraction levels
66
+ it 'notifies the user' do
67
+ click_button 'Submit'
68
+ expect(ActionMailer::Base.deliveries.last.to).to eq([user.email])
69
+ end
70
+ ```
71
+
72
+ ### One Test Per Execution Path
73
+ Use an `it` example or test method for each execution path through the method.
74
+
75
+ ## What to Avoid
76
+
77
+ ### Don't Test Private Methods
78
+ - Never use the `private` keyword in specs
79
+ - Don't test private methods
80
+ - Test public interface and let private methods be covered indirectly
81
+
82
+ ### Avoid Let and Let!
83
+ Extract helper methods instead:
84
+
85
+ ```ruby
86
+ # Good
87
+ def create_authenticated_user
88
+ user = create(:user)
89
+ sign_in(user)
90
+ user
91
+ end
92
+
93
+ it 'shows dashboard' do
94
+ user = create_authenticated_user
95
+ visit dashboard_path
96
+ expect(page).to have_content(user.name)
97
+ end
98
+
99
+ # Avoid
100
+ let!(:user) { create(:user) }
101
+ before { sign_in(user) }
102
+
103
+ it 'shows dashboard' do
104
+ visit dashboard_path
105
+ expect(page).to have_content(user.name)
106
+ end
107
+ ```
108
+
109
+ ### Avoid Subject
110
+ Avoid using `subject` explicitly inside of an RSpec `it` block:
111
+
112
+ ```ruby
113
+ # Good
114
+ subject { user.name }
115
+ it { is_expected.to eq('John') }
116
+
117
+ # Avoid
118
+ it 'has correct name' do
119
+ expect(subject).to eq('John')
120
+ end
121
+ ```
122
+
123
+ ### Avoid Instance Variables
124
+ Don't use instance variables in tests:
125
+
126
+ ```ruby
127
+ # Good
128
+ let(:user) { create(:user) }
129
+
130
+ # Avoid
131
+ before { @user = create(:user) }
132
+ ```
133
+
134
+ ### Avoid Other Constructs
135
+ - Avoid `its`, `specify`, and `before` in RSpec (prefer explicit tests)
136
+ - Avoid `any_instance` in rspec-mocks and mocha; prefer dependency injection
137
+
138
+ ### Skip Boolean Equality Checks
139
+ Use predicate methods and matchers instead:
140
+
141
+ ```ruby
142
+ # Good
143
+ expect(user).to be_valid
144
+ expect(project).to be_persisted
145
+
146
+ # Avoid
147
+ expect(user.valid?).to eq(true)
148
+ expect(project.persisted?).to be_truthy
149
+ ```
150
+
151
+ ## Mocking & Stubbing
152
+
153
+ ### Use Stubs and Spies, Not Mocks
154
+ - Use stubs and spies (not mocks) in isolated tests
155
+ - Use assertions about state for incoming messages
156
+ - Use stubs and spies to assert you sent outgoing messages
157
+
158
+ **Example:**
159
+ ```ruby
160
+ # Good - stub
161
+ allow(service).to receive(:call).and_return(result)
162
+
163
+ # Good - spy
164
+ service = spy('service')
165
+ controller.notify(service)
166
+ expect(service).to have_received(:call)
167
+ ```
168
+
169
+ ### Disable Real HTTP Requests
170
+ Use `WebMock.disable_net_connect!` to prevent real HTTP requests to external services.
171
+
172
+ Use a Fake to stub requests to external services:
173
+
174
+ ```ruby
175
+ class FakeGitHubAPI
176
+ def initialize(stubs = {})
177
+ @stubs = stubs
178
+ end
179
+
180
+ def get_user(username)
181
+ @stubs.fetch(username) { default_user }
182
+ end
183
+
184
+ private
185
+
186
+ def default_user
187
+ {name: 'Test User', email: 'test@example.com'}
188
+ end
189
+ end
190
+ ```
191
+
192
+ ## Acceptance/System Tests
193
+
194
+ ### Use Specific Selectors
195
+ - Use the most specific selectors available
196
+ - Don't locate elements with CSS selectors or `[id]` attributes
197
+ - Use accessible names and descriptions to locate elements
198
+ - Interact with form controls, buttons, and links by accessible names
199
+
200
+ **Good:**
201
+ ```ruby
202
+ click_button 'Create Project'
203
+ fill_in 'Project Name', with: 'Test Device'
204
+ click_link 'Settings'
205
+ ```
206
+
207
+ **Avoid:**
208
+ ```ruby
209
+ find('#create-project-btn').click
210
+ find('.project-name-input').set('Test Device')
211
+ find('a[href="/settings"]').click
212
+ ```
213
+
214
+ ### Don't Assert on Classes or Data Attributes
215
+ - Don't assert an element's state with `[class]` or `[data-*]` attributes
216
+ - Use WAI-ARIA States and Properties when asserting an element's state
217
+ - Prefer implicit semantics and built-in attributes over WAI-ARIA
218
+
219
+ **Good:**
220
+ ```ruby
221
+ expect(page).to have_css('button[disabled]')
222
+ expect(page).to have_css('[aria-hidden="false"]')
223
+ expect(page).to have_content('Success message')
224
+ ```
225
+
226
+ **Avoid:**
227
+ ```ruby
228
+ expect(page).to have_css('.opacity-100')
229
+ expect(page).to have_css('.bg-red-500')
230
+ expect(page).to have_css('[data-visible="true"]')
231
+ ```
232
+
233
+ ### Avoid Meaningless Descriptions
234
+ Avoid `it` block descriptions that add no information:
235
+
236
+ ```ruby
237
+ # Avoid
238
+ it 'successfully creates project' do
239
+
240
+ # Good
241
+ it 'creates project and redirects to project page' do
242
+ ```
243
+
244
+ Avoid repetitive descriptions between `describe` and `it` blocks:
245
+
246
+ ```ruby
247
+ # Avoid
248
+ describe 'creating a project' do
249
+ it 'creates a project' do
250
+
251
+ # Good
252
+ describe 'project creation' do
253
+ it 'redirects to the new project' do
254
+ ```
255
+
256
+ ### System Spec Organization
257
+ - Use file names like `user_changes_password_spec.rb` (role_action format)
258
+ - Store system specs in `spec/system` directory
259
+ - Place helper methods in a top-level `System` module
260
+ - Use only one `describe` block per system spec file
261
+
262
+ **Example:**
263
+ ```ruby
264
+ # spec/system/user_creates_project_spec.rb
265
+ require 'rails_helper'
266
+
267
+ RSpec.describe 'User creates project' do
268
+ it 'creates a new project' do
269
+ # test implementation
270
+ end
271
+ end
272
+ ```
273
+
274
+ ## Unit Tests
275
+
276
+ ### Imperative Descriptions
277
+ Don't prefix descriptions with "should"; use imperative mood:
278
+
279
+ ```ruby
280
+ # Good
281
+ it 'validates presence of name' do
282
+
283
+ # Bad
284
+ it 'should validate presence of name' do
285
+ ```
286
+
287
+ ### Use Subject Blocks
288
+ Use `subject` blocks to define objects for use in one-line specs:
289
+
290
+ ```ruby
291
+ subject { Project.new(name: 'Test') }
292
+
293
+ it { is_expected.to be_valid }
294
+ ```
295
+
296
+ ### Method Documentation Conventions
297
+ - Use `.method` to describe class methods
298
+ - Use `#method` to describe instance methods
299
+
300
+ ```ruby
301
+ describe '.find_by_name' do
302
+ # class method tests
303
+ end
304
+
305
+ describe '#save' do
306
+ # instance method tests
307
+ end
308
+ ```
309
+
310
+ ### Context for Preconditions
311
+ Use `context` to describe testing preconditions:
312
+
313
+ ```ruby
314
+ context 'when user is admin' do
315
+ # tests for admin users
316
+ end
317
+
318
+ context 'with valid parameters' do
319
+ # tests for valid scenarios
320
+ end
321
+ ```
322
+
323
+ ### Test Organization
324
+ - Group tests by method using `describe '#method_name'`
325
+ - Maintain single, top-level `describe ClassName` block
326
+ - Order tests matching class definition: validations, associations, methods
327
+
328
+ **Example:**
329
+ ```ruby
330
+ RSpec.describe Project do
331
+ describe 'validations' do
332
+ # validation tests
333
+ end
334
+
335
+ describe 'associations' do
336
+ # association tests
337
+ end
338
+
339
+ describe '#save' do
340
+ # instance method tests
341
+ end
342
+
343
+ describe '.find_active' do
344
+ # class method tests
345
+ end
346
+ end
347
+ ```
348
+
349
+ ## Factories
350
+
351
+ ### Factory Organization
352
+ Organize `factories.rb`:
353
+ 1. Sequences
354
+ 2. Traits
355
+ 3. Factory definitions
356
+
357
+ Order factory attributes:
358
+ 1. Implicit associations first
359
+ 2. Explicit attributes
360
+ 3. Child factories (alphabetical within sections)
361
+
362
+ Sort factory definitions alphabetically.
363
+
364
+ **Example:**
365
+ ```ruby
366
+ FactoryBot.define do
367
+ # Sequences
368
+ sequence :email do |n|
369
+ "user-#{n}@example.com"
370
+ end
371
+
372
+ # Factories (alphabetically)
373
+ factory :project do
374
+ # Associations (implicit)
375
+ tenant
376
+ created_by factory: %i[user]
377
+
378
+ # Attributes (alphabetical)
379
+ device_description { "A medical device..." }
380
+ fda_class { :class_ii_assumed }
381
+ name { "Heart Rate Monitor" }
382
+ software_safety_class { :to_be_determined }
383
+
384
+ # Traits (alphabetically)
385
+ trait :class_ii do
386
+ fda_class { :class_ii_confirmed }
387
+ end
388
+
389
+ trait :with_github_repo do
390
+ github_repo_owner { "organization" }
391
+ github_repo_name { "awesome-repo" }
392
+ end
393
+ end
394
+ end
395
+ ```
396
+
397
+ ## Integration Testing
398
+
399
+ ### Test the Entire App
400
+ Use integration tests to execute the entire app stack, including:
401
+ - Database operations
402
+ - Background jobs
403
+ - External service interactions (stubbed)
404
+ - Full request/response cycle
405
+
406
+ ### Background Jobs
407
+ Test background jobs with appropriate matchers for your job processor (Sidekiq, DelayedJob, etc.).