Files
oci-portal/internal/api/ociconfig_test.go
T

88 lines
2.7 KiB
Go

package api
import (
"bytes"
"errors"
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
// fakeServiceError 模拟 oci-go-sdk 的 common.ServiceError,驱动 respondOCIError 分支。
type fakeServiceError struct {
status int
code string
message string
}
func (e fakeServiceError) Error() string {
return fmt.Sprintf("Error returned by service. Http Status Code: %d. Error Code: %s. Message: %s",
e.status, e.code, e.message)
}
func (e fakeServiceError) GetHTTPStatusCode() int { return e.status }
func (e fakeServiceError) GetMessage() string { return e.message }
func (e fakeServiceError) GetCode() string { return e.code }
func (e fakeServiceError) GetOpcRequestID() string { return "req-1" }
// TestRespondErrorOCIStatus 覆盖上游状态码改写规则:
// 401/429 在前端有本地专属语义(登出 / 跳 /blocked),上游同码须改写 502。
func TestRespondErrorOCIStatus(t *testing.T) {
gin.SetMode(gin.TestMode)
cases := []struct {
name string
upstream int
wantStatus int
}{
{"404 原样透传", 404, 404},
{"409 原样透传", 409, 409},
{"上游 401 改写 502", 401, 502},
{"上游 429 改写 502", 429, 502},
{"非错误码兜底 502", 200, 502},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
err := fmt.Errorf("获取流量: %w", fakeServiceError{tc.upstream, "TooManyRequests", "slow down"})
respondError(c, err)
if rec.Code != tc.wantStatus {
t.Fatalf("status = %d, want %d", rec.Code, tc.wantStatus)
}
})
}
}
func TestRespondNetworkErrorHidesProxyCredentials(t *testing.T) {
gin.SetMode(gin.TestMode)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest(http.MethodGet, "/api/v1/oci-configs/1/instances", nil)
proxyErr := &url.Error{
Op: "Dial", URL: "http://proxy-user:proxy-pass@proxy.example.com:8080",
Err: errors.New("connect: connection refused"),
}
err := &url.Error{Op: "Get", URL: "https://iaas.example.com/instances",
Err: fmt.Errorf("proxyconnect tcp: %w", proxyErr)}
var logs bytes.Buffer
oldWriter := log.Writer()
log.SetOutput(&logs)
t.Cleanup(func() { log.SetOutput(oldWriter) })
respondError(c, err)
if rec.Code != http.StatusBadGateway || !strings.Contains(logs.String(), "[NET ") {
t.Fatalf("response/log = %d %q / %q", rec.Code, rec.Body.String(), logs.String())
}
for _, output := range []string{rec.Body.String(), logs.String()} {
if strings.Contains(output, "proxy-user") || strings.Contains(output, "proxy-pass") {
t.Fatalf("proxy credentials leaked: %q", output)
}
}
}