//go:build e2e package e2e import ( "encoding/json" "net/http" "testing" "github.com/stretchr/testify/require" ) // TestZZZJourney_SessionLifecycle 走 JWT 的完整生命週期: // refresh 取得新 pair → 用新 access 打 /me 成功 → logout → 同一 access 再打 = 401 // // 與 TestZZZ_AuthTokenRefreshAndLogout 同樣放在 ZZZ 區段最後跑(會撤銷 JWT)。 // 用 isolatedAuthClient 確保不汙染 member / permission journey 使用的 seed token。 func TestZZZJourney_SessionLifecycle(t *testing.T) { j := NewJourney(t, "J-3", "Session 生命週期(refresh → /me → logout → 舊 token 401)") defer j.Summary() c := isolatedAuthClient(t) j.Step("1", "POST /auth/token/refresh — 用 isolated refresh token 取得新 access/refresh pair", func(t *testing.T) { env := c.DoExpectOK(t, http.MethodPost, "/api/v1/auth/token/refresh", map[string]string{ "refresh_token": c.Fixture.RefreshToken, }, false) var pair struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` UID string `json:"uid"` } require.NoError(t, json.Unmarshal(env.Data, &pair)) require.NotEmpty(t, pair.AccessToken) require.NotEmpty(t, pair.RefreshToken) require.Equal(t, c.Fixture.UID, pair.UID) c.Fixture.AccessToken = pair.AccessToken c.Fixture.RefreshToken = pair.RefreshToken }) j.Step("2", "GET /members/me — 用 refresh 完拿到的新 access 打 /me 應該成功", func(t *testing.T) { c.DoExpectOK(t, http.MethodGet, "/api/v1/members/me", nil, true) }) j.Step("3", "POST /auth/logout — 把目前 jti 加入黑名單", func(t *testing.T) { c.DoExpectOK(t, http.MethodPost, "/api/v1/auth/logout", nil, true) }) j.Step("4", "GET /members/me — 同一 access token 再打應該 401(jti blacklisted)", func(t *testing.T) { resp, env := c.Do(t, http.MethodGet, "/api/v1/members/me", nil, true) require.Equal(t, http.StatusUnauthorized, resp.StatusCode) require.NotEqual(t, int64(successCode), env.Code) }) }