package api import ( "context" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "visiona-backend/internal/session" ) // TestNewProxyHandler_NoForwarder 驗證沒注入 Forwarder 時回 501。 func TestNewProxyHandler_NoForwarder(t *testing.T) { r := gin.New() r.Use(RequestIDMiddleware()) g := r.Group("/api") g.POST("/devices/scan", newProxyHandler(Deps{}, proxyOptions{})) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/api/devices/scan", nil)) assert.Equal(t, http.StatusNotImplemented, w.Code) } // TestNewProxyHandler_TunnelDisconnected 驗證沒 session 時回 502 TUNNEL_DISCONNECTED。 // // 這裡用 fakeSessionStore(List 回空)+ 非 nil forwarder 的「半個」 proxy handler; // 因為 nil forwarder 的 path 會先 return 501(見上方 test)。我們用真實 forwarder // 但不 dial — 直接在 pickActiveSessionToken 回 ErrSessionNotFound 就攔掉了。 // // Phase 0.7 security fix C1:handler 強制要求 UserContext;用 injectStaticUserContext // 顯式注入避免回 500(見 .autoflow/05-implementation/review/phase-0.7-security-audit.md)。 func TestNewProxyHandler_TunnelDisconnected(t *testing.T) { r := gin.New() r.Use(RequestIDMiddleware()) r.Use(injectStaticUserContext("demo-user", "")) g := r.Group("/api") g.POST("/devices/scan", newProxyHandler(Deps{ SessionStore: &fakeSessionStore{}, // List 回空 Forwarder: session.NewForwarder("http://localhost:0", nil), }, proxyOptions{})) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/api/devices/scan", nil)) assert.Equal(t, http.StatusBadGateway, w.Code) assert.Contains(t, w.Body.String(), ErrCodeTunnelDisconnect) } // TestPickActiveSessionToken 驗證 helper 回第一筆 match 的 session。 func TestPickActiveSessionToken(t *testing.T) { store := &fakeSessionStore{ sessions: []*session.Summary{ {Token: "t-other", UserID: "other"}, {Token: "t-me", UserID: "demo-user"}, }, } tok, err := pickActiveSessionToken(context.Background(), store, "demo-user", nil) assert.NoError(t, err) assert.Equal(t, "t-me", tok) } // TestPickActiveSessionToken_Empty 驗證沒 session 時回 ErrSessionNotFound。 func TestPickActiveSessionToken_Empty(t *testing.T) { store := &fakeSessionStore{} _, err := pickActiveSessionToken(context.Background(), store, "demo-user", nil) assert.ErrorIs(t, err, session.ErrSessionNotFound) }