UNPKG

@congminh1254/shopee-sdk

Version:
337 lines 14.4 kB
import { jest } from "@jest/globals"; import { CustomTokenStorage } from "../../storage/custom-token-storage.js"; import fs from "fs"; import path from "path"; // Create a unique test directory for each test run const TEST_DIR = path.join(process.cwd(), ".token-test-" + Date.now()); describe("CustomTokenStorage", () => { beforeAll(() => { // Mock process.cwd to use test directory jest.spyOn(process, "cwd").mockReturnValue(TEST_DIR); }); afterAll(() => { // Restore original cwd jest.restoreAllMocks(); // Clean up test directory if (fs.existsSync(path.join(TEST_DIR, ".token"))) { const tokenDir = path.join(TEST_DIR, ".token"); const files = fs.readdirSync(tokenDir); files.forEach((file) => { fs.unlinkSync(path.join(tokenDir, file)); }); fs.rmdirSync(tokenDir); } }); afterEach(() => { // Clean up token files after each test const tokenDir = path.join(TEST_DIR, ".token"); if (fs.existsSync(tokenDir)) { const files = fs.readdirSync(tokenDir); files.forEach((file) => { fs.unlinkSync(path.join(tokenDir, file)); }); } }); describe("constructor", () => { it("should create token directory if it does not exist", () => { new CustomTokenStorage(123456); const tokenDir = path.join(TEST_DIR, ".token"); expect(fs.existsSync(tokenDir)).toBe(true); }); it("should use default.json when no shopId is provided", () => { new CustomTokenStorage(); // We can't directly access private properties, but we can verify the storage is created const tokenDir = path.join(TEST_DIR, ".token"); expect(fs.existsSync(tokenDir)).toBe(true); }); }); describe("store method", () => { it("should store token successfully", async () => { const storage = new CustomTokenStorage(123456); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 123456, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); const tokenPath = path.join(TEST_DIR, ".token", "123456.json"); expect(fs.existsSync(tokenPath)).toBe(true); const storedData = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); expect(storedData).toEqual(token); }); it("should create default token file on first store with shopId", async () => { const storage = new CustomTokenStorage(789012); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 789012, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); const tokenPath = path.join(TEST_DIR, ".token", "789012.json"); const defaultPath = path.join(TEST_DIR, ".token", "default.json"); expect(fs.existsSync(tokenPath)).toBe(true); expect(fs.existsSync(defaultPath)).toBe(true); const storedData = JSON.parse(fs.readFileSync(tokenPath, "utf-8")); const defaultData = JSON.parse(fs.readFileSync(defaultPath, "utf-8")); expect(storedData).toEqual(token); expect(defaultData).toEqual(token); }); it("should not overwrite existing default token", async () => { // First, create a default token const firstStorage = new CustomTokenStorage(111111); const firstToken = { access_token: "first_token", refresh_token: "first_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 111111, request_id: "test-request-id", error: "", message: "", }; await firstStorage.store(firstToken); // Then store a second token with different shopId const secondStorage = new CustomTokenStorage(222222); const secondToken = { access_token: "second_token", refresh_token: "second_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 222222, request_id: "test-request-id", error: "", message: "", }; await secondStorage.store(secondToken); const defaultPath = path.join(TEST_DIR, ".token", "default.json"); const defaultData = JSON.parse(fs.readFileSync(defaultPath, "utf-8")); // Default should still be the first token expect(defaultData.access_token).toBe("first_token"); }); it("should throw error on write failure", async () => { const storage = new CustomTokenStorage(999999); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 999999, request_id: "test-request-id", error: "", message: "", }; // Mock writeFileSync to throw an error const originalWriteFileSync = fs.writeFileSync; jest.spyOn(fs, "writeFileSync").mockImplementation(() => { throw new Error("Write failed"); }); await expect(storage.store(token)).rejects.toThrow("Failed to store token"); await expect(storage.store(token)).rejects.toThrow("Write failed"); // Restore original function fs.writeFileSync = originalWriteFileSync; }); }); describe("get method", () => { it("should retrieve stored token successfully", async () => { const storage = new CustomTokenStorage(123456); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 123456, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); const retrieved = await storage.get(); expect(retrieved).toEqual(token); }); it("should return null when token file does not exist", async () => { const storage = new CustomTokenStorage(999999); const result = await storage.get(); expect(result).toBeNull(); }); it("should throw error on read failure (non-ENOENT)", async () => { const storage = new CustomTokenStorage(555555); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 555555, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); // Mock readFileSync to throw a non-ENOENT error const originalReadFileSync = fs.readFileSync; jest.spyOn(fs, "readFileSync").mockImplementation(() => { const error = new Error("Permission denied"); error.code = "EACCES"; throw error; }); await expect(storage.get()).rejects.toThrow("Failed to get token"); await expect(storage.get()).rejects.toThrow("Permission denied"); // Restore original function fs.readFileSync = originalReadFileSync; }); }); describe("clear method", () => { it("should clear stored token successfully", async () => { const storage = new CustomTokenStorage(123456); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 123456, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); const tokenPath = path.join(TEST_DIR, ".token", "123456.json"); expect(fs.existsSync(tokenPath)).toBe(true); await storage.clear(); expect(fs.existsSync(tokenPath)).toBe(false); const retrieved = await storage.get(); expect(retrieved).toBeNull(); }); it("should handle clearing non-existent token gracefully", async () => { const storage = new CustomTokenStorage(999999); // Should not throw error await expect(storage.clear()).resolves.not.toThrow(); }); it("should throw error on delete failure (non-ENOENT)", async () => { const storage = new CustomTokenStorage(777777); const token = { access_token: "test_token", refresh_token: "test_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 777777, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); // Mock unlinkSync to throw a non-ENOENT error const originalUnlinkSync = fs.unlinkSync; jest.spyOn(fs, "unlinkSync").mockImplementation(() => { const error = new Error("Permission denied"); error.code = "EACCES"; throw error; }); await expect(storage.clear()).rejects.toThrow("Failed to clear token"); await expect(storage.clear()).rejects.toThrow("Permission denied"); // Restore original function fs.unlinkSync = originalUnlinkSync; }); }); describe("integration tests", () => { it("should handle full lifecycle: store, get, clear", async () => { const storage = new CustomTokenStorage(888888); // Initially should be null let result = await storage.get(); expect(result).toBeNull(); // Store a token const token = { access_token: "lifecycle_token", refresh_token: "lifecycle_refresh", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 888888, request_id: "test-request-id", error: "", message: "", }; await storage.store(token); // Retrieve the token result = await storage.get(); expect(result).toEqual(token); // Clear the token await storage.clear(); // Should be null again result = await storage.get(); expect(result).toBeNull(); }); it("should handle multiple shop IDs independently", async () => { const storage1 = new CustomTokenStorage(111); const storage2 = new CustomTokenStorage(222); const token1 = { access_token: "token_111", refresh_token: "refresh_111", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 111, request_id: "test-request-id", error: "", message: "", }; const token2 = { access_token: "token_222", refresh_token: "refresh_222", expire_in: 14400, expired_at: Date.now() + 14400000, shop_id: 222, request_id: "test-request-id", error: "", message: "", }; await storage1.store(token1); await storage2.store(token2); const retrieved1 = await storage1.get(); const retrieved2 = await storage2.get(); expect(retrieved1?.shop_id).toBe(111); expect(retrieved2?.shop_id).toBe(222); }); }); describe("Non-Error Exception Handling", () => { let originalWrite; let originalRead; let originalUnlink; beforeEach(() => { originalWrite = fs.writeFileSync; originalRead = fs.readFileSync; originalUnlink = fs.unlinkSync; }); afterEach(() => { fs.writeFileSync = originalWrite; fs.readFileSync = originalRead; fs.unlinkSync = originalUnlink; jest.restoreAllMocks(); }); it("should catch non-Error exceptions correctly", async () => { const storage = new CustomTokenStorage(888899); // 1. fs.writeFileSync throws string jest.spyOn(fs, "writeFileSync").mockImplementation(() => { throw "String write error"; }); await expect(storage.store({})).rejects.toThrow("Failed to store token: Unknown error"); // 2. fs.readFileSync throws string jest.spyOn(fs, "readFileSync").mockImplementation(() => { throw "String read error"; }); await expect(storage.get()).rejects.toThrow("Failed to get token: Unknown error"); // 3. fs.unlinkSync throws string jest.spyOn(fs, "unlinkSync").mockImplementation(() => { throw "String unlink error"; }); await expect(storage.clear()).rejects.toThrow("Failed to clear token: Unknown error"); }); }); }); //# sourceMappingURL=custom-token-storage.test.js.map