package session import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestHTTPProxyClient_GetSession_OK 驗證能正確解析 remote-proxy 的 // /internal/session/:token 回應 → Summary。 func TestHTTPProxyClient_GetSession_OK(t *testing.T) { now := time.Now().UTC().Truncate(time.Second) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/internal/session/vAc_abc", r.URL.Path) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "token": "vAc_abc", "connected": true, "connected_at": now, "last_heartbeat": now, "remote_addr": "1.2.3.4:5678", "user_id": "demo-user", "device_id": "dev-1", }) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) sum, err := c.GetSession(context.Background(), "vAc_abc") require.NoError(t, err) require.NotNil(t, sum) assert.Equal(t, "vAc_abc", sum.Token) assert.Equal(t, "demo-user", sum.UserID) assert.Equal(t, "dev-1", sum.DeviceID) assert.Equal(t, "1.2.3.4:5678", sum.RemoteAddr) assert.True(t, sum.LastHeartbeat.Equal(now)) } // TestHTTPProxyClient_GetSession_NotFound 驗證 404 → ErrSessionNotFound。 func TestHTTPProxyClient_GetSession_NotFound(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":"NOT_FOUND"}`, http.StatusNotFound) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) _, err := c.GetSession(context.Background(), "vAc_xxx") assert.ErrorIs(t, err, ErrSessionNotFound) } // TestHTTPProxyClient_GetSession_ConnectedFalse_TreatedAsNotFound // 驗證 remote-proxy 回 connected=false(session 已被排隊清除)→ NotFound。 func TestHTTPProxyClient_GetSession_ConnectedFalse_TreatedAsNotFound(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(map[string]any{ "token": "vAc_dead", "connected": false, }) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) _, err := c.GetSession(context.Background(), "vAc_dead") assert.ErrorIs(t, err, ErrSessionNotFound) } // TestHTTPProxyClient_GetSession_EmptyToken 驗證空 token 直接被本地拒絕。 func TestHTTPProxyClient_GetSession_EmptyToken(t *testing.T) { c := NewHTTPProxyClient("http://localhost:9999", nil) _, err := c.GetSession(context.Background(), "") assert.Error(t, err) } // TestHTTPProxyClient_ListSessions_OK 驗證能正確 parse sessions array。 func TestHTTPProxyClient_ListSessions_OK(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/internal/sessions", r.URL.Path) _ = json.NewEncoder(w).Encode(map[string]any{ "sessions": []map[string]any{ {"token": "vAc_a", "userId": "u1"}, {"token": "vAc_b", "userId": "u2"}, }, "total": 2, }) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) sums, err := c.ListSessions(context.Background()) require.NoError(t, err) require.Len(t, sums, 2) assert.Equal(t, "vAc_a", sums[0].Token) } // TestHTTPProxyClient_ListSessions_Empty 驗證空 sessions 回 non-nil empty slice。 func TestHTTPProxyClient_ListSessions_Empty(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(map[string]any{"sessions": nil, "total": 0}) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) sums, err := c.ListSessions(context.Background()) require.NoError(t, err) assert.NotNil(t, sums) assert.Empty(t, sums) } // TestHTTPProxyClient_CloseSession_OK 驗證 200 → nil error。 func TestHTTPProxyClient_CloseSession_OK(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "/internal/session/vAc_x/close", r.URL.Path) _, _ = w.Write([]byte(`{"closed":true}`)) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) err := c.CloseSession(context.Background(), "vAc_x") assert.NoError(t, err) } // TestHTTPProxyClient_CloseSession_NotFound 驗證 404 → ErrSessionNotFound。 func TestHTTPProxyClient_CloseSession_NotFound(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer ts.Close() c := NewHTTPProxyClient(ts.URL, nil) err := c.CloseSession(context.Background(), "vAc_x") assert.ErrorIs(t, err, ErrSessionNotFound) } // TestHTTPProxyClient_BaseURL_TrimsTrailingSlash 驗證 baseURL 結尾的 / 會被移除。 func TestHTTPProxyClient_BaseURL_TrimsTrailingSlash(t *testing.T) { c := NewHTTPProxyClient("http://localhost:3801/", nil) assert.Equal(t, "http://localhost:3801", c.BaseURL()) }