forked from UKSOURCE/ipv6
paypal
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Property-based tests for lib/paypal.ts (PaymentService)
|
||||
* Feature: paypal-payment-integration
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import * as fc from "fast-check";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Property 11: Base URL đúng theo PAYPAL_MODE
|
||||
// ---------------------------------------------------------------------------
|
||||
describe("getPayPalBaseUrl", () => {
|
||||
it(
|
||||
"Feature: paypal-payment-integration, Property 11: Base URL đúng theo PAYPAL_MODE",
|
||||
() => {
|
||||
fc.assert(
|
||||
fc.property(fc.constantFrom("sandbox", "live"), (mode) => {
|
||||
// Test the logic directly without env dependency
|
||||
const getUrl = (m: string) =>
|
||||
m === "sandbox"
|
||||
? "https://api-m.sandbox.paypal.com"
|
||||
: "https://api-m.paypal.com";
|
||||
|
||||
const url = getUrl(mode);
|
||||
if (mode === "sandbox") {
|
||||
expect(url).toBe("https://api-m.sandbox.paypal.com");
|
||||
} else {
|
||||
expect(url).toBe("https://api-m.paypal.com");
|
||||
}
|
||||
}),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it("returns sandbox URL when PAYPAL_MODE=sandbox", async () => {
|
||||
const originalMode = process.env.PAYPAL_MODE;
|
||||
process.env.PAYPAL_MODE = "sandbox";
|
||||
// Dynamic import to pick up env
|
||||
const mod = await import("../paypal?sandbox=" + Date.now());
|
||||
// Since module is cached, test the logic via the exported function
|
||||
// The function reads process.env at call time
|
||||
process.env.PAYPAL_MODE = "sandbox";
|
||||
const { getPayPalBaseUrl } = await import("../paypal");
|
||||
expect(getPayPalBaseUrl()).toBe("https://api-m.sandbox.paypal.com");
|
||||
process.env.PAYPAL_MODE = originalMode;
|
||||
});
|
||||
|
||||
it("returns live URL when PAYPAL_MODE=live", async () => {
|
||||
const originalMode = process.env.PAYPAL_MODE;
|
||||
process.env.PAYPAL_MODE = "live";
|
||||
const { getPayPalBaseUrl } = await import("../paypal");
|
||||
expect(getPayPalBaseUrl()).toBe("https://api-m.paypal.com");
|
||||
process.env.PAYPAL_MODE = originalMode;
|
||||
});
|
||||
|
||||
it("returns live URL when PAYPAL_MODE is not set", async () => {
|
||||
const originalMode = process.env.PAYPAL_MODE;
|
||||
delete process.env.PAYPAL_MODE;
|
||||
const { getPayPalBaseUrl } = await import("../paypal");
|
||||
expect(getPayPalBaseUrl()).toBe("https://api-m.paypal.com");
|
||||
process.env.PAYPAL_MODE = originalMode;
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Property 1: Số tiền order luôn là $120.00 USD
|
||||
// Property 2: Approval URL luôn được trả về
|
||||
// Property 3: Idempotency Key luôn duy nhất
|
||||
// ---------------------------------------------------------------------------
|
||||
describe("createPayPalOrder", () => {
|
||||
beforeEach(() => {
|
||||
process.env.PAYPAL_MODE = "sandbox";
|
||||
process.env.PAYPAL_CLIENT_ID = "test-client-id";
|
||||
process.env.PAYPAL_CLIENT_SECRET = "test-client-secret";
|
||||
process.env.NEXT_PUBLIC_BASE_PATH = "/ipv6";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it(
|
||||
"Feature: paypal-payment-integration, Property 1: Số tiền order luôn là $120.00 USD",
|
||||
async () => {
|
||||
const capturedBodies: unknown[] = [];
|
||||
|
||||
vi.stubGlobal("fetch", vi.fn().mockImplementation((url: string, opts: RequestInit) => {
|
||||
if (String(url).includes("/v1/oauth2/token")) {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ access_token: "mock-token", expires_in: 3600 }),
|
||||
});
|
||||
}
|
||||
if (String(url).includes("/v2/checkout/orders")) {
|
||||
capturedBodies.push(JSON.parse(opts.body as string));
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
id: "ORDER-" + Math.random(),
|
||||
links: [{ rel: "approve", href: "https://paypal.com/approve" }],
|
||||
}),
|
||||
});
|
||||
}
|
||||
return Promise.resolve({ ok: false, text: () => Promise.resolve("unexpected") });
|
||||
}));
|
||||
|
||||
const { createPayPalOrder } = await import("../paypal");
|
||||
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.string({ minLength: 1, maxLength: 20 }),
|
||||
async (registrationId) => {
|
||||
capturedBodies.length = 0;
|
||||
await createPayPalOrder(registrationId);
|
||||
expect(capturedBodies.length).toBeGreaterThan(0);
|
||||
const body = capturedBodies[capturedBodies.length - 1] as any;
|
||||
expect(body.purchase_units[0].amount.value).toBe("120.00");
|
||||
expect(body.purchase_units[0].amount.currency_code).toBe("USD");
|
||||
}
|
||||
),
|
||||
{ numRuns: 20 }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
"Feature: paypal-payment-integration, Property 2: Approval URL luôn được trả về khi tạo order thành công",
|
||||
async () => {
|
||||
const expectedUrl = "https://paypal.com/approve?token=TEST";
|
||||
|
||||
vi.stubGlobal("fetch", vi.fn().mockImplementation((url: string) => {
|
||||
if (String(url).includes("/v1/oauth2/token")) {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ access_token: "mock-token", expires_in: 3600 }),
|
||||
});
|
||||
}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
id: "ORDER-123",
|
||||
links: [
|
||||
{ rel: "self", href: "https://paypal.com/self" },
|
||||
{ rel: "approve", href: expectedUrl },
|
||||
],
|
||||
}),
|
||||
});
|
||||
}));
|
||||
|
||||
const { createPayPalOrder } = await import("../paypal");
|
||||
const result = await createPayPalOrder("reg-test");
|
||||
expect(result.approvalUrl).toBe(expectedUrl);
|
||||
expect(result.orderId).toBe("ORDER-123");
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
"Feature: paypal-payment-integration, Property 3: Idempotency Key luôn duy nhất",
|
||||
async () => {
|
||||
const capturedKeys: string[] = [];
|
||||
|
||||
vi.stubGlobal("fetch", vi.fn().mockImplementation((url: string, opts: RequestInit) => {
|
||||
if (String(url).includes("/v1/oauth2/token")) {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ access_token: "mock-token", expires_in: 3600 }),
|
||||
});
|
||||
}
|
||||
if (String(url).includes("/v2/checkout/orders")) {
|
||||
const key = (opts.headers as Record<string, string>)["PayPal-Request-Id"];
|
||||
capturedKeys.push(key);
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
id: "ORDER-" + Math.random(),
|
||||
links: [{ rel: "approve", href: "https://paypal.com/approve" }],
|
||||
}),
|
||||
});
|
||||
}
|
||||
return Promise.resolve({ ok: false, text: () => Promise.resolve("unexpected") });
|
||||
}));
|
||||
|
||||
const { createPayPalOrder } = await import("../paypal");
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
await createPayPalOrder(`reg-${i}`);
|
||||
}
|
||||
|
||||
const uniqueKeys = new Set(capturedKeys);
|
||||
expect(uniqueKeys.size).toBe(capturedKeys.length);
|
||||
expect(capturedKeys.length).toBe(50);
|
||||
}
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user