@workspace-fs/core
Version:
Multi-project workspace manager for Firesystem with support for multiple sources
283 lines (231 loc) • 9.72 kB
text/typescript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import type { SourceProvider } from "../interfaces/SourceProvider";
import type { IReactiveFileSystem } from "@firesystem/core";
/**
* Test suite genérica para providers do workspace
* Garante que todos os providers implementam corretamente a interface
*/
export function createProviderTestSuite(
providerName: string,
createProvider: () => SourceProvider,
validConfig?: any,
invalidConfigs?: Array<{ config: any; expectedError: string }>,
) {
describe(`${providerName} Provider Test Suite`, () => {
let provider: SourceProvider;
let filesystems: IReactiveFileSystem[] = [];
beforeEach(() => {
provider = createProvider();
filesystems = [];
});
afterEach(async () => {
// Limpar todos os file systems criados
for (const fs of filesystems) {
if (provider.dispose) {
await provider.dispose!(fs);
}
}
filesystems = [];
});
describe("Provider Interface", () => {
it("should have required properties", () => {
expect(provider.scheme).toBeDefined();
expect(typeof provider.scheme).toBe("string");
expect(provider.scheme.length).toBeGreaterThan(0);
expect(provider.displayName).toBeDefined();
expect(typeof provider.displayName).toBe("string");
expect(provider.displayName.length).toBeGreaterThan(0);
});
it("should have required methods", () => {
expect(typeof provider.createFileSystem).toBe("function");
expect(typeof provider.getCapabilities).toBe("function");
});
});
describe("createFileSystem", () => {
it("should create a file system without config", async () => {
const fs = await provider.createFileSystem({});
filesystems.push(fs);
expect(fs).toBeDefined();
expect(fs.readFile).toBeDefined();
expect(fs.writeFile).toBeDefined();
expect(fs.deleteFile).toBeDefined();
expect(fs.exists).toBeDefined();
expect(fs.mkdir).toBeDefined();
expect(fs.readDir).toBeDefined();
});
if (validConfig) {
it("should create a file system with valid config", async () => {
const fs = await provider.createFileSystem(validConfig);
filesystems.push(fs);
expect(fs).toBeDefined();
// Testar operação básica
await fs.writeFile("/test.txt", "Hello Provider");
const file = await fs.readFile("/test.txt");
expect(file.content).toBe("Hello Provider");
});
}
it("should create multiple independent file systems", async () => {
const fs1 = await provider.createFileSystem({});
const fs2 = await provider.createFileSystem({});
filesystems.push(fs1, fs2);
// Escrever em fs1
await fs1.writeFile("/file1.txt", "FS1 Content");
// Verificar que fs2 não tem o arquivo
expect(await fs2.exists("/file1.txt")).toBe(false);
// Escrever em fs2
await fs2.writeFile("/file2.txt", "FS2 Content");
// Verificar independência
expect(await fs1.exists("/file2.txt")).toBe(false);
expect(await fs2.exists("/file1.txt")).toBe(false);
});
});
describe("validateConfiguration", () => {
if (provider.validateConfiguration) {
it("should validate empty config", async () => {
const result = await provider.validateConfiguration!({});
expect(result).toBeDefined();
expect(typeof result.valid).toBe("boolean");
});
if (validConfig) {
it("should accept valid config", async () => {
const result = await provider.validateConfiguration!(validConfig);
expect(result.valid).toBe(true);
expect(result.errors).toBeUndefined();
});
}
if (invalidConfigs) {
invalidConfigs.forEach(({ config, expectedError }, index) => {
it(`should reject invalid config #${index + 1}`, async () => {
const result = await provider.validateConfiguration!(config);
expect(result.valid).toBe(false);
expect(result.errors).toBeDefined();
expect(
result.errors!.some((e) => e.includes(expectedError)),
).toBe(true);
});
});
}
}
});
describe("getCapabilities", () => {
it("should return valid capabilities", () => {
const capabilities = provider.getCapabilities();
expect(capabilities).toBeDefined();
expect(typeof capabilities.readonly).toBe("boolean");
expect(typeof capabilities.caseSensitive).toBe("boolean");
expect(typeof capabilities.atomicRename).toBe("boolean");
expect(typeof capabilities.supportsWatch).toBe("boolean");
expect(typeof capabilities.supportsMetadata).toBe("boolean");
if (capabilities.maxFileSize !== undefined) {
expect(typeof capabilities.maxFileSize).toBe("number");
expect(capabilities.maxFileSize).toBeGreaterThan(0);
}
if (capabilities.maxPathLength !== undefined) {
expect(typeof capabilities.maxPathLength).toBe("number");
expect(capabilities.maxPathLength).toBeGreaterThan(0);
}
});
});
describe("dispose", () => {
if (provider.dispose) {
it("should properly dispose file system", async () => {
const fs = await provider.createFileSystem({});
// Criar alguns arquivos
await fs.writeFile("/test1.txt", "Content 1");
await fs.writeFile("/test2.txt", "Content 2");
await fs.mkdir("/folder");
// Verificar que existem
expect(await fs.exists("/test1.txt")).toBe(true);
expect(await fs.exists("/test2.txt")).toBe(true);
expect(await fs.exists("/folder")).toBe(true);
// Dispose
await provider.dispose!(fs);
// Comportamento após dispose pode variar por provider
// Alguns podem limpar, outros podem desconectar
// Não assumimos comportamento específico aqui
});
}
});
describe("FileSystem Operations", () => {
it("should support basic file operations", async () => {
const fs = await provider.createFileSystem({});
filesystems.push(fs);
// Write
await fs.writeFile("/hello.txt", "Hello World");
// Read
const file = await fs.readFile("/hello.txt");
expect(file.content).toBe("Hello World");
expect(file.type).toBe("file");
// Exists
expect(await fs.exists("/hello.txt")).toBe(true);
expect(await fs.exists("/missing.txt")).toBe(false);
// Delete
await fs.deleteFile("/hello.txt");
expect(await fs.exists("/hello.txt")).toBe(false);
});
it("should support directory operations", async () => {
const fs = await provider.createFileSystem({});
filesystems.push(fs);
// Create directory
await fs.mkdir("/folder");
expect(await fs.exists("/folder")).toBe(true);
// Create nested structure
await fs.mkdir("/folder/subfolder", true);
await fs.writeFile("/folder/file.txt", "In folder");
await fs.writeFile("/folder/subfolder/nested.txt", "Nested");
// Read directory
const entries = await fs.readDir("/folder");
expect(entries.length).toBe(2);
expect(entries.some((e) => e.name === "subfolder")).toBe(true);
expect(entries.some((e) => e.name === "file.txt")).toBe(true);
// Remove directory
await fs.rmdir("/folder/subfolder", true);
expect(await fs.exists("/folder/subfolder")).toBe(false);
});
it("should support glob patterns", async () => {
const fs = await provider.createFileSystem({});
filesystems.push(fs);
// Create test structure
await fs.writeFile("/file1.txt", "1");
await fs.writeFile("/file2.js", "2");
await fs.mkdir("/folder");
await fs.writeFile("/folder/file3.txt", "3");
await fs.writeFile("/folder/file4.js", "4");
// Test patterns
const txtFiles = await fs.glob("**/*.txt");
expect(txtFiles.sort()).toEqual(["/file1.txt", "/folder/file3.txt"]);
const jsFiles = await fs.glob("**/*.js");
expect(jsFiles.sort()).toEqual(["/file2.js", "/folder/file4.js"]);
const allFiles = await fs.glob("**/*");
expect(allFiles.length).toBe(5); // 4 files + 1 directory
});
});
describe("Events (if supported)", () => {
it("should emit events if reactive", async () => {
const fs = await provider.createFileSystem({});
filesystems.push(fs);
if (fs.events) {
const events: string[] = [];
fs.events.on("file:written", ({ path }) => {
events.push(`written:${path}`);
});
fs.events.on("file:deleted", ({ path }) => {
events.push(`deleted:${path}`);
});
await fs.writeFile("/test.txt", "content");
await fs.deleteFile("/test.txt");
// Dar tempo para eventos serem processados
await new Promise((resolve) => setTimeout(resolve, 10));
expect(events).toContain("written:/test.txt");
expect(events).toContain("deleted:/test.txt");
}
});
});
});
}
/**
* Helper para criar testes de provider com configurações mínimas
*/
export function testProvider(provider: SourceProvider) {
return createProviderTestSuite(provider.displayName, () => provider);
}