Files
livedash-node/tests/unit/validation.test.ts
Kaj Kowalski 04d415f2cc feat(sessions): add missing language, sortKey, and sortOrder filtering support
- Add language field with ISO 639-1 validation to sessionFilterSchema
- Add sortKey enum with startTime, category, language, sentiment, sessionId options
- Add sortOrder enum with asc/desc options
- Update tRPC router to support new filtering and sorting parameters
- Uncomment frontend code to enable full filtering functionality
- Add comprehensive validation tests for new schema fields

Resolves commented out filter options in app/dashboard/sessions/page.tsx lines 491-502
2025-07-13 23:07:28 +02:00

360 lines
11 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
registerSchema,
loginSchema,
forgotPasswordSchema,
resetPasswordSchema,
sessionFilterSchema,
companySettingsSchema,
userUpdateSchema,
metricsQuerySchema,
validateInput,
} from "../../lib/validation";
describe("Validation Schemas", () => {
// Helper for password validation
const validPassword = "Password123!";
const invalidPasswordShort = "Pass1!";
const invalidPasswordNoLower = "PASSWORD123!";
const invalidPasswordNoUpper = "password123!";
const invalidPasswordNoNumber = "Password!!";
const invalidPasswordNoSpecial = "Password123";
// Helper for email validation
const validEmail = "test@example.com";
const invalidEmailFormat = "test@example";
const invalidEmailTooLong = "a".repeat(250) + "@example.com"; // 250 + 11 = 261 chars
// Helper for company name validation
const validCompanyName = "My Company Inc.";
const invalidCompanyNameEmpty = "";
const invalidCompanyNameTooLong = "A".repeat(101);
const invalidCompanyNameChars = "My Company #$%";
describe("registerSchema", () => {
it("should validate a valid registration object", () => {
const data = {
email: validEmail,
password: validPassword,
company: validCompanyName,
};
expect(registerSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid email", () => {
const data = {
email: invalidEmailFormat,
password: validPassword,
company: validCompanyName,
};
expect(registerSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid password", () => {
const data = {
email: validEmail,
password: invalidPasswordShort,
company: validCompanyName,
};
expect(registerSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid company name", () => {
const data = {
email: validEmail,
password: validPassword,
company: invalidCompanyNameEmpty,
};
expect(registerSchema.safeParse(data).success).toBe(false);
});
});
describe("loginSchema", () => {
it("should validate a valid login object", () => {
const data = {
email: validEmail,
password: validPassword,
};
expect(loginSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid email", () => {
const data = {
email: invalidEmailFormat,
password: validPassword,
};
expect(loginSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an empty password", () => {
const data = {
email: validEmail,
password: "",
};
expect(loginSchema.safeParse(data).success).toBe(false);
});
});
describe("forgotPasswordSchema", () => {
it("should validate a valid email", () => {
const data = { email: validEmail };
expect(forgotPasswordSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid email", () => {
const data = { email: invalidEmailFormat };
expect(forgotPasswordSchema.safeParse(data).success).toBe(false);
});
});
describe("resetPasswordSchema", () => {
it("should validate a valid reset password object", () => {
const data = {
token: "some-valid-token",
password: validPassword,
};
expect(resetPasswordSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an empty token", () => {
const data = {
token: "",
password: validPassword,
};
expect(resetPasswordSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid password", () => {
const data = {
token: "some-valid-token",
password: invalidPasswordShort,
};
expect(resetPasswordSchema.safeParse(data).success).toBe(false);
});
});
describe("sessionFilterSchema", () => {
it("should validate a valid session filter object", () => {
const data = {
search: "query",
sentiment: "POSITIVE",
category: "SCHEDULE_HOURS",
startDate: "2023-01-01T00:00:00Z",
endDate: "2023-01-31T23:59:59Z",
page: 1,
limit: 20,
};
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
});
it("should validate with only optional fields", () => {
const data = {};
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid sentiment", () => {
const data = { sentiment: "INVALID" };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid category", () => {
const data = { category: "INVALID_CATEGORY" };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid date format", () => {
const data = { startDate: "2023-01-01" }; // Missing time
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should invalidate page less than 1", () => {
const data = { page: 0 };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should invalidate limit greater than 100", () => {
const data = { limit: 101 };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should validate valid language code", () => {
const data = { language: "en" };
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
});
it("should invalidate invalid language code", () => {
const data = { language: "invalid" };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should validate valid sortKey", () => {
const data = { sortKey: "startTime" };
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
});
it("should invalidate invalid sortKey", () => {
const data = { sortKey: "invalid" };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
it("should validate valid sortOrder", () => {
const data = { sortOrder: "asc" };
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
});
it("should invalidate invalid sortOrder", () => {
const data = { sortOrder: "invalid" };
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
});
});
describe("companySettingsSchema", () => {
it("should validate a valid company settings object", () => {
const data = {
name: validCompanyName,
csvUrl: "http://example.com/data.csv",
csvUsername: "user",
csvPassword: "password",
sentimentAlert: 0.5,
dashboardOpts: { theme: "dark" },
};
expect(companySettingsSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid CSV URL", () => {
const data = {
name: validCompanyName,
csvUrl: "invalid-url",
};
expect(companySettingsSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid company name", () => {
const data = {
name: invalidCompanyNameEmpty,
csvUrl: "http://example.com/data.csv",
};
expect(companySettingsSchema.safeParse(data).success).toBe(false);
});
it("should invalidate sentimentAlert out of range", () => {
const data = {
name: validCompanyName,
csvUrl: "http://example.com/data.csv",
sentimentAlert: 1.1,
};
expect(companySettingsSchema.safeParse(data).success).toBe(false);
});
});
describe("userUpdateSchema", () => {
it("should validate a valid user update object with all fields", () => {
const data = {
email: validEmail,
role: "ADMIN",
password: validPassword,
};
expect(userUpdateSchema.safeParse(data).success).toBe(true);
});
it("should validate a valid user update object with only email", () => {
const data = { email: validEmail };
expect(userUpdateSchema.safeParse(data).success).toBe(true);
});
it("should validate a valid user update object with only role", () => {
const data = { role: "USER" };
expect(userUpdateSchema.safeParse(data).success).toBe(true);
});
it("should validate a valid user update object with only password", () => {
const data = { password: validPassword };
expect(userUpdateSchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid email", () => {
const data = { email: invalidEmailFormat };
expect(userUpdateSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid role", () => {
const data = { role: "SUPERUSER" };
expect(userUpdateSchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid password", () => {
const data = { password: invalidPasswordShort };
expect(userUpdateSchema.safeParse(data).success).toBe(false);
});
});
describe("metricsQuerySchema", () => {
it("should validate a valid metrics query object", () => {
const data = {
startDate: "2023-01-01T00:00:00Z",
endDate: "2023-01-31T23:59:59Z",
companyId: "a1b2c3d4-e5f6-7890-1234-567890abcdef",
};
expect(metricsQuerySchema.safeParse(data).success).toBe(true);
});
it("should validate with only optional fields", () => {
const data = {};
expect(metricsQuerySchema.safeParse(data).success).toBe(true);
});
it("should invalidate an invalid date format", () => {
const data = { startDate: "2023-01-01" };
expect(metricsQuerySchema.safeParse(data).success).toBe(false);
});
it("should invalidate an invalid companyId format", () => {
const data = { companyId: "invalid-uuid" };
expect(metricsQuerySchema.safeParse(data).success).toBe(false);
});
});
describe("validateInput", () => {
const testSchema = registerSchema; // Using registerSchema for validateInput tests
it("should return success true and data for valid input", () => {
const data = {
email: validEmail,
password: validPassword,
company: validCompanyName,
};
const result = validateInput(testSchema, data);
expect(result.success).toBe(true);
expect((result as any).data).toEqual(data);
});
it("should return success false and errors for invalid input", () => {
const data = {
email: invalidEmailFormat,
password: invalidPasswordShort,
company: invalidCompanyNameEmpty,
};
const result = validateInput(testSchema, data);
expect(result.success).toBe(false);
expect((result as any).errors).toEqual(
expect.arrayContaining([
"email: Invalid email format",
"password: Password must be at least 12 characters long",
"company: Company name is required",
])
);
});
it("should handle non-ZodError errors gracefully", () => {
const mockSchema = {
parse: () => {
throw new Error("Some unexpected error");
},
} as any;
const result = validateInput(mockSchema, {});
expect(result.success).toBe(false);
expect((result as any).errors).toEqual(["Invalid input"]);
});
});
});