feat: Add App class to start and stop the server

This commit is contained in:
Joachim Van Herwegen
2021-06-11 15:30:21 +02:00
parent 29ddf57341
commit e8a0f63e02
40 changed files with 335 additions and 306 deletions

View File

@@ -3,7 +3,6 @@ import { ComponentsManager } from 'componentsjs';
import * as rimraf from 'rimraf';
import { joinFilePath } from '../../src/util/PathUtil';
export const BASE = 'http://test.com';
let cachedModuleState: IModuleState;
/**
@@ -42,3 +41,13 @@ export function getTestFolder(name: string): string {
export function removeFolder(folder: string): void {
rimraf.sync(folder, { glob: false });
}
export function getDefaultVariables(port: number, baseUrl?: string): Record<string, any> {
return {
'urn:solid-server:default:variable:baseUrl': baseUrl ?? `http://localhost:${port}/`,
'urn:solid-server:default:variable:port': port,
'urn:solid-server:default:variable:loggingLevel': 'off',
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
};
}

View File

@@ -1,13 +1,10 @@
import { mkdirSync } from 'fs';
import type { Server } from 'http';
import { stringify } from 'querystring';
import fetch from 'cross-fetch';
import type { Initializer } from '../../src/init/Initializer';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
import type { App } from '../../src/init/App';
import { joinFilePath } from '../../src/util/PathUtil';
import { getPort } from '../util/Util';
import { getTestConfigPath, getTestFolder, instantiateFromConfig, removeFolder } from './Config';
import { getDefaultVariables, getTestConfigPath, getTestFolder, instantiateFromConfig, removeFolder } from './Config';
const port = getPort('DynamicPods');
const baseUrl = `http://localhost:${port}/`;
@@ -26,45 +23,34 @@ const configs: [string, any][] = [
// Using the actual templates instead of specific test ones to prevent a lot of duplication
// Tests are very similar to subdomain/pod tests. Would be nice if they can be combined
describe.each(configs)('A dynamic pod server with template config %s', (template, { teardown }): void => {
let server: Server;
let initializer: Initializer;
let factory: HttpServerFactory;
let expiringStorage: WrappedExpiringStorage<any, any>;
let app: App;
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', template, createPod: true };
const podUrl = `${baseUrl}${settings.podName}/`;
beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl,
...getDefaultVariables(port, baseUrl),
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
'urn:solid-server:default:variable:podConfigJson': podConfigJson,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
};
// Need to make sure the temp folder exists so the podConfigJson can be written to it
mkdirSync(rootFilePath, { recursive: true });
// Create and initialize the HTTP handler and related components
// Create and start the HTTP handler and related components
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
getTestConfigPath('server-dynamic-unsafe.json'),
variables,
) as Record<string, any>;
({ factory, initializer, expiringStorage } = instances);
({ app } = instances);
// Set up the internal store
await initializer.handleSafe();
server = factory.startServer(port);
await app.start();
});
afterAll(async(): Promise<void> => {
expiringStorage.finalize();
await new Promise((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await teardown();
await app.stop();
});
it('creates a pod with the given config.', async(): Promise<void> => {

View File

@@ -1,17 +1,13 @@
import type { Server } from 'http';
import { stringify } from 'querystring';
import { URL } from 'url';
import { load } from 'cheerio';
import type { Response } from 'cross-fetch';
import { fetch } from 'cross-fetch';
import urljoin from 'url-join';
import type { Initializer } from '../../src/init/Initializer';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
import type { App } from '../../src/init/App';
import { APPLICATION_X_WWW_FORM_URLENCODED } from '../../src/util/ContentTypes';
import { joinFilePath } from '../../src/util/PathUtil';
import { getPort } from '../util/Util';
import { getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
import { IdentityTestState } from './IdentityTestState';
const port = getPort('Identity');
@@ -39,10 +35,7 @@ async function postForm(url: string, formBody: string): Promise<Response> {
// This is why the redirects are handled manually.
// We also need to parse the HTML in several steps since there is no API.
describe('A Solid server with IDP', (): void => {
let server: Server;
let initializer: Initializer;
let expiringStorage: WrappedExpiringStorage<any, any>;
let factory: HttpServerFactory;
let app: App;
const redirectUrl = 'http://mockedredirect/';
const oidcIssuer = baseUrl;
const card = urljoin(baseUrl, 'profile/card');
@@ -61,15 +54,10 @@ describe('A Solid server with IDP', (): void => {
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
getTestConfigPath('server-memory.json'),
{
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
},
getDefaultVariables(port, baseUrl),
) as Record<string, any>;
({ factory, initializer, expiringStorage } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
({ app } = instances);
await app.start();
// Create a simple webId
const turtle = `<${webId}> <http://www.w3.org/ns/solid/terms#oidcIssuer> <${baseUrl}> .`;
@@ -81,10 +69,7 @@ describe('A Solid server with IDP', (): void => {
});
afterAll(async(): Promise<void> => {
expiringStorage.finalize();
await new Promise<void>((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
describe('doing registration', (): void => {

View File

@@ -1,12 +1,11 @@
import type { Server } from 'http';
import fetch from 'cross-fetch';
import type { Initializer, ResourceStore } from '../../src/';
import type { ResourceStore, App } from '../../src/';
import { BasicRepresentation } from '../../src/';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import { AclHelper } from '../util/AclHelper';
import { deleteResource, getResource, postResource, putResource } from '../util/FetchUtil';
import { getPort } from '../util/Util';
import {
getDefaultVariables,
getPresetConfigPath,
getTestConfigPath,
getTestFolder,
@@ -30,21 +29,18 @@ const stores: [string, any][] = [
];
describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig, teardown }): void => {
let server: Server;
let initializer: Initializer;
let factory: HttpServerFactory;
let app: App;
let store: ResourceStore;
let aclHelper: AclHelper;
const permanent = `${baseUrl}document.txt`;
beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
const variables = {
...getDefaultVariables(port, baseUrl),
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
};
// Create and initialize the server
// Create and start the server
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
[
@@ -53,10 +49,9 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
],
variables,
) as Record<string, any>;
({ factory, initializer, store } = instances);
({ app, store } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
await app.start();
// Create test helper for manipulating acl
aclHelper = new AclHelper(store);
@@ -75,9 +70,7 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
afterAll(async(): Promise<void> => {
await teardown();
await new Promise((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('can add a document, read it and delete it if allowed.', async(): Promise<void> => {

View File

@@ -1,14 +1,18 @@
import { createReadStream } from 'fs';
import type { Server } from 'http';
import fetch from 'cross-fetch';
import { DataFactory, Parser } from 'n3';
import { joinFilePath, PIM, RDF } from '../../src/';
import type { Initializer } from '../../src/';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { App } from '../../src/';
import { LDP } from '../../src/util/Vocabularies';
import { deleteResource, expectQuads, getResource, patchResource, postResource, putResource } from '../util/FetchUtil';
import { getPort } from '../util/Util';
import { getPresetConfigPath, getTestConfigPath, getTestFolder, instantiateFromConfig, removeFolder } from './Config';
import {
getDefaultVariables,
getPresetConfigPath,
getTestConfigPath,
getTestFolder,
instantiateFromConfig, removeFolder,
} from './Config';
const { literal, namedNode, quad } = DataFactory;
const port = getPort('LpdHandlerWithoutAuth');
@@ -27,18 +31,15 @@ const stores: [string, any][] = [
];
describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeConfig, teardown }): void => {
let server: Server;
let initializer: Initializer;
let factory: HttpServerFactory;
let app: App;
beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
const variables = {
...getDefaultVariables(port, baseUrl),
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
};
// Create and initialize the server
// Create and start the server
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
[
@@ -47,17 +48,14 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
],
variables,
) as Record<string, any>;
({ factory, initializer } = instances);
({ app } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
await app.start();
});
afterAll(async(): Promise<void> => {
await teardown();
await new Promise((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('can read a container listing.', async(): Promise<void> => {

View File

@@ -15,9 +15,6 @@ import type { ReadWriteLocker } from '../../src/util/locking/ReadWriteLocker';
import { SingleThreadedResourceLocker } from '../../src/util/locking/SingleThreadedResourceLocker';
import { WrappedExpiringReadWriteLocker } from '../../src/util/locking/WrappedExpiringReadWriteLocker';
import { guardedStreamFrom } from '../../src/util/StreamUtil';
import { BASE } from './Config';
// The modern fake timers implementation blocks certain parts with the current setup
jest.useFakeTimers('legacy');
describe('A LockingResourceStore', (): void => {
@@ -44,7 +41,7 @@ describe('A LockingResourceStore', (): void => {
);
// Initialize store
const initializer = new RootContainerInitializer({ store: source, baseUrl: BASE });
const initializer = new RootContainerInitializer({ store: source, baseUrl: base });
await initializer.handleSafe();
locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker());

View File

@@ -1,9 +1,8 @@
import type { Server } from 'http';
import fetch from 'cross-fetch';
import type { RedisResourceLocker } from '../../src';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { App, RedisResourceLocker } from '../../src';
import { describeIf, getPort } from '../util/Util';
import { getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
/**
* Test the general functionality of the server using a RedisResourceLocker
@@ -11,28 +10,21 @@ import { getTestConfigPath, instantiateFromConfig } from './Config';
describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', (): void => {
const port = getPort('RedisResourceLocker');
const baseUrl = `http://localhost:${port}/`;
let server: Server;
let app: App;
let locker: RedisResourceLocker;
let factory: HttpServerFactory;
beforeAll(async(): Promise<void> => {
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
getTestConfigPath('run-with-redlock.json'),
{
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
},
getDefaultVariables(port, baseUrl),
) as Record<string, any>;
({ factory, locker } = instances);
server = factory.startServer(port);
({ app, locker } = instances);
await app.start();
});
afterAll(async(): Promise<void> => {
await locker.finalize();
await new Promise<void>((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('can add a file to the store, read it and delete it.', async(): Promise<void> => {

View File

@@ -1,41 +1,27 @@
import type { Server } from 'http';
import fetch from 'cross-fetch';
import type { Initializer } from '../../src/init/Initializer';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
import type { App } from '../../src/init/App';
import { getPort } from '../util/Util';
import { getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
const port = getPort('ServerFetch');
const baseUrl = `http://localhost:${port}/`;
// Some tests with real Requests/Responses until the mocking library has been removed from the tests
describe('A Solid server', (): void => {
let server: Server;
let initializer: Initializer;
let expiringStorage: WrappedExpiringStorage<any, any>;
let factory: HttpServerFactory;
let app: App;
beforeAll(async(): Promise<void> => {
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
getTestConfigPath('server-memory.json'),
{
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': '',
},
getDefaultVariables(port, baseUrl),
) as Record<string, any>;
({ factory, initializer, expiringStorage } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
({ app } = instances);
await app.start();
});
afterAll(async(): Promise<void> => {
expiringStorage.finalize();
await new Promise<void>((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('can do a successful HEAD request to a container.', async(): Promise<void> => {

View File

@@ -1,28 +1,23 @@
import { promises as fs } from 'fs';
import type { Server } from 'http';
import { joinFilePath } from '../../src/';
import type { Initializer } from '../../src/';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { App } from '../../src/';
import { putResource } from '../util/FetchUtil';
import { describeIf, getPort } from '../util/Util';
import { getPresetConfigPath, getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, getPresetConfigPath, getTestConfigPath, instantiateFromConfig } from './Config';
const port = getPort('SparqlStorage');
const baseUrl = `http://localhost:${port}/`;
describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
let server: Server;
let initializer: Initializer;
let factory: HttpServerFactory;
let app: App;
beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
const variables = {
...getDefaultVariables(port, baseUrl),
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
};
// Create and initialize the server
// Create and start the server
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
[
@@ -31,16 +26,13 @@ describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
],
variables,
) as Record<string, any>;
({ factory, initializer } = instances);
({ app } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
await app.start();
});
afterAll(async(): Promise<void> => {
await new Promise((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('can add a Turtle file to the store.', async(): Promise<void> => {

View File

@@ -1,12 +1,15 @@
import type { Server } from 'http';
import { stringify } from 'querystring';
import fetch from 'cross-fetch';
import type { Initializer } from '../../src/init/Initializer';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
import { joinFilePath } from '../../src/util/PathUtil';
import type { App } from '../../src/init/App';
import { getPort } from '../util/Util';
import { getPresetConfigPath, getTestConfigPath, getTestFolder, instantiateFromConfig, removeFolder } from './Config';
import {
getDefaultVariables,
getPresetConfigPath,
getTestConfigPath,
getTestFolder,
instantiateFromConfig,
removeFolder,
} from './Config';
const port = getPort('Subdomains');
const baseUrl = `http://localhost:${port}/`;
@@ -25,23 +28,18 @@ const stores: [string, any][] = [
// Simulating subdomains using the forwarded header so no DNS changes are required
describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardown }): void => {
let server: Server;
let initializer: Initializer;
let factory: HttpServerFactory;
let expiringStorage: WrappedExpiringStorage<any, any>;
let app: App;
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', createPod: true };
const podHost = `alice.localhost:${port}`;
const podUrl = `http://${podHost}/`;
beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl,
...getDefaultVariables(port, baseUrl),
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
};
// Create and initialize the server
// Create and start the server
const instances = await instantiateFromConfig(
'urn:solid-server:test:Instances',
[
@@ -50,18 +48,14 @@ describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardo
],
variables,
) as Record<string, any>;
({ factory, initializer, expiringStorage } = instances);
({ app } = instances);
await initializer.handleSafe();
server = factory.startServer(port);
await app.start();
});
afterAll(async(): Promise<void> => {
expiringStorage.finalize();
await new Promise((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await teardown();
await app.stop();
});
describe('handling resources', (): void => {

View File

@@ -1,37 +1,31 @@
import type { Server } from 'http';
import fetch from 'cross-fetch';
import WebSocket from 'ws';
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
import type { App } from '../../src/init/App';
import { getPort } from '../util/Util';
import { getTestConfigPath, instantiateFromConfig } from './Config';
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
const port = getPort('WebSocketsProtocol');
const serverUrl = `http://localhost:${port}/`;
const headers = { forwarded: 'host=example.pod;proto=https' };
describe('A server with the Solid WebSockets API behind a proxy', (): void => {
let server: Server;
let app: App;
beforeAll(async(): Promise<void> => {
const factory = await instantiateFromConfig(
'urn:solid-server:default:ServerFactory',
app = await instantiateFromConfig(
'urn:solid-server:default:App',
getTestConfigPath('server-without-auth.json'),
{
'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
'urn:solid-server:default:variable:showStackTrace': true,
},
) as HttpServerFactory;
server = factory.startServer(port);
getDefaultVariables(port, 'https://example.pod/'),
) as App;
await app.start();
});
afterAll(async(): Promise<void> => {
await new Promise<void>((resolve, reject): void => {
server.close((error): void => error ? reject(error) : resolve());
});
await app.stop();
});
it('returns a 404 if no data was initialized.', async(): Promise<void> => {
const response = await fetch(serverUrl, { headers });
it('returns a 404 if there is no data.', async(): Promise<void> => {
const response = await fetch(`${serverUrl}foo`, { headers });
expect(response.status).toBe(404);
});

View File

@@ -1,11 +1,12 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/simple.json",
"files-scs:config/http/middleware/no-websockets.json",
"files-scs:config/http/server-factory/no-websockets.json",
"files-scs:config/http/static/default.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/debug-auth-header.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
@@ -28,17 +29,12 @@
"@type": "RecordObject",
"record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
"RecordObject:_record_key": "app",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
},
{
"RecordObject:_record_key": "store",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ResourceStore" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
}
]
}

View File

@@ -1,11 +1,12 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/simple.json",
"files-scs:config/http/middleware/no-websockets.json",
"files-scs:config/http/server-factory/no-websockets.json",
"files-scs:config/http/static/default.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/debug-auth-header.json",
"files-scs:config/ldp/authorization/allow-everything.json",
"files-scs:config/ldp/handler/default.json",
@@ -29,17 +30,12 @@
"@type": "RecordObject",
"record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
"RecordObject:_record_key": "app",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
},
{
"RecordObject:_record_key": "locker",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:RedisResourceLocker" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
}
]
}

View File

@@ -1,6 +1,8 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/default.json",
"files-scs:config/http/middleware/no-websockets.json",
"files-scs:config/http/server-factory/no-websockets.json",
@@ -9,7 +11,6 @@
"files-scs:config/identity/handler/default.json",
"files-scs:config/identity/ownership/unsafe-no-check.json",
"files-scs:config/identity/pod/dynamic.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/debug-auth-header.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
@@ -33,24 +34,10 @@
"@type": "RecordObject",
"record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
},
{
"comment": "Timer needs to be stopped when tests are finished.",
"RecordObject:_record_key": "expiringStorage",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ExpiringIdpStorage" }
"RecordObject:_record_key": "app",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
}
]
},
{
"@id": "urn:solid-server:default:ResourcesGenerator",
"TemplatedResourcesGenerator:_templateFolder": "$PACKAGE_ROOT/test/assets/templates"
}
]
}

View File

@@ -1,6 +1,8 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/default.json",
"files-scs:config/http/middleware/websockets.json",
"files-scs:config/http/server-factory/websockets.json",
@@ -8,7 +10,6 @@
"files-scs:config/identity/handler/default.json",
"files-scs:config/identity/ownership/token.json",
"files-scs:config/identity/pod/static.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/dpop-bearer.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
@@ -31,18 +32,8 @@
"@type": "RecordObject",
"RecordObject:_record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
},
{
"comment": "Timer needs to be stopped when tests are finished.",
"RecordObject:_record_key": "expiringStorage",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ExpiringIdpStorage" }
"RecordObject:_record_key": "app",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
}
]
},

View File

@@ -1,6 +1,8 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/default.json",
"files-scs:config/http/middleware/no-websockets.json",
"files-scs:config/http/server-factory/no-websockets.json",
@@ -9,7 +11,6 @@
"files-scs:config/identity/handler/default.json",
"files-scs:config/identity/ownership/unsafe-no-check.json",
"files-scs:config/identity/pod/static.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/debug-auth-header.json",
"files-scs:config/ldp/authorization/webacl.json",
"files-scs:config/ldp/handler/default.json",
@@ -32,18 +33,8 @@
"@type": "RecordObject",
"record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
},
{
"comment": "Timer needs to be stopped when tests are finished.",
"RecordObject:_record_key": "expiringStorage",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ExpiringIdpStorage" }
"RecordObject:_record_key": "app",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
}
]
}

View File

@@ -1,11 +1,12 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
"import": [
"files-scs:config/app/app/default.json",
"files-scs:config/app/init/default.json",
"files-scs:config/http/handler/default.json",
"files-scs:config/http/middleware/websockets.json",
"files-scs:config/http/server-factory/websockets.json",
"files-scs:config/http/static/default.json",
"files-scs:config/init/handler/default.json",
"files-scs:config/ldp/authentication/dpop-bearer.json",
"files-scs:config/ldp/authorization/allow-everything.json",
"files-scs:config/ldp/handler/default.json",
@@ -23,22 +24,6 @@
"files-scs:config/util/variables/default.json"
],
"@graph": [
{
"@id": "urn:solid-server:test:Instances",
"@type": "RecordObject",
"RecordObject:_record": [
{
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
"RecordObject:_record_key": "initializer",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
},
{
"RecordObject:_record_key": "factory",
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ServerFactory" }
}
]
},
{
"@id": "urn:solid-server:default:IdentityProviderHandler",
"@type": "UnsupportedAsyncHandler"

View File

@@ -0,0 +1,25 @@
import { App } from '../../../src/init/App';
import type { Finalizable } from '../../../src/init/final/Finalizable';
import type { Initializer } from '../../../src/init/Initializer';
describe('An App', (): void => {
let initializer: Initializer;
let finalizer: Finalizable;
let app: App;
beforeEach(async(): Promise<void> => {
initializer = { handleSafe: jest.fn() } as any;
finalizer = { finalize: jest.fn() };
app = new App(initializer, finalizer);
});
it('can start with the initializer.', async(): Promise<void> => {
await expect(app.start()).resolves.toBeUndefined();
expect(initializer.handleSafe).toHaveBeenCalledTimes(1);
});
it('can stop with the finalizer.', async(): Promise<void> => {
await expect(app.stop()).resolves.toBeUndefined();
expect(finalizer.finalize).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,14 +1,14 @@
import { ComponentsManager } from 'componentsjs';
import type { App } from '../../../src/init/App';
import { AppRunner } from '../../../src/init/AppRunner';
import type { Initializer } from '../../../src/init/Initializer';
import { joinFilePath } from '../../../src/util/PathUtil';
const initializer: jest.Mocked<Initializer> = {
handleSafe: jest.fn(),
const app: jest.Mocked<App> = {
start: jest.fn(),
} as any;
const manager: jest.Mocked<ComponentsManager<Initializer>> = {
instantiate: jest.fn(async(): Promise<Initializer> => initializer),
const manager: jest.Mocked<ComponentsManager<App>> = {
instantiate: jest.fn(async(): Promise<App> => app),
configRegistry: {
register: jest.fn(),
},
@@ -17,7 +17,7 @@ const manager: jest.Mocked<ComponentsManager<Initializer>> = {
jest.mock('componentsjs', (): any => ({
// eslint-disable-next-line @typescript-eslint/naming-convention
ComponentsManager: {
build: jest.fn(async(): Promise<ComponentsManager<Initializer>> => manager),
build: jest.fn(async(): Promise<ComponentsManager<App>> => manager),
},
}));
@@ -60,7 +60,7 @@ describe('AppRunner', (): void => {
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
expect(manager.instantiate).toHaveBeenCalledTimes(1);
expect(manager.instantiate).toHaveBeenCalledWith(
'urn:solid-server:default:Initializer',
'urn:solid-server:default:App',
{
variables: {
'urn:solid-server:default:variable:port': 3000,
@@ -74,8 +74,8 @@ describe('AppRunner', (): void => {
},
},
);
expect(initializer.handleSafe).toHaveBeenCalledTimes(1);
expect(initializer.handleSafe).toHaveBeenCalledWith();
expect(app.start).toHaveBeenCalledTimes(1);
expect(app.start).toHaveBeenCalledWith();
});
});
@@ -85,7 +85,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -101,7 +101,7 @@ describe('AppRunner', (): void => {
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
expect(manager.instantiate).toHaveBeenCalledTimes(1);
expect(manager.instantiate).toHaveBeenCalledWith(
'urn:solid-server:default:Initializer',
'urn:solid-server:default:App',
{
variables: {
'urn:solid-server:default:variable:port': 3000,
@@ -115,8 +115,8 @@ describe('AppRunner', (): void => {
},
},
);
expect(initializer.handleSafe).toHaveBeenCalledTimes(1);
expect(initializer.handleSafe).toHaveBeenCalledWith();
expect(app.start).toHaveBeenCalledTimes(1);
expect(app.start).toHaveBeenCalledWith();
});
it('accepts abbreviated flags.', async(): Promise<void> => {
@@ -136,7 +136,7 @@ describe('AppRunner', (): void => {
],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -150,7 +150,7 @@ describe('AppRunner', (): void => {
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
expect(manager.instantiate).toHaveBeenCalledWith(
'urn:solid-server:default:Initializer',
'urn:solid-server:default:App',
{
variables: {
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
@@ -183,7 +183,7 @@ describe('AppRunner', (): void => {
],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -197,7 +197,7 @@ describe('AppRunner', (): void => {
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
expect(manager.instantiate).toHaveBeenCalledWith(
'urn:solid-server:default:Initializer',
'urn:solid-server:default:App',
{
variables: {
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
@@ -231,7 +231,7 @@ describe('AppRunner', (): void => {
new AppRunner().runCli();
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -245,7 +245,7 @@ describe('AppRunner', (): void => {
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
expect(manager.instantiate).toHaveBeenCalledWith(
'urn:solid-server:default:Initializer',
'urn:solid-server:default:App',
{
variables: {
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
@@ -269,7 +269,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -285,12 +285,12 @@ describe('AppRunner', (): void => {
});
it('exits without output to stderr when initialization fails.', async(): Promise<void> => {
initializer.handleSafe.mockRejectedValueOnce(new Error('Fatal'));
app.start.mockRejectedValueOnce(new Error('Fatal'));
new AppRunner().runCli({
argv: [ 'node', 'script' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -305,7 +305,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script', '--foo' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -320,7 +320,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script', '-s' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -335,7 +335,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script', 'foo', 'bar', 'foo.txt', 'bar.txt' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});
@@ -350,7 +350,7 @@ describe('AppRunner', (): void => {
argv: [ 'node', 'script', '-l', 'info', '-l', 'debug' ],
});
// Wait until initializer has been called, because we can't await AppRunner.run.
// Wait until app.start has been called, because we can't await AppRunner.run.
await new Promise((resolve): void => {
setImmediate(resolve);
});

View File

@@ -0,0 +1,30 @@
import type { Finalizable } from '../../../../src/init/final/Finalizable';
import { ParallelFinalizer } from '../../../../src/init/final/ParallelFinalizer';
describe('A ParallelFinalizer', (): void => {
let finalizers: Finalizable[];
let finalizer: ParallelFinalizer;
let results: number[];
beforeEach(async(): Promise<void> => {
results = [];
finalizers = [
{ finalize: jest.fn((): any => results.push(0)) },
{ finalize: jest.fn((): any => results.push(1)) },
];
finalizer = new ParallelFinalizer(finalizers);
});
it('is finished when all finalizers are finished.', async(): Promise<void> => {
await expect(finalizer.finalize()).resolves.toBeUndefined();
expect(finalizers[0].finalize).toHaveBeenCalledTimes(1);
expect(finalizers[1].finalize).toHaveBeenCalledTimes(1);
expect(results).toEqual([ 0, 1 ]);
});
it('works if there are no input finalizers.', async(): Promise<void> => {
finalizer = new ParallelFinalizer();
await expect(finalizer.finalize()).resolves.toBeUndefined();
});
});