system-timezone.test.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /**
  2. * System Timezone Tests
  3. *
  4. * TDD tests for the system timezone feature:
  5. * 1. Timezone field in SystemSettings
  6. * 2. IANA timezone validation
  7. * 3. Timezone resolver with fallback chain
  8. */
  9. import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
  10. describe("System Timezone", () => {
  11. describe("IANA Timezone Validation", () => {
  12. it("should accept valid IANA timezone strings", async () => {
  13. const { isValidIANATimezone } = await import("@/lib/utils/timezone");
  14. expect(isValidIANATimezone("Asia/Shanghai")).toBe(true);
  15. expect(isValidIANATimezone("America/New_York")).toBe(true);
  16. expect(isValidIANATimezone("Europe/London")).toBe(true);
  17. expect(isValidIANATimezone("UTC")).toBe(true);
  18. expect(isValidIANATimezone("Pacific/Auckland")).toBe(true);
  19. });
  20. it("should reject invalid timezone strings", async () => {
  21. const { isValidIANATimezone } = await import("@/lib/utils/timezone");
  22. expect(isValidIANATimezone("")).toBe(false);
  23. expect(isValidIANATimezone("Invalid/Timezone")).toBe(false);
  24. // Note: Some abbreviations like "CST" may be valid in Intl API depending on environment
  25. // We test clearly invalid values
  26. expect(isValidIANATimezone("NotATimezone/AtAll")).toBe(false);
  27. expect(isValidIANATimezone(null as unknown as string)).toBe(false);
  28. expect(isValidIANATimezone(undefined as unknown as string)).toBe(false);
  29. });
  30. });
  31. describe("toSystemSettings transformer", () => {
  32. beforeEach(() => {
  33. vi.useFakeTimers();
  34. vi.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
  35. });
  36. afterEach(() => {
  37. vi.useRealTimers();
  38. });
  39. it("should map timezone field from database", async () => {
  40. const { toSystemSettings } = await import("@/repository/_shared/transformers");
  41. const result = toSystemSettings({
  42. id: 1,
  43. timezone: "Europe/Paris",
  44. });
  45. expect(result.timezone).toBe("Europe/Paris");
  46. });
  47. it("should default to null when timezone is not set", async () => {
  48. const { toSystemSettings } = await import("@/repository/_shared/transformers");
  49. const result = toSystemSettings({
  50. id: 1,
  51. });
  52. expect(result.timezone).toBeNull();
  53. });
  54. });
  55. describe("UpdateSystemSettingsSchema", () => {
  56. it("should accept valid IANA timezone", async () => {
  57. const { UpdateSystemSettingsSchema } = await import("@/lib/validation/schemas");
  58. const result = UpdateSystemSettingsSchema.safeParse({
  59. timezone: "Asia/Tokyo",
  60. });
  61. expect(result.success).toBe(true);
  62. if (result.success) {
  63. expect(result.data.timezone).toBe("Asia/Tokyo");
  64. }
  65. });
  66. it("should reject invalid timezone", async () => {
  67. const { UpdateSystemSettingsSchema } = await import("@/lib/validation/schemas");
  68. const result = UpdateSystemSettingsSchema.safeParse({
  69. timezone: "Invalid/Zone",
  70. });
  71. expect(result.success).toBe(false);
  72. });
  73. it("should accept undefined timezone (no update)", async () => {
  74. const { UpdateSystemSettingsSchema } = await import("@/lib/validation/schemas");
  75. const result = UpdateSystemSettingsSchema.safeParse({
  76. siteTitle: "Test Site",
  77. });
  78. expect(result.success).toBe(true);
  79. if (result.success) {
  80. expect(result.data.timezone).toBeUndefined();
  81. }
  82. });
  83. it("should accept null timezone (clear setting)", async () => {
  84. const { UpdateSystemSettingsSchema } = await import("@/lib/validation/schemas");
  85. const result = UpdateSystemSettingsSchema.safeParse({
  86. timezone: null,
  87. });
  88. expect(result.success).toBe(true);
  89. if (result.success) {
  90. expect(result.data.timezone).toBeNull();
  91. }
  92. });
  93. });
  94. });