Files
fleet/server/platform/endpointer/transport_error_test.go
Victor Lyuboslavsky d83fd5f384 Fixed client-side errors being incorrectly reported as server errors in OTEL telemetry (#40051)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40028 

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Fixed telemetry misclassification where client-side errors were
incorrectly reported as server errors. Client-side errors and request
cancellations are now properly categorized for improved error tracking
and observability.

* **Tests**
* Added test coverage for client error detection and context
cancellation handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-19 16:06:00 -06:00

127 lines
2.4 KiB
Go

package endpointer
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
"github.com/stretchr/testify/assert"
)
type foreignKeyError struct{}
func (foreignKeyError) IsForeignKey() bool { return true }
func (foreignKeyError) Error() string { return "" }
type alreadyExists struct{}
func (alreadyExists) IsExists() bool { return false }
func (alreadyExists) Error() string { return "" }
type newAndExciting struct{}
func (newAndExciting) Error() string { return "" }
type notFoundError struct {
platform_http.ErrorWithUUID
}
func (e *notFoundError) Error() string {
return "not found"
}
func (e *notFoundError) IsNotFound() bool {
return true
}
// validationError is a test implementation of validationErrorInterface.
type validationError struct {
errors []map[string]string
}
func (e validationError) Error() string {
return "validation failed"
}
func (e validationError) Invalid() []map[string]string {
return e.errors
}
// permissionError is a test implementation of permissionErrorInterface.
type permissionError struct {
message string
}
func (e permissionError) Error() string {
return e.message
}
func (e permissionError) PermissionError() []map[string]string {
return nil
}
func TestHandlesErrorsCode(t *testing.T) {
errorTests := []struct {
name string
err error
code int
}{
{
"validation",
validationError{errors: []map[string]string{{"name": "a", "reason": "b"}}},
http.StatusUnprocessableEntity,
},
{
"permission",
permissionError{message: "a"},
http.StatusForbidden,
},
{
"foreign key",
foreignKeyError{},
http.StatusUnprocessableEntity,
},
{
"data not found",
&notFoundError{},
http.StatusNotFound,
},
{
"already exists",
alreadyExists{},
http.StatusConflict,
},
{
"status coder",
platform_http.NewAuthFailedError(""),
http.StatusUnauthorized,
},
{
"context canceled",
context.Canceled,
499,
},
{
"wrapped context canceled",
fmt.Errorf("db query: %w", context.Canceled),
499,
},
{
"default",
newAndExciting{},
http.StatusInternalServerError,
},
}
for _, tt := range errorTests {
t.Run(tt.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
EncodeError(context.Background(), tt.err, recorder, nil)
assert.Equal(t, recorder.Code, tt.code)
})
}
}