From 216b6f6f4e377fe23513976c6ad65a3c373fc759 Mon Sep 17 00:00:00 2001 From: tmdeveloper007 Date: Fri, 26 Jun 2026 19:04:02 +0000 Subject: [PATCH 1/2] test: add unit tests for crypto-edge.ts --- test/crypto-edge.test.ts | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/crypto-edge.test.ts diff --git a/test/crypto-edge.test.ts b/test/crypto-edge.test.ts new file mode 100644 index 000000000..af0e88486 --- /dev/null +++ b/test/crypto-edge.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, beforeEach } from "vitest"; +const TEST_KEY = "a".repeat(64); +describe("decryptTokenEdge", () => { + beforeEach(() => { process.env.ENCRYPTION_KEY = TEST_KEY; }); + it("returns null when ENCRYPTION_KEY is missing", async () => { + delete process.env.ENCRYPTION_KEY; + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("deadbeef", "a".repeat(24))).toBeNull(); + }); + it("returns null when ENCRYPTION_KEY is invalid format", async () => { + process.env.ENCRYPTION_KEY = "short"; + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("deadbeef", "a".repeat(24))).toBeNull(); + }); + it("returns null for non-hex encrypted string", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("not-hex!", "a".repeat(24))).toBeNull(); + }); + it("returns null for odd-length encrypted string", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("deadbeef", "a".repeat(24))).toBeNull(); + }); + it("returns null for non-hex iv", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("a".repeat(64), "not-hex!")).toBeNull(); + }); + it("returns null when iv is not 24 chars", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("a".repeat(64), "a".repeat(20))).toBeNull(); + }); + it("returns null for tampered ciphertext", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + const { encryptToken } = await import("../src/lib/crypto"); + const { encrypted, iv } = encryptToken("hello world"); + expect(await decryptTokenEdge(encrypted.slice(0,-2)+"ff", iv)).toBeNull(); + }); + it("returns null for hex with invalid characters", async () => { + const { decryptTokenEdge } = await import("../src/lib/crypto-edge"); + expect(await decryptTokenEdge("g".repeat(64), "a".repeat(24))).toBeNull(); + }); +}); From 1ffd83536829f49ad6f3eddd2a0ef334f7ad1b3e Mon Sep 17 00:00:00 2001 From: tmdeveloper007 Date: Fri, 26 Jun 2026 19:04:25 +0000 Subject: [PATCH 2/2] test: add unit tests for upstash-rest.ts --- test/upstash-rest.test.ts | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/upstash-rest.test.ts diff --git a/test/upstash-rest.test.ts b/test/upstash-rest.test.ts new file mode 100644 index 000000000..4074fd5f3 --- /dev/null +++ b/test/upstash-rest.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { getUpstashConfig, upstashPipeline, upstashRateLimitFixedWindow, upstashTryAcquireLock } from "../src/lib/upstash-rest"; +describe("getUpstashConfig", () => { + beforeEach(() => { delete process.env.UPSTASH_REDIS_REST_URL; delete process.env.UPSTASH_REDIS_REST_TOKEN; }); + it("returns null when neither env var is set", () => { expect(getUpstashConfig()).toBeNull(); }); + it("returns null when only URL is set", () => { process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; expect(getUpstashConfig()).toBeNull(); }); + it("returns null when only token is set", () => { process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; expect(getUpstashConfig()).toBeNull(); }); + it("returns config when both env vars are set", () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; + process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + expect(getUpstashConfig()).toEqual({ url: "https://test.upstash.io", token: "token123" }); + }); +}); +describe("upstashPipeline", () => { + beforeEach(() => { delete process.env.UPSTASH_REDIS_REST_URL; delete process.env.UPSTASH_REDIS_REST_TOKEN; vi.restoreAllMocks(); }); + it("returns empty array when not configured", async () => { expect(await upstashPipeline([["GET","key"]])).toEqual([]); }); + it("returns empty array when fetch fails", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(null, { status: 500 })) as unknown as typeof fetch; + expect(await upstashPipeline([["GET","key"]])).toEqual([]); + }); + it("returns parsed JSON on success", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify([{result:"OK"}]), {status:200,headers:{"content-type":"application/json"}})) as unknown as typeof fetch; + expect(await upstashPipeline([["SET","key","value"]])).toEqual([{result:"OK"}]); + }); +}); +describe("upstashRateLimitFixedWindow", () => { + beforeEach(() => { delete process.env.UPSTASH_REDIS_REST_URL; delete process.env.UPSTASH_REDIS_REST_TOKEN; vi.restoreAllMocks(); }); + it("returns allowed when not configured", async () => { expect(await upstashRateLimitFixedWindow({key:"test",limit:5,windowSeconds:60})).toEqual({allowed:true}); }); + it("returns allowed when upstash fetch fails", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(null,{status:500})) as unknown as typeof fetch; + expect(await upstashRateLimitFixedWindow({key:"test",limit:5,windowSeconds:60})).toEqual({allowed:true}); + }); + it("returns allowed when count is NaN", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify([{result:NaN},{result:60}]),{status:200,headers:{"content-type":"application/json"}})) as unknown as typeof fetch; + expect(await upstashRateLimitFixedWindow({key:"test",limit:5,windowSeconds:60})).toEqual({allowed:true}); + }); + it("returns not allowed when count exceeds limit", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify([{result:10},{result:45}]),{status:200,headers:{"content-type":"application/json"}})) as unknown as typeof fetch; + expect(await upstashRateLimitFixedWindow({key:"test",limit:5,windowSeconds:60})).toEqual({allowed:false,retryAfter:45}); + }); +}); +describe("upstashTryAcquireLock", () => { + beforeEach(() => { delete process.env.UPSTASH_REDIS_REST_URL; delete process.env.UPSTASH_REDIS_REST_TOKEN; vi.restoreAllMocks(); }); + it("returns false when not configured", async () => { expect(await upstashTryAcquireLock({key:"lock:test",ttlSeconds:10})).toBe(false); }); + it("returns true when SET returns OK", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify([{result:"OK"}]),{status:200,headers:{"content-type":"application/json"}})) as unknown as typeof fetch; + expect(await upstashTryAcquireLock({key:"lock:test",ttlSeconds:10})).toBe(true); + }); + it("returns false when SET returns null", async () => { + process.env.UPSTASH_REDIS_REST_URL = "https://test.upstash.io"; process.env.UPSTASH_REDIS_REST_TOKEN = "token123"; + globalThis.fetch = vi.fn().mockResolvedValue(new Response(JSON.stringify([{result:null}]),{status:200,headers:{"content-type":"application/json"}})) as unknown as typeof fetch; + expect(await upstashTryAcquireLock({key:"lock:test",ttlSeconds:10})).toBe(false); + }); +});