Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions backend/pkg/server/response/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package response

import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"

"pentagi/pkg/version"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func init() {
gin.SetMode(gin.TestMode)
}

func TestNewHttpError(t *testing.T) {
err := NewHttpError(404, "NotFound", "resource not found")
assert.Equal(t, 404, err.HttpCode())
assert.Equal(t, "NotFound", err.Code())
assert.Equal(t, "resource not found", err.Msg())
}

func TestHttpError_Error(t *testing.T) {
err := NewHttpError(500, "Internal", "something broke")
assert.Equal(t, "Internal: something broke", err.Error())
}

func TestHttpError_ImplementsError(t *testing.T) {
var err error = NewHttpError(400, "Bad", "bad request")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Bad")
}

func TestPredefinedErrors(t *testing.T) {
tests := []struct {
name string
err *HttpError
httpCode int
code string
}{
{"ErrInternal", ErrInternal, 500, "Internal"},
{"ErrNotPermitted", ErrNotPermitted, 403, "NotPermitted"},
{"ErrAuthRequired", ErrAuthRequired, 403, "AuthRequired"},
{"ErrAuthInvalidCredentials", ErrAuthInvalidCredentials, 401, "Auth.InvalidCredentials"},
{"ErrUsersNotFound", ErrUsersNotFound, 404, "Users.NotFound"},
{"ErrRolesNotFound", ErrRolesNotFound, 404, "Roles.NotFound"},
{"ErrFlowsNotFound", ErrFlowsNotFound, 404, "Flows.NotFound"},
{"ErrTasksNotFound", ErrTasksNotFound, 404, "Tasks.NotFound"},
{"ErrTokenNotFound", ErrTokenNotFound, 404, "Token.NotFound"},
{"ErrContainersNotFound", ErrContainersNotFound, 404, "Containers.NotFound"},
{"ErrPromptsInvalidRequest", ErrPromptsInvalidRequest, 400, "Prompts.InvalidRequest"},
{"ErrTokenCreationDisabled", ErrTokenCreationDisabled, 400, "Token.CreationDisabled"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.httpCode, tt.err.HttpCode())
assert.Equal(t, tt.code, tt.err.Code())
assert.NotEmpty(t, tt.err.Msg())
assert.NotEmpty(t, tt.err.Error())
})
}
}

func TestSuccessResponse(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)

data := map[string]string{"id": "123"}
Success(c, http.StatusOK, data)

assert.Equal(t, http.StatusOK, w.Code)

var body map[string]any
err := json.Unmarshal(w.Body.Bytes(), &body)
require.NoError(t, err)
assert.Equal(t, "success", body["status"])
assert.NotNil(t, body["data"])
}

func TestSuccessResponse_Created(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)

Success(c, http.StatusCreated, gin.H{"name": "test"})

assert.Equal(t, http.StatusCreated, w.Code)
}

func TestErrorResponse(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)

Error(c, ErrInternal, errors.New("db connection failed"))

assert.Equal(t, http.StatusInternalServerError, w.Code)

var body map[string]any
err := json.Unmarshal(w.Body.Bytes(), &body)
require.NoError(t, err)
assert.Equal(t, "error", body["status"])
assert.Equal(t, "Internal", body["code"])
assert.Equal(t, "internal server error", body["msg"])
}

func TestErrorResponse_DevMode(t *testing.T) {
// Enable dev mode
version.PackageVer = ""

Comment on lines +112 to +115
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates the global version.PackageVer to force dev mode but never restores the prior value. If PackageVer is set via build flags (or future tests set it), this can leak state across tests in this package. Capture the current value before changing it and defer restoring it at the end of the test.

Copilot uses AI. Check for mistakes.
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)

originalErr := errors.New("detailed error info")
Error(c, ErrInternal, originalErr)

var body map[string]any
err := json.Unmarshal(w.Body.Bytes(), &body)
require.NoError(t, err)

// In dev mode, original error should be included
assert.Equal(t, "detailed error info", body["error"])
}

func TestErrorResponse_ProductionMode(t *testing.T) {
// Set production mode
version.PackageVer = "1.0.0"
defer func() { version.PackageVer = "" }()
Comment on lines +133 to +134
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestErrorResponse_ProductionMode resets version.PackageVer to an empty string unconditionally. If PackageVer had a non-empty value before the test (e.g., injected via ldflags), this will not restore the original state. Store the previous value and restore it in the deferred function instead of hardcoding "".

Suggested change
version.PackageVer = "1.0.0"
defer func() { version.PackageVer = "" }()
originalPackageVer := version.PackageVer
version.PackageVer = "1.0.0"
defer func() { version.PackageVer = originalPackageVer }()

Copilot uses AI. Check for mistakes.

w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)

Error(c, ErrInternal, errors.New("should not appear"))

var body map[string]any
err := json.Unmarshal(w.Body.Bytes(), &body)
require.NoError(t, err)

// In production mode, original error should NOT be included
_, hasError := body["error"]
assert.False(t, hasError)
}

func TestErrorResponse_NilOriginalError(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodGet, "/test", nil)

Error(c, ErrNotPermitted, nil)

assert.Equal(t, http.StatusForbidden, w.Code)

var body map[string]any
err := json.Unmarshal(w.Body.Bytes(), &body)
require.NoError(t, err)
assert.Equal(t, "NotPermitted", body["code"])
}
Loading