fix(auth): return to guest session on logout

This commit is contained in:
2026-07-03 16:14:04 +08:00
parent b22f23bcc4
commit 8f17ea9c9f
11 changed files with 97 additions and 26 deletions
@@ -49,8 +49,9 @@ function createTestAuthMachine(
LoginStatus,
{ accessToken: string }
>(async () => "facebook"),
logout: fromPromise<void>(async () => {
logout: fromPromise<LoginStatus>(async () => {
logoutSpy();
return "guest";
}),
},
});
@@ -125,7 +126,7 @@ describe("authMachine", () => {
actor.stop();
});
it("logout resets auth state and keeps initialization completed", async () => {
it("logout transitions authenticated users back to guest", async () => {
const logoutSpy = vi.fn<() => void>();
const actor = createActor(createTestAuthMachine({ logoutSpy })).start();
@@ -141,7 +142,7 @@ describe("authMachine", () => {
const context = actor.getSnapshot().context;
expect(logoutSpy).toHaveBeenCalledOnce();
expect(context.loginStatus).toBe("notLoggedIn");
expect(context.loginStatus).toBe("guest");
expect(context.hasInitialized).toBe(true);
actor.stop();
+4 -2
View File
@@ -94,10 +94,12 @@ export const oauthSignInActor = fromPromise<void, AuthProvider>(async ({ input }
}
});
export const logoutActor = fromPromise<void>(async () => {
export const logoutActor = fromPromise<LoginStatus>(async () => {
const authRepo = getAuthRepository();
const result = await authRepo.logout();
const deviceId = await deviceIdentifier.getDeviceId();
const result = await authRepo.logoutToGuest(deviceId);
if (Result.isErr(result)) throw result.error;
return "guest" as LoginStatus;
});
// ========== OAuth token → 后端业务 token sync actors ==========
+5 -4
View File
@@ -241,15 +241,16 @@ export const authMachine = setup({
src: "logout",
onDone: {
target: "idle",
actions: assign(() => ({
...initialState,
actions: assign({
loginStatus: ({ event }) => event.output,
hasInitialized: true,
})),
errorMessage: null,
}),
},
onError: {
target: "idle",
actions: assign(({ event }) => ({
...initialState,
loginStatus: "guest",
hasInitialized: true,
errorMessage: toAuthErrorMessage(event.error),
})),
@@ -164,7 +164,7 @@ function createLoadHistoryCallback(
}
describe("chatMachine transitions", () => {
it("keeps guest logout disabled while allowing upgrade to user login", async () => {
it("keeps guest logout disabled and supports user to guest transition", async () => {
const actor = createActor(createTestChatMachine()).start();
actor.send({ type: "ChatGuestLogin" });
@@ -180,6 +180,22 @@ describe("chatMachine transitions", () => {
snapshot.matches({ userSession: "ready" }),
);
actor.send({ type: "ChatGuestLogin" });
await waitFor(actor, (snapshot) =>
snapshot.matches({ guestSession: "ready" }),
);
actor.stop();
});
it("allows explicit logout from user session", async () => {
const actor = createActor(createTestChatMachine()).start();
actor.send({ type: "ChatUserLogin", token: "token" });
await waitFor(actor, (snapshot) =>
snapshot.matches({ userSession: "ready" }),
);
actor.send({ type: "ChatLogout" });
expect(actor.getSnapshot().matches("idle")).toBe(true);
@@ -270,7 +286,7 @@ describe("chatMachine transitions", () => {
actor.stop();
});
it("ignores guest login while an authenticated user session is active", async () => {
it("switches to guest session when guest login arrives during user session", async () => {
const actor = createActor(createTestChatMachine()).start();
actor.send({ type: "ChatUserLogin", token: "token" });
@@ -279,7 +295,9 @@ describe("chatMachine transitions", () => {
);
actor.send({ type: "ChatGuestLogin" });
expect(actor.getSnapshot().matches({ userSession: "ready" })).toBe(true);
await waitFor(actor, (snapshot) =>
snapshot.matches({ guestSession: "ready" }),
);
actor.stop();
});
+4
View File
@@ -235,6 +235,10 @@ export const chatMachine = setup({
},
],
on: {
ChatGuestLogin: {
target: "#chat.guestSession",
actions: "startGuestSession",
},
ChatLogout: {
target: "#chat.idle",
actions: "clearChatSession",