feat(chat): unlock paid messages before payment

This commit is contained in:
2026-06-30 18:56:17 +08:00
parent 5f94105bc6
commit e7a9e7abe5
21 changed files with 774 additions and 31 deletions
@@ -3,23 +3,36 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { ROUTES } from "@/router/routes";
import { consumePendingChatImageReturn } from "../chat_image_return_session";
import {
clearPendingChatUnlock,
peekPendingChatUnlock,
} from "../chat_unlock_session";
import {
consumeSubscriptionExitUrl,
consumeSubscriptionExplicitExitUrl,
getSubscriptionFallbackExitUrl,
peekSubscriptionExplicitExitUrl,
} from "../subscription_exit";
vi.mock("../chat_image_return_session", () => ({
consumePendingChatImageReturn: vi.fn(),
}));
vi.mock("../chat_unlock_session", () => ({
clearPendingChatUnlock: vi.fn(),
peekPendingChatUnlock: vi.fn(),
}));
const consumePendingChatImageReturnMock = vi.mocked(
consumePendingChatImageReturn,
);
const clearPendingChatUnlockMock = vi.mocked(clearPendingChatUnlock);
const peekPendingChatUnlockMock = vi.mocked(peekPendingChatUnlock);
describe("subscription exit helpers", () => {
beforeEach(() => {
consumePendingChatImageReturnMock.mockReset();
clearPendingChatUnlockMock.mockReset();
peekPendingChatUnlockMock.mockReset();
});
it("uses explicit chat image return urls first", () => {
@@ -35,14 +48,45 @@ describe("subscription exit helpers", () => {
it("falls back to chat when requested", () => {
consumePendingChatImageReturnMock.mockReturnValue(null);
peekPendingChatUnlockMock.mockReturnValue(null);
expect(consumeSubscriptionExitUrl("chat")).toBe(ROUTES.chat);
});
it("falls back to sidebar by default", () => {
consumePendingChatImageReturnMock.mockReturnValue(null);
peekPendingChatUnlockMock.mockReturnValue(null);
expect(getSubscriptionFallbackExitUrl(null)).toBe(ROUTES.sidebar);
expect(consumeSubscriptionExitUrl(null)).toBe(ROUTES.sidebar);
});
it("peeks chat unlock return urls without clearing them", () => {
peekPendingChatUnlockMock.mockReturnValue({
reason: "single_message_unlock",
messageId: "msg_1",
kind: "image",
returnUrl: "/chat/image/msg_1",
stage: "payment",
createdAt: 1,
});
expect(peekSubscriptionExplicitExitUrl()).toBe("/chat/image/msg_1");
expect(clearPendingChatUnlockMock).not.toHaveBeenCalled();
});
it("clears chat unlock return urls when consumed", () => {
consumePendingChatImageReturnMock.mockReturnValue(null);
peekPendingChatUnlockMock.mockReturnValue({
reason: "single_message_unlock",
messageId: "msg_1",
kind: "image",
returnUrl: "/chat/image/msg_1",
stage: "payment",
createdAt: 1,
});
expect(consumeSubscriptionExplicitExitUrl()).toBe("/chat/image/msg_1");
expect(clearPendingChatUnlockMock).toHaveBeenCalledTimes(1);
});
});