mirror of
https://github.com/xpzouying/xiaohongshu-mcp.git
synced 2026-08-28 17:45:51 +08:00
738d7c29a2
* fix(log): 4xx 记为 warning,与 5xx 服务端故障区分 respondError 是全仓库共用的错误出口,此前 32 个调用点一律打 ERROR。 鉴权开启后被扫描器打,401 会刷满 ERROR;且 400(调用方传错)与 500(服务端故障)在日志里无法用 level 区分。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(auth): 移除冗余 ENV 并标注测试用例适用范围 - Dockerfile 的 ENV AUTH_TOKEN="" 与不设置行为一致,docker run -e 同样能覆盖 - 标注两个头部带空格的用例只在内存态成立:真实请求的 OWS 已被 net/textproto 剥掉,客户端多打空格实际会放行 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func newAuthTestRouter(token string) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(authMiddleware(token))
|
|
router.GET("/protected", func(c *gin.Context) {
|
|
c.Status(http.StatusNoContent)
|
|
})
|
|
return router
|
|
}
|
|
|
|
func TestAuthMiddlewareAllowsRequestWhenDisabled(t *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
|
|
|
newAuthTestRouter("").ServeHTTP(recorder, request)
|
|
|
|
assert.Equal(t, http.StatusNoContent, recorder.Code)
|
|
}
|
|
|
|
func TestAuthMiddlewareAllowsValidBearerToken(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
authorization string
|
|
}{
|
|
{name: "standard scheme", authorization: "Bearer secret-token"},
|
|
{name: "lowercase scheme", authorization: "bearer secret-token"},
|
|
{name: "extra whitespace after scheme", authorization: "Bearer secret-token"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
|
request.Header.Set("Authorization", tt.authorization)
|
|
|
|
newAuthTestRouter("secret-token").ServeHTTP(recorder, request)
|
|
|
|
assert.Equal(t, http.StatusNoContent, recorder.Code)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAuthMiddlewareRejectsInvalidCredentials(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
authorization string
|
|
}{
|
|
{name: "missing header"},
|
|
{name: "wrong scheme", authorization: "Basic secret-token"},
|
|
{name: "missing token", authorization: "Bearer "},
|
|
{name: "wrong token", authorization: "Bearer wrong-token"},
|
|
// 下面两个头部两侧带空格的用例只在内存态成立:真实请求经 net/textproto
|
|
// 解析时两侧 OWS 已被剥掉,客户端多打空格实际会放行,不会被拒。
|
|
{name: "leading whitespace", authorization: " Bearer secret-token"},
|
|
{name: "trailing whitespace", authorization: "Bearer secret-token "},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
|
if tt.authorization != "" {
|
|
request.Header.Set("Authorization", tt.authorization)
|
|
}
|
|
|
|
newAuthTestRouter("secret-token").ServeHTTP(recorder, request)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, recorder.Code)
|
|
assert.Equal(t, "Bearer", recorder.Header().Get("WWW-Authenticate"))
|
|
assert.JSONEq(t, `{"error":"未授权","code":"UNAUTHORIZED"}`, recorder.Body.String())
|
|
})
|
|
}
|
|
}
|