import { describe, it, expect } from "vitest"; import { // Schemas vinSchema, loginSchema, registerSchema, forgotPasswordSchema, resetPasswordSchema, changelogChangeTypeEnum, changelogEntrySchema, createChangelogEntrySchema, updateChangelogEntrySchema, paginationSchema, // Utils isValidVin, validateVinCheckDigit, extractWmi, extractModelYear, formatTRY, kurusToLira, liraToKurus, formatVin, formatDate, formatDateTime, slugify, generateReferralCode, // Constants VIN_REGEX, EMAIL_REGEX, OEM_CODE_REGEX, PLANS, REFERRAL_REWARDS, CURRENCY, FULL_PLAN_BRAND_LIMIT, ERROR_CODES, } from "./index"; // --------------- schemas/vin --------------- describe("vinSchema", () => { const validVin = "WVWZZZ1JZ3W597935"; it("accepts a valid 17-character VIN", () => { const result = vinSchema.safeParse(validVin); expect(result.success).toBe(true); }); it("transforms VIN to uppercase after validation", () => { const result = vinSchema.safeParse("WVWZZZ1JZ3W597935"); expect(result.success).toBe(true); if (result.success) { expect(result.data).toBe("WVWZZZ1JZ3W597935"); } }); it("rejects VIN shorter than 17 chars", () => { const result = vinSchema.safeParse("WVWZZZ1JZ3W59793"); expect(result.success).toBe(false); }); it("rejects VIN longer than 17 chars", () => { const result = vinSchema.safeParse("WVWZZZ1JZ3W597935X"); expect(result.success).toBe(false); }); it("rejects VIN with I character (not allowed)", () => { const result = vinSchema.safeParse("WVWZZZ1IZ3W597935"); expect(result.success).toBe(false); }); it("rejects VIN with O character (not allowed)", () => { const result = vinSchema.safeParse("WVWZZZ1OZ3W597935"); expect(result.success).toBe(false); }); it("rejects VIN with Q character (not allowed)", () => { const result = vinSchema.safeParse("WVWZZZ1QZ3W597935"); expect(result.success).toBe(false); }); it("rejects empty string", () => { const result = vinSchema.safeParse(""); expect(result.success).toBe(false); }); it("accepts 17-char uppercase string with valid chars", () => { const result = vinSchema.safeParse("ABCDEFGHJKLMNPRST"); expect(result.success).toBe(true); // This is 17 valid chars (no I, O, Q) }); it("rejects string with lowercase chars (regex runs before transform)", () => { const result = vinSchema.safeParse("abcdefghjklmnprst"); expect(result.success).toBe(false); // VIN_REGEX requires uppercase }); }); // --------------- schemas/auth --------------- describe("loginSchema", () => { it("accepts valid login", () => { const result = loginSchema.safeParse({ email: "user@example.com", password: "password123", }); expect(result.success).toBe(true); }); it("rejects invalid email", () => { const result = loginSchema.safeParse({ email: "not-an-email", password: "password123", }); expect(result.success).toBe(false); }); it("rejects short password", () => { const result = loginSchema.safeParse({ email: "user@example.com", password: "short", }); expect(result.success).toBe(false); }); it("rejects missing password", () => { const result = loginSchema.safeParse({ email: "user@example.com" }); expect(result.success).toBe(false); }); it("rejects missing email", () => { const result = loginSchema.safeParse({ password: "password123" }); expect(result.success).toBe(false); }); }); describe("registerSchema", () => { const validRegister = { name: "John Doe", email: "john@example.com", password: "Aa123456", }; it("accepts valid registration", () => { const result = registerSchema.safeParse(validRegister); expect(result.success).toBe(true); }); it("rejects short name", () => { const result = registerSchema.safeParse({ ...validRegister, name: "J" }); expect(result.success).toBe(false); }); it("rejects name exceeding 100 chars", () => { const result = registerSchema.safeParse({ ...validRegister, name: "A".repeat(101) }); expect(result.success).toBe(false); }); it("rejects invalid email", () => { const result = registerSchema.safeParse({ ...validRegister, email: "bad-email" }); expect(result.success).toBe(false); }); it("rejects password without uppercase", () => { const result = registerSchema.safeParse({ ...validRegister, password: "aa123456" }); expect(result.success).toBe(false); }); it("rejects password without lowercase", () => { const result = registerSchema.safeParse({ ...validRegister, password: "AA123456" }); expect(result.success).toBe(false); }); it("rejects password without number", () => { const result = registerSchema.safeParse({ ...validRegister, password: "Aaabcdef" }); expect(result.success).toBe(false); }); it("rejects password shorter than 8 chars", () => { const result = registerSchema.safeParse({ ...validRegister, password: "Aa1" }); expect(result.success).toBe(false); }); it("rejects password longer than 128 chars", () => { const result = registerSchema.safeParse({ ...validRegister, password: "Aa1" + "x".repeat(126), }); expect(result.success).toBe(false); }); }); describe("forgotPasswordSchema", () => { it("accepts valid email", () => { const result = forgotPasswordSchema.safeParse({ email: "user@example.com" }); expect(result.success).toBe(true); }); it("rejects invalid email", () => { const result = forgotPasswordSchema.safeParse({ email: "not-email" }); expect(result.success).toBe(false); }); it("rejects empty object", () => { const result = forgotPasswordSchema.safeParse({}); expect(result.success).toBe(false); }); }); describe("resetPasswordSchema", () => { const validReset = { token: "abc123token", password: "Aa123456", }; it("accepts valid reset input", () => { const result = resetPasswordSchema.safeParse(validReset); expect(result.success).toBe(true); }); it("rejects empty token", () => { const result = resetPasswordSchema.safeParse({ ...validReset, token: "" }); expect(result.success).toBe(false); }); it("rejects missing token", () => { const result = resetPasswordSchema.safeParse({ password: "Aa123456" }); expect(result.success).toBe(false); }); it("rejects weak password", () => { const result = resetPasswordSchema.safeParse({ token: "abc", password: "weak" }); expect(result.success).toBe(false); }); }); // --------------- schemas/pagination --------------- describe("paginationSchema", () => { it("accepts empty object and applies defaults", () => { const result = paginationSchema.safeParse({}); expect(result.success).toBe(true); if (result.success) { expect(result.data.page).toBe(1); expect(result.data.limit).toBe(20); } }); it("coerces string page to number", () => { const result = paginationSchema.safeParse({ page: "3", limit: "50" }); expect(result.success).toBe(true); if (result.success) { expect(result.data.page).toBe(3); expect(result.data.limit).toBe(50); expect(typeof result.data.page).toBe("number"); expect(typeof result.data.limit).toBe("number"); } }); it("rejects page less than 1", () => { const result = paginationSchema.safeParse({ page: 0 }); expect(result.success).toBe(false); }); it("rejects limit greater than 100", () => { const result = paginationSchema.safeParse({ limit: 101 }); expect(result.success).toBe(false); }); it("rejects limit less than 1", () => { const result = paginationSchema.safeParse({ limit: 0 }); expect(result.success).toBe(false); }); it("rejects non-integer page", () => { const result = paginationSchema.safeParse({ page: 1.5 }); expect(result.success).toBe(false); }); }); // --------------- schemas/changelog --------------- describe("changelogChangeTypeEnum", () => { it("accepts fix", () => { expect(changelogChangeTypeEnum.safeParse("fix").success).toBe(true); }); it("accepts feature", () => { expect(changelogChangeTypeEnum.safeParse("feature").success).toBe(true); }); it("accepts improvement", () => { expect(changelogChangeTypeEnum.safeParse("improvement").success).toBe(true); }); it("rejects invalid value", () => { expect(changelogChangeTypeEnum.safeParse("bugfix").success).toBe(false); }); }); describe("changelogEntrySchema", () => { const validEntry = { id: "123e4567-e89b-12d3-a456-426614174000", changeType: "fix", title: "Fixed bug", description: "This is a fix description", publishedAt: "2026-01-15T10:00:00.000Z", createdAt: "2026-01-15T10:00:00.000Z", updatedAt: "2026-01-15T10:00:00.000Z", }; it("accepts valid changelog entry", () => { expect(changelogEntrySchema.safeParse(validEntry).success).toBe(true); }); it("rejects missing id", () => { const { id, ...rest } = validEntry; expect(changelogEntrySchema.safeParse(rest).success).toBe(false); }); it("rejects non-UUID id", () => { expect(changelogEntrySchema.safeParse({ ...validEntry, id: "not-uuid" }).success).toBe(false); }); it("rejects empty title", () => { expect(changelogEntrySchema.safeParse({ ...validEntry, title: "" }).success).toBe(false); }); it("rejects empty description", () => { expect(changelogEntrySchema.safeParse({ ...validEntry, description: "" }).success).toBe(false); }); it("rejects invalid publishedAt datetime", () => { expect( changelogEntrySchema.safeParse({ ...validEntry, publishedAt: "not-a-date" }).success, ).toBe(false); }); it("rejects invalid changeType", () => { expect( changelogEntrySchema.safeParse({ ...validEntry, changeType: "bugfix" }).success, ).toBe(false); }); }); describe("createChangelogEntrySchema", () => { const validCreate = { changeType: "feature", title: "New feature", description: "Description of the new feature", publishedAt: "2026-01-15T10:00:00.000Z", }; it("accepts valid create input", () => { expect(createChangelogEntrySchema.safeParse(validCreate).success).toBe(true); }); it("rejects missing title", () => { const { title, ...rest } = validCreate; expect(createChangelogEntrySchema.safeParse(rest).success).toBe(false); }); it("rejects empty description", () => { expect(createChangelogEntrySchema.safeParse({ ...validCreate, description: "" }).success).toBe( false, ); }); it("does not require id field", () => { // create schema doesn't have id expect(validCreate).not.toHaveProperty("id"); }); }); describe("updateChangelogEntrySchema", () => { it("accepts partial update (empty object)", () => { expect(updateChangelogEntrySchema.safeParse({}).success).toBe(true); }); it("accepts single field update", () => { expect(updateChangelogEntrySchema.safeParse({ title: "Updated" }).success).toBe(true); }); it("accepts full update", () => { expect( updateChangelogEntrySchema.safeParse({ changeType: "fix", title: "Full update", description: "Updated description", publishedAt: "2026-01-15T10:00:00.000Z", }).success, ).toBe(true); }); it("rejects invalid changeType in partial update", () => { expect(updateChangelogEntrySchema.safeParse({ changeType: "bugfix" }).success).toBe(false); }); it("rejects empty title in partial update", () => { expect(updateChangelogEntrySchema.safeParse({ title: "" }).success).toBe(false); }); }); // --------------- utils/vin-validator --------------- describe("isValidVin", () => { it("returns true for valid VIN", () => { expect(isValidVin("WVWZZZ1JZ3W597935")).toBe(true); }); it("returns false for VIN with invalid characters", () => { expect(isValidVin("WVWZZZ1IZ3W597935")).toBe(false); }); it("returns false for short VIN", () => { expect(isValidVin("WVWZZZ1JZ3W59793")).toBe(false); }); it("returns false for empty string", () => { expect(isValidVin("")).toBe(false); }); it("converts lowercase to uppercase for validation", () => { expect(isValidVin("wvwzzz1jz3w597935")).toBe(true); }); }); describe("validateVinCheckDigit", () => { it("returns true for a VIN with valid check digit", () => { // Known-good VIN with check digit that validates expect(validateVinCheckDigit("1HGBH41JXMN109186")).toBe(true); }); it("returns false for invalid VIN", () => { expect(validateVinCheckDigit("INVALIDVIN1234567")).toBe(false); }); it("returns false for VIN with wrong check digit", () => { // Take a valid VIN and change position 8 (the check digit) expect(validateVinCheckDigit("WVWZZZ1JZ9W597935")).toBe(false); }); }); describe("extractWmi", () => { it("extracts first 3 characters as WMI", () => { expect(extractWmi("WVWZZZ1JZ3W597935")).toBe("WVW"); }); it("uppercases the result", () => { expect(extractWmi("wvwzzz1jz3w597935")).toBe("WVW"); }); }); describe("extractModelYear", () => { it("extracts year for 2010 (A)", () => { expect(extractModelYear("WVWZZZ1JZAW597935")).toBe(2010); }); it("extracts year for 2025 (S)", () => { expect(extractModelYear("WVWZZZ1JZSW597935")).toBe(2025); }); it("returns null for unknown year character", () => { expect(extractModelYear("WVWZZZ1JZ0W597935")).toBeNull(); }); it("handles lowercase input", () => { expect(extractModelYear("wvwzzz1jzaw597935")).toBe(2010); }); }); // --------------- utils/currency --------------- describe("formatTRY", () => { it("formats kurus to TRY", () => { const result = formatTRY(200_00); expect(result).toContain("200"); }); it("formats zero", () => { const result = formatTRY(0); expect(result).toContain("0"); }); }); describe("kurusToLira", () => { it("converts 100 kurus to 1 lira", () => { expect(kurusToLira(100)).toBe(1); }); it("converts 20000 kurus to 200 lira", () => { expect(kurusToLira(200_00)).toBe(200); }); it("converts 0", () => { expect(kurusToLira(0)).toBe(0); }); }); describe("liraToKurus", () => { it("converts 1 lira to 100 kurus", () => { expect(liraToKurus(1)).toBe(100); }); it("converts 200 lira to 20000 kurus", () => { expect(liraToKurus(200)).toBe(200_00); }); it("rounds to nearest kurus", () => { expect(liraToKurus(0.005)).toBe(1); }); }); // --------------- utils/formatters --------------- describe("formatVin", () => { it("uppercases and strips invalid chars", () => { expect(formatVin("wvw-zzz 1jz3w597935")).toBe("WVWZZZ1JZ3W597935"); }); it("handles already clean VIN", () => { expect(formatVin("WVWZZZ1JZ3W597935")).toBe("WVWZZZ1JZ3W597935"); }); }); describe("formatDate", () => { it("formats a Date object", () => { const d = new Date(2026, 0, 15); const result = formatDate(d); expect(result).toBe("15.01.2026"); }); it("formats an ISO string", () => { const result = formatDate("2026-01-15"); expect(result).toBe("15.01.2026"); }); }); describe("formatDateTime", () => { it("returns a string containing date and time", () => { const d = new Date(2026, 0, 15, 14, 30); const result = formatDateTime(d); expect(result).toContain("15.01.2026"); }); }); describe("slugify", () => { it("lowercases and replaces spaces with dashes", () => { expect(slugify("Hello World")).toBe("hello-world"); }); it("replaces Turkish characters", () => { expect(slugify("ğüşıöç")).toBe("gusioc"); }); it("removes leading/trailing dashes", () => { expect(slugify(" hello ")).toBe("hello"); }); it("handles special characters", () => { expect(slugify("Merhaba Dünya!")).toBe("merhaba-dunya"); }); }); describe("generateReferralCode", () => { it("returns 8-character string", () => { const code = generateReferralCode(); expect(code).toHaveLength(8); }); it("contains only uppercase letters and numbers", () => { const code = generateReferralCode(); expect(/^[A-Z0-9]+$/.test(code)).toBe(true); }); it("does not contain ambiguous characters", () => { const code = generateReferralCode(); expect(code).not.toMatch(/[IO0]/); }); it("generates different codes on repeated calls", () => { const codes = new Set(Array.from({ length: 10 }, () => generateReferralCode())); // Extremely unlikely all 10 generate the same code expect(codes.size).toBeGreaterThan(1); }); }); // --------------- constants/regex --------------- describe("VIN_REGEX", () => { it("matches a valid VIN", () => { expect(VIN_REGEX.test("WVWZZZ1JZ3W597935")).toBe(true); }); it("does not match VIN with I", () => { expect(VIN_REGEX.test("WVWZZZ1IZ3W597935")).toBe(false); }); it("does not match VIN with O", () => { expect(VIN_REGEX.test("WVWZZZ1OZ3W597935")).toBe(false); }); }); describe("EMAIL_REGEX", () => { it("matches valid email", () => { expect(EMAIL_REGEX.test("user@example.com")).toBe(true); }); it("does not match missing @", () => { expect(EMAIL_REGEX.test("userexample.com")).toBe(false); }); it("does not match missing domain", () => { expect(EMAIL_REGEX.test("user@")).toBe(false); }); }); describe("OEM_CODE_REGEX", () => { it("matches valid OEM code", () => { expect(OEM_CODE_REGEX.test("06A-109-108-B")).toBe(true); }); it("matches short OEM code", () => { expect(OEM_CODE_REGEX.test("ABC")).toBe(true); }); }); // --------------- constants/plans --------------- describe("PLANS", () => { it("has SINGLE, DOUBLE, TRIPLE, and FULL plans", () => { expect(PLANS.SINGLE).toBeDefined(); expect(PLANS.DOUBLE).toBeDefined(); expect(PLANS.TRIPLE).toBeDefined(); expect(PLANS.FULL).toBeDefined(); }); it("FULL plan has brandCount 0 (unlimited)", () => { expect(PLANS.FULL.brandCount).toBe(0); }); it("SINGLE plan has brandCount 1", () => { expect(PLANS.SINGLE.brandCount).toBe(1); }); it("all plans have positive price", () => { for (const plan of Object.values(PLANS)) { expect(plan.priceMonthly).toBeGreaterThan(0); expect(plan.priceYearly).toBeGreaterThan(0); } }); }); describe("FULL_PLAN_BRAND_LIMIT", () => { it("equals 999", () => { expect(FULL_PLAN_BRAND_LIMIT).toBe(999); }); }); describe("REFERRAL_REWARDS", () => { it("TIER_1 requires 3 referrals for 7 extension days", () => { expect(REFERRAL_REWARDS.TIER_1.count).toBe(3); expect(REFERRAL_REWARDS.TIER_1.extensionDays).toBe(7); }); it("TIER_2 requires 5 referrals for 30 extension days", () => { expect(REFERRAL_REWARDS.TIER_2.count).toBe(5); expect(REFERRAL_REWARDS.TIER_2.extensionDays).toBe(30); }); }); describe("CURRENCY", () => { it("is TRY", () => { expect(CURRENCY).toBe("TRY"); }); }); // --------------- constants/error-codes --------------- describe("ERROR_CODES", () => { it("has auth error codes", () => { expect(ERROR_CODES.INVALID_CREDENTIALS).toBe("AUTH_001"); expect(ERROR_CODES.EMAIL_ALREADY_EXISTS).toBe("AUTH_002"); expect(ERROR_CODES.SESSION_EXPIRED).toBe("AUTH_003"); expect(ERROR_CODES.UNAUTHORIZED).toBe("AUTH_004"); expect(ERROR_CODES.FORBIDDEN).toBe("AUTH_005"); }); it("has VIN error codes", () => { expect(ERROR_CODES.INVALID_VIN).toBe("VIN_001"); expect(ERROR_CODES.VIN_DECODE_FAILED).toBe("VIN_002"); expect(ERROR_CODES.BRAND_NOT_SUPPORTED).toBe("VIN_003"); }); it("has subscription error codes", () => { expect(ERROR_CODES.NO_ACTIVE_SUBSCRIPTION).toBe("SUB_001"); expect(ERROR_CODES.BRAND_ACCESS_DENIED).toBe("SUB_002"); expect(ERROR_CODES.INVALID_BRAND_COUNT).toBe("SUB_003"); expect(ERROR_CODES.SUBSCRIPTION_ALREADY_ACTIVE).toBe("SUB_004"); }); it("has payment error codes", () => { expect(ERROR_CODES.PAYMENT_FAILED).toBe("PAY_001"); expect(ERROR_CODES.EFT_RECEIPT_REQUIRED).toBe("PAY_003"); expect(ERROR_CODES.PAYMENT_ALREADY_PROCESSED).toBe("PAY_004"); }); it("has general error codes", () => { expect(ERROR_CODES.NOT_FOUND).toBe("GEN_001"); expect(ERROR_CODES.VALIDATION_ERROR).toBe("GEN_002"); expect(ERROR_CODES.INTERNAL_ERROR).toBe("GEN_003"); expect(ERROR_CODES.RATE_LIMITED).toBe("GEN_004"); expect(ERROR_CODES.CONFLICT).toBe("GEN_005"); }); it("has integration error codes", () => { expect(ERROR_CODES.PL24_ERROR).toBe("INT_001"); expect(ERROR_CODES.EMEX_ERROR).toBe("INT_002"); expect(ERROR_CODES.CORGI_ERROR).toBe("INT_003"); }); });