import { describe, expect, test } from "vitest"; import { runActivityCheck } from "../app/services/activity.server"; import type { ActivityDeps, PushPayload } from "../app/services/activity.server"; function baseDeps(overrides: Partial = {}): ActivityDeps { return { nowMs: () => 1776901800000, uuid: () => "00000000-0000-4000-8000-000000000000", getOrder: async () => ({ id: "gid://shopify/Order/123", legacyId: "123", orderNumber: "#1001", email: "customer@example.com", customerId: "99999", currency: "USD", shopDomain: "demo.myshopify.com", }), getConfig: async () => ({ activityId: 12, apiAppId: "app-id", apiSecret: "secret", statusApiUrl: "https://third.example/status", pushApiUrl: "https://third.example/push", redirectUrl: "https://activity.example.com", buttonText: { en: "Claim Your Reward", "zh-CN": "领取奖励" }, shop: "configured-shop", }), findSuccess: async () => null, recordSuccess: async () => undefined, log: async () => undefined, callStatus: async () => ({ httpOk: true, status: 200, body: { code: 0, data: { status: true, trigger: ["card_add"] } }, requestBody: '{"activity_id":12}', responseBody: '{"code":0,"data":{"status":true}}', }), callPush: async () => ({ httpOk: true, status: 200, body: { code: 0 }, requestBody: "{}", responseBody: '{"code":0}', }), ...overrides, }; } describe("activity flow", () => { test("returns false when status api says activity is inactive", async () => { let pushCalls = 0; const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ callStatus: async () => ({ httpOk: true, status: 200, body: { code: 0, data: { status: false, trigger: ["order_end"] } }, requestBody: '{"activity_id":12}', responseBody: '{"code":0,"data":{"status":false}}', }), callPush: async () => { pushCalls += 1; return { httpOk: true, status: 200, body: { code: 0 }, requestBody: "{}", responseBody: "{}" }; }, }), ); expect(result).toEqual({ success: false }); expect(pushCalls).toBe(0); }); test("returns false when status api code is not zero", async () => { const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ callStatus: async () => ({ httpOk: true, status: 200, body: { code: 401, data: {}, msg: "auth required" }, requestBody: '{"activity_id":12}', responseBody: '{"code":401}', }), }), ); expect(result).toEqual({ success: false }); }); test("pushes order_end with configured shop and no line items", async () => { const capturedPayloads: PushPayload[] = []; const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "gid://shopify/Order/123", orderNumber: "#1001", locale: "zh-CN" }, baseDeps({ callPush: async (_config, payload) => { capturedPayloads.push(payload); return { httpOk: true, status: 200, body: { code: 0 }, requestBody: JSON.stringify(payload), responseBody: '{"code":0}', }; }, }), ); expect(result).toEqual({ success: true, redirectUrl: "https://activity.example.com", buttonText: "领取奖励", }); expect(capturedPayloads).toHaveLength(1); const captured = capturedPayloads[0]; expect(captured).toMatchObject({ activity_id: 12, event_type: "order_end", created_at: 1776901800000, user_id: "99999", email: "customer@example.com", event_id: "00000000-0000-4000-8000-000000000000", properties: { shop: "configured-shop", currency: "USD", order_id: "123", order_number: "#1001", }, }); expect(captured.properties).not.toHaveProperty("lineItems"); expect(captured.event_id).toMatch(/^[0-9a-f-]{36}$/); }); test("returns false when push api fails", async () => { const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ callPush: async () => ({ httpOk: true, status: 200, body: { code: 500 }, requestBody: "{}", responseBody: '{"code":500}', }), }), ); expect(result).toEqual({ success: false }); }); test("uses configured push success code", async () => { const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ getConfig: async () => ({ activityId: 12, apiAppId: "app-id", apiSecret: "secret", statusApiUrl: "https://third.example/status", pushApiUrl: "https://third.example/push", redirectUrl: "https://activity.example.com", buttonText: { en: "Claim Your Reward", "zh-CN": "领取奖励" }, pushSuccessCode: 2001, }), callPush: async () => ({ httpOk: true, status: 200, body: { code: 2001 }, requestBody: "{}", responseBody: '{"code":2001}', }), }), ); expect(result).toEqual({ success: true, redirectUrl: "https://activity.example.com", buttonText: "Claim Your Reward", }); }); test("uses configured status success code and active flag policy", async () => { const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ getConfig: async () => ({ activityId: 12, apiAppId: "app-id", apiSecret: "secret", statusApiUrl: "https://third.example/status", pushApiUrl: "https://third.example/push", redirectUrl: "https://activity.example.com", buttonText: { en: "Claim Your Reward", "zh-CN": "领取奖励" }, statusSuccessCode: 2001, statusActiveRequired: false, }), callStatus: async () => ({ httpOk: true, status: 200, body: { code: 2001 }, requestBody: '{"activity_id":12}', responseBody: '{"code":2001}', }), }), ); expect(result).toEqual({ success: true, redirectUrl: "https://activity.example.com", buttonText: "Claim Your Reward", }); }); test("does not push when email is missing", async () => { let pushCalls = 0; const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ getOrder: async () => ({ id: "gid://shopify/Order/123", legacyId: "123", orderNumber: "#1001", customerId: "99999", currency: "USD", shopDomain: "demo.myshopify.com", }), callPush: async () => { pushCalls += 1; return { httpOk: true, status: 200, body: { code: 0 }, requestBody: "{}", responseBody: "{}" }; }, }), ); expect(result).toEqual({ success: false }); expect(pushCalls).toBe(0); }); test("does not push when customer id is missing", async () => { let pushCalls = 0; const result = await runActivityCheck( { shop: "demo.myshopify.com", orderId: "123", orderNumber: "#1001", locale: "en" }, baseDeps({ getOrder: async () => ({ id: "gid://shopify/Order/123", legacyId: "123", orderNumber: "#1001", email: "customer@example.com", currency: "USD", shopDomain: "demo.myshopify.com", }), callPush: async () => { pushCalls += 1; return { httpOk: true, status: 200, body: { code: 0 }, requestBody: "{}", responseBody: "{}" }; }, }), ); expect(result).toEqual({ success: false }); expect(pushCalls).toBe(0); }); });