mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add App class to start and stop the server
This commit is contained in:
parent
29ddf57341
commit
e8a0f63e02
@ -1,8 +1,12 @@
|
||||
# Init
|
||||
Options related to the server initialization.
|
||||
This is the entry point to the main server setup.
|
||||
# App
|
||||
Options related to the server startup.
|
||||
|
||||
## Handler
|
||||
## Base
|
||||
This is the entry point to the main server setup.
|
||||
* *default*: The main application. This should only be changed/replaced
|
||||
if you want to start from a different kind of class.
|
||||
|
||||
## Init
|
||||
Contains a list of initializer that need to be run when starting the server.
|
||||
For example, when acl authorization is used,
|
||||
an initializer will be added that makes sure there is an acl file in the root.
|
19
config/app/app/default.json
Normal file
19
config/app/app/default.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||
"@graph": [
|
||||
{
|
||||
"comment": "This is the entry point to the application. It can be used to both start and stop the server.",
|
||||
"@id": "urn:solid-server:default:App",
|
||||
"@type": "App",
|
||||
"initializer": { "@id": "urn:solid-server:default:Initializer" },
|
||||
"finalizer": {
|
||||
"comment": "This is going to contain the list of finalizers that need to be called. These should be added in the configs where such classes are configured.",
|
||||
"@id": "urn:solid-server:default:Finalizer",
|
||||
"@type": "ParallelFinalizer",
|
||||
"finalizers": [
|
||||
{ "@id": "urn:solid-server:default:ServerInitializer" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||
"import": [
|
||||
"files-scs:config/init/handler/initializers/logger.json",
|
||||
"files-scs:config/init/handler/initializers/server.json"
|
||||
"files-scs:config/app/init/initializers/logger.json",
|
||||
"files-scs:config/app/init/initializers/server.json"
|
||||
],
|
||||
"@graph": [
|
||||
{
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||
"import": [
|
||||
"files-scs:config/init/handler/base/init.json",
|
||||
"files-scs:config/init/handler/initializers/root-container.json"
|
||||
"files-scs:config/app/init/base/init.json",
|
||||
"files-scs:config/app/init/initializers/root-container.json"
|
||||
],
|
||||
"@graph": [
|
||||
{
|
@ -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",
|
||||
@ -9,7 +11,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",
|
||||
|
@ -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",
|
||||
@ -9,7 +11,6 @@
|
||||
"files-scs:config/identity/handler/default.json",
|
||||
"files-scs:config/identity/ownership/token.json",
|
||||
"files-scs:config/identity/pod/dynamic.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",
|
||||
|
@ -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",
|
||||
|
||||
@ -8,7 +10,6 @@
|
||||
"files-scs:config/identity/email/default.json",
|
||||
"files-scs:config/identity/handler/default.json",
|
||||
"files-scs:config/identity/ownership/token.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",
|
||||
|
@ -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",
|
||||
@ -9,7 +11,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",
|
||||
|
@ -14,6 +14,11 @@
|
||||
"@id": "urn:solid-server:default:ExpiringIdpStorage",
|
||||
"@type": "WrappedExpiringStorage",
|
||||
"source": { "@id": "urn:solid-server:default:IdpStorage" }
|
||||
},
|
||||
{
|
||||
"comment": "Makes sure the expiring storage cleanup timer is stopped when the application needs to stop.",
|
||||
"@id": "urn:solid-server:default:Finalizer",
|
||||
"ParallelFinalizer:_finalizers": [ { "@id": "urn:solid-server:default:ExpiringIdpStorage" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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",
|
||||
@ -9,7 +11,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",
|
||||
|
@ -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",
|
||||
@ -9,7 +11,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",
|
||||
|
@ -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",
|
||||
@ -9,7 +11,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",
|
||||
|
@ -18,6 +18,11 @@
|
||||
"suffixes_write": "write"
|
||||
},
|
||||
"expiration": 3000
|
||||
},
|
||||
{
|
||||
"comment": "Makes sure the redis connection is closed when the application needs to stop.",
|
||||
"@id": "urn:solid-server:default:Finalizer",
|
||||
"ParallelFinalizer:_finalizers": [ { "@id": "urn:solid-server:default:RedisResourceLocker" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -66,8 +66,13 @@ export * from './identity/storage/WrappedFetchAdapterFactory';
|
||||
export * from './identity/IdentityProviderFactory';
|
||||
export * from './identity/IdentityProviderHttpHandler';
|
||||
|
||||
// Init/Final
|
||||
export * from './init/final/Finalizable';
|
||||
export * from './init/final/ParallelFinalizer';
|
||||
|
||||
// Init
|
||||
export * from './init/AclInitializer';
|
||||
export * from './init/App';
|
||||
export * from './init/AppRunner';
|
||||
export * from './init/ConfigPodInitializer';
|
||||
export * from './init/Initializer';
|
||||
|
29
src/init/App.ts
Normal file
29
src/init/App.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import type { Finalizable } from './final/Finalizable';
|
||||
import type { Initializer } from './Initializer';
|
||||
|
||||
/**
|
||||
* Entry point for the entire Solid server.
|
||||
*/
|
||||
export class App {
|
||||
private readonly initializer: Initializer;
|
||||
private readonly finalizer: Finalizable;
|
||||
|
||||
public constructor(initializer: Initializer, finalizer: Finalizable) {
|
||||
this.initializer = initializer;
|
||||
this.finalizer = finalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and starts the application.
|
||||
*/
|
||||
public async start(): Promise<void> {
|
||||
await this.initializer.handleSafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the application and handles cleanup.
|
||||
*/
|
||||
public async stop(): Promise<void> {
|
||||
await this.finalizer.finalize();
|
||||
}
|
||||
}
|
@ -6,11 +6,25 @@ import { ComponentsManager } from 'componentsjs';
|
||||
import yargs from 'yargs';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { absoluteFilePath, ensureTrailingSlash, joinFilePath } from '../util/PathUtil';
|
||||
import type { Initializer } from './Initializer';
|
||||
|
||||
import type { App } from './App';
|
||||
export class AppRunner {
|
||||
private readonly logger = getLoggerFor(this);
|
||||
|
||||
/**
|
||||
* Generic function for getting the app object to start the server from JavaScript for a given config.
|
||||
* @param loaderProperties - Components.js loader properties.
|
||||
* @param configFile - Path to the server config file.
|
||||
* @param variableParams - Variables to pass into the config file.
|
||||
*/
|
||||
public async getApp(
|
||||
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
||||
configFile: string,
|
||||
variableParams: ConfigVariables,
|
||||
): Promise<App> {
|
||||
const variables = this.createVariables(variableParams);
|
||||
return this.createApp(loaderProperties, configFile, variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic run function for starting the server from JavaScript for a given config.
|
||||
* @param loaderProperties - Components.js loader properties.
|
||||
@ -18,13 +32,12 @@ export class AppRunner {
|
||||
* @param variableParams - Variables to pass into the config file.
|
||||
*/
|
||||
public async run(
|
||||
loaderProperties: IComponentsManagerBuilderOptions<Initializer>,
|
||||
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
||||
configFile: string,
|
||||
variableParams: ConfigVariables,
|
||||
): Promise<void> {
|
||||
const variables = this.createVariables(variableParams);
|
||||
const initializer = await this.createInitializer(loaderProperties, configFile, variables);
|
||||
await initializer.handleSafe();
|
||||
const app = await this.getApp(loaderProperties, configFile, variableParams);
|
||||
await app.start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,18 +87,17 @@ export class AppRunner {
|
||||
.help();
|
||||
|
||||
// Gather settings for instantiating the server
|
||||
const loaderProperties: IComponentsManagerBuilderOptions<Initializer> = {
|
||||
const loaderProperties: IComponentsManagerBuilderOptions<App> = {
|
||||
mainModulePath: this.resolveFilePath(params.mainModulePath),
|
||||
dumpErrorState: true,
|
||||
logLevel: params.loggingLevel as LogLevel,
|
||||
};
|
||||
const configFile = this.resolveFilePath(params.config, 'config/default.json');
|
||||
const variables = this.createVariables(params);
|
||||
|
||||
// Create and execute the server initializer
|
||||
this.createInitializer(loaderProperties, configFile, variables)
|
||||
this.getApp(loaderProperties, configFile, params)
|
||||
.then(
|
||||
async(initializer): Promise<void> => initializer.handleSafe(),
|
||||
async(app): Promise<void> => app.start(),
|
||||
(error: Error): void => {
|
||||
// Instantiation of components has failed, so there is no logger to use
|
||||
stderr.write(`Error: could not instantiate server from ${configFile}\n`);
|
||||
@ -93,7 +105,7 @@ export class AppRunner {
|
||||
process.exit(1);
|
||||
},
|
||||
).catch((error): void => {
|
||||
this.logger.error(`Could not initialize server: ${error}`, { error });
|
||||
this.logger.error(`Could not start server: ${error}`, { error });
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@ -131,14 +143,14 @@ export class AppRunner {
|
||||
/**
|
||||
* Creates the server initializer
|
||||
*/
|
||||
protected async createInitializer(
|
||||
componentsProperties: IComponentsManagerBuilderOptions<Initializer>,
|
||||
protected async createApp(
|
||||
componentsProperties: IComponentsManagerBuilderOptions<App>,
|
||||
configFile: string,
|
||||
variables: Record<string, any>,
|
||||
): Promise<Initializer> {
|
||||
): Promise<App> {
|
||||
const componentsManager = await ComponentsManager.build(componentsProperties);
|
||||
|
||||
const initializer = 'urn:solid-server:default:Initializer';
|
||||
const initializer = 'urn:solid-server:default:App';
|
||||
await componentsManager.configRegistry.register(configFile);
|
||||
return await componentsManager.instantiate(initializer, { variables });
|
||||
}
|
||||
|
16
src/init/final/ParallelFinalizer.ts
Normal file
16
src/init/final/ParallelFinalizer.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import type { Finalizable } from './Finalizable';
|
||||
|
||||
/**
|
||||
* Finalizes all the injected Finalizable classes in parallel.
|
||||
*/
|
||||
export class ParallelFinalizer implements Finalizable {
|
||||
private readonly finalizers: Finalizable[];
|
||||
|
||||
public constructor(finalizers: Finalizable[] = []) {
|
||||
this.finalizers = finalizers;
|
||||
}
|
||||
|
||||
public async finalize(): Promise<void> {
|
||||
await Promise.all(this.finalizers.map(async(finalizer): Promise<void> => finalizer.finalize()));
|
||||
}
|
||||
}
|
@ -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'),
|
||||
};
|
||||
}
|
||||
|
@ -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> => {
|
||||
|
@ -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 => {
|
||||
|
@ -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> => {
|
||||
|
@ -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> => {
|
||||
|
@ -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());
|
||||
|
@ -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> => {
|
||||
|
@ -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> => {
|
||||
|
@ -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> => {
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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"
|
||||
|
25
test/unit/init/App.test.ts
Normal file
25
test/unit/init/App.test.ts
Normal 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);
|
||||
});
|
||||
});
|
@ -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);
|
||||
});
|
||||
|
30
test/unit/init/final/ParallelFinalizer.test.ts
Normal file
30
test/unit/init/final/ParallelFinalizer.test.ts
Normal 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();
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user