test: add core modules test cases (#3498)
* chore: add core module test cases * chore: fix tests * chore: add code coverage report * chore: split coverage step * chore: split coverage step * Update jan-electron-linter-and-test.yml * Update jan-electron-linter-and-test.yml * Update jan-electron-linter-and-test.yml * chore: update tests * chore: add web utils test cases * chore: add restful and helper tests * chore: add tests
This commit is contained in:
parent
f759fae55f
commit
846efb3126
@ -37,6 +37,30 @@ on:
|
|||||||
- '!README.md'
|
- '!README.md'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
base_branch_cov:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.base_ref }}
|
||||||
|
- name: Use Node.js v20.9.0
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: v20.9.0
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn
|
||||||
|
|
||||||
|
- name: Run test coverage
|
||||||
|
run: yarn test:coverage
|
||||||
|
|
||||||
|
- name: Upload code coverage for ref branch
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ref-lcov.info
|
||||||
|
path: ./coverage/lcov.info
|
||||||
|
|
||||||
test-on-macos:
|
test-on-macos:
|
||||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||||
runs-on: [self-hosted, macOS, macos-desktop]
|
runs-on: [self-hosted, macOS, macos-desktop]
|
||||||
@ -292,6 +316,56 @@ jobs:
|
|||||||
TURBO_TEAM: 'linux'
|
TURBO_TEAM: 'linux'
|
||||||
TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
||||||
|
|
||||||
|
coverage-check:
|
||||||
|
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
||||||
|
needs: base_branch_cov
|
||||||
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||||
|
steps:
|
||||||
|
- name: Getting the repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Installing node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: 'Cleanup cache'
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
rm -rf ~/jan
|
||||||
|
make clean
|
||||||
|
|
||||||
|
- name: Download code coverage report from base branch
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ref-lcov.info
|
||||||
|
|
||||||
|
- name: Linter and test coverage
|
||||||
|
run: |
|
||||||
|
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
||||||
|
echo -e "Display ID: $DISPLAY"
|
||||||
|
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
||||||
|
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
||||||
|
make lint
|
||||||
|
yarn build:test
|
||||||
|
yarn test:coverage
|
||||||
|
env:
|
||||||
|
TURBO_API: '${{ secrets.TURBO_API }}'
|
||||||
|
TURBO_TEAM: 'linux'
|
||||||
|
TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
||||||
|
|
||||||
|
- name: Generate Code Coverage report
|
||||||
|
id: code-coverage
|
||||||
|
uses: barecheck/code-coverage-action@v1
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
lcov-file: "./coverage/lcov.info"
|
||||||
|
base-lcov-file: "./lcov.info"
|
||||||
|
send-summary-comment: true
|
||||||
|
show-annotations: "warning"
|
||||||
|
|
||||||
test-on-ubuntu-pr-target:
|
test-on-ubuntu-pr-target:
|
||||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
||||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -41,3 +41,5 @@ extensions/*-extension/bin/vulkaninfo
|
|||||||
.turbo
|
.turbo
|
||||||
electron/test-data
|
electron/test-data
|
||||||
electron/test-results
|
electron/test-results
|
||||||
|
core/test_results.html
|
||||||
|
coverage
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -104,7 +104,7 @@ endif
|
|||||||
# Testing
|
# Testing
|
||||||
test: lint
|
test: lint
|
||||||
yarn build:test
|
yarn build:test
|
||||||
yarn test:unit
|
yarn test:coverage
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
# Builds and publishes the app
|
# Builds and publishes the app
|
||||||
|
|||||||
@ -4,4 +4,5 @@ module.exports = {
|
|||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'@/(.*)': '<rootDir>/src/$1',
|
'@/(.*)': '<rootDir>/src/$1',
|
||||||
},
|
},
|
||||||
|
runner: './testRunner.js',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,8 @@
|
|||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-junit": "^16.0.0",
|
||||||
|
"jest-runner": "^29.7.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.38.5",
|
"rollup": "^2.38.5",
|
||||||
"rollup-plugin-commonjs": "^9.1.8",
|
"rollup-plugin-commonjs": "^9.1.8",
|
||||||
@ -53,7 +55,7 @@
|
|||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"rollup-plugin-sourcemaps": "^0.6.3",
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
||||||
"rollup-plugin-typescript2": "^0.36.0",
|
"rollup-plugin-typescript2": "^0.36.0",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.2.5",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
|
|||||||
98
core/src/browser/core.test.ts
Normal file
98
core/src/browser/core.test.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { openExternalUrl } from './core';
|
||||||
|
import { joinPath } from './core';
|
||||||
|
import { openFileExplorer } from './core';
|
||||||
|
import { getJanDataFolderPath } from './core';
|
||||||
|
import { abortDownload } from './core';
|
||||||
|
import { getFileSize } from './core';
|
||||||
|
import { executeOnMain } from './core';
|
||||||
|
|
||||||
|
it('should open external url', async () => {
|
||||||
|
const url = 'http://example.com';
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
openExternalUrl: jest.fn().mockResolvedValue('opened')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await openExternalUrl(url);
|
||||||
|
expect(globalThis.core.api.openExternalUrl).toHaveBeenCalledWith(url);
|
||||||
|
expect(result).toBe('opened');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should join paths', async () => {
|
||||||
|
const paths = ['/path/one', '/path/two'];
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
joinPath: jest.fn().mockResolvedValue('/path/one/path/two')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await joinPath(paths);
|
||||||
|
expect(globalThis.core.api.joinPath).toHaveBeenCalledWith(paths);
|
||||||
|
expect(result).toBe('/path/one/path/two');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should open file explorer', async () => {
|
||||||
|
const path = '/path/to/open';
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
openFileExplorer: jest.fn().mockResolvedValue('opened')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await openFileExplorer(path);
|
||||||
|
expect(globalThis.core.api.openFileExplorer).toHaveBeenCalledWith(path);
|
||||||
|
expect(result).toBe('opened');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should get jan data folder path', async () => {
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
getJanDataFolderPath: jest.fn().mockResolvedValue('/path/to/jan/data')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await getJanDataFolderPath();
|
||||||
|
expect(globalThis.core.api.getJanDataFolderPath).toHaveBeenCalled();
|
||||||
|
expect(result).toBe('/path/to/jan/data');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should abort download', async () => {
|
||||||
|
const fileName = 'testFile';
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
abortDownload: jest.fn().mockResolvedValue('aborted')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await abortDownload(fileName);
|
||||||
|
expect(globalThis.core.api.abortDownload).toHaveBeenCalledWith(fileName);
|
||||||
|
expect(result).toBe('aborted');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should get file size', async () => {
|
||||||
|
const url = 'http://example.com/file';
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
getFileSize: jest.fn().mockResolvedValue(1024)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await getFileSize(url);
|
||||||
|
expect(globalThis.core.api.getFileSize).toHaveBeenCalledWith(url);
|
||||||
|
expect(result).toBe(1024);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should execute function on main process', async () => {
|
||||||
|
const extension = 'testExtension';
|
||||||
|
const method = 'testMethod';
|
||||||
|
const args = ['arg1', 'arg2'];
|
||||||
|
globalThis.core = {
|
||||||
|
api: {
|
||||||
|
invokeExtensionFunc: jest.fn().mockResolvedValue('result')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = await executeOnMain(extension, method, ...args);
|
||||||
|
expect(globalThis.core.api.invokeExtensionFunc).toHaveBeenCalledWith(extension, method, ...args);
|
||||||
|
expect(result).toBe('result');
|
||||||
|
});
|
||||||
37
core/src/browser/events.test.ts
Normal file
37
core/src/browser/events.test.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { events } from './events';
|
||||||
|
import { jest } from '@jest/globals';
|
||||||
|
|
||||||
|
it('should emit an event', () => {
|
||||||
|
const mockObject = { key: 'value' };
|
||||||
|
globalThis.core = {
|
||||||
|
events: {
|
||||||
|
emit: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
events.emit('testEvent', mockObject);
|
||||||
|
expect(globalThis.core.events.emit).toHaveBeenCalledWith('testEvent', mockObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should remove an observer for an event', () => {
|
||||||
|
const mockHandler = jest.fn();
|
||||||
|
globalThis.core = {
|
||||||
|
events: {
|
||||||
|
off: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
events.off('testEvent', mockHandler);
|
||||||
|
expect(globalThis.core.events.off).toHaveBeenCalledWith('testEvent', mockHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should add an observer for an event', () => {
|
||||||
|
const mockHandler = jest.fn();
|
||||||
|
globalThis.core = {
|
||||||
|
events: {
|
||||||
|
on: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
events.on('testEvent', mockHandler);
|
||||||
|
expect(globalThis.core.events.on).toHaveBeenCalledWith('testEvent', mockHandler);
|
||||||
|
});
|
||||||
46
core/src/browser/extension.test.ts
Normal file
46
core/src/browser/extension.test.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { BaseExtension } from './extension'
|
||||||
|
|
||||||
|
class TestBaseExtension extends BaseExtension {
|
||||||
|
onLoad(): void {}
|
||||||
|
onUnload(): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('BaseExtension', () => {
|
||||||
|
let baseExtension: TestBaseExtension
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
baseExtension = new TestBaseExtension('https://example.com', 'TestExtension')
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have the correct properties', () => {
|
||||||
|
expect(baseExtension.name).toBe('TestExtension')
|
||||||
|
expect(baseExtension.productName).toBeUndefined()
|
||||||
|
expect(baseExtension.url).toBe('https://example.com')
|
||||||
|
expect(baseExtension.active).toBeUndefined()
|
||||||
|
expect(baseExtension.description).toBeUndefined()
|
||||||
|
expect(baseExtension.version).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return undefined for type()', () => {
|
||||||
|
expect(baseExtension.type()).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have abstract methods onLoad() and onUnload()', () => {
|
||||||
|
expect(baseExtension.onLoad).toBeDefined()
|
||||||
|
expect(baseExtension.onUnload).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have installationState() return "NotRequired"', async () => {
|
||||||
|
const installationState = await baseExtension.installationState()
|
||||||
|
expect(installationState).toBe('NotRequired')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should install the extension', async () => {
|
||||||
|
await baseExtension.install()
|
||||||
|
// Add your assertions here
|
||||||
|
})
|
||||||
|
})
|
||||||
60
core/src/browser/extensions/engines/helpers/sse.test.ts
Normal file
60
core/src/browser/extensions/engines/helpers/sse.test.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { lastValueFrom, Observable } from 'rxjs'
|
||||||
|
import { requestInference } from './sse'
|
||||||
|
|
||||||
|
describe('requestInference', () => {
|
||||||
|
it('should send a request to the inference server and return an Observable', () => {
|
||||||
|
// Mock the fetch function
|
||||||
|
const mockFetch: any = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve({ choices: [{ message: { content: 'Generated response' } }] }),
|
||||||
|
headers: new Headers(),
|
||||||
|
redirected: false,
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK',
|
||||||
|
// Add other required properties here
|
||||||
|
})
|
||||||
|
)
|
||||||
|
jest.spyOn(global, 'fetch').mockImplementation(mockFetch)
|
||||||
|
|
||||||
|
// Define the test inputs
|
||||||
|
const inferenceUrl = 'https://inference-server.com'
|
||||||
|
const requestBody = { message: 'Hello' }
|
||||||
|
const model = { id: 'model-id', parameters: { stream: false } }
|
||||||
|
|
||||||
|
// Call the function
|
||||||
|
const result = requestInference(inferenceUrl, requestBody, model)
|
||||||
|
|
||||||
|
// Assert the expected behavior
|
||||||
|
expect(result).toBeInstanceOf(Observable)
|
||||||
|
expect(lastValueFrom(result)).resolves.toEqual('Generated response')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 401 error', () => {
|
||||||
|
// Mock the fetch function
|
||||||
|
const mockFetch: any = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: false,
|
||||||
|
json: () => Promise.resolve({ error: { message: 'Wrong API Key', code: 'invalid_api_key' } }),
|
||||||
|
headers: new Headers(),
|
||||||
|
redirected: false,
|
||||||
|
status: 401,
|
||||||
|
statusText: 'invalid_api_key',
|
||||||
|
// Add other required properties here
|
||||||
|
})
|
||||||
|
)
|
||||||
|
jest.spyOn(global, 'fetch').mockImplementation(mockFetch)
|
||||||
|
|
||||||
|
// Define the test inputs
|
||||||
|
const inferenceUrl = 'https://inference-server.com'
|
||||||
|
const requestBody = { message: 'Hello' }
|
||||||
|
const model = { id: 'model-id', parameters: { stream: false } }
|
||||||
|
|
||||||
|
// Call the function
|
||||||
|
const result = requestInference(inferenceUrl, requestBody, model)
|
||||||
|
|
||||||
|
// Assert the expected behavior
|
||||||
|
expect(result).toBeInstanceOf(Observable)
|
||||||
|
expect(lastValueFrom(result)).rejects.toEqual({ message: 'Wrong API Key', code: 'invalid_api_key' })
|
||||||
|
})
|
||||||
|
})
|
||||||
32
core/src/browser/index.test.ts
Normal file
32
core/src/browser/index.test.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import * as Core from './core';
|
||||||
|
import * as Events from './events';
|
||||||
|
import * as FileSystem from './fs';
|
||||||
|
import * as Extension from './extension';
|
||||||
|
import * as Extensions from './extensions';
|
||||||
|
import * as Tools from './tools';
|
||||||
|
|
||||||
|
describe('Module Tests', () => {
|
||||||
|
it('should export Core module', () => {
|
||||||
|
expect(Core).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export Event module', () => {
|
||||||
|
expect(Events).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export Filesystem module', () => {
|
||||||
|
expect(FileSystem).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export Extension module', () => {
|
||||||
|
expect(Extension).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export all base extensions', () => {
|
||||||
|
expect(Extensions).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export all base tools', () => {
|
||||||
|
expect(Tools).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
10
core/src/node/api/common/adapter.test.ts
Normal file
10
core/src/node/api/common/adapter.test.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { RequestAdapter } from './adapter';
|
||||||
|
|
||||||
|
it('should return undefined for unknown route', () => {
|
||||||
|
const adapter = new RequestAdapter();
|
||||||
|
const route = 'unknownRoute';
|
||||||
|
|
||||||
|
const result = adapter.process(route, 'arg1', 'arg2');
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
25
core/src/node/api/common/handler.test.ts
Normal file
25
core/src/node/api/common/handler.test.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { CoreRoutes } from '../../../types/api';
|
||||||
|
import { RequestHandler } from './handler';
|
||||||
|
import { RequestAdapter } from './adapter';
|
||||||
|
|
||||||
|
it('should not call handler if CoreRoutes is empty', () => {
|
||||||
|
const mockHandler = jest.fn();
|
||||||
|
const mockObserver = jest.fn();
|
||||||
|
const requestHandler = new RequestHandler(mockHandler, mockObserver);
|
||||||
|
|
||||||
|
CoreRoutes.length = 0; // Ensure CoreRoutes is empty
|
||||||
|
|
||||||
|
requestHandler.handle();
|
||||||
|
|
||||||
|
expect(mockHandler).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should initialize handler and adapter correctly', () => {
|
||||||
|
const mockHandler = jest.fn();
|
||||||
|
const mockObserver = jest.fn();
|
||||||
|
const requestHandler = new RequestHandler(mockHandler, mockObserver);
|
||||||
|
|
||||||
|
expect(requestHandler.handler).toBe(mockHandler);
|
||||||
|
expect(requestHandler.adapter).toBeInstanceOf(RequestAdapter);
|
||||||
|
});
|
||||||
40
core/src/node/api/processors/app.test.ts
Normal file
40
core/src/node/api/processors/app.test.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { App } from './app';
|
||||||
|
|
||||||
|
it('should call stopServer', () => {
|
||||||
|
const app = new App();
|
||||||
|
const stopServerMock = jest.fn().mockResolvedValue('Server stopped');
|
||||||
|
jest.mock('@janhq/server', () => ({
|
||||||
|
stopServer: stopServerMock
|
||||||
|
}));
|
||||||
|
const result = app.stopServer();
|
||||||
|
expect(stopServerMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly retrieve basename', () => {
|
||||||
|
const app = new App();
|
||||||
|
const result = app.baseName('/path/to/file.txt');
|
||||||
|
expect(result).toBe('file.txt');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly identify subdirectories', () => {
|
||||||
|
const app = new App();
|
||||||
|
const basePath = process.platform === 'win32' ? 'C:\\path\\to' : '/path/to';
|
||||||
|
const subPath = process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir';
|
||||||
|
const result = app.isSubdirectory(basePath, subPath);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly join multiple paths', () => {
|
||||||
|
const app = new App();
|
||||||
|
const result = app.joinPath(['path', 'to', 'file']);
|
||||||
|
const expectedPath = process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file';
|
||||||
|
expect(result).toBe(expectedPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call correct function with provided arguments using process method', () => {
|
||||||
|
const app = new App();
|
||||||
|
const mockFunc = jest.fn();
|
||||||
|
app.joinPath = mockFunc;
|
||||||
|
app.process('joinPath', ['path1', 'path2']);
|
||||||
|
expect(mockFunc).toHaveBeenCalledWith(['path1', 'path2']);
|
||||||
|
});
|
||||||
59
core/src/node/api/processors/download.test.ts
Normal file
59
core/src/node/api/processors/download.test.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Downloader } from './download';
|
||||||
|
import { DownloadEvent } from '../../../types/api';
|
||||||
|
import { DownloadManager } from '../../helper/download';
|
||||||
|
|
||||||
|
it('should handle getFileSize errors correctly', async () => {
|
||||||
|
const observer = jest.fn();
|
||||||
|
const url = 'http://example.com/file';
|
||||||
|
|
||||||
|
const downloader = new Downloader(observer);
|
||||||
|
const requestMock = jest.fn((options, callback) => {
|
||||||
|
callback(new Error('Test error'), null);
|
||||||
|
});
|
||||||
|
jest.mock('request', () => requestMock);
|
||||||
|
|
||||||
|
await expect(downloader.getFileSize(observer, url)).rejects.toThrow('Test error');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should pause download correctly', () => {
|
||||||
|
const observer = jest.fn();
|
||||||
|
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||||
|
|
||||||
|
const downloader = new Downloader(observer);
|
||||||
|
const pauseMock = jest.fn();
|
||||||
|
DownloadManager.instance.networkRequests[fileName] = { pause: pauseMock };
|
||||||
|
|
||||||
|
downloader.pauseDownload(observer, fileName);
|
||||||
|
|
||||||
|
expect(pauseMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resume download correctly', () => {
|
||||||
|
const observer = jest.fn();
|
||||||
|
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||||
|
|
||||||
|
const downloader = new Downloader(observer);
|
||||||
|
const resumeMock = jest.fn();
|
||||||
|
DownloadManager.instance.networkRequests[fileName] = { resume: resumeMock };
|
||||||
|
|
||||||
|
downloader.resumeDownload(observer, fileName);
|
||||||
|
|
||||||
|
expect(resumeMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle aborting a download correctly', () => {
|
||||||
|
const observer = jest.fn();
|
||||||
|
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||||
|
|
||||||
|
const downloader = new Downloader(observer);
|
||||||
|
const abortMock = jest.fn();
|
||||||
|
DownloadManager.instance.networkRequests[fileName] = { abort: abortMock };
|
||||||
|
|
||||||
|
downloader.abortDownload(observer, fileName);
|
||||||
|
|
||||||
|
expect(abortMock).toHaveBeenCalled();
|
||||||
|
expect(observer).toHaveBeenCalledWith(DownloadEvent.onFileDownloadError, expect.objectContaining({
|
||||||
|
error: 'aborted'
|
||||||
|
}));
|
||||||
|
});
|
||||||
9
core/src/node/api/processors/extension.test.ts
Normal file
9
core/src/node/api/processors/extension.test.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Extension } from './extension';
|
||||||
|
|
||||||
|
it('should call function associated with key in process method', () => {
|
||||||
|
const mockFunc = jest.fn();
|
||||||
|
const extension = new Extension();
|
||||||
|
(extension as any).testKey = mockFunc;
|
||||||
|
extension.process('testKey', 'arg1', 'arg2');
|
||||||
|
expect(mockFunc).toHaveBeenCalledWith('arg1', 'arg2');
|
||||||
|
});
|
||||||
18
core/src/node/api/processors/fs.test.ts
Normal file
18
core/src/node/api/processors/fs.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { FileSystem } from './fs';
|
||||||
|
|
||||||
|
it('should throw an error when the route does not exist in process', async () => {
|
||||||
|
const fileSystem = new FileSystem();
|
||||||
|
await expect(fileSystem.process('nonExistentRoute', 'arg1')).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should throw an error for invalid argument in mkdir', async () => {
|
||||||
|
const fileSystem = new FileSystem();
|
||||||
|
expect(() => fileSystem.mkdir(123)).toThrow('mkdir error: Invalid argument [123]');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should throw an error for invalid argument in rm', async () => {
|
||||||
|
const fileSystem = new FileSystem();
|
||||||
|
expect(() => fileSystem.rm(123)).toThrow('rm error: Invalid argument [123]');
|
||||||
|
});
|
||||||
34
core/src/node/api/processors/fsExt.test.ts
Normal file
34
core/src/node/api/processors/fsExt.test.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { FSExt } from './fsExt';
|
||||||
|
import { defaultAppConfig } from '../../helper';
|
||||||
|
|
||||||
|
it('should handle errors in writeBlob', () => {
|
||||||
|
const fsExt = new FSExt();
|
||||||
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
|
fsExt.writeBlob('invalid-path', 'data');
|
||||||
|
expect(consoleSpy).toHaveBeenCalled();
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call correct function in process method', () => {
|
||||||
|
const fsExt = new FSExt();
|
||||||
|
const mockFunction = jest.fn();
|
||||||
|
(fsExt as any).mockFunction = mockFunction;
|
||||||
|
fsExt.process('mockFunction', 'arg1', 'arg2');
|
||||||
|
expect(mockFunction).toHaveBeenCalledWith('arg1', 'arg2');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return correct user home path', () => {
|
||||||
|
const fsExt = new FSExt();
|
||||||
|
const userHomePath = fsExt.getUserHomePath();
|
||||||
|
expect(userHomePath).toBe(defaultAppConfig().data_folder);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('should return empty array when no files are provided', async () => {
|
||||||
|
const fsExt = new FSExt();
|
||||||
|
const result = await fsExt.getGgufFiles([]);
|
||||||
|
expect(result.supportedFiles).toEqual([]);
|
||||||
|
expect(result.unsupportedFiles).toEqual([]);
|
||||||
|
});
|
||||||
0
core/src/node/api/processors/processor.test.ts
Normal file
0
core/src/node/api/processors/processor.test.ts
Normal file
62
core/src/node/api/restful/app/download.test.ts
Normal file
62
core/src/node/api/restful/app/download.test.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { HttpServer } from '../../HttpServer'
|
||||||
|
import { DownloadManager } from '../../../helper/download'
|
||||||
|
|
||||||
|
describe('downloadRouter', () => {
|
||||||
|
let app: HttpServer
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
app = {
|
||||||
|
register: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
get: jest.fn(),
|
||||||
|
patch: jest.fn(),
|
||||||
|
put: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return download progress for a given modelId', async () => {
|
||||||
|
const modelId = '123'
|
||||||
|
const downloadProgress = { progress: 50 }
|
||||||
|
|
||||||
|
DownloadManager.instance.downloadProgressMap[modelId] = downloadProgress as any
|
||||||
|
|
||||||
|
const req = { params: { modelId } }
|
||||||
|
const res = {
|
||||||
|
status: jest.fn(),
|
||||||
|
send: jest.fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
jest.spyOn(app, 'get').mockImplementation((path, handler) => {
|
||||||
|
if (path === `/download/getDownloadProgress/${modelId}`) {
|
||||||
|
res.status(200)
|
||||||
|
res.send(downloadProgress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get(`/download/getDownloadProgress/${modelId}`, req as any)
|
||||||
|
expect(res.status).toHaveBeenCalledWith(200)
|
||||||
|
expect(res.send).toHaveBeenCalledWith(downloadProgress)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return 404 if download progress is not found', async () => {
|
||||||
|
const modelId = '123'
|
||||||
|
|
||||||
|
const req = { params: { modelId } }
|
||||||
|
const res = {
|
||||||
|
status: jest.fn(),
|
||||||
|
send: jest.fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
jest.spyOn(app, 'get').mockImplementation((path, handler) => {
|
||||||
|
if (path === `/download/getDownloadProgress/${modelId}`) {
|
||||||
|
res.status(404)
|
||||||
|
res.send({ message: 'Download progress not found' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.get(`/download/getDownloadProgress/${modelId}`, req as any)
|
||||||
|
expect(res.status).toHaveBeenCalledWith(404)
|
||||||
|
expect(res.send).toHaveBeenCalledWith({ message: 'Download progress not found' })
|
||||||
|
})
|
||||||
|
})
|
||||||
16
core/src/node/api/restful/app/handlers.test.ts
Normal file
16
core/src/node/api/restful/app/handlers.test.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
import { jest } from '@jest/globals';
|
||||||
|
|
||||||
|
import { HttpServer } from '../../HttpServer';
|
||||||
|
import { handleRequests } from './handlers';
|
||||||
|
import { Handler, RequestHandler } from '../../common/handler';
|
||||||
|
|
||||||
|
it('should initialize RequestHandler and call handle', () => {
|
||||||
|
const mockHandle = jest.fn();
|
||||||
|
jest.spyOn(RequestHandler.prototype, 'handle').mockImplementation(mockHandle);
|
||||||
|
|
||||||
|
const mockApp = { post: jest.fn() };
|
||||||
|
handleRequests(mockApp as unknown as HttpServer);
|
||||||
|
|
||||||
|
expect(mockHandle).toHaveBeenCalled();
|
||||||
|
});
|
||||||
21
core/src/node/api/restful/common.test.ts
Normal file
21
core/src/node/api/restful/common.test.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
import { commonRouter } from './common';
|
||||||
|
import { JanApiRouteConfiguration } from './helper/configuration';
|
||||||
|
|
||||||
|
test('commonRouter sets up routes for each key in JanApiRouteConfiguration', async () => {
|
||||||
|
const mockHttpServer = {
|
||||||
|
get: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
patch: jest.fn(),
|
||||||
|
put: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
};
|
||||||
|
await commonRouter(mockHttpServer as any);
|
||||||
|
|
||||||
|
const expectedRoutes = Object.keys(JanApiRouteConfiguration);
|
||||||
|
expectedRoutes.forEach((key) => {
|
||||||
|
expect(mockHttpServer.get).toHaveBeenCalledWith(`/${key}`, expect.any(Function));
|
||||||
|
expect(mockHttpServer.get).toHaveBeenCalledWith(`/${key}/:id`, expect.any(Function));
|
||||||
|
expect(mockHttpServer.delete).toHaveBeenCalledWith(`/${key}/:id`, expect.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
24
core/src/node/api/restful/helper/configuration.test.ts
Normal file
24
core/src/node/api/restful/helper/configuration.test.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { JanApiRouteConfiguration } from './configuration'
|
||||||
|
|
||||||
|
describe('JanApiRouteConfiguration', () => {
|
||||||
|
it('should have the correct models configuration', () => {
|
||||||
|
const modelsConfig = JanApiRouteConfiguration.models;
|
||||||
|
expect(modelsConfig.dirName).toBe('models');
|
||||||
|
expect(modelsConfig.metadataFileName).toBe('model.json');
|
||||||
|
expect(modelsConfig.delete.object).toBe('model');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have the correct assistants configuration', () => {
|
||||||
|
const assistantsConfig = JanApiRouteConfiguration.assistants;
|
||||||
|
expect(assistantsConfig.dirName).toBe('assistants');
|
||||||
|
expect(assistantsConfig.metadataFileName).toBe('assistant.json');
|
||||||
|
expect(assistantsConfig.delete.object).toBe('assistant');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have the correct threads configuration', () => {
|
||||||
|
const threadsConfig = JanApiRouteConfiguration.threads;
|
||||||
|
expect(threadsConfig.dirName).toBe('threads');
|
||||||
|
expect(threadsConfig.metadataFileName).toBe('thread.json');
|
||||||
|
expect(threadsConfig.delete.object).toBe('thread');
|
||||||
|
});
|
||||||
|
});
|
||||||
16
core/src/node/api/restful/v1.test.ts
Normal file
16
core/src/node/api/restful/v1.test.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
import { v1Router } from './v1';
|
||||||
|
import { commonRouter } from './common';
|
||||||
|
|
||||||
|
test('should define v1Router function', () => {
|
||||||
|
expect(v1Router).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should register commonRouter', () => {
|
||||||
|
const mockApp = {
|
||||||
|
register: jest.fn(),
|
||||||
|
};
|
||||||
|
v1Router(mockApp as any);
|
||||||
|
expect(mockApp.register).toHaveBeenCalledWith(commonRouter);
|
||||||
|
});
|
||||||
|
|
||||||
122
core/src/node/extension/extension.test.ts
Normal file
122
core/src/node/extension/extension.test.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import Extension from './extension';
|
||||||
|
import { join } from 'path';
|
||||||
|
import 'pacote';
|
||||||
|
|
||||||
|
it('should set active and call emitUpdate', () => {
|
||||||
|
const extension = new Extension();
|
||||||
|
extension.emitUpdate = jest.fn();
|
||||||
|
|
||||||
|
extension.setActive(true);
|
||||||
|
|
||||||
|
expect(extension._active).toBe(true);
|
||||||
|
expect(extension.emitUpdate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return correct specifier', () => {
|
||||||
|
const origin = 'test-origin';
|
||||||
|
const options = { version: '1.0.0' };
|
||||||
|
const extension = new Extension(origin, options);
|
||||||
|
|
||||||
|
expect(extension.specifier).toBe('test-origin@1.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should set origin and installOptions in constructor', () => {
|
||||||
|
const origin = 'test-origin';
|
||||||
|
const options = { someOption: true };
|
||||||
|
const extension = new Extension(origin, options);
|
||||||
|
|
||||||
|
expect(extension.origin).toBe(origin);
|
||||||
|
expect(extension.installOptions.someOption).toBe(true);
|
||||||
|
expect(extension.installOptions.fullMetadata).toBe(true); // default option
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install extension and set url', async () => {
|
||||||
|
const origin = 'test-origin';
|
||||||
|
const options = {};
|
||||||
|
const extension = new Extension(origin, options);
|
||||||
|
|
||||||
|
const mockManifest = {
|
||||||
|
name: 'test-name',
|
||||||
|
productName: 'Test Product',
|
||||||
|
version: '1.0.0',
|
||||||
|
main: 'index.js',
|
||||||
|
description: 'Test description'
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('pacote', () => ({
|
||||||
|
manifest: jest.fn().mockResolvedValue(mockManifest),
|
||||||
|
extract: jest.fn().mockResolvedValue(null)
|
||||||
|
}));
|
||||||
|
|
||||||
|
extension.emitUpdate = jest.fn();
|
||||||
|
await extension._install();
|
||||||
|
|
||||||
|
expect(extension.url).toBe('extension://test-name/index.js');
|
||||||
|
expect(extension.emitUpdate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should call all listeners in emitUpdate', () => {
|
||||||
|
const extension = new Extension();
|
||||||
|
const callback1 = jest.fn();
|
||||||
|
const callback2 = jest.fn();
|
||||||
|
|
||||||
|
extension.subscribe('listener1', callback1);
|
||||||
|
extension.subscribe('listener2', callback2);
|
||||||
|
|
||||||
|
extension.emitUpdate();
|
||||||
|
|
||||||
|
expect(callback1).toHaveBeenCalledWith(extension);
|
||||||
|
expect(callback2).toHaveBeenCalledWith(extension);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should remove listener in unsubscribe', () => {
|
||||||
|
const extension = new Extension();
|
||||||
|
const callback = jest.fn();
|
||||||
|
|
||||||
|
extension.subscribe('testListener', callback);
|
||||||
|
extension.unsubscribe('testListener');
|
||||||
|
|
||||||
|
expect(extension.listeners['testListener']).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should add listener in subscribe', () => {
|
||||||
|
const extension = new Extension();
|
||||||
|
const callback = jest.fn();
|
||||||
|
|
||||||
|
extension.subscribe('testListener', callback);
|
||||||
|
|
||||||
|
expect(extension.listeners['testListener']).toBe(callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should set properties from manifest', async () => {
|
||||||
|
const origin = 'test-origin';
|
||||||
|
const options = {};
|
||||||
|
const extension = new Extension(origin, options);
|
||||||
|
|
||||||
|
const mockManifest = {
|
||||||
|
name: 'test-name',
|
||||||
|
productName: 'Test Product',
|
||||||
|
version: '1.0.0',
|
||||||
|
main: 'index.js',
|
||||||
|
description: 'Test description'
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('pacote', () => ({
|
||||||
|
manifest: jest.fn().mockResolvedValue(mockManifest)
|
||||||
|
}));
|
||||||
|
|
||||||
|
await extension.getManifest();
|
||||||
|
|
||||||
|
expect(extension.name).toBe('test-name');
|
||||||
|
expect(extension.productName).toBe('Test Product');
|
||||||
|
expect(extension.version).toBe('1.0.0');
|
||||||
|
expect(extension.main).toBe('index.js');
|
||||||
|
expect(extension.description).toBe('Test description');
|
||||||
|
});
|
||||||
|
|
||||||
28
core/src/node/extension/manager.test.ts
Normal file
28
core/src/node/extension/manager.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { ExtensionManager } from './manager';
|
||||||
|
|
||||||
|
it('should throw an error when an invalid path is provided', () => {
|
||||||
|
const manager = new ExtensionManager();
|
||||||
|
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||||
|
expect(() => manager.setExtensionsPath('')).toThrow('Invalid path provided to the extensions folder');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return an empty string when extensionsPath is not set', () => {
|
||||||
|
const manager = new ExtensionManager();
|
||||||
|
expect(manager.getExtensionsFile()).toBe(join('', 'extensions.json'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return undefined if no path is set', () => {
|
||||||
|
const manager = new ExtensionManager();
|
||||||
|
expect(manager.getExtensionsPath()).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return the singleton instance', () => {
|
||||||
|
const instance1 = new ExtensionManager();
|
||||||
|
const instance2 = new ExtensionManager();
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
43
core/src/node/extension/store.test.ts
Normal file
43
core/src/node/extension/store.test.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { getAllExtensions } from './store';
|
||||||
|
import { getActiveExtensions } from './store';
|
||||||
|
import { getExtension } from './store';
|
||||||
|
|
||||||
|
test('should return empty array when no extensions added', () => {
|
||||||
|
expect(getAllExtensions()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should throw error when extension does not exist', () => {
|
||||||
|
expect(() => getExtension('nonExistentExtension')).toThrow('Extension nonExistentExtension does not exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
import { addExtension } from './store';
|
||||||
|
import Extension from './extension';
|
||||||
|
|
||||||
|
test('should return all extensions when multiple extensions added', () => {
|
||||||
|
const ext1 = new Extension('ext1');
|
||||||
|
ext1.name = 'ext1';
|
||||||
|
const ext2 = new Extension('ext2');
|
||||||
|
ext2.name = 'ext2';
|
||||||
|
|
||||||
|
addExtension(ext1, false);
|
||||||
|
addExtension(ext2, false);
|
||||||
|
|
||||||
|
expect(getAllExtensions()).toEqual([ext1, ext2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test('should return only active extensions', () => {
|
||||||
|
const ext1 = new Extension('ext1');
|
||||||
|
ext1.name = 'ext1';
|
||||||
|
ext1.setActive(true);
|
||||||
|
const ext2 = new Extension('ext2');
|
||||||
|
ext2.name = 'ext2';
|
||||||
|
ext2.setActive(false);
|
||||||
|
|
||||||
|
addExtension(ext1, false);
|
||||||
|
addExtension(ext2, false);
|
||||||
|
|
||||||
|
expect(getActiveExtensions()).toEqual([ext1]);
|
||||||
|
});
|
||||||
14
core/src/node/helper/config.test.ts
Normal file
14
core/src/node/helper/config.test.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { getEngineConfiguration } from './config';
|
||||||
|
import { getAppConfigurations, defaultAppConfig } from './config';
|
||||||
|
|
||||||
|
it('should return undefined for invalid engine ID', async () => {
|
||||||
|
const config = await getEngineConfiguration('invalid_engine');
|
||||||
|
expect(config).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return default config when CI is e2e', () => {
|
||||||
|
process.env.CI = 'e2e';
|
||||||
|
const config = getAppConfigurations();
|
||||||
|
expect(config).toEqual(defaultAppConfig());
|
||||||
|
});
|
||||||
11
core/src/node/helper/download.test.ts
Normal file
11
core/src/node/helper/download.test.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { DownloadManager } from './download';
|
||||||
|
|
||||||
|
it('should set a network request for a specific file', () => {
|
||||||
|
const downloadManager = new DownloadManager();
|
||||||
|
const fileName = 'testFile';
|
||||||
|
const request = { url: 'http://example.com' };
|
||||||
|
|
||||||
|
downloadManager.setRequest(fileName, request);
|
||||||
|
|
||||||
|
expect(downloadManager.networkRequests[fileName]).toEqual(request);
|
||||||
|
});
|
||||||
47
core/src/node/helper/logger.test.ts
Normal file
47
core/src/node/helper/logger.test.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Logger, LoggerManager } from './logger';
|
||||||
|
|
||||||
|
it('should flush queued logs to registered loggers', () => {
|
||||||
|
class TestLogger extends Logger {
|
||||||
|
name = 'testLogger';
|
||||||
|
log(args: any): void {
|
||||||
|
console.log(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const loggerManager = new LoggerManager();
|
||||||
|
const testLogger = new TestLogger();
|
||||||
|
loggerManager.register(testLogger);
|
||||||
|
const logSpy = jest.spyOn(testLogger, 'log');
|
||||||
|
loggerManager.log('test log');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('test log');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should unregister a logger', () => {
|
||||||
|
class TestLogger extends Logger {
|
||||||
|
name = 'testLogger';
|
||||||
|
log(args: any): void {
|
||||||
|
console.log(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const loggerManager = new LoggerManager();
|
||||||
|
const testLogger = new TestLogger();
|
||||||
|
loggerManager.register(testLogger);
|
||||||
|
loggerManager.unregister('testLogger');
|
||||||
|
const retrievedLogger = loggerManager.get('testLogger');
|
||||||
|
expect(retrievedLogger).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should register and retrieve a logger', () => {
|
||||||
|
class TestLogger extends Logger {
|
||||||
|
name = 'testLogger';
|
||||||
|
log(args: any): void {
|
||||||
|
console.log(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const loggerManager = new LoggerManager();
|
||||||
|
const testLogger = new TestLogger();
|
||||||
|
loggerManager.register(testLogger);
|
||||||
|
const retrievedLogger = loggerManager.get('testLogger');
|
||||||
|
expect(retrievedLogger).toBe(testLogger);
|
||||||
|
});
|
||||||
23
core/src/node/helper/module.test.ts
Normal file
23
core/src/node/helper/module.test.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ModuleManager } from './module';
|
||||||
|
|
||||||
|
it('should clear all imported modules', () => {
|
||||||
|
const moduleManager = new ModuleManager();
|
||||||
|
moduleManager.setModule('module1', { key: 'value1' });
|
||||||
|
moduleManager.setModule('module2', { key: 'value2' });
|
||||||
|
moduleManager.clearImportedModules();
|
||||||
|
expect(moduleManager.requiredModules).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should set a module correctly', () => {
|
||||||
|
const moduleManager = new ModuleManager();
|
||||||
|
moduleManager.setModule('testModule', { key: 'value' });
|
||||||
|
expect(moduleManager.requiredModules['testModule']).toEqual({ key: 'value' });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return the singleton instance', () => {
|
||||||
|
const instance1 = new ModuleManager();
|
||||||
|
const instance2 = new ModuleManager();
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
29
core/src/node/helper/path.test.ts
Normal file
29
core/src/node/helper/path.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { normalizeFilePath } from './path'
|
||||||
|
|
||||||
|
import { jest } from '@jest/globals'
|
||||||
|
describe('Test file normalize', () => {
|
||||||
|
test('returns no file protocol prefix on Unix', async () => {
|
||||||
|
expect(normalizeFilePath('file://test.txt')).toBe('test.txt')
|
||||||
|
expect(normalizeFilePath('file:/test.txt')).toBe('test.txt')
|
||||||
|
})
|
||||||
|
test('returns no file protocol prefix on Windows', async () => {
|
||||||
|
expect(normalizeFilePath('file:\\\\test.txt')).toBe('test.txt')
|
||||||
|
expect(normalizeFilePath('file:\\test.txt')).toBe('test.txt')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns correct path when Electron is available and app is not packaged', () => {
|
||||||
|
const electronMock = {
|
||||||
|
app: {
|
||||||
|
getAppPath: jest.fn().mockReturnValue('/mocked/path'),
|
||||||
|
isPackaged: false,
|
||||||
|
},
|
||||||
|
protocol: {},
|
||||||
|
}
|
||||||
|
jest.mock('electron', () => electronMock)
|
||||||
|
|
||||||
|
const { appResourcePath } = require('./path')
|
||||||
|
|
||||||
|
const expectedPath = process.platform === 'win32' ? '\\mocked\\path' : '/mocked/path'
|
||||||
|
expect(appResourcePath()).toBe(expectedPath)
|
||||||
|
})
|
||||||
|
})
|
||||||
15
core/src/node/helper/resource.test.ts
Normal file
15
core/src/node/helper/resource.test.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { getSystemResourceInfo } from './resource';
|
||||||
|
|
||||||
|
it('should return the correct system resource information with a valid CPU count', async () => {
|
||||||
|
const mockCpuCount = 4;
|
||||||
|
jest.spyOn(require('./config'), 'physicalCpuCount').mockResolvedValue(mockCpuCount);
|
||||||
|
const logSpy = jest.spyOn(require('./logger'), 'log').mockImplementation(() => {});
|
||||||
|
|
||||||
|
const result = await getSystemResourceInfo();
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
numCpuPhysicalCore: mockCpuCount,
|
||||||
|
memAvailable: 0,
|
||||||
|
});
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`[CORTEX]::CPU information - ${mockCpuCount}`);
|
||||||
|
});
|
||||||
7
core/src/types/assistant/assistantEvent.test.ts
Normal file
7
core/src/types/assistant/assistantEvent.test.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { AssistantEvent } from './assistantEvent';
|
||||||
|
it('dummy test', () => { expect(true).toBe(true); });
|
||||||
|
|
||||||
|
it('should contain OnAssistantsUpdate event', () => {
|
||||||
|
expect(AssistantEvent.OnAssistantsUpdate).toBe('OnAssistantsUpdate');
|
||||||
|
});
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export type DownloadState = {
|
|||||||
|
|
||||||
error?: string
|
error?: string
|
||||||
extensionId?: string
|
extensionId?: string
|
||||||
downloadType?: DownloadType
|
downloadType?: DownloadType | string
|
||||||
localPath?: string
|
localPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ export type DownloadRequest = {
|
|||||||
*/
|
*/
|
||||||
extensionId?: string
|
extensionId?: string
|
||||||
|
|
||||||
downloadType?: DownloadType
|
downloadType?: DownloadType | string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DownloadTime = {
|
type DownloadTime = {
|
||||||
|
|||||||
10
core/testRunner.js
Normal file
10
core/testRunner.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const jestRunner = require('jest-runner');
|
||||||
|
|
||||||
|
class EmptyTestFileRunner extends jestRunner.default {
|
||||||
|
async runTests(tests, watcher, onStart, onResult, onFailure, options) {
|
||||||
|
const nonEmptyTests = tests.filter(test => test.context.hasteFS.getSize(test.path) > 0);
|
||||||
|
return super.runTests(nonEmptyTests, watcher, onStart, onResult, onFailure, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EmptyTestFileRunner;
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import { normalizeFilePath } from "../../src/node/helper/path";
|
|
||||||
|
|
||||||
describe("Test file normalize", () => {
|
|
||||||
test("returns no file protocol prefix on Unix", async () => {
|
|
||||||
expect(normalizeFilePath("file://test.txt")).toBe("test.txt");
|
|
||||||
expect(normalizeFilePath("file:/test.txt")).toBe("test.txt");
|
|
||||||
});
|
|
||||||
test("returns no file protocol prefix on Windows", async () => {
|
|
||||||
expect(normalizeFilePath("file:\\\\test.txt")).toBe("test.txt");
|
|
||||||
expect(normalizeFilePath("file:\\test.txt")).toBe("test.txt");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -16,4 +16,5 @@
|
|||||||
"types": ["@types/jest"],
|
"types": ["@types/jest"],
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
|
"exclude": ["**/*.test.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
projects: ['<rootDir>/core', '<rootDir>/web'],
|
projects: ['<rootDir>/core', '<rootDir>/web', '<rootDir>/joi'],
|
||||||
}
|
}
|
||||||
|
|||||||
35
web/utils/Stack.test.ts
Normal file
35
web/utils/Stack.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import { Stack } from './Stack';
|
||||||
|
|
||||||
|
it('should return elements in reverse order', () => {
|
||||||
|
const stack = new Stack<number>();
|
||||||
|
stack.push(1);
|
||||||
|
stack.push(2);
|
||||||
|
stack.push(3);
|
||||||
|
const reversedOutput = stack.reverseOutput();
|
||||||
|
expect(reversedOutput).toEqual([1, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should pop an element from the stack', () => {
|
||||||
|
const stack = new Stack<number>();
|
||||||
|
stack.push(1);
|
||||||
|
const poppedElement = stack.pop();
|
||||||
|
expect(poppedElement).toBe(1);
|
||||||
|
expect(stack.isEmpty()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should push an element to the stack', () => {
|
||||||
|
const stack = new Stack<number>();
|
||||||
|
stack.push(1);
|
||||||
|
expect(stack.isEmpty()).toBe(false);
|
||||||
|
expect(stack.size()).toBe(1);
|
||||||
|
expect(stack.peek()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should initialize as empty', () => {
|
||||||
|
const stack = new Stack<number>();
|
||||||
|
expect(stack.isEmpty()).toBe(true);
|
||||||
|
});
|
||||||
22
web/utils/componentSettings.test.ts
Normal file
22
web/utils/componentSettings.test.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
import { getConfigurationsData } from './componentSettings';
|
||||||
|
|
||||||
|
it('should process checkbox setting', () => {
|
||||||
|
const settings = { embedding: true };
|
||||||
|
const result = getConfigurationsData(settings);
|
||||||
|
expect(result[0].controllerProps.value).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should process input setting and handle array input', () => {
|
||||||
|
const settings = { prompt_template: ['Hello', 'World', ''] };
|
||||||
|
const result = getConfigurationsData(settings);
|
||||||
|
expect(result[0].controllerProps.value).toBe('Hello World ');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should return an empty array when settings object is empty', () => {
|
||||||
|
const settings = {};
|
||||||
|
const result = getConfigurationsData(settings);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
27
web/utils/datetime.test.ts
Normal file
27
web/utils/datetime.test.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
import { displayDate } from './datetime';
|
||||||
|
import { isToday } from './datetime';
|
||||||
|
|
||||||
|
test('should return only time for today\'s timestamp', () => {
|
||||||
|
const today = new Date();
|
||||||
|
const timestamp = today.getTime();
|
||||||
|
const expectedTime = today.toLocaleTimeString(undefined, {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
});
|
||||||
|
expect(displayDate(timestamp)).toBe(expectedTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should return N/A for undefined timestamp', () => {
|
||||||
|
expect(displayDate()).toBe('N/A');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should return true for today\'s timestamp', () => {
|
||||||
|
const today = new Date();
|
||||||
|
const timestamp = today.setHours(0, 0, 0, 0);
|
||||||
|
expect(isToday(timestamp)).toBe(true);
|
||||||
|
});
|
||||||
17
web/utils/jsonToCssVariables.test.ts
Normal file
17
web/utils/jsonToCssVariables.test.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
import cssVars from './jsonToCssVariables';
|
||||||
|
|
||||||
|
test('should convert nested JSON object to CSS variables', () => {
|
||||||
|
const input = { theme: { color: 'blue', font: { size: '14px', weight: 'bold' } } };
|
||||||
|
const expectedOutput = '--theme-color: blue;--theme-font-size: 14px;--theme-font-weight: bold;';
|
||||||
|
const result = cssVars(input);
|
||||||
|
expect(result).toBe(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should convert simple JSON object to CSS variables', () => {
|
||||||
|
const input = { color: 'red', fontSize: '16px' };
|
||||||
|
const expectedOutput = '--color: red;--fontSize: 16px;';
|
||||||
|
const result = cssVars(input);
|
||||||
|
expect(result).toBe(expectedOutput);
|
||||||
|
});
|
||||||
@ -1,5 +1,3 @@
|
|||||||
// @auto-generated
|
|
||||||
|
|
||||||
import { utilizedMemory } from './memory'
|
import { utilizedMemory } from './memory'
|
||||||
|
|
||||||
test('test_utilizedMemory_arbitraryValues', () => {
|
test('test_utilizedMemory_arbitraryValues', () => {
|
||||||
|
|||||||
18
web/utils/predefinedComponent.test.ts
Normal file
18
web/utils/predefinedComponent.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
import { presetConfiguration } from './predefinedComponent';
|
||||||
|
|
||||||
|
it('should have correct configuration for prompt_template', () => {
|
||||||
|
const config = presetConfiguration['prompt_template'];
|
||||||
|
expect(config).toEqual({
|
||||||
|
key: 'prompt_template',
|
||||||
|
title: 'Prompt template',
|
||||||
|
description: `A predefined text or framework that guides the AI model's response generation. It includes placeholders or instructions for the model to fill in or expand upon.`,
|
||||||
|
controllerType: 'input',
|
||||||
|
controllerProps: {
|
||||||
|
placeholder: 'Prompt template',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
requireModelReload: true,
|
||||||
|
configType: 'setting',
|
||||||
|
});
|
||||||
|
});
|
||||||
9
web/utils/thread.test.ts
Normal file
9
web/utils/thread.test.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
import { generateThreadId } from './thread';
|
||||||
|
|
||||||
|
test('shouldGenerateThreadIdWithCorrectFormat', () => {
|
||||||
|
const assistantId = 'assistant123';
|
||||||
|
const threadId = generateThreadId(assistantId);
|
||||||
|
const regex = /^assistant123_\d{10}$/;
|
||||||
|
expect(threadId).toMatch(regex);
|
||||||
|
});
|
||||||
25
web/utils/titleUtils.test.ts
Normal file
25
web/utils/titleUtils.test.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import { openFileTitle } from './titleUtils';
|
||||||
|
|
||||||
|
test('should return "Open Containing Folder" when neither isMac nor isWindows is true', () => {
|
||||||
|
(global as any).isMac = false;
|
||||||
|
(global as any).isWindows = false;
|
||||||
|
const result = openFileTitle();
|
||||||
|
expect(result).toBe('Open Containing Folder');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should return "Show in File Explorer" when isWindows is true', () => {
|
||||||
|
(global as any).isMac = false;
|
||||||
|
(global as any).isWindows = true;
|
||||||
|
const result = openFileTitle();
|
||||||
|
expect(result).toBe('Show in File Explorer');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should return "Show in Finder" when isMac is true', () => {
|
||||||
|
(global as any).isMac = true;
|
||||||
|
(global as any).isWindows = false;
|
||||||
|
const result = openFileTitle();
|
||||||
|
expect(result).toBe('Show in Finder');
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user