Files
versitygw/rdma/rcroutes/errors_mapping_linux_test.go
T
Jihyeon Gim 158ccdfe58 rdma: test RC route errors through the production server
Cover the route error boundary with the real S3 server: the
shared serializer keeps status and body for wrapped S3 errors,
raw fiber errors stay 500, and the platform stub answers 501.
The stub-answer classifier moves next to the shared marker type
in the same commit so every build answers 501 at the point the
test first runs, and the general CI workflow builds the
session-server archive before go test, which the Linux link of
this package now requires.
2026-09-04 19:44:45 +09:00

131 lines
3.2 KiB
Go

// Copyright 2026 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an "AS
// IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language
// governing permissions and limitations under the License.
//go:build linux && amd64 && cgo
package rcroutes
import (
"errors"
"fmt"
"net/http"
"testing"
"github.com/versity/versitygw/rdma/rcserver"
)
// This file verifies the mapping from session-server failures to
// protocol error responses. It runs where the Linux session
// server links; the s3api package exercises the shared
// serializer through the production S3 server on every platform.
func TestRouteErrorProtocolMapping(t *testing.T) {
tests := []struct {
name string
err error
code string
want int
}{
{
name: "invalid header is 400",
err: invalidHeader(hdrProtocol, "bogus"),
code: "InvalidRdmaRequest",
want: http.StatusBadRequest,
},
{
name: "unknown session is 404",
err: rcserver.ErrNoSession,
code: "NoSuchRdmaSession",
want: http.StatusNotFound,
},
{
name: "owner mismatch answers as unknown session",
err: fmt.Errorf("session owner mismatch: %w", rcserver.ErrNoSession),
code: "NoSuchRdmaSession",
want: http.StatusNotFound,
},
{
name: "stale session is 409",
err: rcserver.ErrStale,
code: "RdmaSessionConflict",
want: http.StatusConflict,
},
{
name: "duplicate borrow is 409",
err: fmt.Errorf("peer busy: %w", rcserver.ErrDouble),
code: "RdmaSessionConflict",
want: http.StatusConflict,
},
{
name: "wrong session state is 409",
err: rcserver.ErrState,
code: "RdmaSessionConflict",
want: http.StatusConflict,
},
{
name: "resource limit is 429",
err: rcserver.ErrLimit,
code: "RdmaResourceLimit",
want: http.StatusTooManyRequests,
},
{
name: "wire failure is 502",
err: rcserver.ErrWire,
code: "RdmaTransferFailed",
want: http.StatusBadGateway,
},
{
name: "short transfer is 400",
err: rcserver.ErrShort,
code: "InvalidRdmaRequest",
want: http.StatusBadRequest,
},
{
name: "value too long is 400",
err: rcserver.ErrTrunc,
code: "InvalidRdmaRequest",
want: http.StatusBadRequest,
},
{
name: "invalid argument is 400",
err: rcserver.ErrArg,
code: "InvalidRdmaRequest",
want: http.StatusBadRequest,
},
{
name: "admission refusal is 503",
err: errNotAdmitted(),
code: "RdmaServiceUnavailable",
want: http.StatusServiceUnavailable,
},
{
name: "unexpected error is 500",
err: errors.New("boom"),
code: "InternalError",
want: http.StatusInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := routeError(tt.err)
if got.HTTPStatusCode != tt.want {
t.Fatalf("status = %d, want %d", got.HTTPStatusCode, tt.want)
}
if got.Code != tt.code {
t.Fatalf("code = %q, want %q", got.Code, tt.code)
}
})
}
}