From 86a56339ab12b4f8523b0c6324f88641005e1b66 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 12 Feb 2026 13:03:09 -0800 Subject: [PATCH] fix: replace Math.random() IDs with crypto.randomUUID() across all services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - billing-service: licenses, subscriptions (pay_, lic_) - growth-service: invitations, referrals (inv_, ref_) - platform-service: auth, audit (usr_, aud_) - tracker-service: items, comments, votes, public (trk_, cmt_, vote_) - Add votes.test.ts — closes the only missing module test --- .../src/modules/licenses/routes.ts | 2 +- .../src/modules/subscriptions/routes.ts | 2 +- .../src/modules/invitations/routes.ts | 4 +- .../src/modules/referrals/routes.ts | 2 +- .../src/modules/audit/routes.ts | 2 +- .../src/modules/auth/routes.ts | 2 +- .../src/modules/comments/routes.ts | 2 +- .../src/modules/items/routes.ts | 2 +- .../src/modules/public/routes.ts | 6 +- .../src/modules/votes/routes.ts | 2 +- .../src/modules/votes/votes.test.ts | 58 +++++++++++++++++++ 11 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 services/tracker-service/src/modules/votes/votes.test.ts diff --git a/services/billing-service/src/modules/licenses/routes.ts b/services/billing-service/src/modules/licenses/routes.ts index f61d7a69..115d7a5d 100644 --- a/services/billing-service/src/modules/licenses/routes.ts +++ b/services/billing-service/src/modules/licenses/routes.ts @@ -31,7 +31,7 @@ export async function licenseRoutes(app: FastifyInstance) { const key = repo.generateKey(); const doc: LicenseDoc = { - id: `lic_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `lic_${crypto.randomUUID()}`, productId: PRODUCT_ID, key, userId: input.userId, diff --git a/services/billing-service/src/modules/subscriptions/routes.ts b/services/billing-service/src/modules/subscriptions/routes.ts index 601d2dea..bf149f47 100644 --- a/services/billing-service/src/modules/subscriptions/routes.ts +++ b/services/billing-service/src/modules/subscriptions/routes.ts @@ -96,7 +96,7 @@ export async function subscriptionRoutes(app: FastifyInstance) { } const input = parsed.data; const doc: PaymentDoc = { - id: `pay_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `pay_${crypto.randomUUID()}`, productId: PRODUCT_ID, ...input, createdAt: new Date().toISOString(), diff --git a/services/growth-service/src/modules/invitations/routes.ts b/services/growth-service/src/modules/invitations/routes.ts index f81c381b..58a71bc4 100644 --- a/services/growth-service/src/modules/invitations/routes.ts +++ b/services/growth-service/src/modules/invitations/routes.ts @@ -54,7 +54,7 @@ export async function invitationRoutes(app: FastifyInstance) { const input = parsed.data; const now = new Date().toISOString(); const doc: InvitationCodeDoc = { - id: `inv_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `inv_${crypto.randomUUID()}`, productId: PRODUCT_ID, code: input.code.toUpperCase().replace(/[^A-Z0-9-]/g, ""), description: input.description, @@ -126,7 +126,7 @@ export async function invitationRoutes(app: FastifyInstance) { const input = parsed.data; const now = new Date().toISOString(); const doc: InvitationCodeDoc = { - id: `inv_${Date.now()}_${i}_${Math.random().toString(36).slice(2, 8)}`, + id: `inv_${crypto.randomUUID()}`, productId: PRODUCT_ID, code: input.code.toUpperCase().replace(/[^A-Z0-9-]/g, ""), description: input.description, diff --git a/services/growth-service/src/modules/referrals/routes.ts b/services/growth-service/src/modules/referrals/routes.ts index 8d4075a9..63392bdd 100644 --- a/services/growth-service/src/modules/referrals/routes.ts +++ b/services/growth-service/src/modules/referrals/routes.ts @@ -64,7 +64,7 @@ export async function referralRoutes(app: FastifyInstance) { const now = new Date().toISOString(); const doc: ReferralDoc = { - id: `ref_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `ref_${crypto.randomUUID()}`, productId: PRODUCT_ID, referrerId: input.referrerId, referrerEmail: input.referrerEmail, diff --git a/services/platform-service/src/modules/audit/routes.ts b/services/platform-service/src/modules/audit/routes.ts index 8e3b540b..5232cc36 100644 --- a/services/platform-service/src/modules/audit/routes.ts +++ b/services/platform-service/src/modules/audit/routes.ts @@ -21,7 +21,7 @@ export async function auditRoutes(app: FastifyInstance) { } const input = parsed.data; const doc: AuditDoc = { - id: `aud_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `aud_${crypto.randomUUID()}`, productId: PRODUCT_ID, ...input, createdAt: new Date().toISOString(), diff --git a/services/platform-service/src/modules/auth/routes.ts b/services/platform-service/src/modules/auth/routes.ts index 6619921a..0ed0518d 100644 --- a/services/platform-service/src/modules/auth/routes.ts +++ b/services/platform-service/src/modules/auth/routes.ts @@ -59,7 +59,7 @@ export async function authRoutes(app: FastifyInstance) { const now = new Date().toISOString(); const user: UserDoc = { - id: `usr_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `usr_${crypto.randomUUID()}`, productId: PRODUCT_ID, email: email.toLowerCase(), passwordHash: await repo.hashPassword(password), diff --git a/services/tracker-service/src/modules/comments/routes.ts b/services/tracker-service/src/modules/comments/routes.ts index a6c0e906..a47796f2 100644 --- a/services/tracker-service/src/modules/comments/routes.ts +++ b/services/tracker-service/src/modules/comments/routes.ts @@ -39,7 +39,7 @@ export async function commentRoutes(app: FastifyInstance) { const now = new Date().toISOString(); const doc: CommentDoc = { - id: `cmt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `cmt_${crypto.randomUUID()}`, itemId, productId: item.productId, authorId: auth.sub, diff --git a/services/tracker-service/src/modules/items/routes.ts b/services/tracker-service/src/modules/items/routes.ts index 8cafdf0f..a25422c2 100644 --- a/services/tracker-service/src/modules/items/routes.ts +++ b/services/tracker-service/src/modules/items/routes.ts @@ -93,7 +93,7 @@ export async function itemRoutes(app: FastifyInstance) { const now = new Date().toISOString(); const doc: TrackerItemDoc = { - id: `trk_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `trk_${crypto.randomUUID()}`, productId: pid, type: input.type, status: "open", diff --git a/services/tracker-service/src/modules/public/routes.ts b/services/tracker-service/src/modules/public/routes.ts index f77747f1..492c6131 100644 --- a/services/tracker-service/src/modules/public/routes.ts +++ b/services/tracker-service/src/modules/public/routes.ts @@ -99,7 +99,7 @@ export async function publicRoutes(app: FastifyInstance) { const now = new Date().toISOString(); const doc: TrackerItemDoc = { - id: `trk_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `trk_${crypto.randomUUID()}`, productId: pid, type: input.type, status: "open", @@ -123,7 +123,7 @@ export async function publicRoutes(app: FastifyInstance) { // Auto-vote for the submitter await voteRepo.create({ - id: `vote_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `vote_${crypto.randomUUID()}`, itemId: created.id, productId: pid, userId: `email:${input.email}`, @@ -157,7 +157,7 @@ export async function publicRoutes(app: FastifyInstance) { return { voted: false, voteCount: newCount }; } else { await voteRepo.create({ - id: `vote_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `vote_${crypto.randomUUID()}`, itemId: id, productId: item.productId, userId: voterId, diff --git a/services/tracker-service/src/modules/votes/routes.ts b/services/tracker-service/src/modules/votes/routes.ts index 39b65590..f3634534 100644 --- a/services/tracker-service/src/modules/votes/routes.ts +++ b/services/tracker-service/src/modules/votes/routes.ts @@ -32,7 +32,7 @@ export async function voteRoutes(app: FastifyInstance) { } else { // Add vote const doc: VoteDoc = { - id: `vote_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, + id: `vote_${crypto.randomUUID()}`, itemId, productId: item.productId, userId: auth.sub, diff --git a/services/tracker-service/src/modules/votes/votes.test.ts b/services/tracker-service/src/modules/votes/votes.test.ts new file mode 100644 index 00000000..b80fca53 --- /dev/null +++ b/services/tracker-service/src/modules/votes/votes.test.ts @@ -0,0 +1,58 @@ +/** + * Votes module unit tests — validates VoteDoc shape and route exports. + */ + +import { describe, it, expect } from "vitest"; +import type { VoteDoc } from "./types.js"; + +describe("VoteDoc", () => { + it("accepts a valid vote document", () => { + const doc: VoteDoc = { + id: `vote_${crypto.randomUUID()}`, + itemId: "trk_abc123", + productId: "lysnrai", + userId: "usr_xyz", + createdAt: new Date().toISOString(), + }; + + expect(doc.id).toMatch(/^vote_/); + expect(doc.itemId).toBe("trk_abc123"); + expect(doc.productId).toBe("lysnrai"); + expect(doc.userId).toBe("usr_xyz"); + expect(doc.createdAt).toBeTruthy(); + }); + + it("generates unique IDs with crypto.randomUUID", () => { + const ids = new Set( + Array.from({ length: 100 }, () => `vote_${crypto.randomUUID()}`) + ); + expect(ids.size).toBe(100); + }); + + it("id format uses UUID (no Math.random)", () => { + const id = `vote_${crypto.randomUUID()}`; + // UUID v4 pattern after prefix + expect(id).toMatch( + /^vote_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/ + ); + }); + + it("createdAt is ISO 8601 format", () => { + const doc: VoteDoc = { + id: `vote_${crypto.randomUUID()}`, + itemId: "trk_test", + productId: "lysnrai", + userId: "usr_test", + createdAt: new Date().toISOString(), + }; + // ISO 8601 pattern + expect(doc.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); + }); +}); + +describe("voteRoutes export", () => { + it("exports voteRoutes function", async () => { + const mod = await import("./routes.js"); + expect(typeof mod.voteRoutes).toBe("function"); + }); +});