mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 09:32:08 +01:00
- 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
360 lines
11 KiB
TypeScript
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"]);
|
|
});
|
|
});
|
|
});
|