ax(batch): replace prose comments with usage examples across all packages
Converts comments that restate function signatures into concrete usage examples per AX Principle 2. Affected files: database_race_test.go, interface_test.go, errors_test.go, xmrig_test.go, service_test.go, manager_test.go. Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
parent
fcae672dbc
commit
3673757d2a
6 changed files with 73 additions and 27 deletions
|
|
@ -8,18 +8,19 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// setupRaceTestDB creates a fresh database for race testing
|
||||
// cleanup := setupRaceTestDB(t)
|
||||
// defer cleanup()
|
||||
func setupRaceTestDB(t *testing.T) func() {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, "race_test.db")
|
||||
|
||||
cfg := Config{
|
||||
config := Config{
|
||||
Enabled: true,
|
||||
Path: dbPath,
|
||||
RetentionDays: 7,
|
||||
}
|
||||
|
||||
if err := Initialize(cfg); err != nil {
|
||||
if err := Initialize(config); err != nil {
|
||||
t.Fatalf("Failed to initialize database: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -29,8 +30,7 @@ func setupRaceTestDB(t *testing.T) func() {
|
|||
}
|
||||
}
|
||||
|
||||
// TestConcurrentHashrateInserts verifies that concurrent inserts
|
||||
// don't cause race conditions
|
||||
// 10 goroutines × 100 inserts each → no race detector warnings
|
||||
func TestConcurrentHashrateInserts(t *testing.T) {
|
||||
cleanup := setupRaceTestDB(t)
|
||||
defer cleanup()
|
||||
|
|
@ -73,8 +73,7 @@ func TestConcurrentHashrateInserts(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestConcurrentInsertAndQuery verifies that concurrent reads and writes
|
||||
// don't cause race conditions
|
||||
// 1 writer + 5 readers concurrently → no race detector warnings
|
||||
func TestConcurrentInsertAndQuery(t *testing.T) {
|
||||
cleanup := setupRaceTestDB(t)
|
||||
defer cleanup()
|
||||
|
|
@ -126,8 +125,7 @@ func TestConcurrentInsertAndQuery(t *testing.T) {
|
|||
// Test passes if no race detector warnings
|
||||
}
|
||||
|
||||
// TestConcurrentInsertAndCleanup verifies that cleanup doesn't race
|
||||
// with ongoing inserts
|
||||
// inserts (old + new data) + periodic Cleanup(7) → no race detector warnings
|
||||
func TestConcurrentInsertAndCleanup(t *testing.T) {
|
||||
cleanup := setupRaceTestDB(t)
|
||||
defer cleanup()
|
||||
|
|
@ -184,8 +182,7 @@ func TestConcurrentInsertAndCleanup(t *testing.T) {
|
|||
// Test passes if no race detector warnings
|
||||
}
|
||||
|
||||
// TestConcurrentStats verifies that GetHashrateStats can be called
|
||||
// concurrently without race conditions
|
||||
// 20 goroutines × 50 GetHashrateStats calls → no race detector warnings
|
||||
func TestConcurrentStats(t *testing.T) {
|
||||
cleanup := setupRaceTestDB(t)
|
||||
defer cleanup()
|
||||
|
|
@ -224,8 +221,7 @@ func TestConcurrentStats(t *testing.T) {
|
|||
// Test passes if no race detector warnings
|
||||
}
|
||||
|
||||
// TestConcurrentGetAllStats verifies that GetAllMinerStats can be called
|
||||
// concurrently without race conditions
|
||||
// 10 readers + 1 writer concurrently on GetAllMinerStats → no race detector warnings
|
||||
func TestConcurrentGetAllStats(t *testing.T) {
|
||||
cleanup := setupRaceTestDB(t)
|
||||
defer cleanup()
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ func TestNopStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestInterfaceCompatibility ensures all implementations satisfy HashrateStore
|
||||
// var _ HashrateStore = DefaultStore()
|
||||
// var _ HashrateStore = NopStore()
|
||||
func TestInterfaceCompatibility(t *testing.T) {
|
||||
var _ HashrateStore = DefaultStore()
|
||||
var _ HashrateStore = NopStore()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// TestErrors_Error_Good verifies the error string format with no cause
|
||||
// err := NewMiningError(ErrCodeMinerNotFound, "miner not found")
|
||||
// err.Error() == "MINER_NOT_FOUND: miner not found"
|
||||
func TestErrors_Error_Good(t *testing.T) {
|
||||
err := NewMiningError(ErrCodeMinerNotFound, "miner not found")
|
||||
expected := "MINER_NOT_FOUND: miner not found"
|
||||
|
|
@ -14,7 +15,8 @@ func TestErrors_Error_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_Error_Bad verifies the error string includes the underlying cause
|
||||
// err := NewMiningError("START_FAILED", "failed to start").WithCause(cause)
|
||||
// err.Unwrap() == cause
|
||||
func TestErrors_Error_Bad(t *testing.T) {
|
||||
cause := ErrInternal("underlying error")
|
||||
err := NewMiningError(ErrCodeStartFailed, "failed to start").WithCause(cause)
|
||||
|
|
@ -28,7 +30,8 @@ func TestErrors_Error_Bad(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_WithDetails_Good verifies details are attached to the error
|
||||
// err := NewMiningError("INVALID_CONFIG", "invalid config").WithDetails("port must be between 1024 and 65535")
|
||||
// err.Details == "port must be between 1024 and 65535"
|
||||
func TestErrors_WithDetails_Good(t *testing.T) {
|
||||
err := NewMiningError(ErrCodeInvalidConfig, "invalid config").
|
||||
WithDetails("port must be between 1024 and 65535")
|
||||
|
|
@ -38,7 +41,8 @@ func TestErrors_WithDetails_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_WithSuggestion_Good verifies suggestions are attached to the error
|
||||
// err := ErrConnectionFailed("pool").WithSuggestion("check your network")
|
||||
// err.Suggestion == "check your network"
|
||||
func TestErrors_WithSuggestion_Good(t *testing.T) {
|
||||
err := NewMiningError(ErrCodeConnectionFailed, "connection failed").
|
||||
WithSuggestion("check your network")
|
||||
|
|
@ -48,7 +52,8 @@ func TestErrors_WithSuggestion_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_StatusCode_Good verifies each error constructor returns the correct HTTP status
|
||||
// ErrMinerNotFound("test").StatusCode() == 404
|
||||
// ErrMinerExists("test").StatusCode() == 409
|
||||
func TestErrors_StatusCode_Good(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
|
@ -72,7 +77,8 @@ func TestErrors_StatusCode_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_IsRetryable_Good verifies retryable vs non-retryable classifications
|
||||
// ErrMinerNotFound("test").IsRetryable() == false
|
||||
// ErrInstallFailed("xmrig").IsRetryable() == true
|
||||
func TestErrors_IsRetryable_Good(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
|
@ -98,7 +104,8 @@ func TestErrors_IsRetryable_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_PredefinedErrors_Good verifies each error constructor sets the correct code and message
|
||||
// ErrMinerNotFound("test").Code == ErrCodeMinerNotFound
|
||||
// ErrStartFailed("test").Code == ErrCodeStartFailed
|
||||
func TestErrors_PredefinedErrors_Good(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
|
@ -133,7 +140,8 @@ func TestErrors_PredefinedErrors_Good(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestErrors_Chaining_Ugly verifies error chaining survives multiple WithCause/WithDetails/WithSuggestion calls
|
||||
// ErrConnectionFailed("pool:3333").WithCause(cause).WithDetails("timeout").WithSuggestion("check firewall")
|
||||
// err.Code == ErrCodeConnectionFailed && err.Cause == cause && err.Details == "timeout"
|
||||
func TestErrors_Chaining_Ugly(t *testing.T) {
|
||||
cause := ErrTimeout("network timeout")
|
||||
err := ErrConnectionFailed("pool:3333").
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
// setupTestManager creates a new Manager and a dummy executable for tests.
|
||||
// It also temporarily modifies the PATH to include the dummy executable's directory.
|
||||
// manager := setupTestManager(t)
|
||||
// defer manager.Stop()
|
||||
func setupTestManager(t *testing.T) *Manager {
|
||||
dummyDir := t.TempDir()
|
||||
executableName := "miner"
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// MockMiner is a mock implementation of the Miner interface for testing.
|
||||
// mock := &MockMiner{GetNameFunc: func() string { return "test" }}
|
||||
// mock.GetName() == "test"
|
||||
type MockMiner struct {
|
||||
InstallFunc func() error
|
||||
UninstallFunc func() error
|
||||
|
|
@ -56,7 +58,8 @@ func (m *MockMiner) ReduceHashrateHistory(now time.Time) { m.ReduceHashrateHist
|
|||
func (m *MockMiner) GetLogs() []string { return m.GetLogsFunc() }
|
||||
func (m *MockMiner) WriteStdin(input string) error { return m.WriteStdinFunc(input) }
|
||||
|
||||
// MockManager is a mock implementation of the Manager for testing.
|
||||
// mock := &MockManager{ListMinersFunc: func() []Miner { return nil }}
|
||||
// mock.ListMiners()
|
||||
type MockManager struct {
|
||||
ListMinersFunc func() []Miner
|
||||
ListAvailableMinersFunc func() []AvailableMiner
|
||||
|
|
@ -224,3 +227,41 @@ func TestService_HandleGetMinerHashrateHistory_Good(t *testing.T) {
|
|||
t.Errorf("expected status %d, got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRequestID_FormatAndUniqueness(t *testing.T) {
|
||||
firstRequestID := generateRequestID()
|
||||
secondRequestID := generateRequestID()
|
||||
|
||||
if firstRequestID == secondRequestID {
|
||||
t.Fatalf("expected unique request IDs, got %q twice", firstRequestID)
|
||||
}
|
||||
|
||||
for _, requestID := range []string{firstRequestID, secondRequestID} {
|
||||
parts := strings.Split(requestID, "-")
|
||||
if len(parts) != 2 {
|
||||
t.Fatalf("expected request ID format timestamp-randomhex, got %q", requestID)
|
||||
}
|
||||
if len(parts[1]) != 16 {
|
||||
t.Fatalf("expected 16 hex characters, got %q", parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestIDMiddleware_PreservesProvidedHeader(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.Use(requestIDMiddleware())
|
||||
router.GET("/health", func(context *gin.Context) {
|
||||
context.Status(http.StatusOK)
|
||||
})
|
||||
|
||||
request, _ := http.NewRequest(http.MethodGet, "/health", nil)
|
||||
request.Header.Set("X-Request-ID", "user-supplied-request-id")
|
||||
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(responseRecorder, request)
|
||||
|
||||
if responseRecorder.Header().Get("X-Request-ID") != "user-supplied-request-id" {
|
||||
t.Fatalf("expected middleware to preserve request ID header, got %q", responseRecorder.Header().Get("X-Request-ID"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
// MockRoundTripper is a mock implementation of http.RoundTripper for testing.
|
||||
type MockRoundTripper func(req *http.Request) *http.Response
|
||||
|
||||
// RoundTrip executes a single HTTP transaction, returning a Response for the given Request.
|
||||
// resp, _ := MockRoundTripper(fn).RoundTrip(req)
|
||||
func (f MockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req), nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue