feat: add WithPlatformCode for domain-level error identity
Adds an optional PlatformCode field to *Err, decoupled from the transport-level Code. Code drives HTTP/gRPC status mapping; PlatformCode is a stable domain identifier for consuming applications (e.g. a frontend performing i18n) to map errors to localised messages. Platform codes are optional — errors without a user-actionable meaning (500s, infrastructure failures, auth rejections) carry none. Fully backwards-compatible: no existing signatures or JSON output changed for errors without a platform code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -243,6 +243,72 @@ func TestErr_DuckTyping_ErrorContext_Nil(t *testing.T) {
|
||||
_ = err.ErrorContext()
|
||||
}
|
||||
|
||||
func TestErr_WithPlatformCode(t *testing.T) {
|
||||
t.Run("set and get", func(t *testing.T) {
|
||||
err := New(ErrNotFound, "employee not found").
|
||||
WithPlatformCode("EMPLOYEE_NOT_FOUND")
|
||||
if err.PlatformCode() != "EMPLOYEE_NOT_FOUND" {
|
||||
t.Errorf("PlatformCode() = %q, want EMPLOYEE_NOT_FOUND", err.PlatformCode())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty by default", func(t *testing.T) {
|
||||
err := New(ErrNotFound, "something not found")
|
||||
if err.PlatformCode() != "" {
|
||||
t.Errorf("PlatformCode() = %q, want empty string", err.PlatformCode())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("chaining with WithContext", func(t *testing.T) {
|
||||
err := New(ErrAlreadyExists, "email taken").
|
||||
WithPlatformCode("EMAIL_ALREADY_EXISTS").
|
||||
WithContext("field", "email")
|
||||
if err.PlatformCode() != "EMAIL_ALREADY_EXISTS" {
|
||||
t.Errorf("PlatformCode() = %q, want EMAIL_ALREADY_EXISTS", err.PlatformCode())
|
||||
}
|
||||
if err.Fields()["field"] != "email" {
|
||||
t.Errorf("Fields()[field] = %v, want email", err.Fields()["field"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("chaining preserves transport code", func(t *testing.T) {
|
||||
err := New(ErrPermissionDenied, "protected").
|
||||
WithPlatformCode("ROLE_SYSTEM_PROTECTED")
|
||||
if err.Code() != ErrPermissionDenied {
|
||||
t.Errorf("Code() = %s, want %s", err.Code(), ErrPermissionDenied)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestErr_MarshalJSON_PlatformCode(t *testing.T) {
|
||||
t.Run("included when set", func(t *testing.T) {
|
||||
err := New(ErrNotFound, "employee not found").
|
||||
WithPlatformCode("EMPLOYEE_NOT_FOUND")
|
||||
b, jsonErr := json.Marshal(err)
|
||||
if jsonErr != nil {
|
||||
t.Fatalf("MarshalJSON error: %v", jsonErr)
|
||||
}
|
||||
var out map[string]any
|
||||
if jsonErr = json.Unmarshal(b, &out); jsonErr != nil {
|
||||
t.Fatalf("unmarshal error: %v", jsonErr)
|
||||
}
|
||||
if out["platformCode"] != "EMPLOYEE_NOT_FOUND" {
|
||||
t.Errorf("json platformCode = %v, want EMPLOYEE_NOT_FOUND", out["platformCode"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("omitted when not set", func(t *testing.T) {
|
||||
err := New(ErrInternal, "unexpected error")
|
||||
b, jsonErr := json.Marshal(err)
|
||||
if jsonErr != nil {
|
||||
t.Fatalf("MarshalJSON error: %v", jsonErr)
|
||||
}
|
||||
if strings.Contains(string(b), "platformCode") {
|
||||
t.Errorf("json should omit platformCode when empty, got: %s", b)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCode_Description(t *testing.T) {
|
||||
tests := []struct {
|
||||
code Code
|
||||
|
||||
Reference in New Issue
Block a user