quickly upload files to a remote server via rsync
at main 127 lines 3.7 kB view raw
1import { describe, test, expect } from "bun:test"; 2import { formatTimestamp, validateFilename } from "./utils"; 3 4describe("formatTimestamp", () => { 5 test("formats date correctly", () => { 6 const date = new Date(2024, 0, 15, 14, 30, 45); // Jan 15, 2024 14:30:45 7 const result = formatTimestamp(date); 8 expect(result).toBe("2024-01-15 at 14.30.45"); 9 }); 10 11 test("pads single digit values", () => { 12 const date = new Date(2024, 2, 5, 9, 5, 5); // Mar 5, 2024 09:05:05 13 const result = formatTimestamp(date); 14 expect(result).toBe("2024-03-05 at 09.05.05"); 15 }); 16 17 test("handles midnight", () => { 18 const date = new Date(2024, 0, 1, 0, 0, 0); 19 const result = formatTimestamp(date); 20 expect(result).toBe("2024-01-01 at 00.00.00"); 21 }); 22 23 test("handles end of day", () => { 24 const date = new Date(2024, 0, 1, 23, 59, 59); 25 const result = formatTimestamp(date); 26 expect(result).toBe("2024-01-01 at 23.59.59"); 27 }); 28}); 29 30describe("validateFilename", () => { 31 test("accepts valid filename", () => { 32 const result = validateFilename("my-file.txt"); 33 expect(result.ok).toBe(true); 34 if (result.ok) { 35 expect(result.value).toBe("my-file.txt"); 36 } 37 }); 38 39 test("accepts filename with spaces", () => { 40 const result = validateFilename("my file.txt"); 41 expect(result.ok).toBe(true); 42 }); 43 44 test("accepts unicode characters", () => { 45 const result = validateFilename("文件.txt"); 46 expect(result.ok).toBe(true); 47 }); 48 49 test("rejects empty string", () => { 50 const result = validateFilename(""); 51 expect(result.ok).toBe(false); 52 if (!result.ok) { 53 expect(result.error).toBe("empty"); 54 } 55 }); 56 57 test("rejects whitespace-only string", () => { 58 const result = validateFilename(" "); 59 expect(result.ok).toBe(false); 60 if (!result.ok) { 61 expect(result.error).toBe("empty"); 62 } 63 }); 64 65 test("rejects path traversal with ..", () => { 66 const result = validateFilename("../etc/passwd"); 67 expect(result.ok).toBe(false); 68 if (!result.ok) { 69 expect(result.error).toBe("path_traversal"); 70 } 71 }); 72 73 test("rejects path traversal with /", () => { 74 const result = validateFilename("/etc/passwd"); 75 expect(result.ok).toBe(false); 76 if (!result.ok) { 77 expect(result.error).toBe("path_traversal"); 78 } 79 }); 80 81 test("rejects path traversal with backslash", () => { 82 const result = validateFilename("file\\..\\passwd"); 83 expect(result.ok).toBe(false); 84 if (!result.ok) { 85 expect(result.error).toBe("path_traversal"); 86 } 87 }); 88 89 test("rejects filename with null byte", () => { 90 const result = validateFilename("file\x00.txt"); 91 expect(result.ok).toBe(false); 92 if (!result.ok) { 93 expect(result.error).toBe("invalid_chars"); 94 } 95 }); 96 97 test("rejects filename with control characters", () => { 98 const result = validateFilename("file\x01.txt"); 99 expect(result.ok).toBe(false); 100 if (!result.ok) { 101 expect(result.error).toBe("invalid_chars"); 102 } 103 }); 104 105 test("rejects filename exceeding max length", () => { 106 const longName = "a".repeat(256); 107 const result = validateFilename(longName); 108 expect(result.ok).toBe(false); 109 if (!result.ok) { 110 expect(result.error).toBe("too_long"); 111 } 112 }); 113 114 test("accepts filename at max length", () => { 115 const maxName = "a".repeat(255); 116 const result = validateFilename(maxName); 117 expect(result.ok).toBe(true); 118 }); 119 120 test("trims whitespace from valid filename", () => { 121 const result = validateFilename(" file.txt "); 122 expect(result.ok).toBe(true); 123 if (result.ok) { 124 expect(result.value).toBe("file.txt"); 125 } 126 }); 127});