diff --git a/src/test/factories/createTestArchive.ts b/src/test/factories/createTestArchive.ts new file mode 100644 index 0000000..c98b1eb --- /dev/null +++ b/src/test/factories/createTestArchive.ts @@ -0,0 +1,19 @@ +import type { Archive } from "@permanentorg/sdk"; + +interface TestArchiveOverrides { + id?: number; + slug?: string; + name?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export const createTestArchive = ( + overrides: TestArchiveOverrides = {}, +): Archive => ({ + id: overrides.id ?? 1, + slug: overrides.slug ?? "test-archive", + name: overrides.name ?? "Test Archive", + createdAt: overrides.createdAt ?? new Date("2024-01-15T12:00:00Z"), + updatedAt: overrides.updatedAt ?? new Date("2024-01-15T12:00:00Z"), +}); diff --git a/src/test/factories/createTestArchiveRecord.ts b/src/test/factories/createTestArchiveRecord.ts new file mode 100644 index 0000000..19c61c4 --- /dev/null +++ b/src/test/factories/createTestArchiveRecord.ts @@ -0,0 +1,31 @@ +import { ArchiveRecordType, Status } from "@permanentorg/sdk"; +import { createTestFile } from "./createTestFile"; +import type { ArchiveRecord, File } from "@permanentorg/sdk"; + +interface TestArchiveRecordOverrides { + id?: number; + fileSystemId?: number; + displayDate?: Date | null; + type?: ArchiveRecordType; + displayName?: string; + files?: File[]; + fileSystemCompatibleName?: string; + status?: Status; + createdAt?: Date; + updatedAt?: Date; +} + +export const createTestArchiveRecord = ( + overrides: TestArchiveRecordOverrides = {}, +): ArchiveRecord => ({ + id: overrides.id ?? 1, + fileSystemId: overrides.fileSystemId ?? 1, + displayDate: overrides.displayDate ?? null, + type: overrides.type ?? ArchiveRecordType.Unknown, + displayName: overrides.displayName ?? "Test Record", + files: overrides.files ?? [createTestFile()], + fileSystemCompatibleName: overrides.fileSystemCompatibleName ?? "test-record", + status: overrides.status ?? Status.Ok, + createdAt: overrides.createdAt ?? new Date("2024-01-15T12:00:00Z"), + updatedAt: overrides.updatedAt ?? new Date("2024-01-15T12:00:00Z"), +}); diff --git a/src/test/factories/index.ts b/src/test/factories/index.ts index 23a16cc..a590ba9 100644 --- a/src/test/factories/index.ts +++ b/src/test/factories/index.ts @@ -1,3 +1,5 @@ +export * from "./createTestArchive"; +export * from "./createTestArchiveRecord"; export * from "./createTestAttributes"; export * from "./createTestFile"; export * from "./createTestFileEntry"; diff --git a/src/utils/deduplicateFileEntries.test.ts b/src/utils/deduplicateFileEntries.test.ts index 2e3a56a..b41bdf6 100644 --- a/src/utils/deduplicateFileEntries.test.ts +++ b/src/utils/deduplicateFileEntries.test.ts @@ -2,143 +2,135 @@ import { createTestFileEntry } from "../test/factories"; import { deduplicateFileEntries } from "./deduplicateFileEntries"; describe("deduplicateFileEntries", () => { - describe("when given an empty array", () => { - it("should return an empty array", () => { - const result = deduplicateFileEntries([]); - expect(result).toEqual([]); - }); + test("should return an empty array when given an empty array", () => { + const result = deduplicateFileEntries([]); + expect(result).toEqual([]); }); - describe("when given entries with unique filenames", () => { - it("should return all entries for a single entry", () => { - const entries = [createTestFileEntry({ filename: "file1.txt" })]; + test("should return all entries for a single entry", () => { + const entries = [createTestFileEntry({ filename: "file1.txt" })]; - const result = deduplicateFileEntries(entries); + const result = deduplicateFileEntries(entries); - expect(result).toHaveLength(1); - expect(result[0].filename).toBe("file1.txt"); - }); + expect(result).toHaveLength(1); + expect(result[0].filename).toBe("file1.txt"); + }); + + test("should return all entries when all are unique", () => { + const entries = [ + createTestFileEntry({ filename: "file1.txt" }), + createTestFileEntry({ filename: "file2.txt" }), + createTestFileEntry({ filename: "file3.txt" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result).toHaveLength(3); + expect(result.map((e) => e.filename)).toEqual([ + "file1.txt", + "file2.txt", + "file3.txt", + ]); + }); + + test("should keep only the first occurrence of a duplicate", () => { + const first = createTestFileEntry({ filename: "file.txt" }); + const duplicate = createTestFileEntry({ filename: "file.txt" }); + const entries = [first, duplicate]; + + const result = deduplicateFileEntries(entries); - it("should return all entries when all are unique", () => { - const entries = [ - createTestFileEntry({ filename: "file1.txt" }), - createTestFileEntry({ filename: "file2.txt" }), - createTestFileEntry({ filename: "file3.txt" }), - ]; + expect(result).toHaveLength(1); + expect(result[0]).toBe(first); + }); + + test("should remove multiple duplicates of the same filename", () => { + const entries = [ + createTestFileEntry({ filename: "file.txt" }), + createTestFileEntry({ filename: "file.txt" }), + createTestFileEntry({ filename: "file.txt" }), + ]; - const result = deduplicateFileEntries(entries); + const result = deduplicateFileEntries(entries); - expect(result).toHaveLength(3); - expect(result.map((e) => e.filename)).toEqual([ - "file1.txt", - "file2.txt", - "file3.txt", - ]); - }); + expect(result).toHaveLength(1); + expect(result[0].filename).toBe("file.txt"); }); - describe("when given entries with duplicate filenames", () => { - it("should keep only the first occurrence of a duplicate", () => { - const first = createTestFileEntry({ filename: "file.txt" }); - const duplicate = createTestFileEntry({ filename: "file.txt" }); - const entries = [first, duplicate]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(1); - expect(result[0]).toBe(first); - }); - - it("should remove multiple duplicates of the same filename", () => { - const entries = [ - createTestFileEntry({ filename: "file.txt" }), - createTestFileEntry({ filename: "file.txt" }), - createTestFileEntry({ filename: "file.txt" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(1); - expect(result[0].filename).toBe("file.txt"); - }); - - it("should handle mixed unique and duplicate entries", () => { - const entries = [ - createTestFileEntry({ filename: "file1.txt" }), - createTestFileEntry({ filename: "file2.txt" }), - createTestFileEntry({ filename: "file1.txt" }), - createTestFileEntry({ filename: "file3.txt" }), - createTestFileEntry({ filename: "file2.txt" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(3); - expect(result.map((e) => e.filename)).toEqual([ - "file1.txt", - "file2.txt", - "file3.txt", - ]); - }); - - it("should preserve original order of first occurrences", () => { - const entries = [ - createTestFileEntry({ filename: "zebra.txt" }), - createTestFileEntry({ filename: "apple.txt" }), - createTestFileEntry({ filename: "zebra.txt" }), - createTestFileEntry({ filename: "mango.txt" }), - createTestFileEntry({ filename: "apple.txt" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result.map((e) => e.filename)).toEqual([ - "zebra.txt", - "apple.txt", - "mango.txt", - ]); - }); + test("should handle mixed unique and duplicate entries", () => { + const entries = [ + createTestFileEntry({ filename: "file1.txt" }), + createTestFileEntry({ filename: "file2.txt" }), + createTestFileEntry({ filename: "file1.txt" }), + createTestFileEntry({ filename: "file3.txt" }), + createTestFileEntry({ filename: "file2.txt" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result).toHaveLength(3); + expect(result.map((e) => e.filename)).toEqual([ + "file1.txt", + "file2.txt", + "file3.txt", + ]); }); - describe("edge cases", () => { - it("should treat filenames as case-sensitive", () => { - const entries = [ - createTestFileEntry({ filename: "File.txt" }), - createTestFileEntry({ filename: "file.txt" }), - createTestFileEntry({ filename: "FILE.txt" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(3); - }); - - it("should handle filenames with special characters", () => { - const entries = [ - createTestFileEntry({ filename: "file (1).txt" }), - createTestFileEntry({ filename: "file (1).txt" }), - createTestFileEntry({ filename: "file (2).txt" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(2); - expect(result.map((e) => e.filename)).toEqual([ - "file (1).txt", - "file (2).txt", - ]); - }); - - it("should handle empty string filenames", () => { - const entries = [ - createTestFileEntry({ filename: "" }), - createTestFileEntry({ filename: "" }), - ]; - - const result = deduplicateFileEntries(entries); - - expect(result).toHaveLength(1); - expect(result[0].filename).toBe(""); - }); + test("should preserve original order of first occurrences", () => { + const entries = [ + createTestFileEntry({ filename: "zebra.txt" }), + createTestFileEntry({ filename: "apple.txt" }), + createTestFileEntry({ filename: "zebra.txt" }), + createTestFileEntry({ filename: "mango.txt" }), + createTestFileEntry({ filename: "apple.txt" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result.map((e) => e.filename)).toEqual([ + "zebra.txt", + "apple.txt", + "mango.txt", + ]); + }); + + test("should treat filenames as case-sensitive", () => { + const entries = [ + createTestFileEntry({ filename: "File.txt" }), + createTestFileEntry({ filename: "file.txt" }), + createTestFileEntry({ filename: "FILE.txt" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result).toHaveLength(3); + }); + + test("should handle filenames with special characters", () => { + const entries = [ + createTestFileEntry({ filename: "file (1).txt" }), + createTestFileEntry({ filename: "file (1).txt" }), + createTestFileEntry({ filename: "file (2).txt" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result).toHaveLength(2); + expect(result.map((e) => e.filename)).toEqual([ + "file (1).txt", + "file (2).txt", + ]); + }); + + test("should handle empty string filenames", () => { + const entries = [ + createTestFileEntry({ filename: "" }), + createTestFileEntry({ filename: "" }), + ]; + + const result = deduplicateFileEntries(entries); + + expect(result).toHaveLength(1); + expect(result[0].filename).toBe(""); }); }); diff --git a/src/utils/generateAttributesForArchive.test.ts b/src/utils/generateAttributesForArchive.test.ts new file mode 100644 index 0000000..4efa84a --- /dev/null +++ b/src/utils/generateAttributesForArchive.test.ts @@ -0,0 +1,46 @@ +import { createTestArchive, DIRECTORY_MODE } from "../test/factories"; +import { generateAttributesForArchive } from "./generateAttributesForArchive"; + +describe("generateAttributesForArchive", () => { + test("should set mode to directory with full permissions", () => { + const archive = createTestArchive(); + + const result = generateAttributesForArchive(archive); + + expect(result.mode).toBe(DIRECTORY_MODE); + }); + + test("should set size to 0", () => { + const archive = createTestArchive(); + + const result = generateAttributesForArchive(archive); + + expect(result.size).toBe(0); + }); + + test("should convert updatedAt to unix timestamp for mtime", () => { + const updatedAt = new Date("2024-06-15T10:30:00Z"); + const archive = createTestArchive({ updatedAt }); + + const result = generateAttributesForArchive(archive); + + expect(result.mtime).toBe(updatedAt.getTime() / 1000); + }); + + test("should set atime to 0", () => { + const archive = createTestArchive(); + + const result = generateAttributesForArchive(archive); + + expect(result.atime).toBe(0); + }); + + test("should set uid and gid to 0", () => { + const archive = createTestArchive(); + + const result = generateAttributesForArchive(archive); + + expect(result.uid).toBe(0); + expect(result.gid).toBe(0); + }); +}); diff --git a/src/utils/generateAttributesForFile.test.ts b/src/utils/generateAttributesForFile.test.ts index ceea799..6dbd4e7 100644 --- a/src/utils/generateAttributesForFile.test.ts +++ b/src/utils/generateAttributesForFile.test.ts @@ -3,122 +3,114 @@ import { createTestFile, FILE_MODE } from "../test/factories"; import { generateAttributesForFile } from "./generateAttributesForFile"; describe("generateAttributesForFile", () => { - describe("when given a file", () => { - it("should set mode to regular file with full permissions", () => { - const file = createTestFile(); + test("should set mode to regular file with full permissions", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mode).toBe(FILE_MODE); - }); + expect(result.mode).toBe(FILE_MODE); + }); - it("should use the file size", () => { - const file = createTestFile({ size: 2048 }); + test("should use the file size", () => { + const file = createTestFile({ size: 2048 }); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.size).toBe(2048); - }); + expect(result.size).toBe(2048); + }); - it("should convert updatedAt to unix timestamp for mtime", () => { - const updatedAt = new Date("2024-06-15T10:30:00Z"); - const file = createTestFile({ updatedAt }); + test("should convert updatedAt to unix timestamp for mtime", () => { + const updatedAt = new Date("2024-06-15T10:30:00Z"); + const file = createTestFile({ updatedAt }); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mtime).toBe(updatedAt.getTime() / 1000); - }); + expect(result.mtime).toBe(updatedAt.getTime() / 1000); + }); - it("should set uid and gid to 0", () => { - const file = createTestFile(); + test("should set uid and gid to 0", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.uid).toBe(0); - expect(result.gid).toBe(0); - }); + expect(result.uid).toBe(0); + expect(result.gid).toBe(0); + }); - it("should set atime to 0", () => { - const file = createTestFile(); + test("should set atime to 0", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.atime).toBe(0); - }); + expect(result.atime).toBe(0); }); - describe("when not given a file", () => { - it("should return default attributes with file mode", () => { - const result = generateAttributesForFile(undefined); + test("should return default attributes with file mode when not given a file", () => { + const result = generateAttributesForFile(undefined); - expect(result.mode).toBe(FILE_MODE); - }); + expect(result.mode).toBe(FILE_MODE); + }); - it("should set size to 0", () => { - const result = generateAttributesForFile(undefined); + test("should set size to 0 when not given a file", () => { + const result = generateAttributesForFile(undefined); - expect(result.size).toBe(0); - }); + expect(result.size).toBe(0); + }); - it("should set all timestamps to 0", () => { - const result = generateAttributesForFile(undefined); + test("should set all timestamps to 0 when not given a file", () => { + const result = generateAttributesForFile(undefined); - expect(result.atime).toBe(0); - expect(result.mtime).toBe(0); - }); + expect(result.atime).toBe(0); + expect(result.mtime).toBe(0); + }); - it("should set uid and gid to 0", () => { - const result = generateAttributesForFile(undefined); + test("should set uid and gid to 0 when not given a file", () => { + const result = generateAttributesForFile(undefined); - expect(result.uid).toBe(0); - expect(result.gid).toBe(0); - }); + expect(result.uid).toBe(0); + expect(result.gid).toBe(0); }); - describe("when called without arguments", () => { - it("should return default attributes", () => { - const result = generateAttributesForFile(); - - expect(result.mode).toBe(FILE_MODE); - expect(result.size).toBe(0); - expect(result.uid).toBe(0); - expect(result.gid).toBe(0); - expect(result.atime).toBe(0); - expect(result.mtime).toBe(0); - }); + test("should return default attributes when called without arguments", () => { + const result = generateAttributesForFile(); + + expect(result.mode).toBe(FILE_MODE); + expect(result.size).toBe(0); + expect(result.uid).toBe(0); + expect(result.gid).toBe(0); + expect(result.atime).toBe(0); + expect(result.mtime).toBe(0); }); - describe("mode verification", () => { - it("should have the regular file type bit set", () => { - const file = createTestFile(); + test("should have the regular file type bit set", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mode & fs.constants.S_IFMT).toBe(fs.constants.S_IFREG); - }); + expect(result.mode & fs.constants.S_IFMT).toBe(fs.constants.S_IFREG); + }); - it("should have read/write/execute for user", () => { - const file = createTestFile(); + test("should have read/write/execute for user", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mode & fs.constants.S_IRWXU).toBe(fs.constants.S_IRWXU); - }); + expect(result.mode & fs.constants.S_IRWXU).toBe(fs.constants.S_IRWXU); + }); - it("should have read/write/execute for group", () => { - const file = createTestFile(); + test("should have read/write/execute for group", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mode & fs.constants.S_IRWXG).toBe(fs.constants.S_IRWXG); - }); + expect(result.mode & fs.constants.S_IRWXG).toBe(fs.constants.S_IRWXG); + }); - it("should have read/write/execute for others", () => { - const file = createTestFile(); + test("should have read/write/execute for others", () => { + const file = createTestFile(); - const result = generateAttributesForFile(file); + const result = generateAttributesForFile(file); - expect(result.mode & fs.constants.S_IRWXO).toBe(fs.constants.S_IRWXO); - }); + expect(result.mode & fs.constants.S_IRWXO).toBe(fs.constants.S_IRWXO); }); }); diff --git a/src/utils/generateAttributesForFolder.test.ts b/src/utils/generateAttributesForFolder.test.ts index caf1e61..3303320 100644 --- a/src/utils/generateAttributesForFolder.test.ts +++ b/src/utils/generateAttributesForFolder.test.ts @@ -2,7 +2,7 @@ import { createTestFolder, DIRECTORY_MODE } from "../test/factories"; import { generateAttributesForFolder } from "./generateAttributesForFolder"; describe("generateAttributesForFolder", () => { - it("should set mode to directory with full permissions", () => { + test("should set mode to directory with full permissions", () => { const folder = createTestFolder(); const result = generateAttributesForFolder(folder); @@ -10,7 +10,7 @@ describe("generateAttributesForFolder", () => { expect(result.mode).toBe(DIRECTORY_MODE); }); - it("should use the folder size", () => { + test("should use the folder size", () => { const folder = createTestFolder({ size: 8192 }); const result = generateAttributesForFolder(folder); @@ -18,7 +18,7 @@ describe("generateAttributesForFolder", () => { expect(result.size).toBe(8192); }); - it("should convert updatedAt to unix timestamp for mtime", () => { + test("should convert updatedAt to unix timestamp for mtime", () => { const updatedAt = new Date("2024-06-15T10:30:00Z"); const folder = createTestFolder({ updatedAt }); @@ -27,7 +27,7 @@ describe("generateAttributesForFolder", () => { expect(result.mtime).toBe(updatedAt.getTime() / 1000); }); - it("should set atime to 0", () => { + test("should set atime to 0", () => { const folder = createTestFolder(); const result = generateAttributesForFolder(folder); @@ -35,7 +35,7 @@ describe("generateAttributesForFolder", () => { expect(result.atime).toBe(0); }); - it("should set uid and gid to 0", () => { + test("should set uid and gid to 0", () => { const folder = createTestFolder(); const result = generateAttributesForFolder(folder); diff --git a/src/utils/generateDefaultAttributes.test.ts b/src/utils/generateDefaultAttributes.test.ts new file mode 100644 index 0000000..3360867 --- /dev/null +++ b/src/utils/generateDefaultAttributes.test.ts @@ -0,0 +1,47 @@ +import fs from "fs"; +import { DIRECTORY_MODE, FILE_MODE } from "../test/factories"; +import { generateDefaultAttributes } from "./generateDefaultAttributes"; + +describe("generateDefaultAttributes", () => { + test("should set mode using generateDefaultMode for regular file", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.mode).toBe(FILE_MODE); + }); + + test("should set mode using generateDefaultMode for directory", () => { + const result = generateDefaultAttributes(fs.constants.S_IFDIR); + + expect(result.mode).toBe(DIRECTORY_MODE); + }); + + test("should set uid to 0", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.uid).toBe(0); + }); + + test("should set gid to 0", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.gid).toBe(0); + }); + + test("should set size to 0", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.size).toBe(0); + }); + + test("should set atime to 0", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.atime).toBe(0); + }); + + test("should set mtime to 0", () => { + const result = generateDefaultAttributes(fs.constants.S_IFREG); + + expect(result.mtime).toBe(0); + }); +}); diff --git a/src/utils/generateDefaultMode.test.ts b/src/utils/generateDefaultMode.test.ts new file mode 100644 index 0000000..9446666 --- /dev/null +++ b/src/utils/generateDefaultMode.test.ts @@ -0,0 +1,43 @@ +import fs from "fs"; +import { DIRECTORY_MODE, FILE_MODE } from "../test/factories"; +import { generateDefaultMode } from "./generateDefaultMode"; + +describe("generateDefaultMode", () => { + test("should add full permissions to a regular file type", () => { + const result = generateDefaultMode(fs.constants.S_IFREG); + + expect(result).toBe(FILE_MODE); + }); + + test("should add full permissions to a directory type", () => { + const result = generateDefaultMode(fs.constants.S_IFDIR); + + expect(result).toBe(DIRECTORY_MODE); + }); + + test("should include read, write, execute for user", () => { + const result = generateDefaultMode(fs.constants.S_IFREG); + + expect(result & fs.constants.S_IRWXU).toBe(fs.constants.S_IRWXU); + }); + + test("should include read, write, execute for group", () => { + const result = generateDefaultMode(fs.constants.S_IFREG); + + expect(result & fs.constants.S_IRWXG).toBe(fs.constants.S_IRWXG); + }); + + test("should include read, write, execute for other", () => { + const result = generateDefaultMode(fs.constants.S_IFREG); + + expect(result & fs.constants.S_IRWXO).toBe(fs.constants.S_IRWXO); + }); + + test("should preserve the base type in the result", () => { + const fileResult = generateDefaultMode(fs.constants.S_IFREG); + const dirResult = generateDefaultMode(fs.constants.S_IFDIR); + + expect(fileResult & fs.constants.S_IFMT).toBe(fs.constants.S_IFREG); + expect(dirResult & fs.constants.S_IFMT).toBe(fs.constants.S_IFDIR); + }); +}); diff --git a/src/utils/generateFileEntriesForArchiveRecords.test.ts b/src/utils/generateFileEntriesForArchiveRecords.test.ts new file mode 100644 index 0000000..1b392c1 --- /dev/null +++ b/src/utils/generateFileEntriesForArchiveRecords.test.ts @@ -0,0 +1,123 @@ +import { DerivativeType } from "@permanentorg/sdk"; +import { + createTestArchiveRecord, + createTestFile, + FILE_MODE, +} from "../test/factories"; +import { generateFileEntriesForArchiveRecords } from "./generateFileEntriesForArchiveRecords"; + +describe("generateFileEntriesForArchiveRecords", () => { + test("should return an empty array when given an empty array", () => { + const result = generateFileEntriesForArchiveRecords([]); + + expect(result).toEqual([]); + }); + + test("should generate a file entry for a single archive record", () => { + const archiveRecord = createTestArchiveRecord({ + fileSystemCompatibleName: "my-file.txt", + }); + + const result = generateFileEntriesForArchiveRecords([archiveRecord]); + + expect(result).toHaveLength(1); + expect(result[0].filename).toBe("my-file.txt"); + }); + + test("should use file mode for archive record entries", () => { + const archiveRecord = createTestArchiveRecord(); + + const result = generateFileEntriesForArchiveRecords([archiveRecord]); + + expect(result[0].attrs.mode).toBe(FILE_MODE); + }); + + test("should use file size from the original file", () => { + const originalFile = createTestFile({ + size: 12345, + derivativeType: DerivativeType.Original, + }); + const archiveRecord = createTestArchiveRecord({ + files: [originalFile], + }); + + const result = generateFileEntriesForArchiveRecords([archiveRecord]); + + expect(result[0].attrs.size).toBe(12345); + }); + + test("should generate file entries for multiple archive records", () => { + const archiveRecords = [ + createTestArchiveRecord({ fileSystemCompatibleName: "file-a.txt" }), + createTestArchiveRecord({ fileSystemCompatibleName: "file-b.txt" }), + createTestArchiveRecord({ fileSystemCompatibleName: "file-c.txt" }), + ]; + + const result = generateFileEntriesForArchiveRecords(archiveRecords); + + expect(result).toHaveLength(3); + expect(result[0].filename).toBe("file-a.txt"); + expect(result[1].filename).toBe("file-b.txt"); + expect(result[2].filename).toBe("file-c.txt"); + }); + + test("should skip archive records without original files", () => { + const convertedFile = createTestFile({ + derivativeType: DerivativeType.Converted, + }); + const archiveRecord = createTestArchiveRecord({ + files: [convertedFile], + }); + + const result = generateFileEntriesForArchiveRecords([archiveRecord]); + + expect(result).toEqual([]); + }); + + test("should skip archive records with empty files array", () => { + const archiveRecord = createTestArchiveRecord({ + files: [], + }); + + const result = generateFileEntriesForArchiveRecords([archiveRecord]); + + expect(result).toEqual([]); + }); + + test("should include valid records and skip invalid ones", () => { + const validRecord = createTestArchiveRecord({ + fileSystemCompatibleName: "valid.txt", + files: [createTestFile({ derivativeType: DerivativeType.Original })], + }); + const invalidRecord = createTestArchiveRecord({ + fileSystemCompatibleName: "invalid.txt", + files: [createTestFile({ derivativeType: DerivativeType.Converted })], + }); + const anotherValidRecord = createTestArchiveRecord({ + fileSystemCompatibleName: "also-valid.txt", + files: [createTestFile({ derivativeType: DerivativeType.Original })], + }); + + const result = generateFileEntriesForArchiveRecords([ + validRecord, + invalidRecord, + anotherValidRecord, + ]); + + expect(result).toHaveLength(2); + expect(result[0].filename).toBe("valid.txt"); + expect(result[1].filename).toBe("also-valid.txt"); + }); + + test("should preserve archive record order in output", () => { + const archiveRecords = [ + createTestArchiveRecord({ fileSystemCompatibleName: "zebra.txt" }), + createTestArchiveRecord({ fileSystemCompatibleName: "apple.txt" }), + ]; + + const result = generateFileEntriesForArchiveRecords(archiveRecords); + + expect(result[0].filename).toBe("zebra.txt"); + expect(result[1].filename).toBe("apple.txt"); + }); +}); diff --git a/src/utils/generateFileEntriesForFolders.test.ts b/src/utils/generateFileEntriesForFolders.test.ts new file mode 100644 index 0000000..6b555bc --- /dev/null +++ b/src/utils/generateFileEntriesForFolders.test.ts @@ -0,0 +1,56 @@ +import { createTestFolder, DIRECTORY_MODE } from "../test/factories"; +import { generateFileEntriesForFolders } from "./generateFileEntriesForFolders"; + +describe("generateFileEntriesForFolders", () => { + test("should return an empty array when given an empty array", () => { + const result = generateFileEntriesForFolders([]); + + expect(result).toEqual([]); + }); + + test("should generate a file entry for a single folder", () => { + const folder = createTestFolder({ + fileSystemCompatibleName: "my-folder", + }); + + const result = generateFileEntriesForFolders([folder]); + + expect(result).toHaveLength(1); + expect(result[0].filename).toBe("my-folder"); + }); + + test("should use directory mode for folder entries", () => { + const folder = createTestFolder(); + + const result = generateFileEntriesForFolders([folder]); + + expect(result[0].attrs.mode).toBe(DIRECTORY_MODE); + }); + + test("should generate file entries for multiple folders", () => { + const folders = [ + createTestFolder({ fileSystemCompatibleName: "folder-a" }), + createTestFolder({ fileSystemCompatibleName: "folder-b" }), + createTestFolder({ fileSystemCompatibleName: "folder-c" }), + ]; + + const result = generateFileEntriesForFolders(folders); + + expect(result).toHaveLength(3); + expect(result[0].filename).toBe("folder-a"); + expect(result[1].filename).toBe("folder-b"); + expect(result[2].filename).toBe("folder-c"); + }); + + test("should preserve folder order in output", () => { + const folders = [ + createTestFolder({ fileSystemCompatibleName: "zebra" }), + createTestFolder({ fileSystemCompatibleName: "apple" }), + ]; + + const result = generateFileEntriesForFolders(folders); + + expect(result[0].filename).toBe("zebra"); + expect(result[1].filename).toBe("apple"); + }); +}); diff --git a/src/utils/generateFileEntry.test.ts b/src/utils/generateFileEntry.test.ts index 28febac..3a32ebf 100644 --- a/src/utils/generateFileEntry.test.ts +++ b/src/utils/generateFileEntry.test.ts @@ -6,109 +6,99 @@ import { import { generateFileEntry } from "./generateFileEntry"; describe("generateFileEntry", () => { - describe("filename handling", () => { - it("should use the full path as the filename", () => { - const attributes = createTestAttributes(); + test("should use the full path as the filename", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("/path/to/file.txt", attributes); + const result = generateFileEntry("/path/to/file.txt", attributes); - expect(result.filename).toBe("/path/to/file.txt"); - }); + expect(result.filename).toBe("/path/to/file.txt"); + }); - it("should handle simple filenames without path", () => { - const attributes = createTestAttributes(); + test("should handle simple filenames without path", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("file.txt", attributes); + const result = generateFileEntry("file.txt", attributes); - expect(result.filename).toBe("file.txt"); - }); + expect(result.filename).toBe("file.txt"); }); - describe("longname generation", () => { - it("should generate longname using basename for files", () => { - const attributes = createTestAttributes({ mode: FILE_MODE }); + test("should generate longname using basename for files", () => { + const attributes = createTestAttributes({ mode: FILE_MODE }); - const result = generateFileEntry("/path/to/document.pdf", attributes); + const result = generateFileEntry("/path/to/document.pdf", attributes); - expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup document.pdf"); - }); + expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup document.pdf"); + }); - it("should generate longname using basename for directories", () => { - const attributes = createTestAttributes({ mode: DIRECTORY_MODE }); + test("should generate longname using basename for directories", () => { + const attributes = createTestAttributes({ mode: DIRECTORY_MODE }); - const result = generateFileEntry("/path/to/my-folder", attributes); + const result = generateFileEntry("/path/to/my-folder", attributes); - expect(result.longname).toBe("drwxrwxrwx 1 nobody nogroup my-folder"); - }); + expect(result.longname).toBe("drwxrwxrwx 1 nobody nogroup my-folder"); + }); - it("should handle paths with multiple segments", () => { - const attributes = createTestAttributes(); + test("should handle paths with multiple segments", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("/a/b/c/d/file.txt", attributes); + const result = generateFileEntry("/a/b/c/d/file.txt", attributes); - expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup file.txt"); - }); + expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup file.txt"); }); - describe("attributes passthrough", () => { - it("should include the provided attributes in the result", () => { - const attributes = createTestAttributes({ - mode: FILE_MODE, - size: 1024, - uid: 1000, - gid: 1000, - }); - - const result = generateFileEntry("/path/to/file.txt", attributes); - - expect(result.attrs).toBe(attributes); + test("should include the provided attributes in the result", () => { + const attributes = createTestAttributes({ + mode: FILE_MODE, + size: 1024, + uid: 1000, + gid: 1000, }); - it("should preserve all attribute properties", () => { - const attributes = createTestAttributes({ - mode: DIRECTORY_MODE, - size: 4096, - atime: 1700000000, - mtime: 1700000001, - }); + const result = generateFileEntry("/path/to/file.txt", attributes); - const result = generateFileEntry("/some/dir", attributes); + expect(result.attrs).toBe(attributes); + }); - expect(result.attrs.mode).toBe(DIRECTORY_MODE); - expect(result.attrs.size).toBe(4096); - expect(result.attrs.atime).toBe(1700000000); - expect(result.attrs.mtime).toBe(1700000001); + test("should preserve all attribute properties", () => { + const attributes = createTestAttributes({ + mode: DIRECTORY_MODE, + size: 4096, + atime: 1700000000, + mtime: 1700000001, }); + + const result = generateFileEntry("/some/dir", attributes); + + expect(result.attrs.mode).toBe(DIRECTORY_MODE); + expect(result.attrs.size).toBe(4096); + expect(result.attrs.atime).toBe(1700000000); + expect(result.attrs.mtime).toBe(1700000001); }); - describe("edge cases", () => { - it("should handle filenames with spaces", () => { - const attributes = createTestAttributes(); + test("should handle filenames with spaces", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("/path/to/my file.txt", attributes); + const result = generateFileEntry("/path/to/my file.txt", attributes); - expect(result.filename).toBe("/path/to/my file.txt"); - expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup my file.txt"); - }); + expect(result.filename).toBe("/path/to/my file.txt"); + expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup my file.txt"); + }); - it("should handle filenames with special characters", () => { - const attributes = createTestAttributes(); + test("should handle filenames with special characters", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("/path/to/file (copy).txt", attributes); + const result = generateFileEntry("/path/to/file (copy).txt", attributes); - expect(result.filename).toBe("/path/to/file (copy).txt"); - expect(result.longname).toBe( - "-rwxrwxrwx 1 nobody nogroup file (copy).txt", - ); - }); + expect(result.filename).toBe("/path/to/file (copy).txt"); + expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup file (copy).txt"); + }); - it("should handle root-level files", () => { - const attributes = createTestAttributes(); + test("should handle root-level files", () => { + const attributes = createTestAttributes(); - const result = generateFileEntry("/file.txt", attributes); + const result = generateFileEntry("/file.txt", attributes); - expect(result.filename).toBe("/file.txt"); - expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup file.txt"); - }); + expect(result.filename).toBe("/file.txt"); + expect(result.longname).toBe("-rwxrwxrwx 1 nobody nogroup file.txt"); }); }); diff --git a/src/utils/getArchiveSlugFromPath.test.ts b/src/utils/getArchiveSlugFromPath.test.ts index 582d31f..9423884 100644 --- a/src/utils/getArchiveSlugFromPath.test.ts +++ b/src/utils/getArchiveSlugFromPath.test.ts @@ -1,71 +1,67 @@ import { getArchiveSlugFromPath } from "./getArchiveSlugFromPath"; describe("getArchiveSlugFromPath", () => { - describe("valid paths", () => { - it("extracts slug from basic archive path", () => { - const path = "/archives/My Archive (abc-123)"; - expect(getArchiveSlugFromPath(path)).toBe("abc-123"); - }); + test("extracts slug from basic archive path", () => { + const path = "/archives/My Archive (abc-123)"; + expect(getArchiveSlugFromPath(path)).toBe("abc-123"); + }); - it("extracts slug from path with subfolder", () => { - const path = "/archives/My Archive (abc-123)/subfolder"; - expect(getArchiveSlugFromPath(path)).toBe("abc-123"); - }); + test("extracts slug from path with subfolder", () => { + const path = "/archives/My Archive (abc-123)/subfolder"; + expect(getArchiveSlugFromPath(path)).toBe("abc-123"); + }); - it("extracts slug from path with deep nesting", () => { - const path = "/archives/My Archive (abc-123)/a/b/c"; - expect(getArchiveSlugFromPath(path)).toBe("abc-123"); - }); + test("extracts slug from path with deep nesting", () => { + const path = "/archives/My Archive (abc-123)/a/b/c"; + expect(getArchiveSlugFromPath(path)).toBe("abc-123"); + }); - it("handles archive names with parentheses", () => { - const path = "/archives/My (Cool) Archive (xyz-789)"; - expect(getArchiveSlugFromPath(path)).toBe("xyz-789"); - }); + test("handles archive names with parentheses", () => { + const path = "/archives/My (Cool) Archive (xyz-789)"; + expect(getArchiveSlugFromPath(path)).toBe("xyz-789"); + }); - it("handles slugs with only letters", () => { - const path = "/archives/Test Archive (abcdef)"; - expect(getArchiveSlugFromPath(path)).toBe("abcdef"); - }); + test("handles slugs with only letters", () => { + const path = "/archives/Test Archive (abcdef)"; + expect(getArchiveSlugFromPath(path)).toBe("abcdef"); + }); - it("handles slugs with only numbers", () => { - const path = "/archives/Test Archive (123456)"; - expect(getArchiveSlugFromPath(path)).toBe("123456"); - }); + test("handles slugs with only numbers", () => { + const path = "/archives/Test Archive (123456)"; + expect(getArchiveSlugFromPath(path)).toBe("123456"); }); - describe("invalid paths", () => { - it("throws error for path without archive section", () => { - const path = "/some/other/path"; - expect(() => getArchiveSlugFromPath(path)).toThrow( - "The specified path did not contain an archive slug", - ); - }); + test("throws error for path without archive section", () => { + const path = "/some/other/path"; + expect(() => getArchiveSlugFromPath(path)).toThrow( + "The specified path did not contain an archive slug", + ); + }); - it("throws error for path with missing slug parentheses", () => { - const path = "/archives/My Archive"; - expect(() => getArchiveSlugFromPath(path)).toThrow( - "The specified path did not contain an archive slug", - ); - }); + test("throws error for path with missing slug parentheses", () => { + const path = "/archives/My Archive"; + expect(() => getArchiveSlugFromPath(path)).toThrow( + "The specified path did not contain an archive slug", + ); + }); - it("throws error for empty string", () => { - expect(() => getArchiveSlugFromPath("")).toThrow( - "The specified path did not contain an archive slug", - ); - }); + test("throws error for empty string", () => { + expect(() => getArchiveSlugFromPath("")).toThrow( + "The specified path did not contain an archive slug", + ); + }); - it("throws error for root archives path", () => { - const path = "/archives"; - expect(() => getArchiveSlugFromPath(path)).toThrow( - "The specified path did not contain an archive slug", - ); - }); + test("throws error for root archives path", () => { + const path = "/archives"; + expect(() => getArchiveSlugFromPath(path)).toThrow( + "The specified path did not contain an archive slug", + ); + }); - it("throws error for path without space before parentheses", () => { - const path = "/archives/MyArchive(abc-123)"; - expect(() => getArchiveSlugFromPath(path)).toThrow( - "The specified path did not contain an archive slug", - ); - }); + test("throws error for path without space before parentheses", () => { + const path = "/archives/MyArchive(abc-123)"; + expect(() => getArchiveSlugFromPath(path)).toThrow( + "The specified path did not contain an archive slug", + ); }); }); diff --git a/src/utils/getLongname.test.ts b/src/utils/getLongname.test.ts index 6f3149b..5a6ad2f 100644 --- a/src/utils/getLongname.test.ts +++ b/src/utils/getLongname.test.ts @@ -6,53 +6,43 @@ import { import { getLongname } from "./getLongname"; describe("getLongname", () => { - describe("when given a regular file", () => { - it("should return a longname starting with '-'", () => { - const result = getLongname("test.txt", createTestAttributes()); - expect(result).toBe("-rwxrwxrwx 1 nobody nogroup test.txt"); - }); + test("should return a longname starting with '-' for a regular file", () => { + const result = getLongname("test.txt", createTestAttributes()); + expect(result).toBe("-rwxrwxrwx 1 nobody nogroup test.txt"); }); - describe("when given a directory", () => { - it("should return a longname starting with 'd'", () => { - const result = getLongname( - "my-folder", - createTestAttributes({ mode: DIRECTORY_MODE }), - ); - expect(result).toBe("drwxrwxrwx 1 nobody nogroup my-folder"); - }); + test("should return a longname starting with 'd' for a directory", () => { + const result = getLongname( + "my-folder", + createTestAttributes({ mode: DIRECTORY_MODE }), + ); + expect(result).toBe("drwxrwxrwx 1 nobody nogroup my-folder"); }); - describe("when given custom owner and group", () => { - it("should use the provided owner and group", () => { - const result = getLongname( - "file.txt", - createTestAttributes({ mode: FILE_MODE }), - "alice", - "staff", - ); - expect(result).toBe("-rwxrwxrwx 1 alice staff file.txt"); - }); + test("should use the provided owner and group", () => { + const result = getLongname( + "file.txt", + createTestAttributes({ mode: FILE_MODE }), + "alice", + "staff", + ); + expect(result).toBe("-rwxrwxrwx 1 alice staff file.txt"); }); - describe("when given only a custom owner", () => { - it("should use the provided owner and default group", () => { - const result = getLongname( - "file.txt", - createTestAttributes({ mode: FILE_MODE }), - "bob", - ); - expect(result).toBe("-rwxrwxrwx 1 bob nogroup file.txt"); - }); + test("should use the provided owner and default group when only owner is given", () => { + const result = getLongname( + "file.txt", + createTestAttributes({ mode: FILE_MODE }), + "bob", + ); + expect(result).toBe("-rwxrwxrwx 1 bob nogroup file.txt"); }); - describe("when given a filename with spaces", () => { - it("should preserve the spaces in the output", () => { - const result = getLongname( - "my file.txt", - createTestAttributes({ mode: FILE_MODE }), - ); - expect(result).toBe("-rwxrwxrwx 1 nobody nogroup my file.txt"); - }); + test("should preserve spaces in filenames", () => { + const result = getLongname( + "my file.txt", + createTestAttributes({ mode: FILE_MODE }), + ); + expect(result).toBe("-rwxrwxrwx 1 nobody nogroup my file.txt"); }); }); diff --git a/src/utils/getOriginalFileForArchiveRecord.test.ts b/src/utils/getOriginalFileForArchiveRecord.test.ts new file mode 100644 index 0000000..87ca515 --- /dev/null +++ b/src/utils/getOriginalFileForArchiveRecord.test.ts @@ -0,0 +1,84 @@ +import { DerivativeType } from "@permanentorg/sdk"; +import { createTestArchiveRecord, createTestFile } from "../test/factories"; +import { getOriginalFileForArchiveRecord } from "./getOriginalFileForArchiveRecord"; + +describe("getOriginalFileForArchiveRecord", () => { + test("should return the original file from the archive record", () => { + const originalFile = createTestFile({ + id: 1, + derivativeType: DerivativeType.Original, + }); + const archiveRecord = createTestArchiveRecord({ + files: [originalFile], + }); + + const result = getOriginalFileForArchiveRecord(archiveRecord); + + expect(result).toBe(originalFile); + }); + + test("should find the original file among multiple files", () => { + const convertedFile = createTestFile({ + id: 1, + derivativeType: DerivativeType.Converted, + }); + const originalFile = createTestFile({ + id: 2, + derivativeType: DerivativeType.Original, + }); + const fullHdFile = createTestFile({ + id: 3, + derivativeType: DerivativeType.FullHd, + }); + const archiveRecord = createTestArchiveRecord({ + files: [convertedFile, originalFile, fullHdFile], + }); + + const result = getOriginalFileForArchiveRecord(archiveRecord); + + expect(result).toBe(originalFile); + }); + + test("should return the first original file when multiple exist", () => { + const firstOriginal = createTestFile({ + id: 1, + derivativeType: DerivativeType.Original, + }); + const secondOriginal = createTestFile({ + id: 2, + derivativeType: DerivativeType.Original, + }); + const archiveRecord = createTestArchiveRecord({ + files: [firstOriginal, secondOriginal], + }); + + const result = getOriginalFileForArchiveRecord(archiveRecord); + + expect(result).toBe(firstOriginal); + }); + + test("should throw an error when no original file exists", () => { + const convertedFile = createTestFile({ + derivativeType: DerivativeType.Converted, + }); + const archiveRecord = createTestArchiveRecord({ + id: 42, + files: [convertedFile], + }); + + expect(() => getOriginalFileForArchiveRecord(archiveRecord)).toThrow( + "Permanent does not have an original file for Archive Record 42", + ); + }); + + test("should throw an error when files array is empty", () => { + const archiveRecord = createTestArchiveRecord({ + id: 99, + files: [], + }); + + expect(() => getOriginalFileForArchiveRecord(archiveRecord)).toThrow( + "Permanent does not have an original file for Archive Record 99", + ); + }); +});