ace-test 0.6.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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.ace-defaults/nav/protocols/agent-sources/ace-test.yml +19 -0
  3. data/.ace-defaults/nav/protocols/guide-sources/ace-test.yml +19 -0
  4. data/.ace-defaults/nav/protocols/tmpl-sources/ace-test.yml +11 -0
  5. data/.ace-defaults/nav/protocols/wfi-sources/ace-test.yml +19 -0
  6. data/CHANGELOG.md +169 -0
  7. data/LICENSE +21 -0
  8. data/README.md +40 -0
  9. data/Rakefile +12 -0
  10. data/handbook/agents/mock.ag.md +164 -0
  11. data/handbook/agents/profile-tests.ag.md +132 -0
  12. data/handbook/agents/test.ag.md +99 -0
  13. data/handbook/guides/SUMMARY.md +95 -0
  14. data/handbook/guides/embedded-testing-guide.g.md +261 -0
  15. data/handbook/guides/mocking-patterns.g.md +464 -0
  16. data/handbook/guides/quick-reference.g.md +46 -0
  17. data/handbook/guides/test-driven-development-cycle/meta-documentation.md +26 -0
  18. data/handbook/guides/test-driven-development-cycle/ruby-application.md +18 -0
  19. data/handbook/guides/test-driven-development-cycle/ruby-gem.md +19 -0
  20. data/handbook/guides/test-driven-development-cycle/rust-cli.md +18 -0
  21. data/handbook/guides/test-driven-development-cycle/rust-wasm-zed.md +19 -0
  22. data/handbook/guides/test-driven-development-cycle/typescript-nuxt.md +18 -0
  23. data/handbook/guides/test-driven-development-cycle/typescript-vue.md +19 -0
  24. data/handbook/guides/test-layer-decision.g.md +261 -0
  25. data/handbook/guides/test-mocking-patterns.g.md +414 -0
  26. data/handbook/guides/test-organization.g.md +140 -0
  27. data/handbook/guides/test-performance.g.md +353 -0
  28. data/handbook/guides/test-responsibility-map.g.md +220 -0
  29. data/handbook/guides/test-review-checklist.g.md +231 -0
  30. data/handbook/guides/test-suite-health.g.md +337 -0
  31. data/handbook/guides/testable-code-patterns.g.md +315 -0
  32. data/handbook/guides/testing/ruby-rspec-config-examples.md +120 -0
  33. data/handbook/guides/testing/ruby-rspec.md +87 -0
  34. data/handbook/guides/testing/rust.md +52 -0
  35. data/handbook/guides/testing/test-maintenance.md +364 -0
  36. data/handbook/guides/testing/typescript-bun.md +47 -0
  37. data/handbook/guides/testing/vue-firebase-auth.md +546 -0
  38. data/handbook/guides/testing/vue-vitest.md +236 -0
  39. data/handbook/guides/testing-philosophy.g.md +82 -0
  40. data/handbook/guides/testing-strategy.g.md +151 -0
  41. data/handbook/guides/testing-tdd-cycle.g.md +146 -0
  42. data/handbook/guides/testing.g.md +170 -0
  43. data/handbook/skills/as-test-create-cases/SKILL.md +24 -0
  44. data/handbook/skills/as-test-fix/SKILL.md +26 -0
  45. data/handbook/skills/as-test-improve-coverage/SKILL.md +22 -0
  46. data/handbook/skills/as-test-optimize/SKILL.md +34 -0
  47. data/handbook/skills/as-test-performance-audit/SKILL.md +34 -0
  48. data/handbook/skills/as-test-plan/SKILL.md +34 -0
  49. data/handbook/skills/as-test-review/SKILL.md +34 -0
  50. data/handbook/skills/as-test-verify-suite/SKILL.md +45 -0
  51. data/handbook/templates/e2e-sandbox-checklist.template.md +289 -0
  52. data/handbook/templates/test-case.template.md +56 -0
  53. data/handbook/templates/test-performance-audit.template.md +132 -0
  54. data/handbook/templates/test-responsibility-map.template.md +92 -0
  55. data/handbook/templates/test-review-checklist.template.md +163 -0
  56. data/handbook/workflow-instructions/test/analyze-failures.wf.md +120 -0
  57. data/handbook/workflow-instructions/test/create-cases.wf.md +675 -0
  58. data/handbook/workflow-instructions/test/fix.wf.md +120 -0
  59. data/handbook/workflow-instructions/test/improve-coverage.wf.md +370 -0
  60. data/handbook/workflow-instructions/test/optimize.wf.md +368 -0
  61. data/handbook/workflow-instructions/test/performance-audit.wf.md +17 -0
  62. data/handbook/workflow-instructions/test/plan.wf.md +323 -0
  63. data/handbook/workflow-instructions/test/review.wf.md +16 -0
  64. data/handbook/workflow-instructions/test/verify-suite.wf.md +343 -0
  65. data/lib/ace/test/version.rb +7 -0
  66. data/lib/ace/test.rb +10 -0
  67. metadata +152 -0
@@ -0,0 +1,140 @@
1
+ ---
2
+ doc-type: guide
3
+ title: Test Organization
4
+ purpose: Test file organization
5
+ ace-docs:
6
+ last-updated: 2026-01-23
7
+ last-checked: 2026-03-21
8
+ ---
9
+
10
+ # Test Organization
11
+
12
+ ## Flat Directory Structure
13
+
14
+ All ACE gems use a **flat test directory structure** that mirrors the ATOM architecture:
15
+
16
+ ```
17
+ test/
18
+ ├── test_helper.rb
19
+ ├── search_test.rb # Main module test
20
+ ├── atoms/
21
+ │ ├── pattern_analyzer_test.rb
22
+ │ ├── result_parser_test.rb
23
+ │ └── tool_checker_test.rb
24
+ ├── molecules/
25
+ │ ├── preset_manager_test.rb
26
+ │ └── git_scope_filter_test.rb
27
+ ├── organisms/
28
+ │ ├── unified_searcher_test.rb
29
+ │ └── result_formatter_test.rb
30
+ ├── models/
31
+ │ └── search_result_test.rb
32
+ └── integration/
33
+ └── cli_integration_test.rb
34
+ ```
35
+
36
+ ## Key Conventions
37
+
38
+ - **Flat structure**: `test/atoms/`, not `test/ace/search/atoms/`
39
+ - **Suffix naming**: `pattern_analyzer_test.rb`, not `test_pattern_analyzer.rb`
40
+ - **Layer directories match ATOM architecture**: atoms, molecules, organisms
41
+ - **Integration tests in separate `integration/` directory**
42
+
43
+ ## Benefits
44
+
45
+ - Easier to navigate and find tests
46
+ - Matches layer boundaries clearly
47
+ - Consistent across all ACE gems
48
+ - Less nesting = simpler paths
49
+
50
+ See `ace-taskflow/test/` for reference implementation.
51
+
52
+ ## Naming Conventions
53
+
54
+ ### Test Files
55
+
56
+ - Use `*_test.rb` suffix (Minitest convention)
57
+ - Name matches the class being tested: `PatternAnalyzer` → `pattern_analyzer_test.rb`
58
+ - One test file per class/module
59
+
60
+ ### Test Methods
61
+
62
+ - Use `test_` prefix: `def test_finds_patterns_in_code`
63
+ - Be descriptive: `test_returns_empty_when_no_matches` not `test_empty`
64
+ - Include the scenario: `test_raises_error_on_invalid_input`
65
+
66
+ ### Test Classes
67
+
68
+ - Mirror the class hierarchy: `class PatternAnalyzerTest < Minitest::Test`
69
+ - Group related tests with modules if needed
70
+
71
+ ## Test Data
72
+
73
+ ### Fixtures
74
+
75
+ Store test data in `test/fixtures/`:
76
+
77
+ ```
78
+ test/fixtures/
79
+ ├── sample_config.yml
80
+ ├── git_diff_output.txt
81
+ └── api_responses/
82
+ └── github_pr_123.json
83
+ ```
84
+
85
+ ### Creating Fixtures
86
+
87
+ Use `yaml_fixture` helper for YAML fixtures:
88
+
89
+ ```ruby
90
+ def test_loads_config
91
+ config = yaml_fixture("sample_config.yml")
92
+ assert_equal "expected_value", config["key"]
93
+ end
94
+ ```
95
+
96
+ ### Inline Data
97
+
98
+ Prefer inline data for small test cases:
99
+
100
+ ```ruby
101
+ def test_parses_simple_input
102
+ input = "key: value"
103
+ result = Parser.parse(input)
104
+ assert_equal "value", result["key"]
105
+ end
106
+ ```
107
+
108
+ ## Test Helpers
109
+
110
+ ### Location
111
+
112
+ Place shared helpers in `test/test_helper.rb` or a dedicated `test/support/` directory:
113
+
114
+ ```
115
+ test/
116
+ ├── test_helper.rb
117
+ └── support/
118
+ ├── mock_git_repo.rb
119
+ └── api_stubs.rb
120
+ ```
121
+
122
+ ### Including Helpers
123
+
124
+ ```ruby
125
+ # test_helper.rb
126
+ require_relative "support/mock_git_repo"
127
+
128
+ module TestHelpers
129
+ include MockGitRepo
130
+ end
131
+
132
+ class Minitest::Test
133
+ include TestHelpers
134
+ end
135
+ ```
136
+
137
+ ## Related Guides
138
+
139
+ - [Testing Philosophy](guide://testing-philosophy) - Why this structure
140
+ - [Mocking Patterns](guide://mocking-patterns) - Test isolation patterns
@@ -0,0 +1,353 @@
1
+ ---
2
+ doc-type: guide
3
+ title: Test Performance
4
+ purpose: Test performance optimization
5
+ ace-docs:
6
+ last-updated: 2026-01-23
7
+ last-checked: 2026-03-21
8
+ ---
9
+
10
+ # Test Performance
11
+
12
+ ## Performance Targets
13
+
14
+ Define explicit performance expectations for each test layer based on patterns established during optimization of 9 ACE packages (60% average improvement).
15
+
16
+ ### Performance Thresholds by Test Type
17
+
18
+ | Test Layer | Target Time | Hard Limit | Common Issues |
19
+ |------------|-------------|------------|---------------|
20
+ | Unit (atoms) | <10ms | 50ms | Real git ops, subprocess spawns |
21
+ | Unit (molecules) | <50ms | 100ms | Unstubbed dependencies |
22
+ | Unit (organisms) | <100ms | 200ms | Missing composite helpers |
23
+ | Integration | <500ms | 1s | Too many real operations |
24
+ | E2E | <2s | 5s | Should be rare - ONE per file |
25
+
26
+ ### Performance Cost Reference
27
+
28
+ Know the cost of common operations to guide optimization:
29
+
30
+ | Operation | Typical Cost | Notes |
31
+ |-----------|--------------|-------|
32
+ | Real `git init` | ~150-200ms | Use MockGitRepo instead |
33
+ | Real `git commit` | ~50-100ms | Stub in unit tests |
34
+ | Subprocess spawn (`Open3.capture3`) | ~150ms | Stub or use API calls |
35
+ | Sleep in retry tests | 1-2s per sleep | Stub `Kernel.sleep` |
36
+ | Cross-package require (cold) | ~50-100ms | Cache or stub dependencies |
37
+ | `ace-nav` subprocess | ~150-400ms | Use `stub_synthesizer_prompt_path` |
38
+ | Real LLM API call | 1-20s | Use WebMock to stub HTTP |
39
+ | Real GitHub API call | 100-500ms | Use WebMock to stub HTTP |
40
+
41
+ ### When Tests Exceed Targets
42
+
43
+ 1. **Profile first**: Run `ace-test --profile 10` to identify actual bottlenecks
44
+ 2. **Check for zombie mocks**: Stubs that don't match actual code paths (see Zombie Mocks section)
45
+ 3. **Verify stubbing layer**: Stub at the boundary closest to your test subject
46
+ 4. **Consider composite helpers**: Reduce setup overhead with consolidated mock helpers
47
+ 5. **Apply E2E rule**: Keep ONE E2E test per file, convert rest to mocked versions
48
+
49
+ ## Sleep Stubbing for Retry Tests
50
+
51
+ Tests with retry logic often include `sleep` calls that add seconds to test runtime.
52
+
53
+ ### Pattern: Stub Kernel.sleep
54
+
55
+ ```ruby
56
+ def with_stubbed_sleep
57
+ Kernel.stub :sleep, nil do
58
+ yield
59
+ end
60
+ end
61
+
62
+ def test_retry_logic
63
+ with_stubbed_sleep do
64
+ # Retry tests now instant instead of 3+ seconds
65
+ result = RetryableOperation.call(max_retries: 3, delay: 1.0)
66
+ assert result.eventually_succeeded?
67
+ end
68
+ end
69
+ ```
70
+
71
+ ### Alternative: Inject Sleep Dependency
72
+
73
+ ```ruby
74
+ # Production code
75
+ class RetryableOperation
76
+ def initialize(sleeper: Kernel)
77
+ @sleeper = sleeper
78
+ end
79
+
80
+ def call
81
+ attempts = 0
82
+ loop do
83
+ result = try_operation
84
+ return result if result.success?
85
+ attempts += 1
86
+ break if attempts >= max_retries
87
+ @sleeper.sleep(delay)
88
+ end
89
+ end
90
+ end
91
+
92
+ # Test with null sleeper
93
+ def test_retry_without_delay
94
+ null_sleeper = Object.new
95
+ null_sleeper.define_singleton_method(:sleep) { |_| nil }
96
+
97
+ op = RetryableOperation.new(sleeper: null_sleeper)
98
+ result = op.call
99
+ assert result.eventually_succeeded?
100
+ end
101
+ ```
102
+
103
+ ## E2E Test Strategy: Keep ONE Per Integration File
104
+
105
+ Keep exactly ONE E2E test per integration test file that exercises real subprocess calls. Convert all other tests to use mocked versions.
106
+
107
+ ### When to Use Real E2E Tests
108
+
109
+ **Keep as E2E (real subprocess)**:
110
+ - CLI parity validation (CLI vs API produce same result)
111
+ - Critical path smoke tests
112
+ - Tool availability checks (gitleaks, git-filter-repo)
113
+ - One representative test per integration file
114
+
115
+ **Convert to Mocked**:
116
+ - Flag/option permutation tests
117
+ - Error handling tests
118
+ - Edge case tests
119
+ - Performance-critical paths
120
+
121
+ ### Migration Pattern: E2E to Mocked
122
+
123
+ ```ruby
124
+ # BEFORE: E2E test using subprocess (~500ms each, 5 tests = 2.5s)
125
+ def test_cli_with_verbose_flag
126
+ output, status = Open3.capture3(BIN, "analyze", "--verbose")
127
+ assert status.success?
128
+ assert_includes output, "Verbose output"
129
+ end
130
+
131
+ def test_cli_with_quiet_flag
132
+ output, status = Open3.capture3(BIN, "analyze", "--quiet")
133
+ assert status.success?
134
+ refute_includes output, "Debug"
135
+ end
136
+
137
+ # AFTER: Keep ONE E2E, convert rest to API tests (~5ms each)
138
+ def test_cli_parity_with_api # Keep this ONE E2E test
139
+ cli_output, status = Open3.capture3(BIN, "analyze", "file.rb")
140
+ api_result = Ace::MyModule.analyze("file.rb")
141
+ assert status.success?
142
+ assert_equal api_result.output, cli_output.strip
143
+ end
144
+
145
+ def test_verbose_flag # Converted to API test
146
+ result = Ace::MyModule.analyze("file.rb", verbose: true)
147
+ assert result.success?
148
+ assert_includes result.output, "Verbose output"
149
+ end
150
+
151
+ def test_quiet_flag # Converted to API test
152
+ result = Ace::MyModule.analyze("file.rb", quiet: true)
153
+ assert result.success?
154
+ refute_includes result.output, "Debug"
155
+ end
156
+ ```
157
+
158
+ ### Real Example: ace-test-runner Optimization
159
+
160
+ Task 175 reduced ace-test-runner tests from 8.25s to 3.3s (60% reduction) by:
161
+ 1. Keeping ONE E2E test for genuine CLI validation
162
+ 2. Converting 2 redundant E2E tests to use `run_ace_test_with_mock` helper
163
+ 3. Adding TestRunnerMocks infrastructure to ace-support-test-helpers
164
+
165
+ ```ruby
166
+ # Helper for mocked CLI tests
167
+ def run_ace_test_with_mock(args, expected_output: "", expected_status: 0)
168
+ mock_status = Object.new
169
+ mock_status.define_singleton_method(:success?) { expected_status == 0 }
170
+ mock_status.define_singleton_method(:exitstatus) { expected_status }
171
+
172
+ Open3.stub :capture3, [expected_output, "", mock_status] do
173
+ yield
174
+ end
175
+ end
176
+ ```
177
+
178
+ ## Composite Test Helpers
179
+
180
+ Reduce deeply nested stubs by creating composite helpers that combine related mocks.
181
+
182
+ ### The Problem: Deep Nesting
183
+
184
+ ```ruby
185
+ # BAD: 6-7 levels of nesting (hard to read, slow due to setup overhead)
186
+ def test_complex_operation
187
+ mock_config_loader do
188
+ mock_diff_generator do
189
+ mock_diff_filter do
190
+ mock_branch_info do
191
+ mock_pr_fetcher do
192
+ mock_commits_fetcher do
193
+ result = SUT.call
194
+ assert result.success?
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
202
+ ```
203
+
204
+ ### The Solution: Composite Helpers
205
+
206
+ ```ruby
207
+ # GOOD: Single composite helper with keyword options
208
+ def test_complex_operation
209
+ with_mock_repo_load(branch: "feature", task_pattern: "123") do
210
+ result = SUT.call
211
+ assert result.success?
212
+ end
213
+ end
214
+
215
+ # In test_helper.rb - consolidates 6 stubs into one helper
216
+ def with_mock_repo_load(branch: "main", task_pattern: nil, usable: true)
217
+ branch_info = build_mock_branch_info(name: branch, task_pattern: task_pattern)
218
+ mock_config = build_mock_config
219
+ mock_diff = Ace::Git::Models::DiffResult.empty
220
+
221
+ Ace::Config.stub :create, mock_config do
222
+ Ace::Git::Molecules::BranchInfo.stub :fetch, branch_info do
223
+ Ace::Git::Organisms::DiffOrchestrator.stub :generate, mock_diff do
224
+ yield
225
+ end
226
+ end
227
+ end
228
+ end
229
+ ```
230
+
231
+ ### Composite Helper Design Principles
232
+
233
+ 1. **Sensible Defaults**: Most tests need standard values; customize only what matters
234
+ 2. **Keyword Arguments**: Allow targeted overrides without changing unrelated values
235
+ 3. **Clear Naming**: `with_mock_<context>` pattern indicates scope
236
+ 4. **Single Responsibility**: Each helper handles one "thing" completely
237
+
238
+ ### Examples from ACE Packages
239
+
240
+ | Package | Helper | Purpose |
241
+ |---------|--------|---------|
242
+ | ace-git | `with_mock_repo_load` | Combines 6 stubs for RepoStatusLoader |
243
+ | ace-git | `with_mock_diff_orchestrator` | ConfigLoader + DiffGenerator + DiffFilter |
244
+ | ace-git-secrets | `with_rewrite_test_mocks` | gitleaks + rewriter + working directory |
245
+ | ace-taskflow | `with_real_test_project` | ConfigResolver + project setup |
246
+ | ace-docs | `with_empty_git_diff` | Simple DiffOrchestrator stub |
247
+ | ace-review | `stub_synthesizer_prompt_path` | ace-nav subprocess stub |
248
+
249
+ ### Implementation Pattern
250
+
251
+ ```ruby
252
+ # In test_helper.rb
253
+ module CompositeHelpers
254
+ def with_empty_git_diff
255
+ empty_result = Ace::Git::Models::DiffResult.empty
256
+ Ace::Git::Organisms::DiffOrchestrator.stub(:generate, empty_result) do
257
+ yield
258
+ end
259
+ end
260
+
261
+ def with_mock_diff(content:, files: [])
262
+ mock_result = Ace::Git::Models::DiffResult.new(
263
+ content: content,
264
+ stats: { additions: 1, deletions: 0, files: files.size },
265
+ files: files
266
+ )
267
+ Ace::Git::Organisms::DiffOrchestrator.stub(:generate, mock_result) do
268
+ yield
269
+ end
270
+ end
271
+
272
+ def build_mock_status(success: true, exitstatus: 0)
273
+ status = Object.new
274
+ status.define_singleton_method(:success?) { success }
275
+ status.define_singleton_method(:exitstatus) { exitstatus }
276
+ status
277
+ end
278
+ end
279
+
280
+ class MyTestCase < Minitest::Test
281
+ include CompositeHelpers
282
+ end
283
+ ```
284
+
285
+ ## Zombie Mocks Pattern
286
+
287
+ "Zombie Mocks" occur when mocks stub methods that are no longer called by the implementation, but tests continue to pass because the real code path happens to work (slowly or otherwise).
288
+
289
+ ### Symptoms
290
+
291
+ - Tests pass but are unexpectedly slow
292
+ - Mock setup doesn't match actual code implementation
293
+ - Refactored code still uses old mock patterns
294
+
295
+ ### Case Study: ace-docs ChangeDetector
296
+
297
+ **Problem**: Tests stubbed `ChangeDetector.stub :execute_git_command` but the implementation had evolved to use `Ace::Git::Organisms::DiffOrchestrator.generate`. Tests passed but each ran real git operations (~1 second each).
298
+
299
+ ```ruby
300
+ # ZOMBIE MOCK - stubs method no longer in code path
301
+ ChangeDetector.stub :execute_git_command, "" do
302
+ result = ChangeDetector.get_diff_for_documents(docs, since: "HEAD~1")
303
+ end
304
+
305
+ # CORRECT - stubs actual method being called
306
+ mock_result = Ace::Git::Models::DiffResult.empty
307
+ Ace::Git::Organisms::DiffOrchestrator.stub :generate, mock_result do
308
+ result = ChangeDetector.get_diff_for_documents(docs, since: "HEAD~1")
309
+ end
310
+ ```
311
+
312
+ **Detection**: Run `ace-test --profile 10` to find slow unit tests. Tests taking >100ms often indicate zombie mocks.
313
+
314
+ **Result**: Fixing zombie mocks reduced test time from 14s to 1.5s (89% improvement).
315
+
316
+ ### Prevention
317
+
318
+ 1. **Profile regularly**: Add `ace-test --profile 10` to development workflow
319
+ 2. **Review mock targets**: When refactoring, update test mocks to match new code paths
320
+ 3. **Extract helpers**: Create reusable mock helpers (like `with_empty_git_diff`) that are easy to maintain
321
+ 4. **Test the mocks**: Verify mocks are being hit by temporarily breaking them
322
+
323
+ ## When to Investigate Test Performance
324
+
325
+ 1. Run tests with profiling: `ace-test --profile 20`
326
+ 2. Look for patterns in slow tests (similar names, same file)
327
+ 3. Check for:
328
+ - Subprocess spawning
329
+ - Network I/O
330
+ - Disk I/O
331
+ - Sleep statements
332
+ - Large data processing
333
+
334
+ ## Monitoring Test Performance
335
+
336
+ Add to your CI pipeline:
337
+
338
+ ```yaml
339
+ - name: Check test performance
340
+ run: |
341
+ ace-test --profile 20 | tee profile.txt
342
+ # Fail if any test takes >100ms (except integration tests)
343
+ if grep -E "^\s+[0-9]+\.\s+test_(?!integration)" profile.txt | awk '{print $NF}' | grep -E "[0-9]+\.[1-9][0-9][0-9]s"; then
344
+ echo "Tests taking >100ms detected"
345
+ exit 1
346
+ fi
347
+ ```
348
+
349
+ ## Related Guides
350
+
351
+ - [Mocking Patterns](guide://mocking-patterns) - How to stub properly
352
+ - [Testing Philosophy](guide://testing-philosophy) - Why performance matters
353
+ - [Testable Code Patterns](guide://testable-code-patterns) - Designing for fast tests
@@ -0,0 +1,220 @@
1
+ ---
2
+ doc-type: guide
3
+ title: Test Responsibility Map Guide
4
+ purpose: Test responsibility mapping and risk-based coverage
5
+ ace-docs:
6
+ last-updated: 2026-02-22
7
+ last-checked: 2026-03-21
8
+ ---
9
+
10
+ # Test Responsibility Map Guide
11
+
12
+ ## Goal
13
+
14
+ A Test Responsibility Map assigns each behavior to the **lowest test layer** that can prove it. This:
15
+ - Prevents duplicate testing across layers
16
+ - Keeps the fast loop fast
17
+ - Ensures critical workflows get E2E coverage
18
+ - Makes coverage gaps visible
19
+
20
+ ## Core Principle
21
+
22
+ > Each behavior belongs to exactly ONE test layer. Test it at the lowest layer possible, promote only when necessary.
23
+
24
+ ## The Mapping Process
25
+
26
+ ### Step 1: List Behaviors
27
+
28
+ Identify all behaviors from requirements:
29
+
30
+ ```markdown
31
+ ## Behaviors for ConfigParser
32
+
33
+ 1. Parse valid YAML file
34
+ 2. Return defaults for missing keys
35
+ 3. Raise error for malformed YAML
36
+ 4. Handle empty file
37
+ 5. Merge cascading configs
38
+ 6. CLI reports config errors with exit code 1
39
+ ```
40
+
41
+ ### Step 2: Assign Risk Levels
42
+
43
+ | Risk Level | Criteria | Testing Intensity |
44
+ |------------|----------|-------------------|
45
+ | **High** | Security, data integrity, core business, user-facing errors | Must have unit + E2E |
46
+ | **Medium** | Important functionality, configuration, integrations | Unit required |
47
+ | **Low** | Logging, cosmetic, internal helpers | Unit if time permits |
48
+
49
+ ### Step 3: Map to Layers
50
+
51
+ For each behavior, ask:
52
+
53
+ 1. **Can a unit test prove this?** (No I/O needed) → Unit
54
+ 2. **Does it need component interaction?** (Stubbed I/O) → Integration
55
+ 3. **Does it require real I/O to prove?** (CLI, network, filesystem) → E2E
56
+
57
+ ### Step 4: Build the Map
58
+
59
+ | Behavior | Risk | Layer | Test File | Source of Truth |
60
+ |----------|------|-------|-----------|-----------------|
61
+ | Parse valid YAML | Medium | Unit | config_parser_test.rb | YAML schema |
62
+ | Return defaults | Medium | Unit | config_parser_test.rb | defaults.yml |
63
+ | Malformed YAML error | High | Unit | config_parser_test.rb | Exception spec |
64
+ | Config cascade merge | Medium | Integration | config_resolver_test.rb | Merge rules |
65
+ | CLI exit code 1 | High | E2E | TS-CONFIG-001 | CLI spec |
66
+
67
+ ## Layer Decision Rules
68
+
69
+ ### Unit Test If:
70
+
71
+ - Pure logic with no side effects
72
+ - Data transformation or validation
73
+ - Error handling for invalid input
74
+ - Edge cases (nil, empty, boundaries)
75
+
76
+ **Stub everything**: filesystem, network, subprocess, git
77
+
78
+ ### Integration Test If:
79
+
80
+ - Multiple components interact
81
+ - Data flows between modules
82
+ - Error propagation matters
83
+ - ONE CLI parity check needed
84
+
85
+ **Stub external dependencies**: APIs, subprocess calls
86
+
87
+ ### E2E Test If:
88
+
89
+ - Complete user workflow
90
+ - Real tool interaction required
91
+ - Environment-specific behavior
92
+ - Cannot be proven without real I/O
93
+
94
+ **Use real I/O**: sandboxed, with cleanup
95
+
96
+ ## Avoiding Redundancy
97
+
98
+ ### Anti-Pattern: Testing Same Behavior at Multiple Layers
99
+
100
+ ```markdown
101
+ # BAD: Same behavior tested 3 times
102
+ - Unit: test_config_parser_returns_defaults
103
+ - Integration: test_config_loader_uses_defaults
104
+ - E2E: TC-001 verifies defaults in CLI output
105
+ ```
106
+
107
+ ### Pattern: Test at Lowest Layer, Verify at Higher
108
+
109
+ ```markdown
110
+ # GOOD: Behavior at lowest, workflow at highest
111
+ - Unit: test_config_parser_returns_defaults (proves logic)
112
+ - Integration: (skip - unit covers it)
113
+ - E2E: TC-001 verifies full config workflow (one test, not per feature)
114
+ ```
115
+
116
+ ## Risk-Based Coverage
117
+
118
+ ### High Risk Behaviors
119
+
120
+ Must be tested thoroughly:
121
+
122
+ ```markdown
123
+ | Behavior | Risk | Why High | Coverage |
124
+ |----------|------|----------|----------|
125
+ | Auth token validation | High | Security | Unit + E2E |
126
+ | Data persistence | High | Data integrity | Unit + Integration + E2E |
127
+ | Payment processing | High | Business critical | Unit + Integration + E2E |
128
+ ```
129
+
130
+ ### Low Risk Behaviors
131
+
132
+ Basic coverage sufficient:
133
+
134
+ ```markdown
135
+ | Behavior | Risk | Why Low | Coverage |
136
+ |----------|------|---------|----------|
137
+ | Log formatting | Low | Cosmetic | Unit happy path |
138
+ | Debug output | Low | Internal | Skip or minimal |
139
+ ```
140
+
141
+ ## Template Usage
142
+
143
+ Use the template at `templates/test-responsibility-map.template.md` when:
144
+
145
+ - Starting a new feature
146
+ - Auditing existing coverage
147
+ - Planning test refactoring
148
+ - Reviewing PR test coverage
149
+
150
+ ## Review Questions
151
+
152
+ When reviewing a responsibility map:
153
+
154
+ - [ ] Is each behavior at the lowest possible layer?
155
+ - [ ] Are high-risk behaviors covered by E2E?
156
+ - [ ] Are edge cases in unit tests, not E2E?
157
+ - [ ] Any duplicate coverage across layers?
158
+ - [ ] Source of truth identified for each behavior?
159
+
160
+ ## Common Mistakes
161
+
162
+ ### Mistake 1: E2E for Edge Cases
163
+
164
+ ```markdown
165
+ # BAD: Testing every edge case in E2E
166
+ - E2E: TC-001 valid config
167
+ - E2E: TC-002 empty config
168
+ - E2E: TC-003 missing key
169
+ - E2E: TC-004 invalid YAML
170
+ - E2E: TC-005 circular reference
171
+
172
+ # GOOD: Edge cases in unit, workflow in E2E
173
+ - Unit: test_empty_config, test_missing_key, test_invalid_yaml, test_circular_ref
174
+ - E2E: TC-001 complete config workflow (happy + one error)
175
+ ```
176
+
177
+ ### Mistake 2: Missing High-Risk E2E
178
+
179
+ ```markdown
180
+ # BAD: High-risk behavior only unit tested
181
+ - Unit: test_auth_token_validation ✓
182
+ - E2E: (none)
183
+
184
+ # GOOD: High-risk has E2E verification
185
+ - Unit: test_auth_token_validation ✓
186
+ - E2E: TC-001 authentication workflow ✓
187
+ ```
188
+
189
+ ### Mistake 3: No Source of Truth
190
+
191
+ ```markdown
192
+ # BAD: Mock data invented
193
+ mock_response = { status: "ok" } # Where does this come from?
194
+
195
+ # GOOD: Mock data from real source
196
+ mock_response = JSON.parse(File.read("fixtures/api_response.json"))
197
+ # fixtures/api_response.json is snapshot from real API
198
+ ```
199
+
200
+ ## Integration with Workflows
201
+
202
+ ### With /ace-test-plan
203
+
204
+ 1. Generate responsibility map
205
+ 2. Identify gaps
206
+ 3. Plan tests by layer
207
+ 4. Output test plan
208
+
209
+ ### With /ace-test-verify-suite
210
+
211
+ 1. Check existing tests against map
212
+ 2. Identify redundancies
213
+ 3. Flag missing coverage
214
+ 4. Suggest optimizations
215
+
216
+ ## See Also
217
+
218
+ - [Test Layer Decision](guide://test-layer-decision) - Layer decision matrix
219
+ - [Test Mocking Patterns](guide://test-mocking-patterns) - How to stub
220
+ - [Test Suite Health](guide://test-suite-health) - Metrics and audits