Files
plainleaf/client/spaces/space_primitives.test.ts
T

224 lines
7.6 KiB
TypeScript

import { expect } from "vitest";
import type { SpacePrimitives } from "./space_primitives.ts";
import type { FileMeta } from "@silverbulletmd/silverbullet/type/index";
import { notFoundError } from "@silverbulletmd/silverbullet/constants";
export async function testSpacePrimitives(spacePrimitives: SpacePrimitives) {
await testBasicOperations(spacePrimitives);
await testFileOverwriting(spacePrimitives);
await testEmptyFiles(spacePrimitives);
await testUnicodeContent(spacePrimitives);
await testSpecialFileNames(spacePrimitives);
await testErrorHandling(spacePrimitives);
await testLargeFiles(spacePrimitives);
await testMetadataPreservation(spacePrimitives);
// Ensure clean state at the end
const finalFiles = await spacePrimitives.fetchFileList();
expect(finalFiles).toEqual([]);
}
async function testBasicOperations(spacePrimitives: SpacePrimitives) {
const files = await spacePrimitives.fetchFileList();
expect(files).toEqual([]);
// Write text file
const fileMeta = await spacePrimitives.writeFile(
"test.txt",
stringToBytes("Hello World"),
{
name: "test.txt",
perm: "rw",
created: 10,
contentType: "text/plain",
lastModified: 20,
size: 11,
},
);
const { data: retrievedData, meta: retrievedMeta } =
await spacePrimitives.readFile("test.txt");
expect(retrievedData).toEqual(stringToBytes("Hello World"));
// Check that the meta data is persisted
expect(retrievedMeta.lastModified).toEqual(20);
const fbContent = (await spacePrimitives.readFile("test.txt")).data;
expect(new TextDecoder().decode(fbContent)).toEqual("Hello World");
expect(await spacePrimitives.fetchFileList()).toEqual([fileMeta]);
// Write binary file
const buf = new Uint8Array(1024 * 1024);
buf.set([1, 2, 3, 4, 5]);
await spacePrimitives.writeFile("test.bin", buf);
const fileData = await spacePrimitives.readFile("test.bin");
expect(fileData.data.length).toEqual(1024 * 1024);
expect((await spacePrimitives.fetchFileList()).length).toEqual(2);
await spacePrimitives.deleteFile("test.bin");
expect(await spacePrimitives.fetchFileList()).toEqual([fileMeta]);
// Clean up
await spacePrimitives.deleteFile("test.txt");
expect(await spacePrimitives.fetchFileList()).toEqual([]);
// Test weird file names
await spacePrimitives.writeFile("test+'s.txt", stringToBytes("Hello world!"));
expect(stringToBytes("Hello world!")).toEqual(
(await spacePrimitives.readFile("test+'s.txt")).data,
);
await spacePrimitives.deleteFile("test+'s.txt");
// Check deletion of weird file file name
try {
await spacePrimitives.getFileMeta("test+'s.txt");
expect(false).toBeTruthy();
} catch (e: any) {
expect(e).toEqual(notFoundError);
}
}
async function testFileOverwriting(spacePrimitives: SpacePrimitives) {
// Test overwriting existing files
await spacePrimitives.writeFile("overwrite.txt", stringToBytes("Original"));
const _originalMeta = await spacePrimitives.getFileMeta("overwrite.txt");
await spacePrimitives.writeFile("overwrite.txt", stringToBytes("Updated"));
const updatedData = await spacePrimitives.readFile("overwrite.txt");
expect(new TextDecoder().decode(updatedData.data)).toEqual("Updated");
// File list should still have only one entry for this file
const filesAfterOverwrite = await spacePrimitives.fetchFileList();
const overwriteFiles = filesAfterOverwrite.filter(
(f) => f.name === "overwrite.txt",
);
expect(overwriteFiles.length).toEqual(1);
await spacePrimitives.deleteFile("overwrite.txt");
}
async function testEmptyFiles(spacePrimitives: SpacePrimitives) {
// Test empty file
await spacePrimitives.writeFile("empty.txt", new Uint8Array(0));
const emptyFile = await spacePrimitives.readFile("empty.txt");
expect(emptyFile.data.length).toEqual(0);
expect(emptyFile.meta.size).toEqual(0);
await spacePrimitives.deleteFile("empty.txt");
}
async function testUnicodeContent(spacePrimitives: SpacePrimitives) {
// Test files with Unicode characters
const unicodeContent = "Hello 世界! 🌍 Здравствуй мир!";
await spacePrimitives.writeFile("unicode.txt", stringToBytes(unicodeContent));
const unicodeFile = await spacePrimitives.readFile("unicode.txt");
expect(new TextDecoder().decode(unicodeFile.data)).toEqual(unicodeContent);
await spacePrimitives.deleteFile("unicode.txt");
}
async function testSpecialFileNames(spacePrimitives: SpacePrimitives) {
// Test file names with various special characters
const specialNames = [
"file with spaces.txt",
"file-with-hyphens.txt",
"file_with_underscores.txt",
"file.with.dots.txt",
"UPPERCASE.TXT",
"123numeric.txt",
"émojis🚀file.txt",
];
for (const fileName of specialNames) {
await spacePrimitives.writeFile(
fileName,
stringToBytes(`Content of ${fileName}`),
);
const fileData = await spacePrimitives.readFile(fileName);
expect(new TextDecoder().decode(fileData.data)).toEqual(
`Content of ${fileName}`,
);
}
// Verify all special files are in the list
const allFiles = await spacePrimitives.fetchFileList();
for (const fileName of specialNames) {
const found = allFiles.find((f) => f.name === fileName);
expect(found, `File ${fileName} should be in the file list`).toBeTruthy();
}
// Clean up special files
for (const fileName of specialNames) {
await spacePrimitives.deleteFile(fileName);
}
}
async function testErrorHandling(spacePrimitives: SpacePrimitives) {
// Test error cases
try {
await spacePrimitives.readFile("nonexistent.txt");
expect(false, "Should throw error for non-existent file").toBeTruthy();
} catch (e: any) {
expect(e).toEqual(notFoundError);
}
try {
await spacePrimitives.deleteFile("nonexistent.txt");
expect(
false,
"Should throw error when deleting non-existent file",
).toBeTruthy();
} catch (e: any) {
expect(e).toEqual(notFoundError);
}
}
async function testLargeFiles(spacePrimitives: SpacePrimitives) {
// Test large file content
const largeContent = new Uint8Array(5 * 1024 * 1024); // 5MB
for (let i = 0; i < largeContent.length; i++) {
largeContent[i] = i % 256;
}
await spacePrimitives.writeFile("large.bin", largeContent);
const largeFile = await spacePrimitives.readFile("large.bin");
expect(largeFile.data.length).toEqual(largeContent.length);
expect(largeFile.meta.size).toEqual(largeContent.length);
// Verify content integrity
for (let i = 0; i < Math.min(1000, largeContent.length); i++) {
expect(largeFile.data[i]).toEqual(largeContent[i]);
}
await spacePrimitives.deleteFile("large.bin");
}
async function testMetadataPreservation(spacePrimitives: SpacePrimitives) {
// Test metadata preservation
const testContent = stringToBytes("Hello meta!");
const customMeta: FileMeta = {
name: "meta-test.txt",
perm: "rw",
created: 1000000,
contentType: "text/plain", // Use a content type that matches the file extension
lastModified: 2000000,
size: testContent.length, // Use actual content length
};
await spacePrimitives.writeFile("meta-test.txt", testContent, customMeta);
const metaFile = await spacePrimitives.readFile("meta-test.txt");
// Check that some metadata is preserved (implementations may handle timestamps differently)
expect(
metaFile.meta.lastModified > 0,
"LastModified timestamp should be set",
).toBeTruthy();
expect(metaFile.meta.name).toEqual("meta-test.txt");
expect(metaFile.meta.size).toEqual(testContent.length);
await spacePrimitives.deleteFile("meta-test.txt");
}
function stringToBytes(str: string): Uint8Array {
return new TextEncoder().encode(str);
}