import { afterEach, expect, test, vi } from "vitest"; import { setAccessToken, setSelectedAppCode } from "@/shared/api/request"; import { getVipConfig, getVipProgram, grantVipTrialCard, updateVipConfig, updateVipProgram } from "./api"; afterEach(() => { setAccessToken(""); setSelectedAppCode("lalu"); vi.unstubAllGlobals(); }); test("vip program and level APIs normalize snake case responses", async () => { setSelectedAppCode("fami"); vi.stubGlobal( "fetch", vi .fn() .mockResolvedValueOnce( envelope({ config: { app_code: "fami", config_version: 3, grant_mode: "trial_card", level_count: 9, program_type: "tiered_privilege_v1", status: "active", trial_card_enabled: true, upgrade_expiry_policy: "replace_from_now", }, benefits: [ { auto_equip: false, benefit_code: "anti_kick", benefit_type: "function", execution_scope: "room", metadata_json: "{}", name: "防踢", resource_id: 0, resource_type: "", sort_order: 320, status: "active", trial_enabled: true, unlock_level: 9, }, ], }), ) .mockResolvedValueOnce( envelope({ levels: [ { benefits: [ { benefit_code: "anti_kick", benefit_type: "function", unlock_level: 9, name: "防踢", resource_id: 0, resource_type: "", execution_scope: "room", auto_equip: false, metadata_json: "{}", sort_order: 10, status: "active", trial_enabled: true, }, { auto_equip: false, benefit_code: "daily_coin_rebate", benefit_type: "function", execution_scope: "wallet", metadata_json: "{}", name: "金币返现", resource_id: 0, resource_type: "", sort_order: 20, status: "active", trial_enabled: true, unlock_level: 6, }, ], duration_ms: 2592000000, level: 9, name: "VIP9", price_coin: 9000, reward_resource_group_id: 99, sort_order: 90, status: "active", }, ], server_time_ms: 1780000000000, }), ), ); const program = await getVipProgram(); const config = await getVipConfig(); expect(program).toMatchObject({ appCode: "fami", benefitInheritancePolicy: "target_only", configVersion: 3, grantMode: "trial_card", levelCount: 9, programType: "tiered_privilege_v1", trialCardEnabled: true, upgradeExpiryPolicy: "replace_from_now", }); expect(program.benefits).toEqual([ expect.objectContaining({ benefitCode: "anti_kick", executionScope: "room", unlockLevel: 9 }), ]); expect(config.levels[0]).toMatchObject({ durationMs: 2592000000, level: 9, priceCoin: 9000, rewardResourceGroupId: 99, }); expect(config.levels[0].benefits[0]).toEqual({ benefitCode: "anti_kick", benefitType: "function", unlockLevel: 9, name: "防踢", resourceId: 0, resourceType: "", executionScope: "room", autoEquip: false, metadataJson: "{}", sortOrder: 10, status: "active", trialEnabled: true, }); expect(config.levels[0].benefits[1]).toMatchObject({ benefitCode: "daily_coin_rebate", trialEnabled: false, }); expect(requestHeaders(vi.mocked(fetch).mock.calls[0][1])["X-App-Code"]).toBe("fami"); }); test("vip updates and trial grant use generated contract paths", async () => { setSelectedAppCode("fami"); vi.stubGlobal( "fetch", vi .fn() .mockResolvedValueOnce( envelope({ appCode: "fami", levelCount: 9, programType: "tiered_privilege_v1", status: "active" }), ) .mockResolvedValueOnce(envelope({ levels: [] })) .mockResolvedValueOnce(envelope({ transactionId: "trial_1" }, 201)), ); await updateVipProgram({ benefitInheritancePolicy: "target_only", downgradePurchasePolicy: "reject", grantMode: "trial_card", levelCount: 9, programType: "tiered_privilege_v1", sameLevelExpiryPolicy: "extend_remaining", status: "active", trialCardEnabled: true, upgradeExpiryPolicy: "replace_from_now", }); await updateVipConfig({ levels: [ { benefits: [ { benefitCode: "room_entry_notice", benefitType: "function", unlockLevel: 2, name: "VIP进房通知", resourceId: 0, resourceType: "", executionScope: "room", autoEquip: false, metadataJson: "{}", sortOrder: 10, status: "active", trialEnabled: true, }, { benefitCode: "daily_coin_rebate", benefitType: "function", unlockLevel: 6, name: "金币返现", resourceId: 0, resourceType: "", executionScope: "wallet", autoEquip: false, metadataJson: "{}", sortOrder: 20, status: "active", trialEnabled: true, }, ], durationMs: 2592000000, level: 2, name: "VIP2", priceCoin: 2000, requiredRechargeCoinAmount: 0, rewardResourceGroupId: 22, sortOrder: 20, status: "active", }, ], }); await grantVipTrialCard({ commandId: "vip-trial-card-grant-test", durationMs: 20 * 24 * 60 * 60 * 1000, level: 5, reason: "manual", targetUserId: "318705991371722752", }); const [programUrl, programInit] = vi.mocked(fetch).mock.calls[0]; const [configUrl, configInit] = vi.mocked(fetch).mock.calls[1]; const [grantUrl, grantInit] = vi.mocked(fetch).mock.calls[2]; expect(String(programUrl)).toContain("/api/v1/admin/activity/vip-program"); expect(programInit?.method).toBe("PUT"); expect(requestBody(programInit)).toMatchObject({ config: { levelCount: 9, programType: "tiered_privilege_v1", status: "active", grantMode: "trial_card", benefitInheritancePolicy: "target_only", upgradeExpiryPolicy: "replace_from_now", }, }); expect(String(configUrl)).toContain("/api/v1/admin/activity/vip-levels"); expect(configInit?.method).toBe("PUT"); expect(requestBody(configInit)).toMatchObject({ levels: [ expect.objectContaining({ benefits: [ expect.objectContaining({ benefitCode: "room_entry_notice" }), expect.objectContaining({ benefitCode: "daily_coin_rebate", trialEnabled: false }), ], }), ], }); expect(String(grantUrl)).toContain("/api/v1/admin/activity/vip-trial-card-grants"); expect(grantInit?.method).toBe("POST"); expect(requestBody(grantInit)).toMatchObject({ durationMs: 1728000000, level: 5 }); expect(requestBody(grantInit)).not.toHaveProperty("resourceId"); }); function envelope(data: unknown, status = 200) { return new Response(JSON.stringify({ code: 0, data }), { status }); } function requestHeaders(init: RequestInit | undefined): Record { return (init?.headers || {}) as Record; } function requestBody(init: RequestInit | undefined): Record { return JSON.parse(String(init?.body || "{}")) as Record; }