diff --git a/bin/server.ts b/bin/server.ts index d06aab527..911bb4fb7 100644 --- a/bin/server.ts +++ b/bin/server.ts @@ -1,4 +1,4 @@ #!/usr/bin/env node import * as Path from 'path'; import { runCli } from '../src/init/CliRunner'; -runCli(Path.join(__dirname, '..')); +runCli(Path.join(__dirname, '..'), process.argv); diff --git a/src/init/CliRunner.ts b/src/init/CliRunner.ts index d7222ba34..89c74cc21 100644 --- a/src/init/CliRunner.ts +++ b/src/init/CliRunner.ts @@ -1,4 +1,4 @@ -import * as Path from 'path'; +import * as path from 'path'; import type { ReadStream, WriteStream } from 'tty'; import type { LoaderProperties } from 'componentsjs'; import { Loader } from 'componentsjs'; @@ -23,22 +23,22 @@ export const runCustom = function( stderr: WriteStream, properties: LoaderProperties, ): void { - const { argv } = yargs + const { argv } = yargs(args) .usage('node ./bin/server.js [args]') .options({ port: { type: 'number', alias: 'p', default: 3000 }, config: { type: 'string', alias: 'c' }, rootFilePath: { type: 'string', alias: 'f' }, sparqlEndpoint: { type: 'string', alias: 's' }, - level: { type: 'string', alias: 'l', default: 'info' }, + loggingLevel: { type: 'string', alias: 'l', default: 'info' }, }) .help(); (async(): Promise => { // Load provided or default config file const configPath = argv.config ? - Path.join(process.cwd(), argv.config) : - `${__dirname}/../../config/config-default.json`; + path.join(process.cwd(), argv.config) : + path.join(__dirname, '/../../config/config-default.json'); // Setup from config file const loader = new Loader(properties); @@ -50,7 +50,7 @@ export const runCustom = function( 'urn:solid-server:default:variable:base': `http://localhost:${argv.port}/`, 'urn:solid-server:default:variable:rootFilePath': argv.rootFilePath ?? process.cwd(), 'urn:solid-server:default:variable:sparqlEndpoint': argv.sparqlEndpoint, - 'urn:solid-server:default:variable:loggingLevel': argv.level, + 'urn:solid-server:default:variable:loggingLevel': argv.loggingLevel, }, }) as Setup; return await setup.setup(); @@ -66,7 +66,6 @@ export const runCustom = function( * Run function for starting the server from the command line * @param moduleRootPath - Path to the module's root. */ -export const runCli = function(moduleRootPath: string): void { - const argv = process.argv.slice(2); - runCustom(argv, process.stdin, process.stdout, process.stderr, { mainModulePath: moduleRootPath }); +export const runCli = function(mainModulePath: string, argv: string[]): void { + runCustom(argv, process.stdin, process.stdout, process.stderr, { mainModulePath }); }; diff --git a/test/unit/init/CliRunner.test.ts b/test/unit/init/CliRunner.test.ts index a4cbb118e..d9c885264 100644 --- a/test/unit/init/CliRunner.test.ts +++ b/test/unit/init/CliRunner.test.ts @@ -1,98 +1,114 @@ -import * as Path from 'path'; +import * as path from 'path'; import type { Loader } from 'componentsjs'; import { runCli } from '../../../src/init/CliRunner'; import type { Setup } from '../../../src/init/Setup'; -let calledInstantiateFromUrl: boolean; -let calledRegisterAvailableModuleResources: boolean; -let throwError: boolean; -let outsideResolve: () => void; -let functionToResolve: Promise; +const mainModulePath = path.join(__dirname, '..'); const mockSetup = { - setup: jest.fn(), + setup: jest.fn(async(): Promise => null), } as unknown as jest.Mocked; +const loader = { + instantiateFromUrl: jest.fn(async(): Promise => mockSetup), + registerAvailableModuleResources: jest.fn(async(): Promise => mockSetup), +} as unknown as jest.Mocked; // Mock the Loader class. -jest.mock('componentsjs', (): any => ( - // eslint-disable-next-line @typescript-eslint/naming-convention, object-shorthand - { Loader: function(): Loader { - return { - instantiateFromUrl(): any { - calledInstantiateFromUrl = true; - if (throwError) { - throw new Error('Error! :o'); - } - return mockSetup; - }, - registerAvailableModuleResources(): any { - calledRegisterAvailableModuleResources = true; - }, - } as unknown as Loader; - } } -)); - -jest.mock('yargs', (): any => ({ - usage(): any { - return this; - }, - options(): any { - return this; - }, - help(): any { - // Return once with and once without values so that both branches are tested. - if (throwError) { - return { - argv: { config: 'value', rootFilePath: 'root' }, - }; - } - return { - argv: { }, - }; - }, +jest.mock('componentsjs', (): any => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention + Loader: jest.fn((): Loader => loader), })); describe('CliRunner', (): void => { - beforeAll(async(): Promise => { - mockSetup.setup.mockImplementation(async(): Promise => { - // The info method will be called when all other code has been executed, so end the waiting function. - outsideResolve(); - }); + afterEach((): void => { + jest.clearAllMocks(); }); - beforeEach(async(): Promise => { - calledInstantiateFromUrl = false; - calledRegisterAvailableModuleResources = false; - throwError = false; + it('starts the server with default settings.', async(): Promise => { + runCli(mainModulePath, [ 'node', 'script' ]); + await mockSetup.setup(); - // Initialize a function that will be resolved as soon as all necessary but asynchronous calls are completed. - functionToResolve = new Promise((resolve): any => { - outsideResolve = resolve; - }); - }); - - it('Runs function for starting the server from the command line.', async(): Promise => { - runCli(Path.join(__dirname, '..')); - - await functionToResolve; - - expect(calledInstantiateFromUrl).toBeTruthy(); - expect(calledRegisterAvailableModuleResources).toBeTruthy(); + expect(loader.instantiateFromUrl).toHaveBeenCalledTimes(1); + expect(loader.instantiateFromUrl).toHaveBeenCalledWith( + 'urn:solid-server:default', + path.join(__dirname, '/../../../config/config-default.json'), + undefined, + { + variables: { + 'urn:solid-server:default:variable:port': 3000, + 'urn:solid-server:default:variable:base': `http://localhost:3000/`, + 'urn:solid-server:default:variable:rootFilePath': process.cwd(), + 'urn:solid-server:default:variable:sparqlEndpoint': undefined, + 'urn:solid-server:default:variable:loggingLevel': 'info', + }, + }, + ); + expect(loader.registerAvailableModuleResources).toHaveBeenCalledTimes(1); + expect(loader.registerAvailableModuleResources).toHaveBeenCalledWith(); expect(mockSetup.setup).toHaveBeenCalledTimes(1); + expect(mockSetup.setup).toHaveBeenCalledWith(); }); - it('Writes to stderr when an exception occurs.', async(): Promise => { - const mockStderr = jest.spyOn(process.stderr, 'write').mockImplementation((): any => { - // This method will be called when an error has occurred, so end the waiting function. - outsideResolve(); - }); - throwError = true; + it('accepts abbreviated flags.', async(): Promise => { + runCli(mainModulePath, [ 'node', 'script', + '-p', '4000', + '-c', 'myconfig.json', + '-f', '/root', + '-s', 'http://localhost:5000/sparql', + '-l', 'debug', + ]); + await mockSetup.setup(); - runCli(Path.join(__dirname, '..')); + expect(loader.instantiateFromUrl).toHaveBeenCalledWith( + 'urn:solid-server:default', + path.join(process.cwd(), 'myconfig.json'), + undefined, + { + variables: { + 'urn:solid-server:default:variable:port': 4000, + 'urn:solid-server:default:variable:base': `http://localhost:4000/`, + 'urn:solid-server:default:variable:rootFilePath': '/root', + 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql', + 'urn:solid-server:default:variable:loggingLevel': 'debug', + }, + }, + ); + }); - await functionToResolve; + it('accepts full flags.', async(): Promise => { + runCli(mainModulePath, [ 'node', 'script', + '--port', '4000', + '--config', 'myconfig.json', + '--rootFilePath', '/root', + '--sparqlEndpoint', 'http://localhost:5000/sparql', + '--loggingLevel', 'debug', + ]); + await mockSetup.setup(); - expect(mockStderr).toHaveBeenCalledTimes(1); - mockStderr.mockRestore(); + expect(loader.instantiateFromUrl).toHaveBeenCalledWith( + 'urn:solid-server:default', + path.join(process.cwd(), 'myconfig.json'), + undefined, + { + variables: { + 'urn:solid-server:default:variable:port': 4000, + 'urn:solid-server:default:variable:base': `http://localhost:4000/`, + 'urn:solid-server:default:variable:rootFilePath': '/root', + 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql', + 'urn:solid-server:default:variable:loggingLevel': 'debug', + }, + }, + ); + }); + + it('writes to stderr when an error occurs.', async(): Promise => { + jest.spyOn(process.stderr, 'write'); + loader.instantiateFromUrl.mockRejectedValueOnce(new Error('Fatal')); + + runCli(mainModulePath, [ 'node', 'script' ]); + await new Promise((resolve): any => setImmediate(resolve)); + + expect(process.stderr.write).toHaveBeenCalledTimes(1); + expect(process.stderr.write).toHaveBeenCalledWith('Error: Fatal\n'); }); });