140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
PrivateAlbumUnlockResponseSchema,
|
|
PrivateAlbumsResponseSchema,
|
|
UnlockPrivateAlbumRequestSchema,
|
|
} from "@/data/schemas/private-zone";
|
|
import { ApiPath } from "@/data/services/api";
|
|
|
|
const ALBUM_ID = "a1b2c3d4-0000-0000-0000-000000000000";
|
|
const COVER_URL =
|
|
"https://dbapi.banlv-ai.com/storage/v1/object/public/elio-schedules/01.jpg";
|
|
|
|
const lockedAlbum = {
|
|
albumId: ALBUM_ID,
|
|
momentId: `album:${ALBUM_ID}`,
|
|
characterId: "elio",
|
|
collectionKey: "manila_202607",
|
|
title: "Private Manila set",
|
|
content: null,
|
|
previewText: "Only for you.",
|
|
imageCount: 8,
|
|
mediaCount: 8,
|
|
images: [{ url: COVER_URL, type: "image", locked: true, index: 0 }],
|
|
locked: true,
|
|
unlocked: false,
|
|
unlockCost: 320,
|
|
requiredCredits: 320,
|
|
creditCostPerImage: 40,
|
|
currency: "credits",
|
|
canUnlockWithCredits: false,
|
|
publishedAt: "2026-07-13T00:00:00+00:00",
|
|
lockDetail: {
|
|
locked: true,
|
|
showContent: false,
|
|
showUpgrade: true,
|
|
reason: "private_album",
|
|
},
|
|
};
|
|
|
|
describe("Private Album schema models", () => {
|
|
it("keeps shared payment guidance on an insufficient-credit response", () => {
|
|
const response = PrivateAlbumUnlockResponseSchema.parse({
|
|
albumId: ALBUM_ID,
|
|
reason: "insufficient_credits",
|
|
paymentGuidance: {
|
|
guidanceId: "019c8f8d-17d3-7a42-b1cc-b6927b1927d5",
|
|
characterId: "elio",
|
|
characterName: "Elio Silvestri",
|
|
scene: "privateZoneLocked",
|
|
mode: "guide",
|
|
title: "Keep talking with Elio",
|
|
copy: "Baby, this album needs credits.",
|
|
ctaLabel: "View VIP & credit options",
|
|
purchaseOptions: ["vip", "topup"],
|
|
target: "subscription",
|
|
ruleId: "payment_guidance_privateZoneLocked",
|
|
},
|
|
});
|
|
|
|
expect(response.paymentGuidance?.characterId).toBe("elio");
|
|
});
|
|
|
|
it("keeps a locked album cover URL while preserving the lock state", () => {
|
|
const response = PrivateAlbumsResponseSchema.parse({
|
|
items: [lockedAlbum],
|
|
creditBalance: 100,
|
|
nextCursor: null,
|
|
hasMore: false,
|
|
});
|
|
|
|
expect(response.items[0]?.locked).toBe(true);
|
|
expect(response.items[0]?.unlocked).toBe(false);
|
|
expect(response.items[0]?.images[0]?.url).toBe(COVER_URL);
|
|
expect(response.items[0]?.lockDetail).toEqual({ locked: true });
|
|
});
|
|
|
|
it("strips response fields that are not used by the frontend", () => {
|
|
const response = PrivateAlbumsResponseSchema.parse({
|
|
items: [lockedAlbum],
|
|
creditBalance: 100,
|
|
packageOptions: [{ imageCount: 8, creditCost: 320 }],
|
|
});
|
|
const albumJson = response.items[0];
|
|
|
|
expect(albumJson).not.toHaveProperty("momentId");
|
|
expect(albumJson).not.toHaveProperty("characterId");
|
|
expect(albumJson).not.toHaveProperty("collectionKey");
|
|
expect(response).not.toHaveProperty("packageOptions");
|
|
});
|
|
|
|
it("keeps incomplete-content metadata without exposing pending images", () => {
|
|
const response = PrivateAlbumsResponseSchema.parse({
|
|
items: [],
|
|
creditBalance: 100,
|
|
pendingImageCount: 7,
|
|
hasIncompleteContent: true,
|
|
pendingImages: [{ url: COVER_URL }],
|
|
});
|
|
|
|
expect(response.pendingImageCount).toBe(7);
|
|
expect(response.hasIncompleteContent).toBe(true);
|
|
expect(response).not.toHaveProperty("pendingImages");
|
|
});
|
|
|
|
it("parses an unlock response with all album images", () => {
|
|
const response = PrivateAlbumUnlockResponseSchema.parse({
|
|
albumId: ALBUM_ID,
|
|
locked: false,
|
|
unlocked: true,
|
|
reason: "ok",
|
|
unlockCost: 320,
|
|
creditBalance: 180,
|
|
images: Array.from({ length: 8 }, (_, index) => ({
|
|
url: `${COVER_URL}?image=${index}`,
|
|
locked: false,
|
|
index,
|
|
})),
|
|
creditsCharged: 320,
|
|
});
|
|
|
|
expect(response.unlocked).toBe(true);
|
|
expect(response.images).toHaveLength(8);
|
|
expect(response.creditBalance).toBe(180);
|
|
expect(response).not.toHaveProperty("creditsCharged");
|
|
});
|
|
|
|
it("serializes the expected unlock cost", () => {
|
|
expect(
|
|
UnlockPrivateAlbumRequestSchema.parse({ expectedCost: 320 }),
|
|
).toEqual({ expectedCost: 320 });
|
|
});
|
|
|
|
it("encodes the album id in the unlock path", () => {
|
|
expect(ApiPath.privateZoneAlbumUnlock("album:91")).toBe(
|
|
"/api/private-zone/albums/album%3A91/unlock",
|
|
);
|
|
});
|
|
});
|