package apperror_test import ( "bytes" "encoding/json" "errors" "fmt" "testing" "github.com/gin-gonic/gin" "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "edge-infra.dev/pkg/edge/iam/apperror" "edge-infra.dev/pkg/lib/fog" ) func TestJSONError(t *testing.T) { dbErr := errors.New("SQL_ERR::NO_ROWS") handlerErr := apperror.NewJSONError(dbErr, 401, "user not found", gin.H{"k1": "v1", "k2": 2}) assert.Equal(t, map[string]interface{}{"k1": "v1", "k2": 2}, handlerErr.JSONDetails()) assert.Equal(t, "user not found. SQL_ERR::NO_ROWS", handlerErr.Error()) } func TestJSONErrorWithNilErr(t *testing.T) { err := apperror.NewJSONError(nil, 401, "subject's profile could not be found", gin.H{"error": "invalid_user"}) assert.Equal(t, "subject's profile could not be found", err.Error()) assert.Equal(t, "[401]: subject's profile could not be found", apperror.ErrorChain(err)) } func TestJSONErrorLogEntry(t *testing.T) { dbErr := errors.New("SQL_ERR::NO_ROWS") handlerErr := apperror.NewJSONError(dbErr, 401, "user not found", gin.H{"k1": "v1", "k2": "v2"}) l, buf := testLogger() code, responseBody := handlerErr.JSONResponse() msg := fmt.Sprintf("(%d) aborting with json", code) l.Error(handlerErr, msg, "details", handlerErr.JSONDetails(), "body", responseBody) out := getLogEntry(buf) assert.Equal(t, "(401) aborting with json", out["message"]) assert.Equal(t, "user not found. SQL_ERR::NO_ROWS", out["error"]) assert.Equal(t, map[string]interface{}{"k1": "v1", "k2": "v2"}, out["details"]) assert.Equal(t, map[string]interface{}{"k1": "v1", "k2": "v2", "message": "user not found"}, out["body"]) } func TestErrorChainWithJSONError(t *testing.T) { // db returns an app error err := errors.New("SQL_ERR::NO_ROWS") dbErr := apperror.New(err, "not_found", "user not found in db") // service wraps the error svcErr := fmt.Errorf("(user service) -> %w", dbErr) // handler wraps it into a status error handlerErr := apperror.NewJSONError(svcErr, 401, "user not found", gin.H{"error": "invalid_user"}) code, responseBody := handlerErr.JSONResponse() assert.Equal(t, 401, code) assert.Equal(t, map[string]interface{}{"error": "invalid_user", "message": "user not found"}, responseBody) errChain := apperror.ErrorChain(handlerErr) assert.Equal(t, "[401]: user not found. (user service) -> user not found in db . SQL_ERR::NO_ROWS", errChain) assert.Equal(t, "user not found. (user service) -> user not found in db . SQL_ERR::NO_ROWS", handlerErr.Error()) assert.Equal(t, map[string]interface{}{"error": "invalid_user"}, handlerErr.JSONDetails()) } func TestWithCaller(t *testing.T) { dbErr := errors.New("SQL_ERR::NO_ROWS") handlerErr := apperror.NewJSONError(dbErr, 401, "user not found", nil) l, buf := testLogger() loc := handlerErr.SourceLocation() l.Error(handlerErr, "testing caller information", "file", loc.File, "line", loc.Line) out := getLogEntry(buf) assert.Equal(t, "testing caller information", out["message"]) } func TestSourceLocationToMap(t *testing.T) { loc := apperror.SourceLocation{ File: "f1", Line: 53, } m := loc.ToMap() assert.Equal(t, m["file"], "f1") assert.Equal(t, m["line"], float64(53)) assert.Nil(t, m["func"]) } func testLogger() (logr.Logger, *bytes.Buffer) { buf := new(bytes.Buffer) l := fog.New(fog.To(buf)) return l, buf } func getLogEntry(buf *bytes.Buffer) map[string]interface{} { var out map[string]interface{} err := json.Unmarshal(buf.Bytes(), &out) if err != nil { return nil } return out }