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
|
# App
|
||||||
Options related to the server initialization.
|
Options related to the server startup.
|
||||||
This is the entry point to the main server setup.
|
|
||||||
|
|
||||||
## 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.
|
Contains a list of initializer that need to be run when starting the server.
|
||||||
For example, when acl authorization is used,
|
For example, when acl authorization is used,
|
||||||
an initializer will be added that makes sure there is an acl file in the root.
|
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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"import": [
|
||||||
"files-scs:config/init/handler/initializers/logger.json",
|
"files-scs:config/app/init/initializers/logger.json",
|
||||||
"files-scs:config/init/handler/initializers/server.json"
|
"files-scs:config/app/init/initializers/server.json"
|
||||||
],
|
],
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"import": [
|
||||||
"files-scs:config/init/handler/base/init.json",
|
"files-scs:config/app/init/base/init.json",
|
||||||
"files-scs:config/init/handler/initializers/root-container.json"
|
"files-scs:config/app/init/initializers/root-container.json"
|
||||||
],
|
],
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/dynamic.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
|
|
||||||
@ -8,7 +10,6 @@
|
|||||||
"files-scs:config/identity/email/default.json",
|
"files-scs:config/identity/email/default.json",
|
||||||
"files-scs:config/identity/handler/default.json",
|
"files-scs:config/identity/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
"@id": "urn:solid-server:default:ExpiringIdpStorage",
|
"@id": "urn:solid-server:default:ExpiringIdpStorage",
|
||||||
"@type": "WrappedExpiringStorage",
|
"@type": "WrappedExpiringStorage",
|
||||||
"source": { "@id": "urn:solid-server:default:IdpStorage" }
|
"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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
"suffixes_write": "write"
|
"suffixes_write": "write"
|
||||||
},
|
},
|
||||||
"expiration": 3000
|
"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/IdentityProviderFactory';
|
||||||
export * from './identity/IdentityProviderHttpHandler';
|
export * from './identity/IdentityProviderHttpHandler';
|
||||||
|
|
||||||
|
// Init/Final
|
||||||
|
export * from './init/final/Finalizable';
|
||||||
|
export * from './init/final/ParallelFinalizer';
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
export * from './init/AclInitializer';
|
export * from './init/AclInitializer';
|
||||||
|
export * from './init/App';
|
||||||
export * from './init/AppRunner';
|
export * from './init/AppRunner';
|
||||||
export * from './init/ConfigPodInitializer';
|
export * from './init/ConfigPodInitializer';
|
||||||
export * from './init/Initializer';
|
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 yargs from 'yargs';
|
||||||
import { getLoggerFor } from '../logging/LogUtil';
|
import { getLoggerFor } from '../logging/LogUtil';
|
||||||
import { absoluteFilePath, ensureTrailingSlash, joinFilePath } from '../util/PathUtil';
|
import { absoluteFilePath, ensureTrailingSlash, joinFilePath } from '../util/PathUtil';
|
||||||
import type { Initializer } from './Initializer';
|
import type { App } from './App';
|
||||||
|
|
||||||
export class AppRunner {
|
export class AppRunner {
|
||||||
private readonly logger = getLoggerFor(this);
|
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.
|
* Generic run function for starting the server from JavaScript for a given config.
|
||||||
* @param loaderProperties - Components.js loader properties.
|
* @param loaderProperties - Components.js loader properties.
|
||||||
@ -18,13 +32,12 @@ export class AppRunner {
|
|||||||
* @param variableParams - Variables to pass into the config file.
|
* @param variableParams - Variables to pass into the config file.
|
||||||
*/
|
*/
|
||||||
public async run(
|
public async run(
|
||||||
loaderProperties: IComponentsManagerBuilderOptions<Initializer>,
|
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
||||||
configFile: string,
|
configFile: string,
|
||||||
variableParams: ConfigVariables,
|
variableParams: ConfigVariables,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const variables = this.createVariables(variableParams);
|
const app = await this.getApp(loaderProperties, configFile, variableParams);
|
||||||
const initializer = await this.createInitializer(loaderProperties, configFile, variables);
|
await app.start();
|
||||||
await initializer.handleSafe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,18 +87,17 @@ export class AppRunner {
|
|||||||
.help();
|
.help();
|
||||||
|
|
||||||
// Gather settings for instantiating the server
|
// Gather settings for instantiating the server
|
||||||
const loaderProperties: IComponentsManagerBuilderOptions<Initializer> = {
|
const loaderProperties: IComponentsManagerBuilderOptions<App> = {
|
||||||
mainModulePath: this.resolveFilePath(params.mainModulePath),
|
mainModulePath: this.resolveFilePath(params.mainModulePath),
|
||||||
dumpErrorState: true,
|
dumpErrorState: true,
|
||||||
logLevel: params.loggingLevel as LogLevel,
|
logLevel: params.loggingLevel as LogLevel,
|
||||||
};
|
};
|
||||||
const configFile = this.resolveFilePath(params.config, 'config/default.json');
|
const configFile = this.resolveFilePath(params.config, 'config/default.json');
|
||||||
const variables = this.createVariables(params);
|
|
||||||
|
|
||||||
// Create and execute the server initializer
|
// Create and execute the server initializer
|
||||||
this.createInitializer(loaderProperties, configFile, variables)
|
this.getApp(loaderProperties, configFile, params)
|
||||||
.then(
|
.then(
|
||||||
async(initializer): Promise<void> => initializer.handleSafe(),
|
async(app): Promise<void> => app.start(),
|
||||||
(error: Error): void => {
|
(error: Error): void => {
|
||||||
// Instantiation of components has failed, so there is no logger to use
|
// Instantiation of components has failed, so there is no logger to use
|
||||||
stderr.write(`Error: could not instantiate server from ${configFile}\n`);
|
stderr.write(`Error: could not instantiate server from ${configFile}\n`);
|
||||||
@ -93,7 +105,7 @@ export class AppRunner {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
},
|
},
|
||||||
).catch((error): void => {
|
).catch((error): void => {
|
||||||
this.logger.error(`Could not initialize server: ${error}`, { error });
|
this.logger.error(`Could not start server: ${error}`, { error });
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -131,14 +143,14 @@ export class AppRunner {
|
|||||||
/**
|
/**
|
||||||
* Creates the server initializer
|
* Creates the server initializer
|
||||||
*/
|
*/
|
||||||
protected async createInitializer(
|
protected async createApp(
|
||||||
componentsProperties: IComponentsManagerBuilderOptions<Initializer>,
|
componentsProperties: IComponentsManagerBuilderOptions<App>,
|
||||||
configFile: string,
|
configFile: string,
|
||||||
variables: Record<string, any>,
|
variables: Record<string, any>,
|
||||||
): Promise<Initializer> {
|
): Promise<App> {
|
||||||
const componentsManager = await ComponentsManager.build(componentsProperties);
|
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);
|
await componentsManager.configRegistry.register(configFile);
|
||||||
return await componentsManager.instantiate(initializer, { variables });
|
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 * as rimraf from 'rimraf';
|
||||||
import { joinFilePath } from '../../src/util/PathUtil';
|
import { joinFilePath } from '../../src/util/PathUtil';
|
||||||
|
|
||||||
export const BASE = 'http://test.com';
|
|
||||||
let cachedModuleState: IModuleState;
|
let cachedModuleState: IModuleState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,3 +41,13 @@ export function getTestFolder(name: string): string {
|
|||||||
export function removeFolder(folder: string): void {
|
export function removeFolder(folder: string): void {
|
||||||
rimraf.sync(folder, { glob: false });
|
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 { mkdirSync } from 'fs';
|
||||||
import type { Server } from 'http';
|
|
||||||
import { stringify } from 'querystring';
|
import { stringify } from 'querystring';
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import type { Initializer } from '../../src/init/Initializer';
|
import type { App } from '../../src/init/App';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
|
|
||||||
import { joinFilePath } from '../../src/util/PathUtil';
|
import { joinFilePath } from '../../src/util/PathUtil';
|
||||||
import { getPort } from '../util/Util';
|
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 port = getPort('DynamicPods');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
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
|
// 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
|
// 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 => {
|
describe.each(configs)('A dynamic pod server with template config %s', (template, { teardown }): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
let expiringStorage: WrappedExpiringStorage<any, any>;
|
|
||||||
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', template, createPod: true };
|
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', template, createPod: true };
|
||||||
const podUrl = `${baseUrl}${settings.podName}/`;
|
const podUrl = `${baseUrl}${settings.podName}/`;
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const variables: Record<string, any> = {
|
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:rootFilePath': rootFilePath,
|
||||||
'urn:solid-server:default:variable:podConfigJson': podConfigJson,
|
'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
|
// Need to make sure the temp folder exists so the podConfigJson can be written to it
|
||||||
mkdirSync(rootFilePath, { recursive: true });
|
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(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
getTestConfigPath('server-dynamic-unsafe.json'),
|
getTestConfigPath('server-dynamic-unsafe.json'),
|
||||||
variables,
|
variables,
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer, expiringStorage } = instances);
|
({ app } = instances);
|
||||||
|
|
||||||
// Set up the internal store
|
await app.start();
|
||||||
await initializer.handleSafe();
|
|
||||||
|
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
expiringStorage.finalize();
|
|
||||||
await new Promise((resolve, reject): void => {
|
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
await teardown();
|
await teardown();
|
||||||
|
await app.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a pod with the given config.', async(): Promise<void> => {
|
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 { stringify } from 'querystring';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import { load } from 'cheerio';
|
import { load } from 'cheerio';
|
||||||
import type { Response } from 'cross-fetch';
|
import type { Response } from 'cross-fetch';
|
||||||
import { fetch } from 'cross-fetch';
|
import { fetch } from 'cross-fetch';
|
||||||
import urljoin from 'url-join';
|
import urljoin from 'url-join';
|
||||||
import type { Initializer } from '../../src/init/Initializer';
|
import type { App } from '../../src/init/App';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
|
|
||||||
import { APPLICATION_X_WWW_FORM_URLENCODED } from '../../src/util/ContentTypes';
|
import { APPLICATION_X_WWW_FORM_URLENCODED } from '../../src/util/ContentTypes';
|
||||||
import { joinFilePath } from '../../src/util/PathUtil';
|
|
||||||
import { getPort } from '../util/Util';
|
import { getPort } from '../util/Util';
|
||||||
import { getTestConfigPath, instantiateFromConfig } from './Config';
|
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
|
||||||
import { IdentityTestState } from './IdentityTestState';
|
import { IdentityTestState } from './IdentityTestState';
|
||||||
|
|
||||||
const port = getPort('Identity');
|
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.
|
// This is why the redirects are handled manually.
|
||||||
// We also need to parse the HTML in several steps since there is no API.
|
// We also need to parse the HTML in several steps since there is no API.
|
||||||
describe('A Solid server with IDP', (): void => {
|
describe('A Solid server with IDP', (): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let expiringStorage: WrappedExpiringStorage<any, any>;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
const redirectUrl = 'http://mockedredirect/';
|
const redirectUrl = 'http://mockedredirect/';
|
||||||
const oidcIssuer = baseUrl;
|
const oidcIssuer = baseUrl;
|
||||||
const card = urljoin(baseUrl, 'profile/card');
|
const card = urljoin(baseUrl, 'profile/card');
|
||||||
@ -61,15 +54,10 @@ describe('A Solid server with IDP', (): void => {
|
|||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
getTestConfigPath('server-memory.json'),
|
getTestConfigPath('server-memory.json'),
|
||||||
{
|
getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
|
|
||||||
},
|
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer, expiringStorage } = instances);
|
({ app } = instances);
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
|
|
||||||
// Create a simple webId
|
// Create a simple webId
|
||||||
const turtle = `<${webId}> <http://www.w3.org/ns/solid/terms#oidcIssuer> <${baseUrl}> .`;
|
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> => {
|
afterAll(async(): Promise<void> => {
|
||||||
expiringStorage.finalize();
|
await app.stop();
|
||||||
await new Promise<void>((resolve, reject): void => {
|
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('doing registration', (): void => {
|
describe('doing registration', (): void => {
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import type { Server } from 'http';
|
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import type { Initializer, ResourceStore } from '../../src/';
|
import type { ResourceStore, App } from '../../src/';
|
||||||
import { BasicRepresentation } from '../../src/';
|
import { BasicRepresentation } from '../../src/';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import { AclHelper } from '../util/AclHelper';
|
import { AclHelper } from '../util/AclHelper';
|
||||||
import { deleteResource, getResource, postResource, putResource } from '../util/FetchUtil';
|
import { deleteResource, getResource, postResource, putResource } from '../util/FetchUtil';
|
||||||
import { getPort } from '../util/Util';
|
import { getPort } from '../util/Util';
|
||||||
import {
|
import {
|
||||||
|
getDefaultVariables,
|
||||||
getPresetConfigPath,
|
getPresetConfigPath,
|
||||||
getTestConfigPath,
|
getTestConfigPath,
|
||||||
getTestFolder,
|
getTestFolder,
|
||||||
@ -30,21 +29,18 @@ const stores: [string, any][] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig, teardown }): void => {
|
describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig, teardown }): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
let store: ResourceStore;
|
let store: ResourceStore;
|
||||||
let aclHelper: AclHelper;
|
let aclHelper: AclHelper;
|
||||||
const permanent = `${baseUrl}document.txt`;
|
const permanent = `${baseUrl}document.txt`;
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const variables: Record<string, any> = {
|
const variables = {
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
...getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
|
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and initialize the server
|
// Create and start the server
|
||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
[
|
[
|
||||||
@ -53,10 +49,9 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
|
|||||||
],
|
],
|
||||||
variables,
|
variables,
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer, store } = instances);
|
({ app, store } = instances);
|
||||||
|
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
|
|
||||||
// Create test helper for manipulating acl
|
// Create test helper for manipulating acl
|
||||||
aclHelper = new AclHelper(store);
|
aclHelper = new AclHelper(store);
|
||||||
@ -75,9 +70,7 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
|
|||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
await teardown();
|
await teardown();
|
||||||
await new Promise((resolve, reject): void => {
|
await app.stop();
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add a document, read it and delete it if allowed.', async(): Promise<void> => {
|
it('can add a document, read it and delete it if allowed.', async(): Promise<void> => {
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import { createReadStream } from 'fs';
|
import { createReadStream } from 'fs';
|
||||||
import type { Server } from 'http';
|
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import { DataFactory, Parser } from 'n3';
|
import { DataFactory, Parser } from 'n3';
|
||||||
import { joinFilePath, PIM, RDF } from '../../src/';
|
import { joinFilePath, PIM, RDF } from '../../src/';
|
||||||
import type { Initializer } from '../../src/';
|
import type { App } from '../../src/';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import { LDP } from '../../src/util/Vocabularies';
|
import { LDP } from '../../src/util/Vocabularies';
|
||||||
import { deleteResource, expectQuads, getResource, patchResource, postResource, putResource } from '../util/FetchUtil';
|
import { deleteResource, expectQuads, getResource, patchResource, postResource, putResource } from '../util/FetchUtil';
|
||||||
import { getPort } from '../util/Util';
|
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 { literal, namedNode, quad } = DataFactory;
|
||||||
|
|
||||||
const port = getPort('LpdHandlerWithoutAuth');
|
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 => {
|
describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeConfig, teardown }): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const variables: Record<string, any> = {
|
const variables = {
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
...getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
|
'urn:solid-server:default:variable:rootFilePath': rootFilePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and initialize the server
|
// Create and start the server
|
||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
[
|
[
|
||||||
@ -47,17 +48,14 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
],
|
],
|
||||||
variables,
|
variables,
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer } = instances);
|
({ app } = instances);
|
||||||
|
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
await teardown();
|
await teardown();
|
||||||
await new Promise((resolve, reject): void => {
|
await app.stop();
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can read a container listing.', async(): Promise<void> => {
|
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 { SingleThreadedResourceLocker } from '../../src/util/locking/SingleThreadedResourceLocker';
|
||||||
import { WrappedExpiringReadWriteLocker } from '../../src/util/locking/WrappedExpiringReadWriteLocker';
|
import { WrappedExpiringReadWriteLocker } from '../../src/util/locking/WrappedExpiringReadWriteLocker';
|
||||||
import { guardedStreamFrom } from '../../src/util/StreamUtil';
|
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');
|
jest.useFakeTimers('legacy');
|
||||||
|
|
||||||
describe('A LockingResourceStore', (): void => {
|
describe('A LockingResourceStore', (): void => {
|
||||||
@ -44,7 +41,7 @@ describe('A LockingResourceStore', (): void => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Initialize store
|
// Initialize store
|
||||||
const initializer = new RootContainerInitializer({ store: source, baseUrl: BASE });
|
const initializer = new RootContainerInitializer({ store: source, baseUrl: base });
|
||||||
await initializer.handleSafe();
|
await initializer.handleSafe();
|
||||||
|
|
||||||
locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker());
|
locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker());
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import type { Server } from 'http';
|
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import type { RedisResourceLocker } from '../../src';
|
import type { App, RedisResourceLocker } from '../../src';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import { describeIf, getPort } from '../util/Util';
|
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
|
* 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 => {
|
describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', (): void => {
|
||||||
const port = getPort('RedisResourceLocker');
|
const port = getPort('RedisResourceLocker');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
const baseUrl = `http://localhost:${port}/`;
|
||||||
let server: Server;
|
let app: App;
|
||||||
let locker: RedisResourceLocker;
|
let locker: RedisResourceLocker;
|
||||||
let factory: HttpServerFactory;
|
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
getTestConfigPath('run-with-redlock.json'),
|
getTestConfigPath('run-with-redlock.json'),
|
||||||
{
|
getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
},
|
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, locker } = instances);
|
({ app, locker } = instances);
|
||||||
server = factory.startServer(port);
|
await app.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
await locker.finalize();
|
await app.stop();
|
||||||
await new Promise<void>((resolve, reject): void => {
|
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add a file to the store, read it and delete it.', async(): Promise<void> => {
|
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 fetch from 'cross-fetch';
|
||||||
import type { Initializer } from '../../src/init/Initializer';
|
import type { App } from '../../src/init/App';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
|
|
||||||
import { getPort } from '../util/Util';
|
import { getPort } from '../util/Util';
|
||||||
import { getTestConfigPath, instantiateFromConfig } from './Config';
|
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
|
||||||
|
|
||||||
const port = getPort('ServerFetch');
|
const port = getPort('ServerFetch');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
const baseUrl = `http://localhost:${port}/`;
|
||||||
|
|
||||||
// Some tests with real Requests/Responses until the mocking library has been removed from the tests
|
// Some tests with real Requests/Responses until the mocking library has been removed from the tests
|
||||||
describe('A Solid server', (): void => {
|
describe('A Solid server', (): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let expiringStorage: WrappedExpiringStorage<any, any>;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
getTestConfigPath('server-memory.json'),
|
getTestConfigPath('server-memory.json'),
|
||||||
{
|
getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
'urn:solid-server:default:variable:idpTemplateFolder': '',
|
|
||||||
},
|
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer, expiringStorage } = instances);
|
({ app } = instances);
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
expiringStorage.finalize();
|
await app.stop();
|
||||||
await new Promise<void>((resolve, reject): void => {
|
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can do a successful HEAD request to a container.', async(): Promise<void> => {
|
it('can do a successful HEAD request to a container.', async(): Promise<void> => {
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import type { Server } from 'http';
|
|
||||||
import { joinFilePath } from '../../src/';
|
import { joinFilePath } from '../../src/';
|
||||||
import type { Initializer } from '../../src/';
|
import type { App } from '../../src/';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import { putResource } from '../util/FetchUtil';
|
import { putResource } from '../util/FetchUtil';
|
||||||
import { describeIf, getPort } from '../util/Util';
|
import { describeIf, getPort } from '../util/Util';
|
||||||
import { getPresetConfigPath, getTestConfigPath, instantiateFromConfig } from './Config';
|
import { getDefaultVariables, getPresetConfigPath, getTestConfigPath, instantiateFromConfig } from './Config';
|
||||||
|
|
||||||
const port = getPort('SparqlStorage');
|
const port = getPort('SparqlStorage');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
const baseUrl = `http://localhost:${port}/`;
|
||||||
|
|
||||||
describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
|
describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const variables: Record<string, any> = {
|
const variables = {
|
||||||
'urn:solid-server:default:variable:baseUrl': baseUrl,
|
...getDefaultVariables(port, baseUrl),
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
|
||||||
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
|
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and initialize the server
|
// Create and start the server
|
||||||
const instances = await instantiateFromConfig(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
[
|
[
|
||||||
@ -31,16 +26,13 @@ describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
|
|||||||
],
|
],
|
||||||
variables,
|
variables,
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer } = instances);
|
({ app } = instances);
|
||||||
|
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
await new Promise((resolve, reject): void => {
|
await app.stop();
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add a Turtle file to the store.', async(): Promise<void> => {
|
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 { stringify } from 'querystring';
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import type { Initializer } from '../../src/init/Initializer';
|
import type { App } from '../../src/init/App';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
|
||||||
import type { WrappedExpiringStorage } from '../../src/storage/keyvalue/WrappedExpiringStorage';
|
|
||||||
import { joinFilePath } from '../../src/util/PathUtil';
|
|
||||||
import { getPort } from '../util/Util';
|
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 port = getPort('Subdomains');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
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
|
// Simulating subdomains using the forwarded header so no DNS changes are required
|
||||||
describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardown }): void => {
|
describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardown }): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
let initializer: Initializer;
|
|
||||||
let factory: HttpServerFactory;
|
|
||||||
let expiringStorage: WrappedExpiringStorage<any, any>;
|
|
||||||
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', createPod: true };
|
const settings = { podName: 'alice', webId: 'http://test.com/#alice', email: 'alice@test.email', createPod: true };
|
||||||
const podHost = `alice.localhost:${port}`;
|
const podHost = `alice.localhost:${port}`;
|
||||||
const podUrl = `http://${podHost}/`;
|
const podUrl = `http://${podHost}/`;
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const variables: Record<string, any> = {
|
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: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(
|
const instances = await instantiateFromConfig(
|
||||||
'urn:solid-server:test:Instances',
|
'urn:solid-server:test:Instances',
|
||||||
[
|
[
|
||||||
@ -50,18 +48,14 @@ describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardo
|
|||||||
],
|
],
|
||||||
variables,
|
variables,
|
||||||
) as Record<string, any>;
|
) as Record<string, any>;
|
||||||
({ factory, initializer, expiringStorage } = instances);
|
({ app } = instances);
|
||||||
|
|
||||||
await initializer.handleSafe();
|
await app.start();
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
expiringStorage.finalize();
|
|
||||||
await new Promise((resolve, reject): void => {
|
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
await teardown();
|
await teardown();
|
||||||
|
await app.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('handling resources', (): void => {
|
describe('handling resources', (): void => {
|
||||||
|
@ -1,37 +1,31 @@
|
|||||||
import type { Server } from 'http';
|
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
|
import type { App } from '../../src/init/App';
|
||||||
import { getPort } from '../util/Util';
|
import { getPort } from '../util/Util';
|
||||||
import { getTestConfigPath, instantiateFromConfig } from './Config';
|
import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config';
|
||||||
|
|
||||||
const port = getPort('WebSocketsProtocol');
|
const port = getPort('WebSocketsProtocol');
|
||||||
const serverUrl = `http://localhost:${port}/`;
|
const serverUrl = `http://localhost:${port}/`;
|
||||||
const headers = { forwarded: 'host=example.pod;proto=https' };
|
const headers = { forwarded: 'host=example.pod;proto=https' };
|
||||||
|
|
||||||
describe('A server with the Solid WebSockets API behind a proxy', (): void => {
|
describe('A server with the Solid WebSockets API behind a proxy', (): void => {
|
||||||
let server: Server;
|
let app: App;
|
||||||
|
|
||||||
beforeAll(async(): Promise<void> => {
|
beforeAll(async(): Promise<void> => {
|
||||||
const factory = await instantiateFromConfig(
|
app = await instantiateFromConfig(
|
||||||
'urn:solid-server:default:ServerFactory',
|
'urn:solid-server:default:App',
|
||||||
getTestConfigPath('server-without-auth.json'),
|
getTestConfigPath('server-without-auth.json'),
|
||||||
{
|
getDefaultVariables(port, 'https://example.pod/'),
|
||||||
'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
|
) as App;
|
||||||
'urn:solid-server:default:variable:showStackTrace': true,
|
await app.start();
|
||||||
},
|
|
||||||
) as HttpServerFactory;
|
|
||||||
server = factory.startServer(port);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async(): Promise<void> => {
|
afterAll(async(): Promise<void> => {
|
||||||
await new Promise<void>((resolve, reject): void => {
|
await app.stop();
|
||||||
server.close((error): void => error ? reject(error) : resolve());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a 404 if no data was initialized.', async(): Promise<void> => {
|
it('returns a 404 if there is no data.', async(): Promise<void> => {
|
||||||
const response = await fetch(serverUrl, { headers });
|
const response = await fetch(`${serverUrl}foo`, { headers });
|
||||||
expect(response.status).toBe(404);
|
expect(response.status).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/simple.json",
|
||||||
"files-scs:config/http/middleware/no-websockets.json",
|
"files-scs:config/http/middleware/no-websockets.json",
|
||||||
"files-scs:config/http/server-factory/no-websockets.json",
|
"files-scs:config/http/server-factory/no-websockets.json",
|
||||||
"files-scs:config/http/static/default.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/authentication/debug-auth-header.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -28,17 +29,12 @@
|
|||||||
"@type": "RecordObject",
|
"@type": "RecordObject",
|
||||||
"record": [
|
"record": [
|
||||||
{
|
{
|
||||||
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
|
"RecordObject:_record_key": "app",
|
||||||
"RecordObject:_record_key": "initializer",
|
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
|
||||||
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"RecordObject:_record_key": "store",
|
"RecordObject:_record_key": "store",
|
||||||
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ResourceStore" }
|
"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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/simple.json",
|
||||||
"files-scs:config/http/middleware/no-websockets.json",
|
"files-scs:config/http/middleware/no-websockets.json",
|
||||||
"files-scs:config/http/server-factory/no-websockets.json",
|
"files-scs:config/http/server-factory/no-websockets.json",
|
||||||
"files-scs:config/http/static/default.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/authentication/debug-auth-header.json",
|
||||||
"files-scs:config/ldp/authorization/allow-everything.json",
|
"files-scs:config/ldp/authorization/allow-everything.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -29,17 +30,12 @@
|
|||||||
"@type": "RecordObject",
|
"@type": "RecordObject",
|
||||||
"record": [
|
"record": [
|
||||||
{
|
{
|
||||||
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
|
"RecordObject:_record_key": "app",
|
||||||
"RecordObject:_record_key": "initializer",
|
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
|
||||||
"RecordObject:_record_value": { "@id": "urn:solid-server:default:ParallelInitializer" }
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"RecordObject:_record_key": "locker",
|
"RecordObject:_record_key": "locker",
|
||||||
"RecordObject:_record_value": { "@id": "urn:solid-server:default:RedisResourceLocker" }
|
"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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/no-websockets.json",
|
"files-scs:config/http/middleware/no-websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/unsafe-no-check.json",
|
"files-scs:config/identity/ownership/unsafe-no-check.json",
|
||||||
"files-scs:config/identity/pod/dynamic.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/authentication/debug-auth-header.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -33,24 +34,10 @@
|
|||||||
"@type": "RecordObject",
|
"@type": "RecordObject",
|
||||||
"record": [
|
"record": [
|
||||||
{
|
{
|
||||||
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
|
"RecordObject:_record_key": "app",
|
||||||
"RecordObject:_record_key": "initializer",
|
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
|
||||||
"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" }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"@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",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/token.json",
|
"files-scs:config/identity/ownership/token.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -31,18 +32,8 @@
|
|||||||
"@type": "RecordObject",
|
"@type": "RecordObject",
|
||||||
"RecordObject:_record": [
|
"RecordObject:_record": [
|
||||||
{
|
{
|
||||||
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
|
"RecordObject:_record_key": "app",
|
||||||
"RecordObject:_record_key": "initializer",
|
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
|
||||||
"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" }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/no-websockets.json",
|
"files-scs:config/http/middleware/no-websockets.json",
|
||||||
"files-scs:config/http/server-factory/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/handler/default.json",
|
||||||
"files-scs:config/identity/ownership/unsafe-no-check.json",
|
"files-scs:config/identity/ownership/unsafe-no-check.json",
|
||||||
"files-scs:config/identity/pod/static.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/authentication/debug-auth-header.json",
|
||||||
"files-scs:config/ldp/authorization/webacl.json",
|
"files-scs:config/ldp/authorization/webacl.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -32,18 +33,8 @@
|
|||||||
"@type": "RecordObject",
|
"@type": "RecordObject",
|
||||||
"record": [
|
"record": [
|
||||||
{
|
{
|
||||||
"comment": "Only use the parallel initializer. Starting the server is done in the test code..",
|
"RecordObject:_record_key": "app",
|
||||||
"RecordObject:_record_key": "initializer",
|
"RecordObject:_record_value": { "@id": "urn:solid-server:default:App" }
|
||||||
"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" }
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld",
|
||||||
"import": [
|
"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/handler/default.json",
|
||||||
"files-scs:config/http/middleware/websockets.json",
|
"files-scs:config/http/middleware/websockets.json",
|
||||||
"files-scs:config/http/server-factory/websockets.json",
|
"files-scs:config/http/server-factory/websockets.json",
|
||||||
"files-scs:config/http/static/default.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/authentication/dpop-bearer.json",
|
||||||
"files-scs:config/ldp/authorization/allow-everything.json",
|
"files-scs:config/ldp/authorization/allow-everything.json",
|
||||||
"files-scs:config/ldp/handler/default.json",
|
"files-scs:config/ldp/handler/default.json",
|
||||||
@ -23,22 +24,6 @@
|
|||||||
"files-scs:config/util/variables/default.json"
|
"files-scs:config/util/variables/default.json"
|
||||||
],
|
],
|
||||||
"@graph": [
|
"@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",
|
"@id": "urn:solid-server:default:IdentityProviderHandler",
|
||||||
"@type": "UnsupportedAsyncHandler"
|
"@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 { ComponentsManager } from 'componentsjs';
|
||||||
|
import type { App } from '../../../src/init/App';
|
||||||
import { AppRunner } from '../../../src/init/AppRunner';
|
import { AppRunner } from '../../../src/init/AppRunner';
|
||||||
import type { Initializer } from '../../../src/init/Initializer';
|
|
||||||
import { joinFilePath } from '../../../src/util/PathUtil';
|
import { joinFilePath } from '../../../src/util/PathUtil';
|
||||||
|
|
||||||
const initializer: jest.Mocked<Initializer> = {
|
const app: jest.Mocked<App> = {
|
||||||
handleSafe: jest.fn(),
|
start: jest.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const manager: jest.Mocked<ComponentsManager<Initializer>> = {
|
const manager: jest.Mocked<ComponentsManager<App>> = {
|
||||||
instantiate: jest.fn(async(): Promise<Initializer> => initializer),
|
instantiate: jest.fn(async(): Promise<App> => app),
|
||||||
configRegistry: {
|
configRegistry: {
|
||||||
register: jest.fn(),
|
register: jest.fn(),
|
||||||
},
|
},
|
||||||
@ -17,7 +17,7 @@ const manager: jest.Mocked<ComponentsManager<Initializer>> = {
|
|||||||
jest.mock('componentsjs', (): any => ({
|
jest.mock('componentsjs', (): any => ({
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
ComponentsManager: {
|
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'));
|
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
||||||
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.instantiate).toHaveBeenCalledWith(
|
expect(manager.instantiate).toHaveBeenCalledWith(
|
||||||
'urn:solid-server:default:Initializer',
|
'urn:solid-server:default:App',
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
'urn:solid-server:default:variable:port': 3000,
|
'urn:solid-server:default:variable:port': 3000,
|
||||||
@ -74,8 +74,8 @@ describe('AppRunner', (): void => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(initializer.handleSafe).toHaveBeenCalledTimes(1);
|
expect(app.start).toHaveBeenCalledTimes(1);
|
||||||
expect(initializer.handleSafe).toHaveBeenCalledWith();
|
expect(app.start).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -101,7 +101,7 @@ describe('AppRunner', (): void => {
|
|||||||
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
||||||
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.instantiate).toHaveBeenCalledWith(
|
expect(manager.instantiate).toHaveBeenCalledWith(
|
||||||
'urn:solid-server:default:Initializer',
|
'urn:solid-server:default:App',
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
'urn:solid-server:default:variable:port': 3000,
|
'urn:solid-server:default:variable:port': 3000,
|
||||||
@ -115,8 +115,8 @@ describe('AppRunner', (): void => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
expect(initializer.handleSafe).toHaveBeenCalledTimes(1);
|
expect(app.start).toHaveBeenCalledTimes(1);
|
||||||
expect(initializer.handleSafe).toHaveBeenCalledWith();
|
expect(app.start).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accepts abbreviated flags.', async(): Promise<void> => {
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -150,7 +150,7 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
||||||
expect(manager.instantiate).toHaveBeenCalledWith(
|
expect(manager.instantiate).toHaveBeenCalledWith(
|
||||||
'urn:solid-server:default:Initializer',
|
'urn:solid-server:default:App',
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
|
'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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -197,7 +197,7 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
||||||
expect(manager.instantiate).toHaveBeenCalledWith(
|
expect(manager.instantiate).toHaveBeenCalledWith(
|
||||||
'urn:solid-server:default:Initializer',
|
'urn:solid-server:default:App',
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
|
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
|
||||||
@ -231,7 +231,7 @@ describe('AppRunner', (): void => {
|
|||||||
|
|
||||||
new AppRunner().runCli();
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -245,7 +245,7 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
expect(manager.configRegistry.register).toHaveBeenCalledWith('/var/cwd/myconfig.json');
|
||||||
expect(manager.instantiate).toHaveBeenCalledWith(
|
expect(manager.instantiate).toHaveBeenCalledWith(
|
||||||
'urn:solid-server:default:Initializer',
|
'urn:solid-server:default:App',
|
||||||
{
|
{
|
||||||
variables: {
|
variables: {
|
||||||
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
|
'urn:solid-server:default:variable:baseUrl': 'http://pod.example/',
|
||||||
@ -269,7 +269,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -285,12 +285,12 @@ describe('AppRunner', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('exits without output to stderr when initialization fails.', async(): Promise<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({
|
new AppRunner().runCli({
|
||||||
argv: [ 'node', 'script' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -305,7 +305,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script', '--foo' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -320,7 +320,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script', '-s' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -335,7 +335,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script', 'foo', 'bar', 'foo.txt', 'bar.txt' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
setImmediate(resolve);
|
||||||
});
|
});
|
||||||
@ -350,7 +350,7 @@ describe('AppRunner', (): void => {
|
|||||||
argv: [ 'node', 'script', '-l', 'info', '-l', 'debug' ],
|
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 => {
|
await new Promise((resolve): void => {
|
||||||
setImmediate(resolve);
|
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