github.com/grafana/pyroscope@v1.18.0/.cursor/rules/go-testing.mdc (about)

     1  ---
     2  description: Go testing standards and patterns for Pyroscope
     3  globs:
     4    - "**/*_test.go"
     5  ---
     6  
     7  # Go Testing Standards
     8  
     9  ## Test File Naming
    10  
    11  - Test files: `*_test.go`
    12  - Test function naming: `TestFunctionName` or `TestComponentName_Method`
    13  
    14  ## Test Structure
    15  
    16  Use table-driven tests for multiple cases:
    17  
    18  ```go
    19  func TestDistributor_Push(t *testing.T) {
    20      t.Parallel()
    21  
    22      tests := []struct {
    23          name    string
    24          input   *pushv1.PushRequest
    25          wantErr bool
    26      }{
    27          {name: "valid request", input: validRequest(), wantErr: false},
    28          {name: "invalid tenant", input: invalidRequest(), wantErr: true},
    29      }
    30  
    31      for _, tt := range tests {
    32          t.Run(tt.name, func(t *testing.T) {
    33              d := setupDistributor(t)
    34              err := d.Push(context.Background(), tt.input)
    35              if tt.wantErr {
    36                  require.Error(t, err)
    37              } else {
    38                  require.NoError(t, err)
    39              }
    40          })
    41      }
    42  }
    43  ```
    44  
    45  ## Assertions
    46  
    47  - Use `require` for fatal assertions (test stops on failure)
    48  - Use `assert` for non-fatal assertions (test continues)
    49  
    50  ```go
    51  import (
    52      "github.com/stretchr/testify/assert"
    53      "github.com/stretchr/testify/require"
    54  )
    55  
    56  // Fatal - stops test immediately
    57  require.NoError(t, err)
    58  require.NotNil(t, result)
    59  
    60  // Non-fatal - continues test
    61  assert.Equal(t, expected, actual)
    62  ```
    63  
    64  ## Test Organization
    65  
    66  1. **Subtests**: Use `t.Run()` for grouping related cases
    67  2. **Parallel Tests**: Use `t.Parallel()` when tests are independent
    68  3. **Cleanup**: Always use `t.Cleanup()` for resource cleanup
    69  
    70  ```go
    71  func TestComponent(t *testing.T) {
    72      t.Parallel()
    73  
    74      resource := createResource(t)
    75      t.Cleanup(func() {
    76          resource.Close()
    77      })
    78  
    79      t.Run("subtest", func(t *testing.T) {
    80          // test logic
    81      })
    82  }
    83  ```
    84  
    85  ## Test Data
    86  
    87  - Store test data in `testdata/` directories
    88  - Use relative paths from the test file
    89  
    90  ## Integration Tests
    91  
    92  Use build tags for integration tests:
    93  
    94  ```go
    95  //go:build integration
    96  
    97  package mypackage_test
    98  
    99  func TestIntegration(t *testing.T) {
   100      // ...
   101  }
   102  ```
   103  
   104  ## Mocking
   105  
   106  - Use `mockery` for generating mocks from interfaces
   107  - Place mocks in appropriate locations based on scope
   108  
   109  ## Multi-tenancy Testing
   110  
   111  **Always test with multiple tenants** to catch isolation issues:
   112  
   113  ```go
   114  func TestMultiTenant(t *testing.T) {
   115      tenants := []string{"tenant-a", "tenant-b"}
   116  
   117      for _, tenant := range tenants {
   118          t.Run(tenant, func(t *testing.T) {
   119              ctx := tenant.InjectTenantID(context.Background(), tenant)
   120              // verify tenant isolation
   121          })
   122      }
   123  }
   124  ```
   125  
   126  ## Running Tests
   127  
   128  ```bash
   129  # Run all tests
   130  make go/test
   131  
   132  # Run specific package tests
   133  go test ./pkg/distributor/...
   134  
   135  # Run with race detector
   136  go test -race ./...
   137  
   138  # Run benchmarks
   139  go test -bench=. ./pkg/...
   140  ```