一键导入
suggest-tests
Scan service-layer files for test coverage gaps. No args = plan + TODO; service name = single analysis; "deep" = full read-only report.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scan service-layer files for test coverage gaps. No args = plan + TODO; service name = single analysis; "deep" = full read-only report.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Security-focused code review of the CFML codebase that checks for SQL injection, XSS, XSRF, access control, input validation, error translation, HTML sanitization, debug output, and unsafe evaluation issues.
Review CLAUDE.md, auto-memory, WIP docs, and skills against recent development activity for accuracy, compaction, drift, and cross-file consistency.
| name | suggest-tests |
| description | Scan service-layer files for test coverage gaps. No args = plan + TODO; service name = single analysis; "deep" = full read-only report. |
| context | fork |
| agent | Explore |
| allowed-tools | Read, Write, Edit, Grep, Glob, Bash |
Scan service-layer files and cross-reference against existing test suites. Output a prioritized list of suggested tests — do NOT generate test code.
.claude/wip/TEST_EXPANSION.md, and output an iteration prompt the user can feed back to Claude to work through the list one service at a time./suggest-tests deep)./suggest-tests PoemService, /suggest-tests CollectionService). Match by name — the user doesn't need to provide the full path.Glob for all *Service.cfc files under cfml/app/core/lib/service/. Every service is in scope.
Glob for *Service.cfc files under cfml/app/core/lib/service/ and find the one matching the user's input. If no match is found, list the available services and exit.
Glob for /cfml/app/spec/suite/*Test.cfc to get all existing test suites.
Build a mapping of service name to test suite:
PoemService.cfc → PoemServiceTest.cfcCollectionService.cfc → CollectionServiceTest.cfcFor each changed service file:
public ... function methodName)test)testCreate* covers create()testDelete* covers delete() or deleteByFilter()testUpdate* covers update()For each service in scope, trace through the call chain to find all possible error types:
Most errors originate in *Validation.cfc files, not in the service itself. To find them:
*Model methods each public service method calls (e.g., poemModel.create(), poemModel.update())*Model.cfc to see which *Validation methods it calls for each operation (e.g., poemValidation.nameFrom(), poemValidation.contentFrom())*Validation.cfc to extract the actual error types from each validation method's pipelinecreate() and update() often validate the same fields but not always (e.g., update() may accept optional fields that create() requires)For each error type found, check whether the test suite has a corresponding assertThrows call. Report any that lack coverage.
Note on SuspiciousEncoding errors: All validation components share the same assertUniformEncoding check from BaseValidation, which compares the input against canonicalize( input, true, true ). A double-encoded percent string like "%2525" reliably triggers this across all fields.
Service methods may call into other entities' access or validation layers. For example, PoemService.create() calls collectionAccess.getContext() internally, which can throw App.Model.Collection.NotFound. Look for calls to other entities' access/validation components within the service and check whether those error types are tested.
Service methods that call *Access.getContext( authContext, id, "canSomething" ) or *Access.getContextForParent(...) enforce ownership — accessing another user's entity should throw a not-found error. Check whether the test suite has a single consolidated test (e.g., testOtherUserEntityThrowsNotFound) that:
provisionAuthContext() (inherited from BaseTest)App.Model.*.NotFound when accessed by the primary test userThe key insight: the entity must actually exist (created by user A) so that the error is a permission-denied-as-not-found, not a legitimate not-found. Report any access-gated services that lack this cross-user test.
## Test Coverage Suggestions
**Mode**: Last N commits (since <oldest commit hash>) | Deep dive (all services)
**Services scanned**: X
**With test suites**: Y
**Needing attention**: Z
### <ServiceName> — No test suite exists
- **Path**: `cfml/app/core/lib/service/.../ServiceName.cfc`
- **Suggested suite**: `spec.suite.ServiceNameTest`
- **Public methods**:
- `create` — brief description
- `delete` — brief description
- **Suggested test methods**:
- Happy path: `testCreate` (include related side-effects like revision creation)
- Happy path: `testDelete`
- Sad path: `testCreateWithInvalidInputThrows` (all create validation errors in one method)
- Sad path: `testUpdateWithInvalidInputThrows` (all update validation errors in one method)
- Sad path: `testOtherUserEntityThrowsNotFound` (assert both update and delete)
### <ServiceName> — Partial coverage
- **Path**: `cfml/app/core/lib/service/.../ServiceName.cfc`
- **Existing suite**: `spec.suite.ServiceNameTest`
- **Uncovered methods**:
- `newMethod` — brief description
- **Untested error paths**:
- `throw( type = "App.Model.Thing.Name.Empty" )` in `create()` — no `assertThrows` for this type
- **Missing permission tests**:
- No `testOtherUserThingThrowsNotFound` — service has access-gated methods
- **Suggested test methods**:
- Happy path: `testNewMethod`
- Sad path: `testCreateWithInvalidInputThrows` (consolidate all untested create validations)
- Sad path: `testOtherUserThingThrowsNotFound`
If all scanned services have full test coverage, state: "All scanned services have test coverage."
Skip this step for deep-dive and single-service modes.
.claude/wip/TEST_EXPANSION.mdWrite a TODO file with checkboxes for each service needing test coverage. Services with full coverage should be pre-checked. Order services by estimated complexity (simplest CRUD services first, services with external dependencies like cookies/sessions last). Include brief notes about each service's scope.
# Test Expansion TODO
Track progress as we add test suites for each service.
## Services
- [ ] ServiceName — brief scope note
- [ ] ServiceName — brief scope note
- [x] ServiceName — already covered
## Notes
- Run `/suggest-tests <ServiceName>` before writing each suite for detailed gap analysis
- Services ordered by estimated complexity (simplest first)
## Prompt
Copy and paste this to process the next unchecked service:
We're working through the test expansion TODO at `.claude/wip/TEST_EXPANSION.md`. Pick the next unchecked service from the list, run `/suggest-tests` against it, then create the test suite based on the output. Follow the testing patterns established in `PoemServiceTest.cfc` — happy/sad path sections, consolidated methods, permissions test using `provisionAuthContext()`. Run the tests to confirm they pass, then commit. Mark the item as done in the TODO file. If you discover a latent bug in the production code while writing tests, fix it and include the fix in the commit.
If the file already exists, update it: preserve checked items, add any new uncovered services, remove services that now have full coverage.
After the report and TODO file, output this prompt exactly (the user will copy-paste it into new Claude sessions to work through the list):
We're working through the test expansion TODO at `.claude/wip/TEST_EXPANSION.md`. Pick the next unchecked service from the list, run `/suggest-tests` against it, then create the test suite based on the output. Follow the testing patterns established in `PoemServiceTest.cfc` — happy/sad path sections, consolidated methods, permissions test using `provisionAuthContext()`. Run the tests to confirm they pass, then commit. Mark the item as done in the TODO file. If you discover a latent bug in the production code while writing tests, fix it and include the fix in the commit.
.claude/wip/TEST_EXPANSION.md in default mode.testCreate, testUpdate, testDelete. Sad-path validation methods consolidate by operation: testCreateWithInvalidInputThrows, testUpdateWithInvalidInputThrows. Sad-path permission methods use: testOtherUser<Entity>ThrowsNotFound.HAPPY PATH TESTS and SAD PATH TESTS sections (see examples above).testCreate should verify both persistence and side-effects like revision creation). Don't suggest separate methods when one method with multiple assertions covers the same ground.create() can throw Name.Empty, Content.TooLong, and Name.SuspiciousEncoding, suggest a single testCreateWithInvalidInputThrows — not three separate methods. Similarly for update(). Name the method after the operation and the broad category (e.g., testCreateWithInvalidInputThrows, testUpdateWithInvalidInputThrows).testOtherUser<Entity>ThrowsNotFound method that asserts both update and delete fail — not separate methods per operation.