// tstest:deno:allowAll /** * Tests for Rust provider */ import * as path from 'node:path'; import * as fs from 'node:fs/promises'; import { tap, expect } from '@push.rocks/tapbundle'; import { SmartFs, SmartFsProviderRust } from '../ts/index.js'; // Create temp directory for tests const tempDir = path.join(process.cwd(), '.nogit', 'test-temp-rust'); // Create test instance const rustProvider = new SmartFsProviderRust(); const smartFs = new SmartFs(rustProvider); tap.preTask('setup temp directory', async () => { await fs.rm(tempDir, { recursive: true, force: true }); await fs.mkdir(tempDir, { recursive: true }); }); tap.test('should create SmartFS instance with Rust provider', async () => { expect(smartFs).toBeInstanceOf(SmartFs); expect(smartFs.getProviderName()).toEqual('rust'); }); tap.test('should write and read a file', async () => { const filePath = path.join(tempDir, 'test.txt'); await smartFs.file(filePath).write('Hello, World!'); const content = await smartFs.file(filePath).encoding('utf8').read(); expect(content).toEqual('Hello, World!'); }); tap.test('should write atomically', async () => { const filePath = path.join(tempDir, 'atomic.txt'); await smartFs.file(filePath).atomic().write('Atomic write test'); const content = await smartFs.file(filePath).encoding('utf8').read(); expect(content).toEqual('Atomic write test'); }); tap.test('should check if file exists', async () => { const filePath = path.join(tempDir, 'exists-test.txt'); await smartFs.file(filePath).write('exists'); const exists = await smartFs.file(filePath).exists(); expect(exists).toEqual(true); const notExists = await smartFs.file(path.join(tempDir, 'nonexistent.txt')).exists(); expect(notExists).toEqual(false); }); tap.test('should get file stats', async () => { const filePath = path.join(tempDir, 'stats-test.txt'); await smartFs.file(filePath).write('stats test'); const stats = await smartFs.file(filePath).stat(); expect(stats).toHaveProperty('size'); expect(stats).toHaveProperty('mtime'); expect(stats).toHaveProperty('birthtime'); expect(stats.isFile).toEqual(true); expect(stats.isDirectory).toEqual(false); }); tap.test('should delete a file', async () => { const filePath = path.join(tempDir, 'delete-test.txt'); await smartFs.file(filePath).write('to delete'); await smartFs.file(filePath).delete(); const exists = await smartFs.file(filePath).exists(); expect(exists).toEqual(false); }); tap.test('should copy a file', async () => { const srcPath = path.join(tempDir, 'copy-src.txt'); const destPath = path.join(tempDir, 'copy-dest.txt'); await smartFs.file(srcPath).write('copy me'); await smartFs.file(srcPath).copy(destPath); const content = await smartFs.file(destPath).encoding('utf8').read(); expect(content).toEqual('copy me'); }); tap.test('should move a file', async () => { const srcPath = path.join(tempDir, 'move-src.txt'); const destPath = path.join(tempDir, 'move-dest.txt'); await smartFs.file(srcPath).write('move me'); await smartFs.file(srcPath).move(destPath); const exists = await smartFs.file(srcPath).exists(); expect(exists).toEqual(false); const content = await smartFs.file(destPath).encoding('utf8').read(); expect(content).toEqual('move me'); }); tap.test('should create and list a directory', async () => { const dirPath = path.join(tempDir, 'list-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'a.txt')).write('a'); await smartFs.file(path.join(dirPath, 'b.txt')).write('b'); const entries = await smartFs.directory(dirPath).list(); expect(entries.length).toEqual(2); const names = entries.map(e => e.name).sort(); expect(names).toEqual(['a.txt', 'b.txt']); }); tap.test('should check directory exists', async () => { const dirPath = path.join(tempDir, 'exists-dir'); await smartFs.directory(dirPath).create(); const exists = await smartFs.directory(dirPath).exists(); expect(exists).toEqual(true); const notExists = await smartFs.directory(path.join(tempDir, 'nonexistent-dir')).exists(); expect(notExists).toEqual(false); }); tap.test('should delete directory recursively', async () => { const dirPath = path.join(tempDir, 'delete-dir'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file.txt')).write('data'); await smartFs.directory(dirPath).delete(); const exists = await smartFs.directory(dirPath).exists(); expect(exists).toEqual(false); }); tap.test('should list directory recursively', async () => { const dirPath = path.join(tempDir, 'recursive-test'); await smartFs.directory(dirPath).create(); await smartFs.directory(path.join(dirPath, 'sub')).create(); await smartFs.file(path.join(dirPath, 'root.txt')).write('root'); await smartFs.file(path.join(dirPath, 'sub', 'child.txt')).write('child'); const entries = await smartFs.directory(dirPath).recursive().list(); const names = entries.map(e => e.name).sort(); expect(names).toContain('root.txt'); expect(names).toContain('child.txt'); expect(names).toContain('sub'); }); // ── Append file ────────────────────────────────────────────────────────────── tap.test('should append to a file', async () => { const filePath = path.join(tempDir, 'append-test.txt'); await smartFs.file(filePath).write('Hello'); await smartFs.file(filePath).append(' World!'); const content = await smartFs.file(filePath).encoding('utf8').read(); expect(content).toEqual('Hello World!'); }); // ── Binary Buffer round-trip ───────────────────────────────────────────────── tap.test('should write and read binary data (Buffer)', async () => { const filePath = path.join(tempDir, 'binary-test.bin'); const binaryData = Buffer.from([0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD, 0x80, 0x7F]); await rustProvider.writeFile(filePath, binaryData); const result = await rustProvider.readFile(filePath, { encoding: 'buffer' }); expect(Buffer.isBuffer(result)).toEqual(true); expect(Buffer.compare(result as Buffer, binaryData)).toEqual(0); }); // ── Streaming ──────────────────────────────────────────────────────────────── tap.test('should read a file via stream', async () => { const filePath = path.join(tempDir, 'stream-read.txt'); const testData = 'Stream test data with enough content to verify streaming works correctly'; await smartFs.file(filePath).write(testData); const readStream = await smartFs.file(filePath).readStream(); const chunks: Uint8Array[] = []; const reader = readStream.getReader(); let done = false; while (!done) { const result = await reader.read(); done = result.done; if (result.value) chunks.push(result.value); } const content = Buffer.concat(chunks.map(c => Buffer.from(c))).toString('utf8'); expect(content).toEqual(testData); }); tap.test('should write a file via stream', async () => { const filePath = path.join(tempDir, 'stream-write.txt'); const testData = 'Writing via stream'; const writeStream = await smartFs.file(filePath).writeStream(); const writer = writeStream.getWriter(); await writer.write(new Uint8Array(Buffer.from(testData))); await writer.close(); const content = await smartFs.file(filePath).encoding('utf8').read(); expect(content).toEqual(testData); }); // ── Watch ──────────────────────────────────────────────────────────────────── tap.test('should watch for file changes', async () => { const dirPath = path.join(tempDir, 'watch-test'); await smartFs.directory(dirPath).create(); const filePath = path.join(dirPath, 'watched.txt'); const received: any[] = []; const watcher = await smartFs .watch(dirPath) .onAll((event) => { received.push(event); }) .start(); // Give watcher time to start await new Promise((resolve) => setTimeout(resolve, 300)); await smartFs.file(filePath).write('changed'); // Wait for event to propagate await new Promise((resolve) => setTimeout(resolve, 500)); await watcher.stop(); expect(received.length).toBeGreaterThan(0); expect(received[0]).toHaveProperty('type'); expect(received[0]).toHaveProperty('path'); }); // ── Transactions ───────────────────────────────────────────────────────────── tap.test('should execute a transaction', async () => { const file1 = path.join(tempDir, 'tx-file1.txt'); const file2 = path.join(tempDir, 'tx-file2.txt'); await smartFs.transaction() .file(file1).write('tx content 1') .file(file2).write('tx content 2') .commit(); const c1 = await smartFs.file(file1).encoding('utf8').read(); const c2 = await smartFs.file(file2).encoding('utf8').read(); expect(c1).toEqual('tx content 1'); expect(c2).toEqual('tx content 2'); }); // ── Directory filter ───────────────────────────────────────────────────────── tap.test('should filter directory listings with regex', async () => { const dirPath = path.join(tempDir, 'filter-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file1.ts')).write('ts'); await smartFs.file(path.join(dirPath, 'file2.js')).write('js'); await smartFs.file(path.join(dirPath, 'file3.ts')).write('ts'); const entries = await smartFs.directory(dirPath).filter(/\.ts$/).list(); expect(entries.length).toEqual(2); const allTs = entries.every(e => e.name.endsWith('.ts')); expect(allTs).toEqual(true); }); // ── Shutdown ───────────────────────────────────────────────────────────────── tap.test('should shutdown the Rust provider', async () => { await rustProvider.shutdown(); await fs.rm(tempDir, { recursive: true, force: true }); }); export default tap.start();