fix: replace Math.random() IDs with crypto.randomUUID() across all services
- 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
This commit is contained in:
parent
d39c447c52
commit
86a56339ab
@ -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,
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
58
services/tracker-service/src/modules/votes/votes.test.ts
Normal file
58
services/tracker-service/src/modules/votes/votes.test.ts
Normal file
@ -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");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user