go-crypto-wallet / go
Cursor rule128 starsChanged 6 months agoMITView on GitHub
Go Test Rules
We split this go-test.mdc into the rules it gives an agent
8 rules. About 0 are common, 0 are distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 40
Always use testify. Never use t.Errorf, t.Fatalf, or reflect.DeepEqual directly.
- Very specific: line 47
Rule: Use require when subsequent test code depends on the assertion passing.
- Very specific: line 53
Always use t.Run with a slice of test cases:
- Very specific: line 101
Always call t.Helper() in helper functions so failures point to the call site.
- Very specific: line 114
Create a testutil/ subdirectory inside the package the utilities belong to.
- Very specific: line 141
Add t.Parallel() to tests with no shared mutable state.
- Very specific: line 147
When the code under test spawns multiple goroutines that all call the same mock method, using per-call .Once() expectations with different …
- Very specific: line 151
Rule: When N goroutines call the same mock method, set up a single expectation with .Times(N) returning a value that satisfies ALL callers …
≈ “Common” is an estimate: at least 1 in 5 of the 6,350 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
---
description: Go Test Rules
globs: ["**/*test.go"]
alwaysApply: false
---
# Go Test Rules
## Overview
Rules for writing and modifying test files (`*_test.go`) in go-crypto-wallet.
**Full reference (SSOT)**: @docs/guidelines/testing.md
---
## Test File Organization
| Test type | Filename | Package | Build tag |
|-----------|----------|---------|-----------|
| Unit test | `*_test.go` | same package (e.g. `package btc`) | none |
| Integration test (DB / external) | `*_integration_test.go` | external (e.g. `package btc_test`) | `//go:build integration` |
| E2E / complex multi-service | `*_test.go` in `internal/integration_test/` | `package integration_test` | `//go:build integration` |
```go
// unit_test.go — same package
package btc
// repository_integration_test.go — external package + build tag
//go:build integration
package postgres_test
```
---
## Test Framework — Mandatory
Always use [testify](https://github.com/stretchr/testify). **Never** use `t.Errorf`, `t.Fatalf`, or `reflect.DeepEqual` directly.
| Package | When to use |
|---------|-------------|
| `require` | Fatal — test stops immediately on failure |
| `assert` | Non-fatal — test continues after failure |
**Rule**: Use `require` when subsequent test code depends on the assertion passing; use `assert` otherwise.
---
## Table-Driven Tests — Mandatory
Always use `t.Run` with a slice of test cases:
```go
func TestFoo(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{"valid", "ok", false},
{"empty", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Foo(tt.input)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
```
---
## Mock Usage — Mandatory
**Always use mockery-generated mocks. Never write mock structs by hand.**
See @.claude/rules/internal/mockery.md for placement convention and exceptions.
**Import alias convention**:
| Mocks package | Import alias |
|---------------|--------------|
| `infrastructure/api/btc/mocks` | `btcapiamocks` |
| `infrastructure/api/eth/mocks` | `ethapiamocks` |
| `infrastructure/api/xrp/mocks` | `xrpapiamocks` |
| `infrastructure/repository/cold/mocks` | `coldmocks` |
| `infrastructure/repository/watch/mocks` | `repomocks` |
| `infrastructure/storage/file/transaction/mocks` | `storagemocks` |
---
## Helper Functions
Always call `t.Helper()` in helper functions so failures point to the call site.
---
## Test Utilities (`testutil/`)
**Rule**: A global `pkg/testutil` package is **prohibited**. Co-locate test utilities with the package they support.
```
pkg/db/testutil/ ← helpers for pkg/db (e.g. OpenTestDB)
internal/foo/testutil/ ← helpers for internal/foo
```
Create a `testutil/` subdirectory inside the package the utilities belong to. Never create a standalone `pkg/testutil/` or `internal/testutil/` package.
---
## What NOT to Do
```go
// ❌ Standard library assertions — use testify
t.Errorf("got %v, want %v", got, expected)
// ❌ Hand-written mock structs — use mockery
type stubRepo struct{}
// ❌ Manual AssertExpectations — automatic via NewMock*(t)
mock.AssertExpectations(t)
// ❌ reflect.DeepEqual — use assert.Equal
if !reflect.DeepEqual(expected, got) { t.Error(...) }
// ❌ Global testutil package
import "github.com/hiromaily/go-crypto-wallet/pkg/testutil"
```
---
## Parallelism
Add `t.Parallel()` to tests with no shared mutable state. Do **not** add it to tests sharing DB connections.
---
## Concurrent Goroutines and Mock Expectations
When the code under test spawns multiple goroutines that all call the **same mock method**, using
per-call `.Once()` expectations with different return values is non-deterministic — goroutine A
may steal goroutine B's response, causing "called over N times" panics or unsatisfied expectations.
**Rule**: When N goroutines call the same mock method, set up a single expectation with `.Times(N)`
returning a value that satisfies ALL callers simultaneously.
```go
// ❌ BAD: two goroutines race; either may receive the wrong response
deps.ledgerPoller.EXPECT().LedgerCurrent(mock.Anything).Return(res1, nil).Once()
deps.ledgerPoller.EXPECT().LedgerCurrent(mock.Anything).Return(res2, nil).Once()
// ✅ GOOD: use Times(N) with a response value that satisfies every caller
// (e.g. use the max of all required ledger indices)
ledgerRes := &ResponseLedgerCurrent{}
ledgerRes.Result.LedgerCurrentIndex = maxRequiredIndex
deps.ledgerPoller.EXPECT().LedgerCurrent(mock.Anything).Return(ledgerRes, nil).Times(2)
```
This applies whenever use-case code fans out work into `go func()` goroutines and each goroutine
calls the same mock method (e.g. `LedgerCurrent`, `GetBalance`, `GetTransaction`).
---
## Verification
```bash
make go-lint # Required: zero lint errors
make go-test # Required: all unit tests pass
```
---
## Related Rules
- @.claude/rules/internal/mockery.md — mock generation placement convention
- @.claude/rules/go/conventions.md — Go coding conventions
- @docs/guidelines/testing.md — full testing guidelines (SSOT)
Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.
Of the Cursor rule files we read, the 3,978 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository62 other files it gives its agents.
- AGENTS.mdAGENTS.md
- internal/AGENTS.mdAGENTS.md
- pkg/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- internal/CLAUDE.mdCLAUDE.md
- pkg/CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- .cursor/rules/agent-files.mdcCursor rule
- .cursor/rules/apps/bun.mdcCursor rule
- .cursor/rules/apps/eth-contracts.mdcCursor rule
- .cursor/rules/apps/xrp-server.mdcCursor rule
- .cursor/rules/database/atlas-hcl.mdcCursor rule
- .cursor/rules/database/sqlc.mdcCursor rule
- .cursor/rules/documentation-language.mdcCursor rule
- .cursor/rules/general.mdcCursor rule
- .cursor/rules/github-actions.mdcCursor rule
- .cursor/rules/go/conventions.mdcCursor rule
- .cursor/rules/go/di.mdcCursor rule
- .cursor/rules/go/go-path.mdcCursor rule
- .cursor/rules/internal/application-layer.mdcCursor rule
- .cursor/rules/internal/btc-shared.mdcCursor rule
- .cursor/rules/internal/clean-architecture.mdcCursor rule
- .cursor/rules/internal/cli-structure.mdcCursor rule
- .cursor/rules/internal/domain-layer.mdcCursor rule
- .cursor/rules/internal/dto.mdcCursor rule
- .cursor/rules/internal/infrastructure-layer.mdcCursor rule
- .cursor/rules/internal/interface-adapters-layer.mdcCursor rule
- .cursor/rules/internal/internal-interface.mdcCursor rule
- .cursor/rules/internal/mockery.mdcCursor rule
- .cursor/rules/internal/repository.mdcCursor rule
- .cursor/rules/internal/usecase.mdcCursor rule
- .cursor/rules/proto.mdcCursor rule
- .cursor/rules/README.mdCursor rule
- .cursor/rules/security.mdcCursor rule
- .cursor/rules/shell-script.mdcCursor rule
- .cursor/rules/ssot.mdcCursor rule
- .cursor/rules/task-context-loading.mdcCursor rule
- .cursor/rules/typescript.mdcCursor rule
- .cursor/rules/yaml.mdcCursor rule
- llms.txtllms.txt
- bch-developmentSkill
- btc-terminologySkill
- db-migrationSkill
- devopsSkill
- docs-ssotSkill
- docs-updateSkill
- fix-pr-reviewSkill
- github-issue-creationSkill
- git-workflowSkill
- go-developmentSkill
- knowledge-gapSkill
- label-context-mappingSkill
- makefile-updateSkill
- mockerySkill
- openspec-apply-changeSkill
- openspec-archive-changeSkill
- openspec-exploreSkill
- openspec-proposeSkill
- shell-scriptsSkill
- solidity-developmentSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

