|
|
@@ -173,17 +173,17 @@ describe("AuthService", () => {
|
|
|
expect(loggedOutSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
})
|
|
|
|
|
|
- it("should transition to inactive-session when valid credentials exist", async () => {
|
|
|
+ it("should transition to attempting-session when valid credentials exist", async () => {
|
|
|
const credentials = { clientToken: "test-token", sessionId: "test-session" }
|
|
|
mockContext.secrets.get.mockResolvedValue(JSON.stringify(credentials))
|
|
|
|
|
|
- const inactiveSessionSpy = vi.fn()
|
|
|
- authService.on("inactive-session", inactiveSessionSpy)
|
|
|
+ const attemptingSessionSpy = vi.fn()
|
|
|
+ authService.on("attempting-session", attemptingSessionSpy)
|
|
|
|
|
|
await authService.initialize()
|
|
|
|
|
|
- expect(authService.getState()).toBe("inactive-session")
|
|
|
- expect(inactiveSessionSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
+ expect(authService.getState()).toBe("attempting-session")
|
|
|
+ expect(attemptingSessionSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
expect(mockTimer.start).toHaveBeenCalled()
|
|
|
})
|
|
|
|
|
|
@@ -213,13 +213,13 @@ describe("AuthService", () => {
|
|
|
const newCredentials = { clientToken: "new-token", sessionId: "new-session" }
|
|
|
mockContext.secrets.get.mockResolvedValue(JSON.stringify(newCredentials))
|
|
|
|
|
|
- const inactiveSessionSpy = vi.fn()
|
|
|
- authService.on("inactive-session", inactiveSessionSpy)
|
|
|
+ const attemptingSessionSpy = vi.fn()
|
|
|
+ authService.on("attempting-session", attemptingSessionSpy)
|
|
|
|
|
|
onDidChangeCallback!({ key: "clerk-auth-credentials" })
|
|
|
await new Promise((resolve) => setTimeout(resolve, 0)) // Wait for async handling
|
|
|
|
|
|
- expect(inactiveSessionSpy).toHaveBeenCalled()
|
|
|
+ expect(attemptingSessionSpy).toHaveBeenCalled()
|
|
|
})
|
|
|
})
|
|
|
|
|
|
@@ -451,6 +451,26 @@ describe("AuthService", () => {
|
|
|
|
|
|
expect(authService.getSessionToken()).toBe("test-jwt")
|
|
|
})
|
|
|
+
|
|
|
+ it("should return correct values for new methods", async () => {
|
|
|
+ await authService.initialize()
|
|
|
+ expect(authService.hasOrIsAcquiringActiveSession()).toBe(false)
|
|
|
+
|
|
|
+ // Create a new service instance with credentials (attempting-session)
|
|
|
+ const credentials = { clientToken: "test-token", sessionId: "test-session" }
|
|
|
+ mockContext.secrets.get.mockResolvedValue(JSON.stringify(credentials))
|
|
|
+
|
|
|
+ const attemptingService = new AuthService(mockContext as unknown as vscode.ExtensionContext, mockLog)
|
|
|
+ await attemptingService.initialize()
|
|
|
+
|
|
|
+ expect(attemptingService.hasOrIsAcquiringActiveSession()).toBe(true)
|
|
|
+ expect(attemptingService.hasActiveSession()).toBe(false)
|
|
|
+
|
|
|
+ // Manually set state to active-session for testing
|
|
|
+ attemptingService["state"] = "active-session"
|
|
|
+ expect(attemptingService.hasOrIsAcquiringActiveSession()).toBe(true)
|
|
|
+ expect(attemptingService.hasActiveSession()).toBe(true)
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
describe("session refresh", () => {
|
|
|
@@ -497,7 +517,7 @@ describe("AuthService", () => {
|
|
|
expect(authService.getState()).toBe("active-session")
|
|
|
expect(authService.hasActiveSession()).toBe(true)
|
|
|
expect(authService.getSessionToken()).toBe("new-jwt-token")
|
|
|
- expect(activeSessionSpy).toHaveBeenCalledWith({ previousState: "inactive-session" })
|
|
|
+ expect(activeSessionSpy).toHaveBeenCalledWith({ previousState: "attempting-session" })
|
|
|
expect(userInfoSpy).toHaveBeenCalledWith({
|
|
|
userInfo: {
|
|
|
name: "John Doe",
|
|
|
@@ -530,6 +550,82 @@ describe("AuthService", () => {
|
|
|
await expect(timerCallback()).rejects.toThrow("Network error")
|
|
|
expect(mockLog).toHaveBeenCalledWith("[auth] Failed to refresh session", expect.any(Error))
|
|
|
})
|
|
|
+
|
|
|
+ it("should transition to inactive-session on first attempt failure", async () => {
|
|
|
+ // Mock failed token creation response
|
|
|
+ mockFetch.mockResolvedValue({
|
|
|
+ ok: false,
|
|
|
+ status: 500,
|
|
|
+ statusText: "Internal Server Error",
|
|
|
+ })
|
|
|
+
|
|
|
+ const inactiveSessionSpy = vi.fn()
|
|
|
+ authService.on("inactive-session", inactiveSessionSpy)
|
|
|
+
|
|
|
+ // Verify we start in attempting-session state
|
|
|
+ expect(authService.getState()).toBe("attempting-session")
|
|
|
+ expect(authService["isFirstRefreshAttempt"]).toBe(true)
|
|
|
+
|
|
|
+ const timerCallback = vi.mocked(RefreshTimer).mock.calls[0][0].callback
|
|
|
+
|
|
|
+ await expect(timerCallback()).rejects.toThrow()
|
|
|
+
|
|
|
+ // Should transition to inactive-session after first failure
|
|
|
+ expect(authService.getState()).toBe("inactive-session")
|
|
|
+ expect(authService["isFirstRefreshAttempt"]).toBe(false)
|
|
|
+ expect(inactiveSessionSpy).toHaveBeenCalledWith({ previousState: "attempting-session" })
|
|
|
+ })
|
|
|
+
|
|
|
+ it("should not transition to inactive-session on subsequent failures", async () => {
|
|
|
+ // First, transition to inactive-session by failing the first attempt
|
|
|
+ mockFetch.mockResolvedValue({
|
|
|
+ ok: false,
|
|
|
+ status: 500,
|
|
|
+ statusText: "Internal Server Error",
|
|
|
+ })
|
|
|
+
|
|
|
+ const timerCallback = vi.mocked(RefreshTimer).mock.calls[0][0].callback
|
|
|
+ await expect(timerCallback()).rejects.toThrow()
|
|
|
+
|
|
|
+ // Verify we're now in inactive-session
|
|
|
+ expect(authService.getState()).toBe("inactive-session")
|
|
|
+ expect(authService["isFirstRefreshAttempt"]).toBe(false)
|
|
|
+
|
|
|
+ const inactiveSessionSpy = vi.fn()
|
|
|
+ authService.on("inactive-session", inactiveSessionSpy)
|
|
|
+
|
|
|
+ // Subsequent failure should not trigger another transition
|
|
|
+ await expect(timerCallback()).rejects.toThrow()
|
|
|
+
|
|
|
+ expect(authService.getState()).toBe("inactive-session")
|
|
|
+ expect(inactiveSessionSpy).not.toHaveBeenCalled()
|
|
|
+ })
|
|
|
+
|
|
|
+ it("should clear credentials on 401 during first refresh attempt (bug fix)", async () => {
|
|
|
+ // Mock 401 response during first refresh attempt
|
|
|
+ mockFetch.mockResolvedValue({
|
|
|
+ ok: false,
|
|
|
+ status: 401,
|
|
|
+ statusText: "Unauthorized",
|
|
|
+ })
|
|
|
+
|
|
|
+ const loggedOutSpy = vi.fn()
|
|
|
+ authService.on("logged-out", loggedOutSpy)
|
|
|
+
|
|
|
+ const timerCallback = vi.mocked(RefreshTimer).mock.calls[0][0].callback
|
|
|
+ await expect(timerCallback()).rejects.toThrow()
|
|
|
+
|
|
|
+ // Should clear credentials (not just transition to inactive-session)
|
|
|
+ expect(mockContext.secrets.delete).toHaveBeenCalledWith("clerk-auth-credentials")
|
|
|
+ expect(mockLog).toHaveBeenCalledWith("[auth] Invalid/Expired client token: clearing credentials")
|
|
|
+
|
|
|
+ // Simulate credentials cleared event
|
|
|
+ mockContext.secrets.get.mockResolvedValue(undefined)
|
|
|
+ await authService["handleCredentialsChange"]()
|
|
|
+
|
|
|
+ expect(authService.getState()).toBe("logged-out")
|
|
|
+ expect(loggedOutSpy).toHaveBeenCalledWith({ previousState: "attempting-session" })
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
describe("user info", () => {
|
|
|
@@ -654,16 +750,16 @@ describe("AuthService", () => {
|
|
|
expect(loggedOutSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
})
|
|
|
|
|
|
- it("should emit inactive-session event", async () => {
|
|
|
+ it("should emit attempting-session event", async () => {
|
|
|
const credentials = { clientToken: "test-token", sessionId: "test-session" }
|
|
|
mockContext.secrets.get.mockResolvedValue(JSON.stringify(credentials))
|
|
|
|
|
|
- const inactiveSessionSpy = vi.fn()
|
|
|
- authService.on("inactive-session", inactiveSessionSpy)
|
|
|
+ const attemptingSessionSpy = vi.fn()
|
|
|
+ authService.on("attempting-session", attemptingSessionSpy)
|
|
|
|
|
|
await authService.initialize()
|
|
|
|
|
|
- expect(inactiveSessionSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
+ expect(attemptingSessionSpy).toHaveBeenCalledWith({ previousState: "initializing" })
|
|
|
})
|
|
|
|
|
|
it("should emit active-session event", async () => {
|
|
|
@@ -701,7 +797,7 @@ describe("AuthService", () => {
|
|
|
// Wait for async operations to complete
|
|
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
|
|
|
|
- expect(activeSessionSpy).toHaveBeenCalledWith({ previousState: "inactive-session" })
|
|
|
+ expect(activeSessionSpy).toHaveBeenCalledWith({ previousState: "attempting-session" })
|
|
|
})
|
|
|
|
|
|
it("should emit user-info event", async () => {
|
|
|
@@ -803,7 +899,7 @@ describe("AuthService", () => {
|
|
|
expect(mockTimer.stop).toHaveBeenCalled()
|
|
|
})
|
|
|
|
|
|
- it("should start timer on inactive-session transition", async () => {
|
|
|
+ it("should start timer on attempting-session transition", async () => {
|
|
|
const credentials = { clientToken: "test-token", sessionId: "test-session" }
|
|
|
mockContext.secrets.get.mockResolvedValue(JSON.stringify(credentials))
|
|
|
|
|
|
@@ -892,13 +988,13 @@ describe("AuthService", () => {
|
|
|
const newCredentials = { clientToken: "new-token", sessionId: "new-session" }
|
|
|
mockContext.secrets.get.mockResolvedValue(JSON.stringify(newCredentials))
|
|
|
|
|
|
- const inactiveSessionSpy = vi.fn()
|
|
|
- service.on("inactive-session", inactiveSessionSpy)
|
|
|
+ const attemptingSessionSpy = vi.fn()
|
|
|
+ service.on("attempting-session", attemptingSessionSpy)
|
|
|
|
|
|
onDidChangeCallback!({ key: `clerk-auth-credentials-${customUrl}` })
|
|
|
await new Promise((resolve) => setTimeout(resolve, 0)) // Wait for async handling
|
|
|
|
|
|
- expect(inactiveSessionSpy).toHaveBeenCalled()
|
|
|
+ expect(attemptingSessionSpy).toHaveBeenCalled()
|
|
|
})
|
|
|
|
|
|
it("should not respond to changes on different scoped keys", async () => {
|