/** * Tests for Memory provider */ import { tap, expect } from '@push.rocks/tapbundle'; import { SmartFs, SmartFsProviderMemory } from '../ts/index.js'; // Create test instance const memoryProvider = new SmartFsProviderMemory(); const fs = new SmartFs(memoryProvider); tap.test('should create SmartFS instance with Memory provider', async () => { expect(fs).toBeInstanceOf(SmartFs); expect(fs.getProviderName()).toEqual('memory'); }); tap.test('should write and read a file', async () => { await fs.file('/test.txt').write('Hello, World!'); const content = await fs.file('/test.txt').encoding('utf8').read(); expect(content).toEqual('Hello, World!'); }); tap.test('should write and read a file with encoding', async () => { await fs.file('/test2.txt').encoding('utf8').write('Test content'); const content = await fs.file('/test2.txt').encoding('utf8').read(); expect(content).toEqual('Test content'); }); tap.test('should check if file exists', async () => { const exists = await fs.file('/test.txt').exists(); expect(exists).toEqual(true); const notExists = await fs.file('/nonexistent.txt').exists(); expect(notExists).toEqual(false); }); tap.test('should get file stats', async () => { await fs.file('/stats-test.txt').write('stats test'); const stats = await fs.file('/stats-test.txt').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 append to a file', async () => { await fs.file('/append-test.txt').write('Hello'); await fs.file('/append-test.txt').append(' World!'); const content = await fs.file('/append-test.txt').encoding('utf8').read(); expect(content).toEqual('Hello World!'); }); tap.test('should delete a file', async () => { await fs.file('/delete-test.txt').write('to be deleted'); await fs.file('/delete-test.txt').delete(); const exists = await fs.file('/delete-test.txt').exists(); expect(exists).toEqual(false); }); tap.test('should copy a file', async () => { await fs.file('/copy-source.txt').write('copy me'); await fs.file('/copy-source.txt').copy('/copy-dest.txt'); const sourceContent = await fs.file('/copy-source.txt').encoding('utf8').read(); const destContent = await fs.file('/copy-dest.txt').encoding('utf8').read(); expect(sourceContent).toEqual('copy me'); expect(destContent).toEqual('copy me'); }); tap.test('should move a file', async () => { await fs.file('/move-source.txt').write('move me'); await fs.file('/move-source.txt').move('/move-dest.txt'); const sourceExists = await fs.file('/move-source.txt').exists(); const destContent = await fs.file('/move-dest.txt').encoding('utf8').read(); expect(sourceExists).toEqual(false); expect(destContent).toEqual('move me'); }); tap.test('should create a directory', async () => { await fs.directory('/test-dir').create(); const exists = await fs.directory('/test-dir').exists(); expect(exists).toEqual(true); }); tap.test('should create nested directories recursively', async () => { await fs.directory('/nested/deep/path').recursive().create(); const exists = await fs.directory('/nested/deep/path').exists(); expect(exists).toEqual(true); }); tap.test('should list directory contents', async () => { await fs.directory('/list-test').create(); await fs.file('/list-test/file1.txt').write('file1'); await fs.file('/list-test/file2.txt').write('file2'); await fs.directory('/list-test/subdir').create(); const entries = await fs.directory('/list-test').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 () => { await fs.directory('/recursive-test').create(); await fs.file('/recursive-test/file1.txt').write('file1'); await fs.directory('/recursive-test/subdir').create(); await fs.file('/recursive-test/subdir/file2.txt').write('file2'); const entries = await fs.directory('/recursive-test').recursive().list(); expect(entries.length).toBeGreaterThanOrEqual(3); }); tap.test('should filter directory listings', async () => { await fs.directory('/filter-test').create(); await fs.file('/filter-test/file1.ts').write('ts file'); await fs.file('/filter-test/file2.js').write('js file'); await fs.file('/filter-test/file3.ts').write('ts file'); const entries = await fs.directory('/filter-test').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 () => { await fs.directory('/delete-dir-test').create(); await fs.file('/delete-dir-test/file.txt').write('file'); await fs.directory('/delete-dir-test/subdir').create(); await fs.directory('/delete-dir-test').recursive().delete(); const exists = await fs.directory('/delete-dir-test').exists(); expect(exists).toEqual(false); }); tap.test('should handle file streams', async () => { const testData = 'Stream test data with some content'; await fs.file('/stream-test.txt').write(testData); const readStream = await fs.file('/stream-test.txt').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 testData = 'Writing via stream'; const buffer = Buffer.from(testData); const writeStream = await fs.file('/write-stream-test.txt').writeStream(); const writer = writeStream.getWriter(); await writer.write(new Uint8Array(buffer)); await writer.close(); const content = await fs.file('/write-stream-test.txt').encoding('utf8').read(); expect(content).toEqual(testData); }); tap.test('should execute transactions', async () => { await fs .transaction() .file('/tx-file1.txt') .write('transaction file 1') .file('/tx-file2.txt') .write('transaction file 2') .file('/tx-file3.txt') .write('transaction file 3') .commit(); const content1 = await fs.file('/tx-file1.txt').encoding('utf8').read(); const content2 = await fs.file('/tx-file2.txt').encoding('utf8').read(); const content3 = await fs.file('/tx-file3.txt').encoding('utf8').read(); expect(content1).toEqual('transaction file 1'); expect(content2).toEqual('transaction file 2'); expect(content3).toEqual('transaction file 3'); }); tap.test('should rollback transactions on error', async () => { // This test verifies that transaction operations are prepared with backups // Since memory provider doesn't naturally fail, we just verify the mechanism exists await fs.file('/tx-rollback-test.txt').write('original'); const tx = fs.transaction(); tx.file('/tx-rollback-test.txt').write('modified'); // Verify operations are tracked const operations = tx.getOperations(); expect(operations.length).toEqual(1); expect(operations[0].type).toEqual('write'); // Commit normally (rollback test would require mocking provider failure) await tx.commit(); expect(tx.isCommitted()).toEqual(true); }); tap.test('should handle file watching', async () => { await fs.file('/watch-test.txt').write('initial'); // Create watcher (verifies the API works) const watcher = await fs .watch('/watch-test.txt') .onChange((event) => { // Event handler registered }) .start(); // Verify watcher can be stopped await watcher.stop(); expect(true).toEqual(true); // Test passes if we get here }); export default tap.start();