/** * Tests for Node.js provider */ import * as path from 'path'; import * as fs from 'fs/promises'; import { tap, expect } from '@push.rocks/tapbundle'; import { SmartFs, SmartFsProviderNode } from '../ts/index.js'; // Create temp directory for tests const tempDir = path.join(process.cwd(), '.nogit', 'test-temp'); // Create test instance const nodeProvider = new SmartFsProviderNode(); const smartFs = new SmartFs(nodeProvider); tap.preTask('setup temp directory', async () => { await fs.mkdir(tempDir, { recursive: true }); }); tap.test('should create SmartFS instance with Node provider', async () => { expect(smartFs).toBeInstanceOf(SmartFs); expect(smartFs.getProviderName()).toEqual('node'); }); 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); expect(stats.size).toBeGreaterThan(0); }); 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!'); }); tap.test('should delete a file', async () => { const filePath = path.join(tempDir, 'delete-test.txt'); await smartFs.file(filePath).write('to be deleted'); await smartFs.file(filePath).delete(); const exists = await smartFs.file(filePath).exists(); expect(exists).toEqual(false); }); tap.test('should copy a file', async () => { const sourcePath = path.join(tempDir, 'copy-source.txt'); const destPath = path.join(tempDir, 'copy-dest.txt'); await smartFs.file(sourcePath).write('copy me'); await smartFs.file(sourcePath).copy(destPath); const sourceContent = await smartFs.file(sourcePath).encoding('utf8').read(); const destContent = await smartFs.file(destPath).encoding('utf8').read(); expect(sourceContent).toEqual('copy me'); expect(destContent).toEqual('copy me'); }); tap.test('should move a file', async () => { const sourcePath = path.join(tempDir, 'move-source.txt'); const destPath = path.join(tempDir, 'move-dest.txt'); await smartFs.file(sourcePath).write('move me'); await smartFs.file(sourcePath).move(destPath); const sourceExists = await smartFs.file(sourcePath).exists(); const destContent = await smartFs.file(destPath).encoding('utf8').read(); expect(sourceExists).toEqual(false); expect(destContent).toEqual('move me'); }); tap.test('should create a directory', async () => { const dirPath = path.join(tempDir, 'test-dir'); await smartFs.directory(dirPath).create(); const exists = await smartFs.directory(dirPath).exists(); expect(exists).toEqual(true); }); tap.test('should create nested directories recursively', async () => { const dirPath = path.join(tempDir, 'nested', 'deep', 'path'); await smartFs.directory(dirPath).recursive().create(); const exists = await smartFs.directory(dirPath).exists(); expect(exists).toEqual(true); }); tap.test('should list directory contents', async () => { const dirPath = path.join(tempDir, 'list-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file1.txt')).write('file1'); await smartFs.file(path.join(dirPath, 'file2.txt')).write('file2'); await smartFs.directory(path.join(dirPath, 'subdir')).create(); const entries = await smartFs.directory(dirPath).list(); expect(entries).toHaveLength(3); const names = entries.map((e) => e.name).sort(); expect(names).toEqual(['file1.txt', 'file2.txt', 'subdir']); }); tap.test('should list directory contents recursively', async () => { const dirPath = path.join(tempDir, 'recursive-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file1.txt')).write('file1'); await smartFs.directory(path.join(dirPath, 'subdir')).create(); await smartFs.file(path.join(dirPath, 'subdir', 'file2.txt')).write('file2'); const entries = await smartFs.directory(dirPath).recursive().list(); expect(entries.length).toBeGreaterThanOrEqual(3); }); tap.test('should filter directory listings with RegExp', async () => { const dirPath = path.join(tempDir, 'filter-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file1.ts')).write('ts file'); await smartFs.file(path.join(dirPath, 'file2.js')).write('js file'); await smartFs.file(path.join(dirPath, 'file3.ts')).write('ts file'); const entries = await smartFs.directory(dirPath).filter(/\.ts$/).list(); expect(entries).toHaveLength(2); expect(entries.every((e) => e.name.endsWith('.ts'))).toEqual(true); }); tap.test('should delete a directory recursively', async () => { const dirPath = path.join(tempDir, 'delete-dir-test'); await smartFs.directory(dirPath).create(); await smartFs.file(path.join(dirPath, 'file.txt')).write('file'); await smartFs.directory(path.join(dirPath, 'subdir')).create(); await smartFs.directory(dirPath).recursive().delete(); const exists = await smartFs.directory(dirPath).exists(); expect(exists).toEqual(false); }); tap.test('should handle file streams', async () => { const filePath = path.join(tempDir, 'stream-test.txt'); const testData = 'Stream test data with some content'; 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 buffer = Buffer.concat(chunks.map((c) => Buffer.from(c))); const content = buffer.toString('utf8'); expect(content).toEqual(testData); }); tap.test('should write file streams', async () => { const filePath = path.join(tempDir, 'write-stream-test.txt'); const testData = 'Writing via stream'; const buffer = Buffer.from(testData); const writeStream = await smartFs.file(filePath).writeStream(); const writer = writeStream.getWriter(); await writer.write(new Uint8Array(buffer)); await writer.close(); const content = await smartFs.file(filePath).encoding('utf8').read(); expect(content).toEqual(testData); }); tap.test('should execute transactions', async () => { const tx = smartFs.transaction(); const file1Path = path.join(tempDir, 'tx-file1.txt'); const file2Path = path.join(tempDir, 'tx-file2.txt'); const file3Path = path.join(tempDir, 'tx-file3.txt'); await tx .file(file1Path) .write('transaction file 1') .file(file2Path) .write('transaction file 2') .file(file3Path) .write('transaction file 3') .commit(); const content1 = await smartFs.file(file1Path).encoding('utf8').read(); const content2 = await smartFs.file(file2Path).encoding('utf8').read(); const content3 = await smartFs.file(file3Path).encoding('utf8').read(); expect(content1).toEqual('transaction file 1'); expect(content2).toEqual('transaction file 2'); expect(content3).toEqual('transaction file 3'); }); tap.test('should handle file watching', async () => { const filePath = path.join(tempDir, 'watch-test.txt'); await smartFs.file(filePath).write('initial'); return new Promise(async (resolve) => { const watcher = await smartFs .watch(filePath) .onChange(async (event) => { expect(event.type).toEqual('change'); await watcher.stop(); resolve(); }) .start(); // Wait a bit for watcher to be ready setTimeout(async () => { await smartFs.file(filePath).write('changed'); }, 100); }); }); tap.test('cleanup temp directory', async () => { await fs.rm(tempDir, { recursive: true, force: true }); expect(true).toEqual(true); }); export default tap.start();