Files
plainleaf/lib/spaces/space_primitives.test.ts
T
Zef HemelandGitHub dc2987a3f9 Sync engine rearchitecture (#1516)
* Don't require auth for .ping
* Finally introducing loggers
* Remove file list caching from disk_space_primitives
I don't think this is required anymore with the sync model
* Simplification of space primitives and more tests
* More reliable online status
* Add .zed settings
* Remove selfUpdate argument from `writeFile` no longer used
- Add `sync` config schema and document sync options
- Move sync config handling to client boot logic
- Move BootConfig type to ui_types.ts
- Remove sync env vars from server
- Update documentation for sync and architecture
* Improve sync logic and proxy routing for full sync state
* Add BoxProxy for late-bound client and improve Lua boot
- Add isMobile syscall for editor
* Add concurrency to sync process and log indexing duration
* Better service worker lifecycle management
2025-09-12 14:43:57 +02:00

228 lines
7.5 KiB
TypeScript

import { assert, assertEquals } from "@std/assert";
import type { SpacePrimitives } from "./space_primitives.ts";
import type { FileMeta } from "../../type/index.ts";
import { notFoundError } from "../constants.ts";
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();
assertEquals(finalFiles, []);
}
async function testBasicOperations(spacePrimitives: SpacePrimitives) {
const files = await spacePrimitives.fetchFileList();
assertEquals(files, []);
// 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");
assertEquals(retrievedData, stringToBytes("Hello World"));
// Check that the meta data is persisted
assertEquals(retrievedMeta.lastModified, 20);
const fbContent = (await spacePrimitives.readFile("test.txt"))
.data;
assertEquals(new TextDecoder().decode(fbContent), "Hello World");
assertEquals(await spacePrimitives.fetchFileList(), [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");
assertEquals(fileData.data.length, 1024 * 1024);
assertEquals((await spacePrimitives.fetchFileList()).length, 2);
await spacePrimitives.deleteFile("test.bin");
assertEquals(await spacePrimitives.fetchFileList(), [fileMeta]);
// Clean up
await spacePrimitives.deleteFile("test.txt");
assertEquals(await spacePrimitives.fetchFileList(), []);
// Test weird file names
await spacePrimitives.writeFile("test+'s.txt", stringToBytes("Hello world!"));
assertEquals(
stringToBytes("Hello world!"),
(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");
assert(false);
} catch (e: any) {
assertEquals(e, 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");
assertEquals(new TextDecoder().decode(updatedData.data), "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"
);
assertEquals(overwriteFiles.length, 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");
assertEquals(emptyFile.data.length, 0);
assertEquals(emptyFile.meta.size, 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");
assertEquals(new TextDecoder().decode(unicodeFile.data), 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);
assertEquals(
new TextDecoder().decode(fileData.data),
`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);
assert(found, `File ${fileName} should be in the file list`);
}
// 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");
assert(false, "Should throw error for non-existent file");
} catch (e: any) {
assertEquals(e, notFoundError);
}
try {
await spacePrimitives.deleteFile("nonexistent.txt");
assert(false, "Should throw error when deleting non-existent file");
} catch (e: any) {
assertEquals(e, 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");
assertEquals(largeFile.data.length, largeContent.length);
assertEquals(largeFile.meta.size, largeContent.length);
// Verify content integrity
for (let i = 0; i < Math.min(1000, largeContent.length); i++) {
assertEquals(largeFile.data[i], 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)
assert(
metaFile.meta.lastModified > 0,
"LastModified timestamp should be set",
);
assertEquals(metaFile.meta.name, "meta-test.txt");
assertEquals(metaFile.meta.size, testContent.length);
await spacePrimitives.deleteFile("meta-test.txt");
}
function stringToBytes(str: string): Uint8Array {
return new TextEncoder().encode(str);
}