package mining import ( "net/http" "testing" ) // 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" if err.Error() != expected { t.Errorf("Expected %q, got %q", expected, err.Error()) } } // 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) if err.Cause != cause { t.Error("Cause was not set") } if err.Unwrap() != cause { t.Error("Unwrap did not return cause") } } // 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") if err.Details != "port must be between 1024 and 65535" { t.Errorf("Details not set correctly: %s", err.Details) } } // 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") if err.Suggestion != "check your network" { t.Errorf("Suggestion not set correctly: %s", err.Suggestion) } } // ErrMinerNotFound("test").StatusCode() == 404 // ErrMinerExists("test").StatusCode() == 409 func TestErrors_StatusCode_Good(t *testing.T) { cases := []struct { name string err *MiningError expected int }{ {"default", NewMiningError("TEST", "test"), http.StatusInternalServerError}, {"not found", ErrMinerNotFound("test"), http.StatusNotFound}, {"conflict", ErrMinerExists("test"), http.StatusConflict}, {"bad request", ErrInvalidConfig("bad"), http.StatusBadRequest}, {"service unavailable", ErrConnectionFailed("pool"), http.StatusServiceUnavailable}, {"timeout", ErrTimeout("operation"), http.StatusGatewayTimeout}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if c.err.StatusCode() != c.expected { t.Errorf("Expected status %d, got %d", c.expected, c.err.StatusCode()) } }) } } // ErrMinerNotFound("test").IsRetryable() == false // ErrInstallFailed("xmrig").IsRetryable() == true func TestErrors_IsRetryable_Good(t *testing.T) { cases := []struct { name string err *MiningError retryable bool }{ {"not found", ErrMinerNotFound("test"), false}, {"exists", ErrMinerExists("test"), false}, {"invalid config", ErrInvalidConfig("bad"), false}, {"install failed", ErrInstallFailed("xmrig"), true}, {"start failed", ErrStartFailed("test"), true}, {"connection failed", ErrConnectionFailed("pool"), true}, {"timeout", ErrTimeout("operation"), true}, {"database error", ErrDatabaseError("query"), true}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if c.err.IsRetryable() != c.retryable { t.Errorf("Expected retryable=%v, got %v", c.retryable, c.err.IsRetryable()) } }) } } // ErrMinerNotFound("test").Code == ErrCodeMinerNotFound // ErrStartFailed("test").Code == ErrCodeStartFailed func TestErrors_PredefinedErrors_Good(t *testing.T) { cases := []struct { name string err *MiningError code string }{ {"ErrMinerNotFound", ErrMinerNotFound("test"), ErrCodeMinerNotFound}, {"ErrMinerExists", ErrMinerExists("test"), ErrCodeMinerExists}, {"ErrMinerNotRunning", ErrMinerNotRunning("test"), ErrCodeMinerNotRunning}, {"ErrInstallFailed", ErrInstallFailed("xmrig"), ErrCodeInstallFailed}, {"ErrStartFailed", ErrStartFailed("test"), ErrCodeStartFailed}, {"ErrStopFailed", ErrStopFailed("test"), ErrCodeStopFailed}, {"ErrInvalidConfig", ErrInvalidConfig("bad port"), ErrCodeInvalidConfig}, {"ErrUnsupportedMiner", ErrUnsupportedMiner("unknown"), ErrCodeUnsupportedMiner}, {"ErrConnectionFailed", ErrConnectionFailed("pool:3333"), ErrCodeConnectionFailed}, {"ErrTimeout", ErrTimeout("GetStats"), ErrCodeTimeout}, {"ErrDatabaseError", ErrDatabaseError("insert"), ErrCodeDatabaseError}, {"ErrProfileNotFound", ErrProfileNotFound("abc123"), ErrCodeProfileNotFound}, {"ErrProfileExists", ErrProfileExists("My Profile"), ErrCodeProfileExists}, {"ErrInternal", ErrInternal("unexpected error"), ErrCodeInternalError}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if c.err.Code != c.code { t.Errorf("Expected code %s, got %s", c.code, c.err.Code) } if c.err.Message == "" { t.Error("Message should not be empty") } }) } } // 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"). WithCause(cause). WithDetails("timeout after 30s"). WithSuggestion("check firewall settings") if err.Code != ErrCodeConnectionFailed { t.Errorf("Code changed: %s", err.Code) } if err.Cause != cause { t.Error("Cause not set") } if err.Details != "timeout after 30s" { t.Errorf("Details not set: %s", err.Details) } if err.Suggestion != "check firewall settings" { t.Errorf("Suggestion not set: %s", err.Suggestion) } }