fix(fs): Improve fs and stream handling, enhance SmartFile/StreamFile, update tests and CI configs

This commit is contained in:
2025-08-18 00:13:03 +00:00
parent 9b0d89b9ef
commit cd147ca38e
25 changed files with 3003 additions and 1221 deletions

View File

@@ -9,53 +9,83 @@ const testAssetsPath = './test/testassets/';
// StreamFile tests
// ---------------------------
tap.test('StreamFile.fromPath should create a StreamFile from a file path', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
const contentBuffer = await streamFile.getContentAsBuffer();
expect(contentBuffer).toBeInstanceOf(Buffer);
});
tap.test(
'StreamFile.fromPath should create a StreamFile from a file path',
async () => {
const streamFile = await smartfile.StreamFile.fromPath(
path.join(testAssetsPath, 'mytest.json'),
);
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
const contentBuffer = await streamFile.getContentAsBuffer();
expect(contentBuffer).toBeInstanceOf(Buffer);
},
);
tap.test('StreamFile.fromUrl should create a StreamFile from a URL', async () => {
const streamFile = await smartfile.StreamFile.fromUrl('http://example.com/somefile.json');
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
});
tap.test(
'StreamFile.fromUrl should create a StreamFile from a URL',
async () => {
const streamFile = await smartfile.StreamFile.fromUrl(
'http://example.com/somefile.json',
);
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
},
);
tap.test('StreamFile.fromBuffer should create a StreamFile from a Buffer', async () => {
const buffer = Buffer.from('Some content');
const streamFile = smartfile.StreamFile.fromBuffer(buffer, 'bufferfile.txt');
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
});
tap.test(
'StreamFile.fromBuffer should create a StreamFile from a Buffer',
async () => {
const buffer = Buffer.from('Some content');
const streamFile = smartfile.StreamFile.fromBuffer(
buffer,
'bufferfile.txt',
);
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
},
);
tap.test('StreamFile should write the stream to disk', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
await streamFile.writeToDisk(path.join(testAssetsPath, 'temp', 'mytest.json'));
const streamFile = await smartfile.StreamFile.fromPath(
path.join(testAssetsPath, 'mytest.json'),
);
await streamFile.writeToDisk(
path.join(testAssetsPath, 'temp', 'mytest.json'),
);
// Verify the file was written
expect(
// We'll use the fileExists method from your smartfile library
// Replace with the actual method you use to check file existence
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
await smartfile.fs.fileExists(
path.join(testAssetsPath, 'temp', 'mytest.json'),
),
).toBeTrue();
});
tap.test('StreamFile should write to a directory', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
const streamFile = await smartfile.StreamFile.fromPath(
path.join(testAssetsPath, 'mytest.json'),
);
await streamFile.writeToDir(path.join(testAssetsPath, 'temp'));
// Verify the file was written
expect(
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
await smartfile.fs.fileExists(
path.join(testAssetsPath, 'temp', 'mytest.json'),
),
).toBeTrue();
});
tap.test('StreamFile should return content as a buffer', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
const streamFile = await smartfile.StreamFile.fromPath(
path.join(testAssetsPath, 'mytest.json'),
);
const contentBuffer = await streamFile.getContentAsBuffer();
expect(contentBuffer).toBeInstanceOf(Buffer);
// Further checks on the content can be added here if necessary
});
tap.test('StreamFile should return content as a string', async () => {
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
const streamFile = await smartfile.StreamFile.fromPath(
path.join(testAssetsPath, 'mytest.json'),
);
const contentString = await streamFile.getContentAsString();
expect(contentString).toBeTypeofString();
// Verify the content matches what's expected

View File

@@ -7,111 +7,162 @@ import { expect, tap } from '@git.zone/tstest/tapbundle';
// smartfile.fs
// ---------------------------
tap.test('.fs.fileExistsSync -> should return an accurate boolean', async () => {
// tslint:disable-next-line: no-unused-expression
expect(smartfile.fs.fileExistsSync('./test/testassets/mytest.json')).toBeTrue();
// tslint:disable-next-line: no-unused-expression
expect(smartfile.fs.fileExistsSync('./test/testassets/notthere.json')).toBeFalse();
});
tap.test(
'.fs.fileExistsSync -> should return an accurate boolean',
async () => {
// tslint:disable-next-line: no-unused-expression
expect(
smartfile.fs.fileExistsSync('./test/testassets/mytest.json'),
).toBeTrue();
// tslint:disable-next-line: no-unused-expression
expect(
smartfile.fs.fileExistsSync('./test/testassets/notthere.json'),
).toBeFalse();
},
);
tap.test('.fs.fileExists -> should resolve or reject a promise', async () => {
await expect(smartfile.fs.fileExists('./test/testassets/mytest.json')).resolves.toBeTrue();
await expect(smartfile.fs.fileExists('./test/testassets/notthere.json')).resolves.toBeFalse();
await expect(
smartfile.fs.fileExists('./test/testassets/mytest.json'),
).resolves.toBeTrue();
await expect(
smartfile.fs.fileExists('./test/testassets/notthere.json'),
).resolves.toBeFalse();
});
tap.test('.fs.listFoldersSync() -> should get the file type from a string', async () => {
expect(smartfile.fs.listFoldersSync('./test/testassets/')).toContain('testfolder');
expect(smartfile.fs.listFoldersSync('./test/testassets/')).not.toContain('notExistentFolder');
});
tap.test(
'.fs.listFoldersSync() -> should get the file type from a string',
async () => {
expect(smartfile.fs.listFoldersSync('./test/testassets/')).toContain(
'testfolder',
);
expect(smartfile.fs.listFoldersSync('./test/testassets/')).not.toContain(
'notExistentFolder',
);
},
);
tap.test('.fs.listFolders() -> should get the file type from a string', async () => {
const folderArrayArg = await smartfile.fs.listFolders('./test/testassets/');
expect(folderArrayArg).toContain('testfolder');
expect(folderArrayArg).not.toContain('notExistentFolder');
});
tap.test(
'.fs.listFolders() -> should get the file type from a string',
async () => {
const folderArrayArg = await smartfile.fs.listFolders('./test/testassets/');
expect(folderArrayArg).toContain('testfolder');
expect(folderArrayArg).not.toContain('notExistentFolder');
},
);
tap.test('.fs.listFilesSync() -> should get the file type from a string', async () => {
expect(smartfile.fs.listFilesSync('./test/testassets/')).toContain('mytest.json');
expect(smartfile.fs.listFilesSync('./test/testassets/')).not.toContain('notExistentFile');
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytest\.json/)).toContain('mytest.json');
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytests.json/)).not.toContain(
'mytest.json'
);
});
tap.test(
'.fs.listFilesSync() -> should get the file type from a string',
async () => {
expect(smartfile.fs.listFilesSync('./test/testassets/')).toContain(
'mytest.json',
);
expect(smartfile.fs.listFilesSync('./test/testassets/')).not.toContain(
'notExistentFile',
);
expect(
smartfile.fs.listFilesSync('./test/testassets/', /mytest\.json/),
).toContain('mytest.json');
expect(
smartfile.fs.listFilesSync('./test/testassets/', /mytests.json/),
).not.toContain('mytest.json');
},
);
tap.test('.fs.listFiles() -> should get the file type from a string', async () => {
const folderArrayArg = await smartfile.fs.listFiles('./test/testassets/');
expect(folderArrayArg).toContain('mytest.json');
expect(folderArrayArg).not.toContain('notExistentFile');
});
tap.test(
'.fs.listFiles() -> should get the file type from a string',
async () => {
const folderArrayArg = await smartfile.fs.listFiles('./test/testassets/');
expect(folderArrayArg).toContain('mytest.json');
expect(folderArrayArg).not.toContain('notExistentFile');
},
);
tap.test('.fs.listFileTree() -> should get a file tree', async () => {
const folderArrayArg = await smartfile.fs.listFileTree(
path.resolve('./test/testassets/'),
'**/*.txt'
'**/*.txt',
);
expect(folderArrayArg).toContain('testfolder/testfile1.txt');
expect(folderArrayArg).not.toContain('mytest.json');
});
tap.test('.fs.listFileTree() -> should find both root and nested .ts files with **/*.ts pattern', async () => {
const tsFiles = await smartfile.fs.listFileTree(
process.cwd(),
'**/*.ts'
);
// Should find both root-level and nested TypeScript files
expect(tsFiles).toContain('ts/index.ts');
expect(tsFiles).toContain('ts/classes.smartfile.ts');
expect(tsFiles).toContain('test/test.ts');
// Should find files in multiple levels of nesting
expect(tsFiles.filter(f => f.endsWith('.ts')).length).toBeGreaterThan(5);
// Verify it finds files at all levels (root 'ts/' and nested 'test/')
const hasRootLevelTs = tsFiles.some(f => f.startsWith('ts/') && f.endsWith('.ts'));
const hasNestedTs = tsFiles.some(f => f.startsWith('test/') && f.endsWith('.ts'));
expect(hasRootLevelTs).toBeTrue();
expect(hasNestedTs).toBeTrue();
});
tap.test(
'.fs.listFileTree() -> should find both root and nested .ts files with **/*.ts pattern',
async () => {
const tsFiles = await smartfile.fs.listFileTree(process.cwd(), '**/*.ts');
// Should find both root-level and nested TypeScript files
expect(tsFiles).toContain('ts/index.ts');
expect(tsFiles).toContain('ts/classes.smartfile.ts');
expect(tsFiles).toContain('test/test.ts');
// Should find files in multiple levels of nesting
expect(tsFiles.filter((f) => f.endsWith('.ts')).length).toBeGreaterThan(5);
// Verify it finds files at all levels (root 'ts/' and nested 'test/')
const hasRootLevelTs = tsFiles.some(
(f) => f.startsWith('ts/') && f.endsWith('.ts'),
);
const hasNestedTs = tsFiles.some(
(f) => f.startsWith('test/') && f.endsWith('.ts'),
);
expect(hasRootLevelTs).toBeTrue();
expect(hasNestedTs).toBeTrue();
},
);
tap.test('.fs.listFileTree() -> should handle edge cases with **/ patterns consistently', async () => {
// Test that our fix ensures no duplicate files in results
const jsonFiles = await smartfile.fs.listFileTree(
path.resolve('./test/testassets/'),
'**/*.json'
);
const uniqueFiles = [...new Set(jsonFiles)];
expect(jsonFiles.length).toEqual(uniqueFiles.length);
// Test that it finds root level files with **/ patterns
const txtFiles = await smartfile.fs.listFileTree(
path.resolve('./test/testassets/'),
'**/*.txt'
);
// Should include both direct files and nested files
expect(txtFiles).toContain('mytest.txt');
expect(txtFiles).toContain('testfolder/testfile1.txt');
});
tap.test(
'.fs.listFileTree() -> should handle edge cases with **/ patterns consistently',
async () => {
// Test that our fix ensures no duplicate files in results
const jsonFiles = await smartfile.fs.listFileTree(
path.resolve('./test/testassets/'),
'**/*.json',
);
const uniqueFiles = [...new Set(jsonFiles)];
expect(jsonFiles.length).toEqual(uniqueFiles.length);
tap.test('.fs.fileTreeToObject -> should read a file tree into an Object', async () => {
const fileArrayArg = await smartfile.fs.fileTreeToObject(
path.resolve('./test/testassets/'),
'**/*.txt'
);
expect(fileArrayArg[0]).toBeInstanceOf(smartfile.SmartFile);
expect(fileArrayArg[0].contents.toString()).toEqual(fileArrayArg[0].contentBuffer.toString());
});
// Test that it finds root level files with **/ patterns
const txtFiles = await smartfile.fs.listFileTree(
path.resolve('./test/testassets/'),
'**/*.txt',
);
// Should include both direct files and nested files
expect(txtFiles).toContain('mytest.txt');
expect(txtFiles).toContain('testfolder/testfile1.txt');
},
);
tap.test(
'.fs.fileTreeToObject -> should read a file tree into an Object',
async () => {
const fileArrayArg = await smartfile.fs.fileTreeToObject(
path.resolve('./test/testassets/'),
'**/*.txt',
);
expect(fileArrayArg[0]).toBeInstanceOf(smartfile.SmartFile);
expect(fileArrayArg[0].contents.toString()).toEqual(
fileArrayArg[0].contentBuffer.toString(),
);
},
);
tap.test('.fs.copy() -> should copy a directory', async () => {
await smartfile.fs.copy('./test/testassets/testfolder/', './test/testassets/temp/');
await smartfile.fs.copy(
'./test/testassets/testfolder/',
'./test/testassets/temp/',
);
});
tap.test('.fs.copy() -> should copy a file', async () => {
await smartfile.fs.copy('./test/testassets/mytest.yaml', './test/testassets/temp/mytest.yaml');
await smartfile.fs.copy(
'./test/testassets/mytest.yaml',
'./test/testassets/temp/mytest.yaml',
);
});
tap.test('.fs.copy() -> should copy a file and rename it', async () => {
await smartfile.fs.copy(
'./test/testassets/mytest.yaml',
'./test/testassets/temp/mytestRenamed.yaml'
'./test/testassets/temp/mytestRenamed.yaml',
);
});
@@ -121,70 +172,105 @@ tap.test('.fs.remove -> should remove single files', async () => {
await smartfile.fs.remove('./test/testassets/temp/mytestRenamed.yaml');
});
tap.test('.fs.removeSync -> should remove single files synchronouly', async () => {
smartfile.fs.removeSync('./test/testassets/temp/testfile1.txt');
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
});
tap.test(
'.fs.removeSync -> should remove single files synchronouly',
async () => {
smartfile.fs.removeSync('./test/testassets/temp/testfile1.txt');
expect(
smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt'),
).toBeFalse();
},
);
tap.test('.fs.removeMany -> should remove and array of files', async () => {
smartfile.fs
.removeMany(['./test/testassets/temp/testfile1.txt', './test/testassets/temp/testfile2.txt'])
.removeMany([
'./test/testassets/temp/testfile1.txt',
'./test/testassets/temp/testfile2.txt',
])
.then(() => {
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).toBeFalse();
expect(
smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt'),
).toBeFalse();
expect(
smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt'),
).toBeFalse();
});
});
tap.test('.fs.removeManySync -> should remove and array of single files synchronouly', async () => {
smartfile.fs.removeManySync([
'./test/testassets/temp/testfile1.txt',
'./test/testassets/temp/testfile2.txt',
]);
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).toBeFalse();
});
tap.test(
'.fs.removeManySync -> should remove and array of single files synchronouly',
async () => {
smartfile.fs.removeManySync([
'./test/testassets/temp/testfile1.txt',
'./test/testassets/temp/testfile2.txt',
]);
expect(
smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt'),
).toBeFalse();
expect(
smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt'),
).toBeFalse();
},
);
tap.test('.fs.toObjectSync() -> should read an .yaml file to an object', async () => {
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.yaml');
expect(testData.key1).toEqual('this works');
expect(testData.key2).toEqual('this works too');
});
tap.test(
'.fs.toObjectSync() -> should read an .yaml file to an object',
async () => {
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.yaml');
expect(testData.key1).toEqual('this works');
expect(testData.key2).toEqual('this works too');
},
);
tap.test(
'.fs.toObjectSync() -> should state unknown file type for unknown file types',
async () => {
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.txt');
}
},
);
tap.test('.fs.toObjectSync() -> should read an .json file to an object', async () => {
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.json');
expect(testData.key1).toEqual('this works');
expect(testData.key2).toEqual('this works too');
});
tap.test(
'.fs.toObjectSync() -> should read an .json file to an object',
async () => {
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.json');
expect(testData.key1).toEqual('this works');
expect(testData.key2).toEqual('this works too');
},
);
tap.test('.fs.toStringSync() -> should read a file to a string', async () => {
expect(smartfile.fs.toStringSync('./test/testassets/mytest.txt')).toEqual('Some TestString &&%$');
expect(smartfile.fs.toStringSync('./test/testassets/mytest.txt')).toEqual(
'Some TestString &&%$',
);
});
// ---------------------------
// smartfile.interpreter
// ---------------------------
tap.test('.interpreter.filetype() -> should get the file type from a string', async () => {
expect(smartfile.interpreter.filetype('./somefolder/data.json')).toEqual('json');
});
tap.test(
'.interpreter.filetype() -> should get the file type from a string',
async () => {
expect(smartfile.interpreter.filetype('./somefolder/data.json')).toEqual(
'json',
);
},
);
// ---------------------------
// smartfile.memory
// ---------------------------
tap.test('.memory.toFs() -> should write a file to disk and return a promise', async () => {
const localString = 'myString';
await smartfile.memory.toFs(
localString,
path.join(process.cwd(), './test/testassets/temp/testMemToFs.txt')
);
});
tap.test(
'.memory.toFs() -> should write a file to disk and return a promise',
async () => {
const localString = 'myString';
await smartfile.memory.toFs(
localString,
path.join(process.cwd(), './test/testassets/temp/testMemToFs.txt'),
);
},
);
tap.test(
'.memory.toFsSync() -> should write a file to disk and return true if successfull',
@@ -192,9 +278,9 @@ tap.test(
const localString = 'myString';
smartfile.memory.toFsSync(
localString,
path.join(process.cwd(), './test/testassets/temp/testMemToFsSync.txt')
path.join(process.cwd(), './test/testassets/temp/testMemToFsSync.txt'),
);
}
},
);
// ---------------------------
@@ -204,7 +290,7 @@ tap.test(
tap.test('.Smartfile -> should produce vinyl compatible files', async () => {
const smartfileArray = await smartfile.fs.fileTreeToObject(
process.cwd(),
'./test/testassets/testfolder/**/*'
'./test/testassets/testfolder/**/*',
);
const localSmartfile = smartfileArray[0];
expect(localSmartfile).toBeInstanceOf(smartfile.SmartFile);
@@ -218,7 +304,10 @@ tap.test('.Smartfile -> should produce vinyl compatible files', async () => {
});
tap.test('should output a smartfile array to disk', async () => {
const smartfileArray = await smartfile.fs.fileTreeToObject('./test/testassets/testfolder/', '*');
const smartfileArray = await smartfile.fs.fileTreeToObject(
'./test/testassets/testfolder/',
'*',
);
for (const smartfileInstance of smartfileArray) {
console.log(smartfileInstance.relative);
console.log(smartfileInstance.path);
@@ -227,14 +316,18 @@ tap.test('should output a smartfile array to disk', async () => {
}
await smartfile.memory.smartfileArrayToFs(
smartfileArray,
path.resolve('./test/testassets/temp/testoutput/')
path.resolve('./test/testassets/temp/testoutput/'),
);
});
tap.test('should create, store and retrieve valid smartfiles', async () => {
const fileString = 'hi there';
const filePath = './test/testassets/utf8.txt';
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
const smartfileInstance = await smartfile.SmartFile.fromString(
filePath,
fileString,
'utf8',
);
smartfileInstance.write();
const smartfileInstance2 = await smartfile.SmartFile.fromFilePath(filePath);
const retrievedString = smartfileInstance.contents.toString();
@@ -244,7 +337,11 @@ tap.test('should create, store and retrieve valid smartfiles', async () => {
tap.test('should get a hash', async () => {
const fileString = 'hi there';
const filePath = './test/testassets/utf8.txt';
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
const smartfileInstance = await smartfile.SmartFile.fromString(
filePath,
fileString,
'utf8',
);
const hash = await smartfileInstance.getHash();
console.log(hash);
});

View File

@@ -3,12 +3,16 @@ import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartfile from '../ts/index.js';
tap.test('should create a virtualdirectory', async () => {
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath('./test/testassets/testfolder');
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath(
'./test/testassets/testfolder',
);
expect(virtualDir.smartfileArray.length).toEqual(4);
});
tap.test('should write to a directory', async () => {
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath('./test/testassets/testfolder');
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath(
'./test/testassets/testfolder',
);
virtualDir.saveToDisk('./test/testassets/test');
});

View File

@@ -5,4 +5,3 @@
"nestedkey1": "hello"
}
}

View File

@@ -1,4 +1,4 @@
key1: this works
key2: this works too
key3:
nestedkey1: hello
nestedkey1: hello

View File

@@ -5,4 +5,3 @@
"nestedkey1": "hello"
}
}

View File

@@ -1,4 +1,4 @@
key1: this works
key2: this works too
key3:
nestedkey1: hello
nestedkey1: hello

View File

@@ -5,4 +5,3 @@
"nestedkey1": "hello"
}
}