Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Test #11

Merged
merged 6 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions adapters/in-memory/src/exceptions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, test, expect } from 'vitest';
import { AlreadyExistsException, NotFoundException } from './exceptions';
import { MEMORY_TYPE } from './definitions';

describe('exceptions', () => {
test('NotFoundException with root', () => {
const error = new NotFoundException('test', { $type: MEMORY_TYPE.ROOT, content: [] });
expect(error.message).toBe('Could not find test');
expect(error.last).toEqual({ $type: MEMORY_TYPE.ROOT, content: [] });
expect(error.depth).toBe(0);
});

test('NotFoundException with directory', () => {
const error = new NotFoundException('test', { $type: MEMORY_TYPE.DIRECTORY, name:'test', content: [] });
expect(error.message).toBe('Could not find test');
expect(error.last).toEqual({ $type: MEMORY_TYPE.DIRECTORY, name: 'test', content: [] });
expect(error.depth).toBe(0);
});

test('AlreadyExistsException with root', () => {
const error = new AlreadyExistsException('test', { $type: MEMORY_TYPE.ROOT, content: [] });
expect(error.message).toBe('Already exists test');
expect(error.ref).toEqual({ $type: MEMORY_TYPE.ROOT, content: [] });
});

test('AlreadyExistsException with directory', () => {
const error = new AlreadyExistsException('test', { $type: MEMORY_TYPE.DIRECTORY, name:'test', content: [] });
expect(error.message).toBe('Already exists test');
expect(error.ref).toEqual({ $type: MEMORY_TYPE.DIRECTORY, name: 'test', content: [] });
});
});
2 changes: 1 addition & 1 deletion adapters/in-memory/src/exports/lib.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { source } from '../core/source';
import { PLUGIN_TYPE, type LoomSourceAdapter, Directory, LoomFile } from '@loom-io/core';

export default (key: string = 'file://') => ({
export default (key: string = 'memory://') => ({
$type: PLUGIN_TYPE.SOURCE_ADAPTER,
source: (link: string, Type?: typeof Directory | typeof LoomFile) => {
if(link.startsWith(key)) {
Expand Down
4 changes: 4 additions & 0 deletions adapters/in-memory/tests/source.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { testSource } from '@loom-io/interface-tests';
import InMemorySourceAdapter from '../src/exports/lib';

testSource('memory://', InMemorySourceAdapter());
4 changes: 2 additions & 2 deletions adapters/minio/src/core/source.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Directory, LoomFile } from '@loom-io/core/internal';
import Minio, { ClientOptions as S3Options} from 'minio';
import { Client as MinioClient, ClientOptions as S3Options} from 'minio';
import { Adapter } from './adapter';
import { dirname } from 'path';

export { S3Options };

export async function source (link: string, bucket: string, options: S3Options, Type?: typeof Directory | typeof LoomFile ): Promise<Directory | LoomFile>{
const minio = new Minio.Client(options);
const minio = new MinioClient(options);
const adapter = new Adapter(minio, bucket);
if(Type === Directory) {
return new Directory(adapter, link);
Expand Down
20 changes: 20 additions & 0 deletions adapters/minio/src/utils/typechecks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, test, expect } from 'vitest';
import { isMinioException } from './typechecks.js';
import { } from 'minio';

class MinioLikeException extends Error{
code: string;
message: string;
}


describe('typechecks', () => {
test('isMinioException', () => {
expect(isMinioException(new Error('test'))).toBe(false);
expect(isMinioException({})).toBe(false);
expect(isMinioException({ code: 'test' })).toBe(false);
expect(isMinioException({ message: 'test' })).toBe(false);
expect(isMinioException({ code: 'test', message: 'test' })).toBe(false);
expect(isMinioException(new MinioLikeException('test'))).toBe(true);
});
});
16 changes: 16 additions & 0 deletions adapters/minio/test/source.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { testSource } from '@loom-io/interface-tests';
import S3MinioSourceAdapter from '../src/exports/lib';

const s3config = {
endPoint: 'play.min.io',
port: 9000,
useSSL: true,
accessKey: 'Q3AM3UQ867SPQQA43P2F',
secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG',
};


testSource('s3://', S3MinioSourceAdapter(undefined, {
bucket: 'test-bucket',
...s3config
}));
14 changes: 9 additions & 5 deletions adapters/node-fs/src/core/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ export const source = async (path: string, rootdir?: PathLike, Type?: typeof Dir
} else if(Type === Directory || path.endsWith('/')) {
return new Directory(adapter, path);
} else {
const stats = await fs.stat(path);
try {
const stats = await fs.stat(path);

if(stats.isFile()) {
const dir = new Directory(adapter, dirname(path));
return new LoomFile(adapter, dir, basename(path));
} else {
if(stats.isFile()) {
const dir = new Directory(adapter, dirname(path));
return new LoomFile(adapter, dir, basename(path));
} else {
return new Directory(adapter, path);
}
} catch {
return new Directory(adapter, path);
}
}
Expand Down
26 changes: 26 additions & 0 deletions adapters/node-fs/tests/source.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { testSource } from '@loom-io/interface-tests';
import FilesystemSourceAdapter from '../src/exports/lib';
import { describe, expect, test } from 'vitest';
import { source } from '../src/core/source';
import { LoomFile } from '@loom-io/core/internal';



testSource('file://', FilesystemSourceAdapter());

describe('Detail source test for node-fs adapter', () => {
test('should return a LoomFile for a existing file path', async () => {
const file = await source('adapters/node-fs/src/core/source.ts');
expect(file).toBeDefined();
expect(file).toBeInstanceOf(LoomFile);
expect(file).toHaveProperty('path', 'adapters/node-fs/src/core/source.ts');
});

test('should return a Directory for a existing directory path', async () => {
const dir = await source('adapters/node-fs/src/core');
expect(dir).toBeDefined();
expect(dir).not.toBeInstanceOf(LoomFile);
expect(dir).toHaveProperty('path', 'adapters/node-fs/src/core');
});

});
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
],
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@types/node": "^20.11.28",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"@vitest/coverage-v8": "^1.4.0",
"eslint": "^8.57.0",
"publint": "^0.2.7",
"typescript": "^5.4.2",
"typescript": "^5.4.3",
"vitest": "^1.4.0"
},
"repository": {
Expand Down
6 changes: 3 additions & 3 deletions packages/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"build": "tsc -p tsconfig.json"
},
"dependencies": {
"@loom-io/core": "workspace:^0.6.0",
"@loom-io/yamlConverter": "workspace:^0.6.0",
"@loom-io/jsonConverter": "workspace:^0.6.0"
"@loom-io/core": "workspace:^",
"@loom-io/jsonConverter": "workspace:^",
"@loom-io/yamlConverter": "workspace:^"
},
"exports": {
".": {
Expand Down
114 changes: 107 additions & 7 deletions packages/common/src/path.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import { describe, test, expect } from 'vitest';
import { addPrecedingAndTailingSlash, getSegmentsOfPath, getPathDepth, removePrecedingAndTrailingSlash, splitTailingPath } from './path';

import { addPrecedingAndTailingSlash, getUniqSegmentsOfPath, getSegmentsOfPath, getPathDepth, removePrecedingAndTrailingSlash, splitTailingPath, removePrecedingSlash, removeTailingSlash } from './path';

describe('path utils', async () => {

test('removePrecedingSlash', async () => {
expect(removePrecedingSlash('/test')).toBe('test');
expect(removePrecedingSlash('test')).toBe('test');
expect(removePrecedingSlash('/')).toBe('');
expect(removePrecedingSlash('')).toBe('');
expect(removePrecedingSlash('/test/some/slashing')).toBe('test/some/slashing');
});

test('removeTailingSlash', async () => {
expect(removeTailingSlash('/test/')).toBe('/test');
expect(removeTailingSlash('/test')).toBe('/test');
expect(removeTailingSlash('test/')).toBe('test');
expect(removeTailingSlash('test')).toBe('test');
expect(removeTailingSlash('/')).toBe('');
expect(removeTailingSlash('')).toBe('');
expect(removeTailingSlash('/test/tailing/sla_sigs-d/')).toBe('/test/tailing/sla_sigs-d');
});


test('removePresentedAndTrailingSlash', async () => {
expect(removePrecedingAndTrailingSlash('/test/')).toBe('test');
expect(removePrecedingAndTrailingSlash('/test')).toBe('test');
Expand Down Expand Up @@ -45,19 +64,100 @@ describe('path utils', async () => {
expect(getPathDepth('/test/some/slashing/long/with/file.txt')).toBe(6);
});

test('getFirstElementsOfPath', async () => {
test('getSegmentsOfPath', async () => {
expect(getSegmentsOfPath('/test/some/slashing/', 3)).toBe('/test/some/slashing/');
expect(getSegmentsOfPath('/test/some/slashing', 2)).toBe('/test/some');
expect(getSegmentsOfPath('/test/some/slashing', 2)).toBe('/test/some/');
expect(getSegmentsOfPath('test/some/slashing/', 2)).toBe('test/some/');
expect(getSegmentsOfPath('test/some/slashing', 3)).toBe('test/some/slashing');
expect(getSegmentsOfPath('/test/some/slashing/', 1)).toBe('/test/');
expect(getSegmentsOfPath('/test/some/slashing', 1)).toBe('/test');
expect(getSegmentsOfPath('/test/some/slashing', 1)).toBe('/test/');
expect(getSegmentsOfPath('test/some/slashing/', 1)).toBe('test/');
expect(getSegmentsOfPath('test/some/slashing', 1)).toBe('test');
expect(getSegmentsOfPath('/test/some/slashing/', 0)).toBe('/');
expect(getSegmentsOfPath('test/some/slashing', 1)).toBe('test/');
expect(getSegmentsOfPath('/cotton/slashing/', 0)).toBe('/');
expect(getSegmentsOfPath('/test/some/slashing', 0)).toBe('/');
expect(getSegmentsOfPath('test/some/slashing/', 0)).toBe('/');
expect(getSegmentsOfPath('test/some/slashing', 0)).toBe('/');
expect(getSegmentsOfPath('../../cotton-going-up/loop', 1)).toBe('../');
expect(getSegmentsOfPath('./relative/', 1)).toBe('./relative/');
expect(getSegmentsOfPath('./relative/cotton', 1)).toBe('./relative/');
expect(getSegmentsOfPath('./relative/cotton', 2)).toBe('./relative/cotton');
expect(getSegmentsOfPath('.hidden', 1)).toBe('.hidden');
});


test('getUniqSegmentsOfPath', async () => {
const paths = [
'/cotton/wool/silk',
'coding/programming/',
'coding/with/cotton/coding',
'/cotton/coding/is the best/',
'/some-thing/else',
'cotton-coding/programming',
'cotton-coding/is_not_belong_to_trees/maybe/sourceTrees',
'/coding/programming',
'./some_relative_path',
'some_relative_path',
'.hidden/file',
'/.hidden/at/root',
'a/long/.with/all/_po-si-ble/&symbols/and/numbers/1234567890',
];

expect(getUniqSegmentsOfPath(paths, 1)).toEqual([
'/cotton',
'coding',
'/some-thing',
'cotton-coding',
'/coding',
'some_relative_path',
'.hidden',
'/.hidden',
'a'
]);

expect(getUniqSegmentsOfPath(paths, 2)).toEqual([
'/cotton/wool',
'coding/programming',
'coding/with',
'/cotton/coding',
'/some-thing/else',
'cotton-coding/programming',
'cotton-coding/is_not_belong_to_trees',
'/coding/programming',
'some_relative_path',
'.hidden/file',
'/.hidden/at',
'a/long'
]);

expect(getUniqSegmentsOfPath(paths, 3)).toEqual([
'/cotton/wool/silk',
'coding/programming',
'coding/with/cotton',
'/cotton/coding/is the best',
'/some-thing/else',
'cotton-coding/programming',
'cotton-coding/is_not_belong_to_trees/maybe',
'/coding/programming',
'some_relative_path',
'.hidden/file',
'/.hidden/at/root',
'a/long/.with'
]);

expect(getUniqSegmentsOfPath(paths, 7)).toEqual([
'/cotton/wool/silk',
'coding/programming',
'coding/with/cotton/coding',
'/cotton/coding/is the best',
'/some-thing/else',
'cotton-coding/programming',
'cotton-coding/is_not_belong_to_trees/maybe/sourceTrees',
'/coding/programming',
'some_relative_path',
'.hidden/file',
'/.hidden/at/root',
'a/long/.with/all/_po-si-ble/&symbols/and'
]);
});

});
30 changes: 26 additions & 4 deletions packages/common/src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,46 @@ export function getPathDepth(path: string): number {
return elements.length;
}


/**
* Take relative and fix paths. The function will ignore preceding, trailing slashes and relative slashes on the same level (./).
* If the return is a subpath it will be returned with a trailing slash.
* If depth is higher than the path depth, the function will return the path as it is given.
* @param path
* @param depth
* @returns
*/
export function getSegmentsOfPath(path: string, depth: number): string {
const isRelative = path.startsWith('./');
const firstIsSlash = path.startsWith('/');
const lastIsSlash = path.endsWith('/');
const parts = removePrecedingAndTrailingSlash(path).split('/');
if (depth === 0) {
return '/';
} else if (depth >= parts.length) {
} else if (depth >= parts.length || (isRelative && depth === parts.length - 1)) {
return path;
} else {
if(isRelative) {
return `${parts.slice(0, depth+1).join('/')}/`;
}
const subPath = parts.slice(0, depth).join('/');
return `${firstIsSlash ? '/' : ''}${subPath}${lastIsSlash ? '/' : ''}`;
return `${firstIsSlash ? '/' : ''}${subPath}/`;
}
}


/**
* Returns a list of unique paths with the given depth.
* Relative paths without preceding slash will be treated as relatives (./).
* To better compare preceding and trailing slashes are removed.
* @param paths
* @param depth
* @returns
*/
export function getUniqSegmentsOfPath(paths: string[], depth: number): string[] {
return Array.from(paths.reduce((acc, path) => {
path = path.startsWith('./') ? path.slice(2) : path;
const first = getSegmentsOfPath(path, depth);
acc.add(first);
acc.add(removeTailingSlash(first));
return acc;
}, new Set<string>()));
}
3 changes: 2 additions & 1 deletion packages/core/src/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ export class NoSourceAdapterException extends Error implements LoomException {
}

export class DirectoryNotEmptyException extends Error implements LoomException {

__loomExceptionRef = EXCEPTION_REF.DIRECTORY_NOT_EMPTY;
protected _path: string;
_path: string;
constructor(path: string) {
super(`Directory ${path} is not empty`);
this._path = path;
Expand Down
Loading
Loading