@baselane/packs 0.1.4 → 0.1.5

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 (97) hide show
  1. package/dist/aliases.js +1 -0
  2. package/package.json +2 -2
  3. package/packs/database-review/pack.json +75 -9
  4. package/packs/database-review/rendered/.claude/agents/database-reviewer.md +75 -2
  5. package/packs/database-review/rendered/.claude/commands/migration-review.md +6 -0
  6. package/packs/database-review/rendered/.claude/settings.json +11 -0
  7. package/packs/database-review/rendered/.claude/skills/database-migrations/SKILL.md +431 -0
  8. package/packs/database-review/rendered/.claude/skills/mysql-patterns/SKILL.md +414 -0
  9. package/packs/database-review/rendered/.claude/skills/postgres-patterns/SKILL.md +149 -0
  10. package/packs/database-review/rendered/.github/copilot-instructions.md +1096 -14
  11. package/packs/database-review/rendered/AGENTS.md +45 -16
  12. package/packs/database-review/rendered/CLAUDE.md +4 -0
  13. package/packs/database-review/rendered/GEMINI.md +987 -0
  14. package/packs/go-rules/pack.json +65 -9
  15. package/packs/go-rules/rendered/.claude/agents/go-build-resolver.md +90 -0
  16. package/packs/go-rules/rendered/.claude/agents/go-reviewer.md +72 -2
  17. package/packs/go-rules/rendered/.claude/commands/go-build.md +6 -0
  18. package/packs/go-rules/rendered/.claude/commands/go-review.md +1 -1
  19. package/packs/go-rules/rendered/.claude/commands/go-test.md +6 -0
  20. package/packs/go-rules/rendered/.claude/settings.json +9 -0
  21. package/packs/go-rules/rendered/.claude/skills/golang-patterns/SKILL.md +673 -0
  22. package/packs/go-rules/rendered/.claude/skills/golang-testing/SKILL.md +719 -0
  23. package/packs/go-rules/rendered/.github/copilot-instructions.md +1783 -7
  24. package/packs/go-rules/rendered/AGENTS.md +244 -8
  25. package/packs/go-rules/rendered/CLAUDE.md +5 -0
  26. package/packs/go-rules/rendered/GEMINI.md +1394 -0
  27. package/packs/python-rules/pack.json +87 -10
  28. package/packs/python-rules/rendered/.claude/agents/django-reviewer.md +159 -0
  29. package/packs/python-rules/rendered/.claude/agents/fastapi-reviewer.md +70 -0
  30. package/packs/python-rules/rendered/.claude/agents/python-reviewer.md +92 -2
  31. package/packs/python-rules/rendered/.claude/commands/django-review.md +6 -0
  32. package/packs/python-rules/rendered/.claude/commands/fastapi-review.md +6 -0
  33. package/packs/python-rules/rendered/.claude/commands/python-review.md +1 -1
  34. package/packs/python-rules/rendered/.claude/settings.json +1 -1
  35. package/packs/python-rules/rendered/.claude/skills/django-security/SKILL.md +642 -0
  36. package/packs/python-rules/rendered/.claude/skills/django-tdd/SKILL.md +728 -0
  37. package/packs/python-rules/rendered/.claude/skills/fastapi-patterns/SKILL.md +512 -0
  38. package/packs/python-rules/rendered/.claude/skills/python-patterns/SKILL.md +749 -0
  39. package/packs/python-rules/rendered/.claude/skills/python-testing/SKILL.md +815 -0
  40. package/packs/python-rules/rendered/.github/copilot-instructions.md +3815 -12
  41. package/packs/python-rules/rendered/AGENTS.md +73 -13
  42. package/packs/python-rules/rendered/CLAUDE.md +9 -0
  43. package/packs/python-rules/rendered/GEMINI.md +3448 -0
  44. package/packs/security-review/pack.json +51 -7
  45. package/packs/security-review/rendered/.claude/agents/security-reviewer.md +29 -2
  46. package/packs/security-review/rendered/.claude/settings.json +11 -0
  47. package/packs/security-review/rendered/.claude/skills/security-review/SKILL.md +302 -0
  48. package/packs/security-review/rendered/.claude/skills/security-review/cloud-infrastructure-security.md +171 -0
  49. package/packs/security-review/rendered/.claude/skills/security-scan-config/SKILL.md +56 -0
  50. package/packs/security-review/rendered/.github/copilot-instructions.md +658 -15
  51. package/packs/security-review/rendered/AGENTS.md +283 -17
  52. package/packs/security-review/rendered/CLAUDE.md +2 -0
  53. package/packs/security-review/rendered/GEMINI.md +352 -0
  54. package/packs/software-engineer-harness/pack.json +249 -13
  55. package/packs/software-engineer-harness/rendered/.claude/agents/code-explorer.md +67 -0
  56. package/packs/software-engineer-harness/rendered/.claude/agents/performance-optimizer.md +444 -0
  57. package/packs/software-engineer-harness/rendered/.claude/agents/pr-test-analyzer.md +43 -0
  58. package/packs/software-engineer-harness/rendered/.claude/commands/feature-dev.md +6 -0
  59. package/packs/software-engineer-harness/rendered/.claude/settings.json +29 -0
  60. package/packs/software-engineer-harness/rendered/.claude/skills/api-design/SKILL.md +522 -0
  61. package/packs/software-engineer-harness/rendered/.claude/skills/architecture-decision-records/SKILL.md +178 -0
  62. package/packs/software-engineer-harness/rendered/.claude/skills/backend-patterns/SKILL.md +560 -0
  63. package/packs/software-engineer-harness/rendered/.claude/skills/codebase-onboarding/SKILL.md +232 -0
  64. package/packs/software-engineer-harness/rendered/.claude/skills/deployment-patterns/SKILL.md +426 -0
  65. package/packs/software-engineer-harness/rendered/.claude/skills/docker-patterns/SKILL.md +363 -0
  66. package/packs/software-engineer-harness/rendered/.claude/skills/e2e-testing/SKILL.md +325 -0
  67. package/packs/software-engineer-harness/rendered/.claude/skills/tdd-workflow/SKILL.md +580 -0
  68. package/packs/software-engineer-harness/rendered/.github/copilot-instructions.md +3948 -2
  69. package/packs/software-engineer-harness/rendered/AGENTS.md +194 -4
  70. package/packs/software-engineer-harness/rendered/CLAUDE.md +20 -0
  71. package/packs/software-engineer-harness/rendered/GEMINI.md +3208 -0
  72. package/packs/typescript-rules/pack.json +68 -5
  73. package/packs/typescript-rules/rendered/.claude/agents/react-reviewer.md +165 -0
  74. package/packs/typescript-rules/rendered/.claude/agents/type-design-analyzer.md +48 -0
  75. package/packs/typescript-rules/rendered/.claude/commands/react-review.md +6 -0
  76. package/packs/typescript-rules/rendered/.claude/settings.json +9 -0
  77. package/packs/typescript-rules/rendered/.claude/skills/react-patterns/SKILL.md +340 -0
  78. package/packs/typescript-rules/rendered/.claude/skills/react-performance/SKILL.md +573 -0
  79. package/packs/typescript-rules/rendered/.claude/skills/react-testing/SKILL.md +422 -0
  80. package/packs/typescript-rules/rendered/.github/copilot-instructions.md +2459 -53
  81. package/packs/typescript-rules/rendered/AGENTS.md +893 -17
  82. package/packs/typescript-rules/rendered/CLAUDE.md +6 -0
  83. package/packs/typescript-rules/rendered/GEMINI.md +1335 -0
  84. package/packs/ecc-essentials/pack.json +0 -93
  85. package/packs/ecc-essentials/rendered/.claude/settings.json +0 -37
  86. package/packs/ecc-essentials/rendered/.github/copilot-instructions.md +0 -110
  87. package/packs/ecc-essentials/rendered/AGENTS.md +0 -74
  88. package/packs/ecc-essentials/rendered/CLAUDE.md +0 -14
  89. package/packs/ecc-essentials/rendered/GEMINI.md +0 -25
  90. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/build-error-resolver.md +0 -0
  91. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/refactor-cleaner.md +0 -0
  92. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/silent-failure-hunter.md +0 -0
  93. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/agents/tdd-guide.md +0 -0
  94. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/fix-build.md +0 -0
  95. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/hunt-silent-failures.md +0 -0
  96. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/commands/tdd.md +0 -0
  97. /package/packs/{ecc-essentials → software-engineer-harness}/rendered/.claude/skills/tdd-red-green-refactor/SKILL.md +0 -0
@@ -1,3 +1,1397 @@
1
1
  # GEMINI.md
2
2
 
3
3
  @AGENTS.md
4
+
5
+ ## Skills
6
+
7
+ ### golang-patterns
8
+
9
+ Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications — API shape, error handling, concurrency, interface design, package organization, and performance. Use when writing, reviewing, or refactoring Go code, or designing Go packages/modules.
10
+
11
+ # Go Development Patterns
12
+
13
+ Idiomatic Go patterns and best practices for building robust, efficient, and maintainable applications.
14
+
15
+ ## When to use
16
+
17
+ - Writing new Go code.
18
+ - Reviewing Go code.
19
+ - Refactoring existing Go code.
20
+ - Designing Go packages/modules.
21
+
22
+ ## Core principles
23
+
24
+ ### 1. Simplicity and clarity
25
+
26
+ Go favors simplicity over cleverness. Code should be obvious and easy to read.
27
+
28
+ \`\`\`go
29
+ // Good: clear and direct
30
+ func GetUser(id string) (*User, error) {
31
+ user, err := db.FindUser(id)
32
+ if err != nil {
33
+ return nil, fmt.Errorf("get user %s: %w", id, err)
34
+ }
35
+ return user, nil
36
+ }
37
+
38
+ // Bad: overly clever
39
+ func GetUser(id string) (*User, error) {
40
+ return func() (*User, error) {
41
+ if u, e := db.FindUser(id); e == nil {
42
+ return u, nil
43
+ } else {
44
+ return nil, e
45
+ }
46
+ }()
47
+ }
48
+ \`\`\`
49
+
50
+ ### 2. Make the zero value useful
51
+
52
+ Design types so their zero value is immediately usable without initialization.
53
+
54
+ \`\`\`go
55
+ // Good: zero value is useful
56
+ type Counter struct {
57
+ mu sync.Mutex
58
+ count int // zero value is 0, ready to use
59
+ }
60
+
61
+ func (c *Counter) Inc() {
62
+ c.mu.Lock()
63
+ c.count++
64
+ c.mu.Unlock()
65
+ }
66
+
67
+ // Good: bytes.Buffer works with zero value
68
+ var buf bytes.Buffer
69
+ buf.WriteString("hello")
70
+
71
+ // Bad: requires initialization
72
+ type BadCounter struct {
73
+ counts map[string]int // nil map will panic
74
+ }
75
+ \`\`\`
76
+
77
+ ### 3. Accept interfaces, return structs
78
+
79
+ Functions should accept interface parameters and return concrete types.
80
+
81
+ \`\`\`go
82
+ // Good: accepts interface, returns concrete type
83
+ func ProcessData(r io.Reader) (*Result, error) {
84
+ data, err := io.ReadAll(r)
85
+ if err != nil {
86
+ return nil, err
87
+ }
88
+ return &Result{Data: data}, nil
89
+ }
90
+
91
+ // Bad: returns interface (hides implementation details unnecessarily)
92
+ func ProcessData(r io.Reader) (io.Reader, error) {
93
+ // ...
94
+ }
95
+ \`\`\`
96
+
97
+ ## Error handling patterns
98
+
99
+ ### Error wrapping with context
100
+
101
+ \`\`\`go
102
+ func LoadConfig(path string) (*Config, error) {
103
+ data, err := os.ReadFile(path)
104
+ if err != nil {
105
+ return nil, fmt.Errorf("load config %s: %w", path, err)
106
+ }
107
+
108
+ var cfg Config
109
+ if err := json.Unmarshal(data, &cfg); err != nil {
110
+ return nil, fmt.Errorf("parse config %s: %w", path, err)
111
+ }
112
+
113
+ return &cfg, nil
114
+ }
115
+ \`\`\`
116
+
117
+ ### Custom error types
118
+
119
+ \`\`\`go
120
+ // Define domain-specific errors
121
+ type ValidationError struct {
122
+ Field string
123
+ Message string
124
+ }
125
+
126
+ func (e *ValidationError) Error() string {
127
+ return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
128
+ }
129
+
130
+ // Sentinel errors for common cases
131
+ var (
132
+ ErrNotFound = errors.New("resource not found")
133
+ ErrUnauthorized = errors.New("unauthorized")
134
+ ErrInvalidInput = errors.New("invalid input")
135
+ )
136
+ \`\`\`
137
+
138
+ ### Error checking with errors.Is and errors.As
139
+
140
+ \`\`\`go
141
+ func HandleError(err error) {
142
+ // Check for specific error
143
+ if errors.Is(err, sql.ErrNoRows) {
144
+ log.Println("No records found")
145
+ return
146
+ }
147
+
148
+ // Check for error type
149
+ var validationErr *ValidationError
150
+ if errors.As(err, &validationErr) {
151
+ log.Printf("Validation error on field %s: %s",
152
+ validationErr.Field, validationErr.Message)
153
+ return
154
+ }
155
+
156
+ // Unknown error
157
+ log.Printf("Unexpected error: %v", err)
158
+ }
159
+ \`\`\`
160
+
161
+ ### Never ignore errors
162
+
163
+ \`\`\`go
164
+ // Bad: ignoring error with blank identifier
165
+ result, _ := doSomething()
166
+
167
+ // Good: handle or explicitly document why it's safe to ignore
168
+ result, err := doSomething()
169
+ if err != nil {
170
+ return err
171
+ }
172
+
173
+ // Acceptable: when error truly doesn't matter (rare)
174
+ _ = writer.Close() // Best-effort cleanup, error logged elsewhere
175
+ \`\`\`
176
+
177
+ ## Concurrency patterns
178
+
179
+ ### Worker pool
180
+
181
+ \`\`\`go
182
+ func WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
183
+ var wg sync.WaitGroup
184
+
185
+ for i := 0; i < numWorkers; i++ {
186
+ wg.Add(1)
187
+ go func() {
188
+ defer wg.Done()
189
+ for job := range jobs {
190
+ results <- process(job)
191
+ }
192
+ }()
193
+ }
194
+
195
+ wg.Wait()
196
+ close(results)
197
+ }
198
+ \`\`\`
199
+
200
+ ### Context for cancellation and timeouts
201
+
202
+ \`\`\`go
203
+ func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
204
+ ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
205
+ defer cancel()
206
+
207
+ req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
208
+ if err != nil {
209
+ return nil, fmt.Errorf("create request: %w", err)
210
+ }
211
+
212
+ resp, err := http.DefaultClient.Do(req)
213
+ if err != nil {
214
+ return nil, fmt.Errorf("fetch %s: %w", url, err)
215
+ }
216
+ defer resp.Body.Close()
217
+
218
+ return io.ReadAll(resp.Body)
219
+ }
220
+ \`\`\`
221
+
222
+ ### Graceful shutdown
223
+
224
+ \`\`\`go
225
+ func GracefulShutdown(server *http.Server) {
226
+ quit := make(chan os.Signal, 1)
227
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
228
+
229
+ <-quit
230
+ log.Println("Shutting down server...")
231
+
232
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
233
+ defer cancel()
234
+
235
+ if err := server.Shutdown(ctx); err != nil {
236
+ log.Fatalf("Server forced to shutdown: %v", err)
237
+ }
238
+
239
+ log.Println("Server exited")
240
+ }
241
+ \`\`\`
242
+
243
+ ### errgroup for coordinated goroutines
244
+
245
+ \`\`\`go
246
+ import "golang.org/x/sync/errgroup"
247
+
248
+ func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
249
+ g, ctx := errgroup.WithContext(ctx)
250
+ results := make([][]byte, len(urls))
251
+
252
+ for i, url := range urls {
253
+ i, url := i, url // Capture loop variables
254
+ g.Go(func() error {
255
+ data, err := FetchWithTimeout(ctx, url)
256
+ if err != nil {
257
+ return err
258
+ }
259
+ results[i] = data
260
+ return nil
261
+ })
262
+ }
263
+
264
+ if err := g.Wait(); err != nil {
265
+ return nil, err
266
+ }
267
+ return results, nil
268
+ }
269
+ \`\`\`
270
+
271
+ ### Avoiding goroutine leaks
272
+
273
+ \`\`\`go
274
+ // Bad: goroutine leak if context is cancelled
275
+ func leakyFetch(ctx context.Context, url string) <-chan []byte {
276
+ ch := make(chan []byte)
277
+ go func() {
278
+ data, _ := fetch(url)
279
+ ch <- data // Blocks forever if no receiver
280
+ }()
281
+ return ch
282
+ }
283
+
284
+ // Good: properly handles cancellation
285
+ func safeFetch(ctx context.Context, url string) <-chan []byte {
286
+ ch := make(chan []byte, 1) // Buffered channel
287
+ go func() {
288
+ data, err := fetch(url)
289
+ if err != nil {
290
+ return
291
+ }
292
+ select {
293
+ case ch <- data:
294
+ case <-ctx.Done():
295
+ }
296
+ }()
297
+ return ch
298
+ }
299
+ \`\`\`
300
+
301
+ ## Interface design
302
+
303
+ ### Small, focused interfaces
304
+
305
+ \`\`\`go
306
+ // Good: single-method interfaces
307
+ type Reader interface {
308
+ Read(p []byte) (n int, err error)
309
+ }
310
+
311
+ type Writer interface {
312
+ Write(p []byte) (n int, err error)
313
+ }
314
+
315
+ type Closer interface {
316
+ Close() error
317
+ }
318
+
319
+ // Compose interfaces as needed
320
+ type ReadWriteCloser interface {
321
+ Reader
322
+ Writer
323
+ Closer
324
+ }
325
+ \`\`\`
326
+
327
+ ### Define interfaces where they're used
328
+
329
+ \`\`\`go
330
+ // In the consumer package, not the provider
331
+ package service
332
+
333
+ // UserStore defines what this service needs
334
+ type UserStore interface {
335
+ GetUser(id string) (*User, error)
336
+ SaveUser(user *User) error
337
+ }
338
+
339
+ type Service struct {
340
+ store UserStore
341
+ }
342
+
343
+ // Concrete implementation can be in another package
344
+ // It doesn't need to know about this interface
345
+ \`\`\`
346
+
347
+ ### Optional behavior with type assertions
348
+
349
+ \`\`\`go
350
+ type Flusher interface {
351
+ Flush() error
352
+ }
353
+
354
+ func WriteAndFlush(w io.Writer, data []byte) error {
355
+ if _, err := w.Write(data); err != nil {
356
+ return err
357
+ }
358
+
359
+ // Flush if supported
360
+ if f, ok := w.(Flusher); ok {
361
+ return f.Flush()
362
+ }
363
+ return nil
364
+ }
365
+ \`\`\`
366
+
367
+ ## Package organization
368
+
369
+ ### Standard project layout
370
+
371
+ \`\`\`text
372
+ myproject/
373
+ ├── cmd/
374
+ │ └── myapp/
375
+ │ └── main.go # Entry point
376
+ ├── internal/
377
+ │ ├── handler/ # HTTP handlers
378
+ │ ├── service/ # Business logic
379
+ │ ├── repository/ # Data access
380
+ │ └── config/ # Configuration
381
+ ├── pkg/
382
+ │ └── client/ # Public API client
383
+ ├── api/
384
+ │ └── v1/ # API definitions (proto, OpenAPI)
385
+ ├── testdata/ # Test fixtures
386
+ ├── go.mod
387
+ ├── go.sum
388
+ └── Makefile
389
+ \`\`\`
390
+
391
+ ### Package naming
392
+
393
+ \`\`\`go
394
+ // Good: short, lowercase, no underscores
395
+ package http
396
+ package json
397
+ package user
398
+
399
+ // Bad: verbose, mixed case, or redundant
400
+ package httpHandler
401
+ package json_parser
402
+ package userService // Redundant 'Service' suffix
403
+ \`\`\`
404
+
405
+ ### Avoid package-level state
406
+
407
+ \`\`\`go
408
+ // Bad: global mutable state
409
+ var db *sql.DB
410
+
411
+ func init() {
412
+ db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
413
+ }
414
+
415
+ // Good: dependency injection
416
+ type Server struct {
417
+ db *sql.DB
418
+ }
419
+
420
+ func NewServer(db *sql.DB) *Server {
421
+ return &Server{db: db}
422
+ }
423
+ \`\`\`
424
+
425
+ ## Struct design
426
+
427
+ ### Functional options pattern
428
+
429
+ \`\`\`go
430
+ type Server struct {
431
+ addr string
432
+ timeout time.Duration
433
+ logger *log.Logger
434
+ }
435
+
436
+ type Option func(*Server)
437
+
438
+ func WithTimeout(d time.Duration) Option {
439
+ return func(s *Server) {
440
+ s.timeout = d
441
+ }
442
+ }
443
+
444
+ func WithLogger(l *log.Logger) Option {
445
+ return func(s *Server) {
446
+ s.logger = l
447
+ }
448
+ }
449
+
450
+ func NewServer(addr string, opts ...Option) *Server {
451
+ s := &Server{
452
+ addr: addr,
453
+ timeout: 30 * time.Second, // default
454
+ logger: log.Default(), // default
455
+ }
456
+ for _, opt := range opts {
457
+ opt(s)
458
+ }
459
+ return s
460
+ }
461
+
462
+ // Usage
463
+ server := NewServer(":8080",
464
+ WithTimeout(60*time.Second),
465
+ WithLogger(customLogger),
466
+ )
467
+ \`\`\`
468
+
469
+ ### Embedding for composition
470
+
471
+ \`\`\`go
472
+ type Logger struct {
473
+ prefix string
474
+ }
475
+
476
+ func (l *Logger) Log(msg string) {
477
+ fmt.Printf("[%s] %s\n", l.prefix, msg)
478
+ }
479
+
480
+ type Server struct {
481
+ *Logger // Embedding - Server gets Log method
482
+ addr string
483
+ }
484
+
485
+ func NewServer(addr string) *Server {
486
+ return &Server{
487
+ Logger: &Logger{prefix: "SERVER"},
488
+ addr: addr,
489
+ }
490
+ }
491
+
492
+ // Usage
493
+ s := NewServer(":8080")
494
+ s.Log("Starting...") // Calls embedded Logger.Log
495
+ \`\`\`
496
+
497
+ ## Memory and performance
498
+
499
+ ### Preallocate slices when size is known
500
+
501
+ \`\`\`go
502
+ // Bad: grows slice multiple times
503
+ func processItems(items []Item) []Result {
504
+ var results []Result
505
+ for _, item := range items {
506
+ results = append(results, process(item))
507
+ }
508
+ return results
509
+ }
510
+
511
+ // Good: single allocation
512
+ func processItems(items []Item) []Result {
513
+ results := make([]Result, 0, len(items))
514
+ for _, item := range items {
515
+ results = append(results, process(item))
516
+ }
517
+ return results
518
+ }
519
+ \`\`\`
520
+
521
+ ### Use sync.Pool for frequent allocations
522
+
523
+ \`\`\`go
524
+ var bufferPool = sync.Pool{
525
+ New: func() interface{} {
526
+ return new(bytes.Buffer)
527
+ },
528
+ }
529
+
530
+ func ProcessRequest(data []byte) []byte {
531
+ buf := bufferPool.Get().(*bytes.Buffer)
532
+ defer func() {
533
+ buf.Reset()
534
+ bufferPool.Put(buf)
535
+ }()
536
+
537
+ buf.Write(data)
538
+ // Process...
539
+ return buf.Bytes()
540
+ }
541
+ \`\`\`
542
+
543
+ ### Avoid string concatenation in loops
544
+
545
+ \`\`\`go
546
+ // Bad: creates many string allocations
547
+ func join(parts []string) string {
548
+ var result string
549
+ for _, p := range parts {
550
+ result += p + ","
551
+ }
552
+ return result
553
+ }
554
+
555
+ // Good: single allocation with strings.Builder
556
+ func join(parts []string) string {
557
+ var sb strings.Builder
558
+ for i, p := range parts {
559
+ if i > 0 {
560
+ sb.WriteString(",")
561
+ }
562
+ sb.WriteString(p)
563
+ }
564
+ return sb.String()
565
+ }
566
+
567
+ // Best: use standard library
568
+ func join(parts []string) string {
569
+ return strings.Join(parts, ",")
570
+ }
571
+ \`\`\`
572
+
573
+ ## Go tooling integration
574
+
575
+ ### Essential commands
576
+
577
+ \`\`\`bash
578
+ # Build and run
579
+ go build ./...
580
+ go run ./cmd/myapp
581
+
582
+ # Testing
583
+ go test ./...
584
+ go test -race ./...
585
+ go test -cover ./...
586
+
587
+ # Static analysis
588
+ go vet ./...
589
+ staticcheck ./...
590
+ golangci-lint run
591
+
592
+ # Module management
593
+ go mod tidy
594
+ go mod verify
595
+
596
+ # Formatting
597
+ gofmt -w .
598
+ goimports -w .
599
+ \`\`\`
600
+
601
+ ### Recommended linter configuration (.golangci.yml)
602
+
603
+ \`\`\`yaml
604
+ linters:
605
+ enable:
606
+ - errcheck
607
+ - gosimple
608
+ - govet
609
+ - ineffassign
610
+ - staticcheck
611
+ - unused
612
+ - gofmt
613
+ - goimports
614
+ - misspell
615
+ - unconvert
616
+ - unparam
617
+
618
+ linters-settings:
619
+ errcheck:
620
+ check-type-assertions: true
621
+ govet:
622
+ enable:
623
+ - shadow
624
+
625
+ issues:
626
+ exclude-use-default: false
627
+ \`\`\`
628
+
629
+ ## Quick reference: Go idioms
630
+
631
+ | Idiom | Description |
632
+ |-------|-------------|
633
+ | Accept interfaces, return structs | Functions accept interface params, return concrete types |
634
+ | Errors are values | Treat errors as first-class values, not exceptions |
635
+ | Don't communicate by sharing memory | Use channels for coordination between goroutines |
636
+ | Make the zero value useful | Types should work without explicit initialization |
637
+ | A little copying is better than a little dependency | Avoid unnecessary external dependencies |
638
+ | Clear is better than clever | Prioritize readability over cleverness |
639
+ | gofmt is no one's favorite but everyone's friend | Always format with gofmt/goimports |
640
+ | Return early | Handle errors first, keep happy path unindented |
641
+
642
+ ## Anti-patterns to avoid
643
+
644
+ \`\`\`go
645
+ // Bad: naked returns in long functions
646
+ func process() (result int, err error) {
647
+ // ... 50 lines ...
648
+ return // What is being returned?
649
+ }
650
+
651
+ // Bad: using panic for control flow
652
+ func GetUser(id string) *User {
653
+ user, err := db.Find(id)
654
+ if err != nil {
655
+ panic(err) // Don't do this
656
+ }
657
+ return user
658
+ }
659
+
660
+ // Bad: passing context in struct
661
+ type Request struct {
662
+ ctx context.Context // Context should be first param
663
+ ID string
664
+ }
665
+
666
+ // Good: context as first parameter
667
+ func ProcessRequest(ctx context.Context, id string) error {
668
+ // ...
669
+ }
670
+
671
+ // Bad: mixing value and pointer receivers
672
+ type Counter struct{ n int }
673
+ func (c Counter) Value() int { return c.n } // Value receiver
674
+ func (c *Counter) Increment() { c.n++ } // Pointer receiver
675
+ // Pick one style and be consistent
676
+ \`\`\`
677
+
678
+ **Remember**: Go code should be boring in the best way — predictable, consistent, and easy to understand. When in doubt, keep it simple.
679
+
680
+ ### golang-testing
681
+
682
+ Go testing patterns including TDD workflow, table-driven tests, subtests, benchmarks, fuzzing, golden files, HTTP handler testing, mocking with interfaces, and coverage targets. Use when writing new Go functions, adding test coverage, creating benchmarks, or following TDD in a Go project.
683
+
684
+ # Go Testing Patterns
685
+
686
+ Comprehensive Go testing patterns for writing reliable, maintainable tests following TDD methodology.
687
+
688
+ ## When to use
689
+
690
+ - Writing new Go functions or methods.
691
+ - Adding test coverage to existing code.
692
+ - Creating benchmarks for performance-critical code.
693
+ - Implementing fuzz tests for input validation.
694
+ - Following a TDD workflow in Go projects.
695
+
696
+ ## TDD workflow for Go
697
+
698
+ ### The RED-GREEN-REFACTOR cycle
699
+
700
+ \`\`\`
701
+ RED → Write a failing test first
702
+ GREEN → Write minimal code to pass the test
703
+ REFACTOR → Improve code while keeping tests green
704
+ REPEAT → Continue with next requirement
705
+ \`\`\`
706
+
707
+ ### Step-by-step TDD in Go
708
+
709
+ \`\`\`go
710
+ // Step 1: Define the interface/signature
711
+ // calculator.go
712
+ package calculator
713
+
714
+ func Add(a, b int) int {
715
+ panic("not implemented") // Placeholder
716
+ }
717
+
718
+ // Step 2: Write failing test (RED)
719
+ // calculator_test.go
720
+ package calculator
721
+
722
+ import "testing"
723
+
724
+ func TestAdd(t *testing.T) {
725
+ got := Add(2, 3)
726
+ want := 5
727
+ if got != want {
728
+ t.Errorf("Add(2, 3) = %d; want %d", got, want)
729
+ }
730
+ }
731
+
732
+ // Step 3: Run test - verify FAIL
733
+ // $ go test
734
+ // --- FAIL: TestAdd (0.00s)
735
+ // panic: not implemented
736
+
737
+ // Step 4: Implement minimal code (GREEN)
738
+ func Add(a, b int) int {
739
+ return a + b
740
+ }
741
+
742
+ // Step 5: Run test - verify PASS
743
+ // $ go test
744
+ // PASS
745
+
746
+ // Step 6: Refactor if needed, verify tests still pass
747
+ \`\`\`
748
+
749
+ ## Table-driven tests
750
+
751
+ The standard pattern for Go tests. Enables comprehensive coverage with minimal code.
752
+
753
+ \`\`\`go
754
+ func TestAdd(t *testing.T) {
755
+ tests := []struct {
756
+ name string
757
+ a, b int
758
+ expected int
759
+ }{
760
+ {"positive numbers", 2, 3, 5},
761
+ {"negative numbers", -1, -2, -3},
762
+ {"zero values", 0, 0, 0},
763
+ {"mixed signs", -1, 1, 0},
764
+ {"large numbers", 1000000, 2000000, 3000000},
765
+ }
766
+
767
+ for _, tt := range tests {
768
+ t.Run(tt.name, func(t *testing.T) {
769
+ got := Add(tt.a, tt.b)
770
+ if got != tt.expected {
771
+ t.Errorf("Add(%d, %d) = %d; want %d",
772
+ tt.a, tt.b, got, tt.expected)
773
+ }
774
+ })
775
+ }
776
+ }
777
+ \`\`\`
778
+
779
+ ### Table-driven tests with error cases
780
+
781
+ \`\`\`go
782
+ func TestParseConfig(t *testing.T) {
783
+ tests := []struct {
784
+ name string
785
+ input string
786
+ want *Config
787
+ wantErr bool
788
+ }{
789
+ {
790
+ name: "valid config",
791
+ input: \`{"host": "localhost", "port": 8080}\`,
792
+ want: &Config{Host: "localhost", Port: 8080},
793
+ },
794
+ {
795
+ name: "invalid JSON",
796
+ input: \`{invalid}\`,
797
+ wantErr: true,
798
+ },
799
+ {
800
+ name: "empty input",
801
+ input: "",
802
+ wantErr: true,
803
+ },
804
+ {
805
+ name: "minimal config",
806
+ input: \`{}\`,
807
+ want: &Config{}, // Zero value config
808
+ },
809
+ }
810
+
811
+ for _, tt := range tests {
812
+ t.Run(tt.name, func(t *testing.T) {
813
+ got, err := ParseConfig(tt.input)
814
+
815
+ if tt.wantErr {
816
+ if err == nil {
817
+ t.Error("expected error, got nil")
818
+ }
819
+ return
820
+ }
821
+
822
+ if err != nil {
823
+ t.Fatalf("unexpected error: %v", err)
824
+ }
825
+
826
+ if !reflect.DeepEqual(got, tt.want) {
827
+ t.Errorf("got %+v; want %+v", got, tt.want)
828
+ }
829
+ })
830
+ }
831
+ }
832
+ \`\`\`
833
+
834
+ ## Subtests and sub-benchmarks
835
+
836
+ ### Organizing related tests
837
+
838
+ \`\`\`go
839
+ func TestUser(t *testing.T) {
840
+ // Setup shared by all subtests
841
+ db := setupTestDB(t)
842
+
843
+ t.Run("Create", func(t *testing.T) {
844
+ user := &User{Name: "Alice"}
845
+ err := db.CreateUser(user)
846
+ if err != nil {
847
+ t.Fatalf("CreateUser failed: %v", err)
848
+ }
849
+ if user.ID == "" {
850
+ t.Error("expected user ID to be set")
851
+ }
852
+ })
853
+
854
+ t.Run("Get", func(t *testing.T) {
855
+ user, err := db.GetUser("alice-id")
856
+ if err != nil {
857
+ t.Fatalf("GetUser failed: %v", err)
858
+ }
859
+ if user.Name != "Alice" {
860
+ t.Errorf("got name %q; want %q", user.Name, "Alice")
861
+ }
862
+ })
863
+
864
+ t.Run("Update", func(t *testing.T) {
865
+ // ...
866
+ })
867
+
868
+ t.Run("Delete", func(t *testing.T) {
869
+ // ...
870
+ })
871
+ }
872
+ \`\`\`
873
+
874
+ ### Parallel subtests
875
+
876
+ \`\`\`go
877
+ func TestParallel(t *testing.T) {
878
+ tests := []struct {
879
+ name string
880
+ input string
881
+ }{
882
+ {"case1", "input1"},
883
+ {"case2", "input2"},
884
+ {"case3", "input3"},
885
+ }
886
+
887
+ for _, tt := range tests {
888
+ tt := tt // Capture range variable
889
+ t.Run(tt.name, func(t *testing.T) {
890
+ t.Parallel() // Run subtests in parallel
891
+ result := Process(tt.input)
892
+ // assertions...
893
+ _ = result
894
+ })
895
+ }
896
+ }
897
+ \`\`\`
898
+
899
+ ## Test helpers
900
+
901
+ ### Helper functions
902
+
903
+ \`\`\`go
904
+ func setupTestDB(t *testing.T) *sql.DB {
905
+ t.Helper() // Marks this as a helper function
906
+
907
+ db, err := sql.Open("sqlite3", ":memory:")
908
+ if err != nil {
909
+ t.Fatalf("failed to open database: %v", err)
910
+ }
911
+
912
+ // Cleanup when test finishes
913
+ t.Cleanup(func() {
914
+ db.Close()
915
+ })
916
+
917
+ // Run migrations
918
+ if _, err := db.Exec(schema); err != nil {
919
+ t.Fatalf("failed to create schema: %v", err)
920
+ }
921
+
922
+ return db
923
+ }
924
+
925
+ func assertNoError(t *testing.T, err error) {
926
+ t.Helper()
927
+ if err != nil {
928
+ t.Fatalf("unexpected error: %v", err)
929
+ }
930
+ }
931
+
932
+ func assertEqual[T comparable](t *testing.T, got, want T) {
933
+ t.Helper()
934
+ if got != want {
935
+ t.Errorf("got %v; want %v", got, want)
936
+ }
937
+ }
938
+ \`\`\`
939
+
940
+ ### Temporary files and directories
941
+
942
+ \`\`\`go
943
+ func TestFileProcessing(t *testing.T) {
944
+ // Create temp directory - automatically cleaned up
945
+ tmpDir := t.TempDir()
946
+
947
+ // Create test file
948
+ testFile := filepath.Join(tmpDir, "test.txt")
949
+ err := os.WriteFile(testFile, []byte("test content"), 0644)
950
+ if err != nil {
951
+ t.Fatalf("failed to create test file: %v", err)
952
+ }
953
+
954
+ // Run test
955
+ result, err := ProcessFile(testFile)
956
+ if err != nil {
957
+ t.Fatalf("ProcessFile failed: %v", err)
958
+ }
959
+
960
+ // Assert...
961
+ _ = result
962
+ }
963
+ \`\`\`
964
+
965
+ ## Golden files
966
+
967
+ Testing against expected output files stored in \`testdata/\`.
968
+
969
+ \`\`\`go
970
+ var update = flag.Bool("update", false, "update golden files")
971
+
972
+ func TestRender(t *testing.T) {
973
+ tests := []struct {
974
+ name string
975
+ input Template
976
+ }{
977
+ {"simple", Template{Name: "test"}},
978
+ {"complex", Template{Name: "test", Items: []string{"a", "b"}}},
979
+ }
980
+
981
+ for _, tt := range tests {
982
+ t.Run(tt.name, func(t *testing.T) {
983
+ got := Render(tt.input)
984
+
985
+ golden := filepath.Join("testdata", tt.name+".golden")
986
+
987
+ if *update {
988
+ // Update golden file: go test -update
989
+ err := os.WriteFile(golden, got, 0644)
990
+ if err != nil {
991
+ t.Fatalf("failed to update golden file: %v", err)
992
+ }
993
+ }
994
+
995
+ want, err := os.ReadFile(golden)
996
+ if err != nil {
997
+ t.Fatalf("failed to read golden file: %v", err)
998
+ }
999
+
1000
+ if !bytes.Equal(got, want) {
1001
+ t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want)
1002
+ }
1003
+ })
1004
+ }
1005
+ }
1006
+ \`\`\`
1007
+
1008
+ ## Mocking with interfaces
1009
+
1010
+ ### Interface-based mocking
1011
+
1012
+ \`\`\`go
1013
+ // Define interface for dependencies
1014
+ type UserRepository interface {
1015
+ GetUser(id string) (*User, error)
1016
+ SaveUser(user *User) error
1017
+ }
1018
+
1019
+ // Production implementation
1020
+ type PostgresUserRepository struct {
1021
+ db *sql.DB
1022
+ }
1023
+
1024
+ func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
1025
+ // Real database query
1026
+ }
1027
+
1028
+ // Mock implementation for tests
1029
+ type MockUserRepository struct {
1030
+ GetUserFunc func(id string) (*User, error)
1031
+ SaveUserFunc func(user *User) error
1032
+ }
1033
+
1034
+ func (m *MockUserRepository) GetUser(id string) (*User, error) {
1035
+ return m.GetUserFunc(id)
1036
+ }
1037
+
1038
+ func (m *MockUserRepository) SaveUser(user *User) error {
1039
+ return m.SaveUserFunc(user)
1040
+ }
1041
+
1042
+ // Test using mock
1043
+ func TestUserService(t *testing.T) {
1044
+ mock := &MockUserRepository{
1045
+ GetUserFunc: func(id string) (*User, error) {
1046
+ if id == "123" {
1047
+ return &User{ID: "123", Name: "Alice"}, nil
1048
+ }
1049
+ return nil, ErrNotFound
1050
+ },
1051
+ }
1052
+
1053
+ service := NewUserService(mock)
1054
+
1055
+ user, err := service.GetUserProfile("123")
1056
+ if err != nil {
1057
+ t.Fatalf("unexpected error: %v", err)
1058
+ }
1059
+ if user.Name != "Alice" {
1060
+ t.Errorf("got name %q; want %q", user.Name, "Alice")
1061
+ }
1062
+ }
1063
+ \`\`\`
1064
+
1065
+ ## Benchmarks
1066
+
1067
+ ### Basic benchmarks
1068
+
1069
+ \`\`\`go
1070
+ func BenchmarkProcess(b *testing.B) {
1071
+ data := generateTestData(1000)
1072
+ b.ResetTimer() // Don't count setup time
1073
+
1074
+ for i := 0; i < b.N; i++ {
1075
+ Process(data)
1076
+ }
1077
+ }
1078
+
1079
+ // Run: go test -bench=BenchmarkProcess -benchmem
1080
+ // Output: BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op
1081
+ \`\`\`
1082
+
1083
+ ### Benchmark with different sizes
1084
+
1085
+ \`\`\`go
1086
+ func BenchmarkSort(b *testing.B) {
1087
+ sizes := []int{100, 1000, 10000, 100000}
1088
+
1089
+ for _, size := range sizes {
1090
+ b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
1091
+ data := generateRandomSlice(size)
1092
+ b.ResetTimer()
1093
+
1094
+ for i := 0; i < b.N; i++ {
1095
+ // Make a copy to avoid sorting already sorted data
1096
+ tmp := make([]int, len(data))
1097
+ copy(tmp, data)
1098
+ sort.Ints(tmp)
1099
+ }
1100
+ })
1101
+ }
1102
+ }
1103
+ \`\`\`
1104
+
1105
+ ### Memory allocation benchmarks
1106
+
1107
+ \`\`\`go
1108
+ func BenchmarkStringConcat(b *testing.B) {
1109
+ parts := []string{"hello", "world", "foo", "bar", "baz"}
1110
+
1111
+ b.Run("plus", func(b *testing.B) {
1112
+ for i := 0; i < b.N; i++ {
1113
+ var s string
1114
+ for _, p := range parts {
1115
+ s += p
1116
+ }
1117
+ _ = s
1118
+ }
1119
+ })
1120
+
1121
+ b.Run("builder", func(b *testing.B) {
1122
+ for i := 0; i < b.N; i++ {
1123
+ var sb strings.Builder
1124
+ for _, p := range parts {
1125
+ sb.WriteString(p)
1126
+ }
1127
+ _ = sb.String()
1128
+ }
1129
+ })
1130
+
1131
+ b.Run("join", func(b *testing.B) {
1132
+ for i := 0; i < b.N; i++ {
1133
+ _ = strings.Join(parts, "")
1134
+ }
1135
+ })
1136
+ }
1137
+ \`\`\`
1138
+
1139
+ ## Fuzzing (Go 1.18+)
1140
+
1141
+ ### Basic fuzz test
1142
+
1143
+ \`\`\`go
1144
+ func FuzzParseJSON(f *testing.F) {
1145
+ // Add seed corpus
1146
+ f.Add(\`{"name": "test"}\`)
1147
+ f.Add(\`{"count": 123}\`)
1148
+ f.Add(\`[]\`)
1149
+ f.Add(\`""\`)
1150
+
1151
+ f.Fuzz(func(t *testing.T, input string) {
1152
+ var result map[string]interface{}
1153
+ err := json.Unmarshal([]byte(input), &result)
1154
+
1155
+ if err != nil {
1156
+ // Invalid JSON is expected for random input
1157
+ return
1158
+ }
1159
+
1160
+ // If parsing succeeded, re-encoding should work
1161
+ _, err = json.Marshal(result)
1162
+ if err != nil {
1163
+ t.Errorf("Marshal failed after successful Unmarshal: %v", err)
1164
+ }
1165
+ })
1166
+ }
1167
+
1168
+ // Run: go test -fuzz=FuzzParseJSON -fuzztime=30s
1169
+ \`\`\`
1170
+
1171
+ ### Fuzz test with multiple inputs
1172
+
1173
+ \`\`\`go
1174
+ func FuzzCompare(f *testing.F) {
1175
+ f.Add("hello", "world")
1176
+ f.Add("", "")
1177
+ f.Add("abc", "abc")
1178
+
1179
+ f.Fuzz(func(t *testing.T, a, b string) {
1180
+ result := Compare(a, b)
1181
+
1182
+ // Property: Compare(a, a) should always equal 0
1183
+ if a == b && result != 0 {
1184
+ t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result)
1185
+ }
1186
+
1187
+ // Property: Compare(a, b) and Compare(b, a) should have opposite signs
1188
+ reverse := Compare(b, a)
1189
+ if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {
1190
+ if result != 0 || reverse != 0 {
1191
+ t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent",
1192
+ a, b, result, b, a, reverse)
1193
+ }
1194
+ }
1195
+ })
1196
+ }
1197
+ \`\`\`
1198
+
1199
+ ## Test coverage
1200
+
1201
+ ### Running coverage
1202
+
1203
+ \`\`\`bash
1204
+ # Basic coverage
1205
+ go test -cover ./...
1206
+
1207
+ # Generate coverage profile
1208
+ go test -coverprofile=coverage.out ./...
1209
+
1210
+ # View coverage in browser
1211
+ go tool cover -html=coverage.out
1212
+
1213
+ # View coverage by function
1214
+ go tool cover -func=coverage.out
1215
+
1216
+ # Coverage with race detection
1217
+ go test -race -coverprofile=coverage.out ./...
1218
+ \`\`\`
1219
+
1220
+ ### Coverage targets
1221
+
1222
+ | Code Type | Target |
1223
+ |-----------|--------|
1224
+ | Critical business logic | 100% |
1225
+ | Public APIs | 90%+ |
1226
+ | General code | 80%+ |
1227
+ | Generated code | Exclude |
1228
+
1229
+ ### Excluding generated code from coverage
1230
+
1231
+ \`\`\`go
1232
+ //go:generate mockgen -source=interface.go -destination=mock_interface.go
1233
+
1234
+ // In coverage profile, exclude with build tags:
1235
+ // go test -cover -tags=!generate ./...
1236
+ \`\`\`
1237
+
1238
+ ## HTTP handler testing
1239
+
1240
+ \`\`\`go
1241
+ func TestHealthHandler(t *testing.T) {
1242
+ // Create request
1243
+ req := httptest.NewRequest(http.MethodGet, "/health", nil)
1244
+ w := httptest.NewRecorder()
1245
+
1246
+ // Call handler
1247
+ HealthHandler(w, req)
1248
+
1249
+ // Check response
1250
+ resp := w.Result()
1251
+ defer resp.Body.Close()
1252
+
1253
+ if resp.StatusCode != http.StatusOK {
1254
+ t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK)
1255
+ }
1256
+
1257
+ body, _ := io.ReadAll(resp.Body)
1258
+ if string(body) != "OK" {
1259
+ t.Errorf("got body %q; want %q", body, "OK")
1260
+ }
1261
+ }
1262
+
1263
+ func TestAPIHandler(t *testing.T) {
1264
+ tests := []struct {
1265
+ name string
1266
+ method string
1267
+ path string
1268
+ body string
1269
+ wantStatus int
1270
+ wantBody string
1271
+ }{
1272
+ {
1273
+ name: "get user",
1274
+ method: http.MethodGet,
1275
+ path: "/users/123",
1276
+ wantStatus: http.StatusOK,
1277
+ wantBody: \`{"id":"123","name":"Alice"}\`,
1278
+ },
1279
+ {
1280
+ name: "not found",
1281
+ method: http.MethodGet,
1282
+ path: "/users/999",
1283
+ wantStatus: http.StatusNotFound,
1284
+ },
1285
+ {
1286
+ name: "create user",
1287
+ method: http.MethodPost,
1288
+ path: "/users",
1289
+ body: \`{"name":"Bob"}\`,
1290
+ wantStatus: http.StatusCreated,
1291
+ },
1292
+ }
1293
+
1294
+ handler := NewAPIHandler()
1295
+
1296
+ for _, tt := range tests {
1297
+ t.Run(tt.name, func(t *testing.T) {
1298
+ var body io.Reader
1299
+ if tt.body != "" {
1300
+ body = strings.NewReader(tt.body)
1301
+ }
1302
+
1303
+ req := httptest.NewRequest(tt.method, tt.path, body)
1304
+ req.Header.Set("Content-Type", "application/json")
1305
+ w := httptest.NewRecorder()
1306
+
1307
+ handler.ServeHTTP(w, req)
1308
+
1309
+ if w.Code != tt.wantStatus {
1310
+ t.Errorf("got status %d; want %d", w.Code, tt.wantStatus)
1311
+ }
1312
+
1313
+ if tt.wantBody != "" && w.Body.String() != tt.wantBody {
1314
+ t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody)
1315
+ }
1316
+ })
1317
+ }
1318
+ }
1319
+ \`\`\`
1320
+
1321
+ ## Testing commands
1322
+
1323
+ \`\`\`bash
1324
+ # Run all tests
1325
+ go test ./...
1326
+
1327
+ # Run tests with verbose output
1328
+ go test -v ./...
1329
+
1330
+ # Run specific test
1331
+ go test -run TestAdd ./...
1332
+
1333
+ # Run tests matching pattern
1334
+ go test -run "TestUser/Create" ./...
1335
+
1336
+ # Run tests with race detector
1337
+ go test -race ./...
1338
+
1339
+ # Run tests with coverage
1340
+ go test -cover -coverprofile=coverage.out ./...
1341
+
1342
+ # Run short tests only
1343
+ go test -short ./...
1344
+
1345
+ # Run tests with timeout
1346
+ go test -timeout 30s ./...
1347
+
1348
+ # Run benchmarks
1349
+ go test -bench=. -benchmem ./...
1350
+
1351
+ # Run fuzzing
1352
+ go test -fuzz=FuzzParse -fuzztime=30s ./...
1353
+
1354
+ # Count test runs (for flaky test detection)
1355
+ go test -count=10 ./...
1356
+ \`\`\`
1357
+
1358
+ ## Best practices
1359
+
1360
+ **DO:**
1361
+ - Write tests FIRST (TDD).
1362
+ - Use table-driven tests for comprehensive coverage.
1363
+ - Test behavior, not implementation.
1364
+ - Use \`t.Helper()\` in helper functions.
1365
+ - Use \`t.Parallel()\` for independent tests.
1366
+ - Clean up resources with \`t.Cleanup()\`.
1367
+ - Use meaningful test names that describe the scenario.
1368
+
1369
+ **DON'T:**
1370
+ - Test private functions directly (test through the public API).
1371
+ - Use \`time.Sleep()\` in tests (use channels or conditions).
1372
+ - Ignore flaky tests (fix or remove them).
1373
+ - Mock everything (prefer integration tests when possible).
1374
+ - Skip error path testing.
1375
+
1376
+ ## Integration with CI/CD
1377
+
1378
+ \`\`\`yaml
1379
+ # GitHub Actions example
1380
+ test:
1381
+ runs-on: ubuntu-latest
1382
+ steps:
1383
+ - uses: actions/checkout@v4
1384
+ - uses: actions/setup-go@v5
1385
+ with:
1386
+ go-version: '1.22'
1387
+
1388
+ - name: Run tests
1389
+ run: go test -race -coverprofile=coverage.out ./...
1390
+
1391
+ - name: Check coverage
1392
+ run: |
1393
+ go tool cover -func=coverage.out | grep total | awk '{print $3}' | \
1394
+ awk -F'%' '{if ($1 < 80) exit 1}'
1395
+ \`\`\`
1396
+
1397
+ **Remember**: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.