Repository: solid/community-server (ts)

Issue:
Clear up RuntimeConfig
As a result of https://github.com/solid/community-server/issues/67, `RuntimeConfig` was implemented in https://github.com/solid/community-server/pull/71. While a good interim solution, it creates some annoyances that we should strive to fix.

For instance, `SimpleResourceStore` now gets a `RuntimeConfig` argument. That is quite messy, because now `SimpleResourceStore` needs to know (and depends on) which arguments are passed as config files and which are passed via CLI, which is none of its concern.

What I think we should be doing instead, is pass command-line arguments to the configuration JSON _before_ things get instantiated. Does Components.js somehow allow the definition of constants, which are passed around? If so, the CLI could write such a constants file, which then includes the others.

I guess I simply don't understand (from #67)

> we depend on values from the CLI, which are only available after component instantiation.

It seems to me that `base`, `port`, `rootFilepath` can all be read from the CLI before we instantiate components?

No matter how we solve it, it seems to be that:
- `SimpleResourceStore` should not know about CLI or config params
- Hence, `SimpleResourceStore` should only be instantiated after CLI params have been propagated/merged with config

Pull request:
refactor: Remove RuntimeConfig in favor of config variables, Closes #106
This uses the new [Components.js variables](https://componentsjs.readthedocs.io/en/latest/configuration/configurations/variables/) functionality.

Patch:
diff --git a/config/config-default.json b/config/config-default.json
index 181b016b1..88508589d 100644
--- a/config/config-default.json
+++ b/config/config-default.json
@@ -10,6 +10,7 @@
     "files-scs:config/presets/ldp/request-parser.json",
     "files-scs:config/presets/setup.json",
     "files-scs:config/presets/storage.json",
-    "files-scs:config/presets/storage_wrapper.json"
+    "files-scs:config/presets/storage_wrapper.json",
+    "files-scs:config/presets/cli-params.json"
   ]
 }
diff --git a/config/presets/cli-params.json b/config/presets/cli-params.json
new file mode 100644
index 000000000..4fa65fb78
--- /dev/null
+++ b/config/presets/cli-params.json
@@ -0,0 +1,17 @@
+{
+  "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
+  "@graph": [
+    {
+      "@id": "urn:solid-server:default:variable:port",
+      "@type": "Variable"
+    },
+    {
+      "@id": "urn:solid-server:default:variable:base",
+      "@type": "Variable"
+    },
+    {
+      "@id": "urn:solid-server:default:variable:rootFilePath",
+      "@type": "Variable"
+    }
+  ]
+}
diff --git a/config/presets/setup.json b/config/presets/setup.json
index 0486ee969..c3b6d06e3 100644
--- a/config/presets/setup.json
+++ b/config/presets/setup.json
@@ -13,10 +13,11 @@
       "Setup:_aclManager": {
         "@id": "urn:solid-server:default:AclManager"
       },
-      "Setup:_runtimeConfig": {
-        "@id": "urn:solid-server:default:RuntimeConfig",
-        "@type": "RuntimeConfig",
-        "RuntimeConfigData:_port": 3000
+      "Setup:_base": {
+        "@id": "urn:solid-server:default:variable:base"
+      },
+      "Setup:_port": {
+        "@id": "urn:solid-server:default:variable:port"
       }
     }
   ]
diff --git a/config/presets/storage.json b/config/presets/storage.json
index 3b00ef80e..daad2ca1e 100644
--- a/config/presets/storage.json
+++ b/config/presets/storage.json
@@ -4,8 +4,8 @@
     {
       "@id": "urn:solid-server:default:ResourceStore",
       "@type": "InMemoryResourceStore",
-      "InMemoryResourceStore:_runtimeConfig": {
-        "@id": "urn:solid-server:default:RuntimeConfig"
+      "InMemoryResourceStore:_base": {
+        "@id": "urn:solid-server:default:variable:base"
       }
     }
   ]
diff --git a/config/presets/storage_wrapper.json b/config/presets/storage_wrapper.json
index 0cd962c7a..c6eca7e97 100644
--- a/config/presets/storage_wrapper.json
+++ b/config/presets/storage_wrapper.json
@@ -72,8 +72,8 @@
     {
       "@id": "urn:solid-server:default:UrlContainerManager",
       "@type": "UrlContainerManager",
-      "UrlContainerManager:_runtimeConfig": {
-        "@id": "urn:solid-server:default:RuntimeConfig"
+      "UrlContainerManager:_base": {
+        "@id": "urn:solid-server:default:variable:base"
       }
     }
   ]
diff --git a/index.ts b/index.ts
index d0ceae8a8..247053547 100644
--- a/index.ts
+++ b/index.ts
@@ -12,7 +12,6 @@ export * from './src/authorization/WebAclAuthorizer';
 
 // Init
 export * from './src/init/CliRunner';
-export * from './src/init/RuntimeConfig';
 export * from './src/init/Setup';
 
 // LDP/HTTP
diff --git a/package-lock.json b/package-lock.json
index 12c461288..0f3d31409 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2519,9 +2519,9 @@
       }
     },
     "componentsjs": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmjs.org/componentsjs/-/componentsjs-3.4.2.tgz",
-      "integrity": "sha512-ICaSvvxY/CvWwFt0lLsoRL/DHY09akoI6x9WakQQ9g4GYHVaZumWSAdOrzM/htjGpBumpVh1C/4hk0/ghh9jXA==",
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/componentsjs/-/componentsjs-3.6.0.tgz",
+      "integrity": "sha512-G3lMrIbE7iiZpERoPXnxM0aDopq9q1s1C5aIUrnHW3rcRDa3kcCytc4ASt5aFRjNiwBubivcMfJsvF2ihXg7jQ==",
       "requires": {
         "@types/lodash": "^4.14.56",
         "@types/minimist": "^1.2.0",
diff --git a/package.json b/package.json
index f476c1b7c..38b1ede33 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "prepare": "npm run build",
     "start": "node ./bin/server.js -p 3000",
     "test": "jest",
-    "validate": "componentsjs-compile-config urn:solid-server:default -c config/config-default.json > /dev/null",
+    "validate": "componentsjs-compile-config urn:solid-server:default -c config/config-default.json -f > /dev/null",
     "version": "manual-git-changelog onversion",
     "watch": "nodemon --watch \"src/**/*.js\" --watch \"bin/**/*.js\" --exec npm start"
   },
@@ -76,7 +76,7 @@
     "@types/yargs": "^15.0.5",
     "arrayify-stream": "^1.0.0",
     "async-lock": "^1.2.4",
-    "componentsjs": "^3.4.2",
+    "componentsjs": "^3.6.0",
     "cors": "^2.8.5",
     "express": "^4.17.1",
     "mime-types": "^2.1.27",
diff --git a/src/init/CliRunner.ts b/src/init/CliRunner.ts
index b2c484117..f068cafbf 100644
--- a/src/init/CliRunner.ts
+++ b/src/init/CliRunner.ts
@@ -2,7 +2,6 @@ import * as Path from 'path';
 import { ReadStream, WriteStream } from 'tty';
 import { Loader, LoaderProperties } from 'componentsjs';
 import yargs from 'yargs';
-import { RuntimeConfig } from './RuntimeConfig';
 import { Setup } from './Setup';
 
 /**
@@ -23,12 +22,12 @@ export const runCustom = function(
   const { argv } = yargs
     .usage('node ./bin/server.js [args]')
     .options({
-      port: { type: 'number', alias: 'p' },
+      port: { type: 'number', alias: 'p', default: 3000 },
       config: { type: 'string', alias: 'c' },
     })
     .help();
 
-  new Promise<RuntimeConfig>(async(resolve): Promise<void> => {
+  (async(): Promise<string> => {
     // Load provided or default config file
     const configPath = argv.config ?
       Path.join(process.cwd(), argv.config) :
@@ -38,10 +37,16 @@ export const runCustom = function(
     const loader = new Loader(properties);
     await loader.registerAvailableModuleResources();
     const setup: Setup = await loader
-      .instantiateFromUrl('urn:solid-server:default', configPath);
-    resolve(await setup.setup({ port: argv.port }));
-  }).then((runtimeConfig: RuntimeConfig): void => {
-    stdout.write(`Running at ${runtimeConfig.base}\n`);
+      .instantiateFromUrl('urn:solid-server:default', configPath, undefined, {
+        variables: {
+          'urn:solid-server:default:variable:port': argv.port,
+          'urn:solid-server:default:variable:base': `http://localhost:${argv.port}/`,
+          'urn:solid-server:default:variable:rootFilePath': process.cwd(),
+        },
+      });
+    return await setup.setup();
+  })().then((base: string): void => {
+    stdout.write(`Running at ${base}\n`);
   }).catch((error): void => {
     stderr.write(`${error}\n`);
   });
diff --git a/src/init/RuntimeConfig.ts b/src/init/RuntimeConfig.ts
deleted file mode 100644
index 517a0f36c..000000000
--- a/src/init/RuntimeConfig.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { ensureTrailingSlash } from '../util/Util';
-
-/**
- * This class holds all configuration options that can be defined by the user via the command line.
- *
- * Concretely, this contains data that is only relevant *after* dependency injection.
- */
-export class RuntimeConfig implements RuntimeConfigData {
-  private pport!: number;
-  private pbase!: string;
-  private prootFilepath!: string;
-
-  public constructor(data: RuntimeConfigData = {}) {
-    this.reset(data);
-  }
-
-  public reset(data: RuntimeConfigData): void {
-    this.pport = data.port ?? 3000;
-    this.pbase = ensureTrailingSlash(data.base ?? `http://localhost:${this.port}/`);
-    this.prootFilepath = data.rootFilepath ?? process.cwd();
-  }
-
-  /**
-   * The returned URL is ensured to have a trailing slash.
-   */
-  public get base(): string {
-    return this.pbase;
-  }
-
-  public get port(): number {
-    return this.pport;
-  }
-
-  public get rootFilepath(): string {
-    return this.prootFilepath;
-  }
-}
-
-export interface RuntimeConfigData {
-  port?: number;
-  base?: string;
-  rootFilepath?: string;
-}
diff --git a/src/init/Setup.ts b/src/init/Setup.ts
index e54676749..782e49431 100644
--- a/src/init/Setup.ts
+++ b/src/init/Setup.ts
@@ -3,7 +3,6 @@ import { AclManager } from '../authorization/AclManager';
 import { ExpressHttpServer } from '../server/ExpressHttpServer';
 import { ResourceStore } from '../storage/ResourceStore';
 import { TEXT_TURTLE } from '../util/ContentTypes';
-import { RuntimeConfig, RuntimeConfigData } from './RuntimeConfig';
 
 /**
  * Invokes all logic to setup a server.
@@ -12,27 +11,27 @@ export class Setup {
   private readonly httpServer: ExpressHttpServer;
   private readonly store: ResourceStore;
   private readonly aclManager: AclManager;
-  private readonly runtimeConfig: RuntimeConfig;
+  private readonly base: string;
+  private readonly port: number;
 
   public constructor(
     httpServer: ExpressHttpServer,
     store: ResourceStore,
     aclManager: AclManager,
-    runtimeConfig: RuntimeConfig,
+    base: string,
+    port: number,
   ) {
     this.httpServer = httpServer;
     this.store = store;
     this.aclManager = aclManager;
-    this.runtimeConfig = runtimeConfig;
+    this.base = base;
+    this.port = port;
   }
 
   /**
-   * Set up a server at the given port and base URL.
-   * @param data - Runtime config data.
+   * Set up a server.
    */
-  public async setup(data: RuntimeConfigData = {}): Promise<RuntimeConfig> {
-    this.runtimeConfig.reset(data);
-
+  public async setup(): Promise<string> {
     // Set up acl so everything can still be done by default
     // Note that this will need to be adapted to go through all the correct channels later on
     const aclSetup = async(): Promise<void> => {
@@ -47,10 +46,10 @@ export class Setup {
     acl:mode        acl:Append;
     acl:mode        acl:Delete;
     acl:mode        acl:Control;
-    acl:accessTo    <${this.runtimeConfig.base}>;
-    acl:default     <${this.runtimeConfig.base}>.`;
+    acl:accessTo    <${this.base}>;
+    acl:default     <${this.base}>.`;
       await this.store.setRepresentation(
-        await this.aclManager.getAcl({ path: this.runtimeConfig.base }),
+        await this.aclManager.getAcl({ path: this.base }),
         {
           binary: true,
           data: streamifyArray([ acl ]),
@@ -64,8 +63,8 @@ export class Setup {
     };
     await aclSetup();
 
-    this.httpServer.listen(this.runtimeConfig.port);
+    this.httpServer.listen(this.port);
 
-    return this.runtimeConfig;
+    return this.base;
   }
 }
diff --git a/src/storage/ExtensionBasedMapper.ts b/src/storage/ExtensionBasedMapper.ts
index b0b29edba..6d8634d93 100644
--- a/src/storage/ExtensionBasedMapper.ts
+++ b/src/storage/ExtensionBasedMapper.ts
@@ -1,6 +1,5 @@
 import { posix } from 'path';
 import { types } from 'mime-types';
-import { RuntimeConfig } from '../init/RuntimeConfig';
 import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
 import { APPLICATION_OCTET_STREAM, TEXT_TURTLE } from '../util/ContentTypes';
 import { ConflictHttpError } from '../util/errors/ConflictHttpError';
@@ -24,22 +23,22 @@ export interface ResourcePath {
 }
 
 export class ExtensionBasedMapper implements FileIdentifierMapper {
-  private readonly runtimeConfig: RuntimeConfig;
+  private readonly base: string;
+  private readonly prootFilepath: string;
   private readonly types: Record<string, any>;
 
-  public constructor(runtimeConfig: RuntimeConfig, overrideTypes = { acl: TEXT_TURTLE, metadata: TEXT_TURTLE }) {
-    this.runtimeConfig = runtimeConfig;
+  public constructor(base: string, rootFilepath: string, overrideTypes = { acl: TEXT_TURTLE, metadata: TEXT_TURTLE }) {
+    this.base = base;
+    this.prootFilepath = rootFilepath;
     this.types = { ...types, ...overrideTypes };
   }
 
-  // Using getters because the values of runtimeConfig get filled in at runtime (so they are still empty at
-  // construction time until issue #106 gets resolved.)
   public get baseRequestURI(): string {
-    return trimTrailingSlashes(this.runtimeConfig.base);
+    return trimTrailingSlashes(this.base);
   }
 
   public get rootFilepath(): string {
-    return trimTrailingSlashes(this.runtimeConfig.rootFilepath);
+    return trimTrailingSlashes(this.prootFilepath);
   }
 
   /**
diff --git a/src/storage/InMemoryResourceStore.ts b/src/storage/InMemoryResourceStore.ts
index 650080cea..80b749b25 100644
--- a/src/storage/InMemoryResourceStore.ts
+++ b/src/storage/InMemoryResourceStore.ts
@@ -1,7 +1,6 @@
 import { PassThrough } from 'stream';
 import arrayifyStream from 'arrayify-stream';
 import streamifyArray from 'streamify-array';
-import { RuntimeConfig } from '../init/RuntimeConfig';
 import { Representation } from '../ldp/representation/Representation';
 import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
 import { TEXT_TURTLE } from '../util/ContentTypes';
@@ -15,15 +14,15 @@ import { ResourceStore } from './ResourceStore';
  */
 export class InMemoryResourceStore implements ResourceStore {
   private readonly store: { [id: string]: Representation };
-  private readonly runtimeConfig: RuntimeConfig;
+  private readonly base: string;
   private index = 0;
 
   /**
-   * @param runtimeConfig - Config containing base that will be stripped of all incoming URIs
-   *                        and added to all outgoing ones to find the relative path.
+   * @param base - Base that will be stripped of all incoming URIs
+   *               and added to all outgoing ones to find the relative path.
    */
-  public constructor(runtimeConfig: RuntimeConfig) {
-    this.runtimeConfig = runtimeConfig;
+  public constructor(base: string) {
+    this.base = ensureTrailingSlash(base);
 
     this.store = {
       // Default root entry (what you get when the identifier is equal to the base)
@@ -105,8 +104,8 @@ export class InMemoryResourceStore implements ResourceStore {
    * @returns A string representing the relative path.
    */
   private parseIdentifier(identifier: ResourceIdentifier): string {
-    const path = identifier.path.slice(this.runtimeConfig.base.length);
-    if (!identifier.path.startsWith(this.runtimeConfig.base)) {
+    const path = identifier.path.slice(this.base.length);
+    if (!identifier.path.startsWith(this.base)) {
       throw new NotFoundHttpError();
     }
     return path;
diff --git a/src/storage/UrlContainerManager.ts b/src/storage/UrlContainerManager.ts
index a364b8004..ca74c384d 100644
--- a/src/storage/UrlContainerManager.ts
+++ b/src/storage/UrlContainerManager.ts
@@ -1,4 +1,3 @@
-import { RuntimeConfig } from '../init/RuntimeConfig';
 import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
 import { ensureTrailingSlash } from '../util/Util';
 import { ContainerManager } from './ContainerManager';
@@ -7,15 +6,15 @@ import { ContainerManager } from './ContainerManager';
  * Determines containers based on URL decomposition.
  */
 export class UrlContainerManager implements ContainerManager {
-  private readonly runtimeConfig: RuntimeConfig;
+  private readonly base: string;
 
-  public constructor(runtimeConfig: RuntimeConfig) {
-    this.runtimeConfig = runtimeConfig;
+  public constructor(base: string) {
+    this.base = base;
   }
 
   public async getContainer(id: ResourceIdentifier): Promise<ResourceIdentifier> {
     const path = this.canonicalUrl(id.path);
-    if (this.runtimeConfig.base === path) {
+    if (this.base === path) {
       throw new Error('Root does not have a container.');
     }
 


Test patch:
diff --git a/test/configs/AuthenticatedFileResourceStoreConfig.ts b/test/configs/AuthenticatedFileResourceStoreConfig.ts
index 84bdf8f20..ce0f3c087 100644
--- a/test/configs/AuthenticatedFileResourceStoreConfig.ts
+++ b/test/configs/AuthenticatedFileResourceStoreConfig.ts
@@ -8,7 +8,6 @@ import {
   ResourceStore,
   UnsecureWebIdExtractor,
   QuadToRdfConverter,
-  RuntimeConfig,
 } from '../../index';
 import { ServerConfig } from './ServerConfig';
 import {
@@ -27,13 +26,13 @@ import {
  */
 
 export class AuthenticatedFileResourceStoreConfig implements ServerConfig {
-  private readonly runtimeConfig: RuntimeConfig;
+  public base: string;
   public store: ResourceStore;
 
-  public constructor(runtimeConfig: RuntimeConfig) {
-    this.runtimeConfig = runtimeConfig;
+  public constructor(base: string, rootFilepath: string) {
+    this.base = base;
     this.store = getConvertingStore(
-      getFileResourceStore(runtimeConfig),
+      getFileResourceStore(base, rootFilepath),
       [ new QuadToRdfConverter(),
         new RdfToQuadConverter() ],
     );
@@ -50,7 +49,7 @@ export class AuthenticatedFileResourceStoreConfig implements ServerConfig {
     const operationHandler = getOperationHandler(this.store);
 
     const responseWriter = new BasicResponseWriter();
-    const authorizer = getWebAclAuthorizer(this.store, this.runtimeConfig.base);
+    const authorizer = getWebAclAuthorizer(this.store, this.base);
 
     const handler = new AuthenticatedLdpHandler({
       requestParser,
diff --git a/test/configs/FileResourceStoreConfig.ts b/test/configs/FileResourceStoreConfig.ts
index 6b6eeb0af..a00dca829 100644
--- a/test/configs/FileResourceStoreConfig.ts
+++ b/test/configs/FileResourceStoreConfig.ts
@@ -9,7 +9,6 @@ import {
   RawBodyParser,
   RdfToQuadConverter,
   ResourceStore,
-  RuntimeConfig,
   UnsecureWebIdExtractor,
 } from '../../index';
 import { ServerConfig } from './ServerConfig';
@@ -25,9 +24,9 @@ import { getFileResourceStore, getOperationHandler, getConvertingStore, getBasic
 export class FileResourceStoreConfig implements ServerConfig {
   public store: ResourceStore;
 
-  public constructor(runtimeConfig: RuntimeConfig) {
+  public constructor(base: string, rootFilepath: string) {
     this.store = getConvertingStore(
-      getFileResourceStore(runtimeConfig),
+      getFileResourceStore(base, rootFilepath),
       [ new QuadToRdfConverter(), new RdfToQuadConverter() ],
     );
   }
diff --git a/test/configs/Util.ts b/test/configs/Util.ts
index 428fd74bf..bc716b67a 100644
--- a/test/configs/Util.ts
+++ b/test/configs/Util.ts
@@ -23,7 +23,6 @@ import {
   RepresentationConvertingStore,
   ResourceStore,
   ResponseDescription,
-  RuntimeConfig,
   SingleThreadedResourceLocker,
   SparqlUpdatePatchHandler,
   UrlBasedAclManager,
@@ -32,26 +31,24 @@ import {
 } from '../../index';
 import { ExtensionBasedMapper } from '../../src/storage/ExtensionBasedMapper';
 
-const BASE = 'http://test.com';
+export const BASE = 'http://test.com';
 
 /**
  * Creates a RuntimeConfig with its rootFilePath set based on the given subfolder.
  * @param subfolder - Folder to use in the global testData folder.
  */
-export const getRuntimeConfig = (subfolder: string): RuntimeConfig => new RuntimeConfig({
-  base: BASE,
-  rootFilepath: join(__dirname, '../testData', subfolder),
-});
+export const getRootFilePath = (subfolder: string): string => join(__dirname, '../testData', subfolder);
 
 /**
  * Gives a file resource store based on (default) runtime config.
- * @param runtimeConfig - Optional runtime config.
+ * @param base - Base URL.
+ * @param rootFilepath - The root file path.
  *
  * @returns The file resource store.
  */
-export const getFileResourceStore = (runtimeConfig: RuntimeConfig): FileResourceStore =>
+export const getFileResourceStore = (base: string, rootFilepath: string): FileResourceStore =>
   new FileResourceStore(
-    new ExtensionBasedMapper(runtimeConfig),
+    new ExtensionBasedMapper(base, rootFilepath),
     new InteractionController(),
     new MetadataController(),
   );
@@ -63,7 +60,7 @@ export const getFileResourceStore = (runtimeConfig: RuntimeConfig): FileResource
  * @returns The in memory resource store.
  */
 export const getInMemoryResourceStore = (base = BASE): InMemoryResourceStore =>
-  new InMemoryResourceStore(new RuntimeConfig({ base }));
+  new InMemoryResourceStore(base);
 
 /**
  * Gives a converting store given some converters.
@@ -138,6 +135,6 @@ export const getBasicRequestParser = (bodyParsers: BodyParser[] = []): BasicRequ
  */
 export const getWebAclAuthorizer =
 (store: ResourceStore, base = BASE, aclManager = new UrlBasedAclManager()): WebAclAuthorizer => {
-  const containerManager = new UrlContainerManager(new RuntimeConfig({ base }));
+  const containerManager = new UrlContainerManager(base);
   return new WebAclAuthorizer(aclManager, containerManager, store);
 };
diff --git a/test/integration/AuthenticatedFileResourceStore.test.ts b/test/integration/AuthenticatedFileResourceStore.test.ts
index 647eb5095..1b0f2db42 100644
--- a/test/integration/AuthenticatedFileResourceStore.test.ts
+++ b/test/integration/AuthenticatedFileResourceStore.test.ts
@@ -1,9 +1,10 @@
 import { copyFileSync, mkdirSync } from 'fs';
 import { join } from 'path';
 import * as rimraf from 'rimraf';
-import { HttpHandler, ResourceStore, RuntimeConfig } from '../../index';
+import { HttpHandler, ResourceStore } from '../../index';
+import { ensureTrailingSlash } from '../../src/util/Util';
 import { AuthenticatedFileResourceStoreConfig } from '../configs/AuthenticatedFileResourceStoreConfig';
-import { getRuntimeConfig } from '../configs/Util';
+import { BASE, getRootFilePath } from '../configs/Util';
 import { AclTestHelper, FileTestHelper } from '../util/TestHelpers';
 
 describe('A server using a AuthenticatedFileResourceStore', (): void => {
@@ -12,24 +13,23 @@ describe('A server using a AuthenticatedFileResourceStore', (): void => {
   let store: ResourceStore;
   let aclHelper: AclTestHelper;
   let fileHelper: FileTestHelper;
-  let runtimeConfig: RuntimeConfig;
+  let rootFilePath: string;
 
   beforeAll(async(): Promise<void> => {
-    runtimeConfig = getRuntimeConfig('AuthenticatedFileResourceStore');
-    config = new AuthenticatedFileResourceStoreConfig(runtimeConfig);
-    const { base, rootFilepath } = runtimeConfig;
+    rootFilePath = getRootFilePath('AuthenticatedFileResourceStore');
+    config = new AuthenticatedFileResourceStoreConfig(BASE, rootFilePath);
     handler = config.getHttpHandler();
     ({ store } = config);
-    aclHelper = new AclTestHelper(store, base);
-    fileHelper = new FileTestHelper(handler, new URL('http://test.com/'));
+    aclHelper = new AclTestHelper(store, ensureTrailingSlash(BASE));
+    fileHelper = new FileTestHelper(handler, new URL(ensureTrailingSlash(BASE)));
 
     // Make sure the root directory exists
-    mkdirSync(rootFilepath, { recursive: true });
-    copyFileSync(join(__dirname, '../assets/permanent.txt'), `${rootFilepath}/permanent.txt`);
+    mkdirSync(rootFilePath, { recursive: true });
+    copyFileSync(join(__dirname, '../assets/permanent.txt'), `${rootFilePath}/permanent.txt`);
   });
 
   afterAll(async(): Promise<void> => {
-    rimraf.sync(runtimeConfig.rootFilepath, { glob: false });
+    rimraf.sync(rootFilePath, { glob: false });
   });
 
   describe('with acl', (): void => {
diff --git a/test/integration/FileResourceStore.test.ts b/test/integration/FileResourceStore.test.ts
index 594a4b1bc..3b3adce9e 100644
--- a/test/integration/FileResourceStore.test.ts
+++ b/test/integration/FileResourceStore.test.ts
@@ -1,26 +1,25 @@
 import * as rimraf from 'rimraf';
-import { RuntimeConfig } from '../../src/init/RuntimeConfig';
 import { HttpHandler } from '../../src/server/HttpHandler';
 import { FileResourceStoreConfig } from '../configs/FileResourceStoreConfig';
-import { getRuntimeConfig } from '../configs/Util';
+import { BASE, getRootFilePath } from '../configs/Util';
 import { FileTestHelper } from '../util/TestHelpers';
 
 describe('A server using a FileResourceStore', (): void => {
   describe('without acl', (): void => {
+    let rootFilePath: string;
     let config: FileResourceStoreConfig;
     let handler: HttpHandler;
     let fileHelper: FileTestHelper;
-    let runtimeConfig: RuntimeConfig;
 
     beforeAll(async(): Promise<void> => {
-      runtimeConfig = getRuntimeConfig('FileResourceStore');
-      config = new FileResourceStoreConfig(runtimeConfig);
+      rootFilePath = getRootFilePath('FileResourceStore');
+      config = new FileResourceStoreConfig(BASE, rootFilePath);
       handler = config.getHttpHandler();
-      fileHelper = new FileTestHelper(handler, new URL(runtimeConfig.base));
+      fileHelper = new FileTestHelper(handler, new URL(BASE));
     });
 
     afterAll(async(): Promise<void> => {
-      rimraf.sync(runtimeConfig.rootFilepath, { glob: false });
+      rimraf.sync(rootFilePath, { glob: false });
     });
 
     it('can add a file to the store, read it and delete it.', async():
diff --git a/test/unit/init/RuntimeConfig.test.ts b/test/unit/init/RuntimeConfig.test.ts
deleted file mode 100644
index 23a65ab44..000000000
--- a/test/unit/init/RuntimeConfig.test.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
-
-describe('RuntimeConfig', (): void => {
-  it('handles undefined args.', async(): Promise<void> => {
-    const config = new RuntimeConfig();
-    expect(config.port).toEqual(3000);
-    expect(config.base).toEqual('http://localhost:3000/');
-  });
-
-  it('handles empty args.', async(): Promise<void> => {
-    const config = new RuntimeConfig({});
-    expect(config.port).toEqual(3000);
-    expect(config.base).toEqual('http://localhost:3000/');
-  });
-
-  it('handles args with port.', async(): Promise<void> => {
-    const config = new RuntimeConfig({ port: 1234 });
-    expect(config.port).toEqual(1234);
-    expect(config.base).toEqual('http://localhost:1234/');
-  });
-
-  it('handles args with base.', async(): Promise<void> => {
-    const config = new RuntimeConfig({ base: 'http://example.org/' });
-    expect(config.port).toEqual(3000);
-    expect(config.base).toEqual('http://example.org/');
-  });
-
-  it('handles args with port and base.', async(): Promise<void> => {
-    const config = new RuntimeConfig({ port: 1234, base: 'http://example.org/' });
-    expect(config.port).toEqual(1234);
-    expect(config.base).toEqual('http://example.org/');
-  });
-
-  it('handles resetting data.', async(): Promise<void> => {
-    const config = new RuntimeConfig({});
-    expect(config.port).toEqual(3000);
-    expect(config.base).toEqual('http://localhost:3000/');
-
-    config.reset({ port: 1234, base: 'http://example.org/' });
-    expect(config.port).toEqual(1234);
-    expect(config.base).toEqual('http://example.org/');
-  });
-
-  it('ensures trailing slash in base.', async(): Promise<void> => {
-    const config = new RuntimeConfig({ base: 'http://example.org' });
-    expect(config.base).toEqual('http://example.org/');
-  });
-});
diff --git a/test/unit/init/Setup.test.ts b/test/unit/init/Setup.test.ts
index 1a9311c8e..e5b9335db 100644
--- a/test/unit/init/Setup.test.ts
+++ b/test/unit/init/Setup.test.ts
@@ -1,4 +1,3 @@
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
 import { Setup } from '../../../src/init/Setup';
 
 describe('Setup', (): void => {
@@ -16,7 +15,7 @@ describe('Setup', (): void => {
     httpServer = {
       listen: jest.fn(),
     };
-    setup = new Setup(httpServer, store, aclManager, new RuntimeConfig());
+    setup = new Setup(httpServer, store, aclManager, 'http://localhost:3000/', 3000);
   });
 
   it('starts an HTTP server.', async(): Promise<void> => {
diff --git a/test/unit/storage/ExtensionBasedMapper.test.ts b/test/unit/storage/ExtensionBasedMapper.test.ts
index a00ce2d28..a86e33596 100644
--- a/test/unit/storage/ExtensionBasedMapper.test.ts
+++ b/test/unit/storage/ExtensionBasedMapper.test.ts
@@ -1,10 +1,9 @@
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
 import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper';
 
 describe('An ExtensionBasedMapper', (): void => {
   const base = 'http://test.com/';
   const rootFilepath = 'uploads/';
-  const resourceMapper = new ExtensionBasedMapper(new RuntimeConfig({ base, rootFilepath }));
+  const resourceMapper = new ExtensionBasedMapper(base, rootFilepath);
 
   it('returns the correct url of a file.', async(): Promise<void> => {
     let result = resourceMapper.mapFilePathToUrl(`${rootFilepath}test.txt`);
diff --git a/test/unit/storage/FileResourceStore.test.ts b/test/unit/storage/FileResourceStore.test.ts
index 0463cf61f..ca8e7c6ca 100644
--- a/test/unit/storage/FileResourceStore.test.ts
+++ b/test/unit/storage/FileResourceStore.test.ts
@@ -5,7 +5,6 @@ import { literal, namedNode, quad as quadRDF, triple } from '@rdfjs/data-model';
 import arrayifyStream from 'arrayify-stream';
 import { DataFactory } from 'n3';
 import streamifyArray from 'streamify-array';
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
 import { Representation } from '../../../src/ldp/representation/Representation';
 import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
 import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper';
@@ -55,7 +54,7 @@ describe('A FileResourceStore', (): void => {
     jest.clearAllMocks();
 
     store = new FileResourceStore(
-      new ExtensionBasedMapper(new RuntimeConfig({ base, rootFilepath })),
+      new ExtensionBasedMapper(base, rootFilepath),
       new InteractionController(),
       new MetadataController(),
     );
diff --git a/test/unit/storage/InMemoryResourceStore.test.ts b/test/unit/storage/InMemoryResourceStore.test.ts
index dd5b79044..66583ed27 100644
--- a/test/unit/storage/InMemoryResourceStore.test.ts
+++ b/test/unit/storage/InMemoryResourceStore.test.ts
@@ -1,6 +1,5 @@
 import { Readable } from 'stream';
 import streamifyArray from 'streamify-array';
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
 import { Representation } from '../../../src/ldp/representation/Representation';
 import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
 import { InMemoryResourceStore } from '../../../src/storage/InMemoryResourceStore';
@@ -15,7 +14,7 @@ describe('A InMemoryResourceStore', (): void => {
   const dataString = '<http://test.com/s> <http://test.com/p> <http://test.com/o>.';
 
   beforeEach(async(): Promise<void> => {
-    store = new InMemoryResourceStore(new RuntimeConfig({ base }));
+    store = new InMemoryResourceStore(base);
 
     representation = {
       binary: true,
diff --git a/test/unit/storage/UrlContainerManager.test.ts b/test/unit/storage/UrlContainerManager.test.ts
index 4df65bf8f..2e8e27adf 100644
--- a/test/unit/storage/UrlContainerManager.test.ts
+++ b/test/unit/storage/UrlContainerManager.test.ts
@@ -1,9 +1,8 @@
-import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
 import { UrlContainerManager } from '../../../src/storage/UrlContainerManager';
 
 describe('An UrlContainerManager', (): void => {
   it('returns the parent URl for a single call.', async(): Promise<void> => {
-    const manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' }));
+    const manager = new UrlContainerManager('http://test.com/foo/');
     await expect(manager.getContainer({ path: 'http://test.com/foo/bar' }))
       .resolves.toEqual({ path: 'http://test.com/foo/' });
     await expect(manager.getContainer({ path: 'http://test.com/foo/bar/' }))
@@ -11,13 +10,13 @@ describe('An UrlContainerManager', (): void => {
   });
 
   it('errors when getting the container of root.', async(): Promise<void> => {
-    let manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' }));
+    let manager = new UrlContainerManager('http://test.com/foo/');
     await expect(manager.getContainer({ path: 'http://test.com/foo/' }))
       .rejects.toThrow('Root does not have a container.');
     await expect(manager.getContainer({ path: 'http://test.com/foo' }))
       .rejects.toThrow('Root does not have a container.');
 
-    manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo' }));
+    manager = new UrlContainerManager('http://test.com/foo/');
     await expect(manager.getContainer({ path: 'http://test.com/foo/' }))
       .rejects.toThrow('Root does not have a container.');
     await expect(manager.getContainer({ path: 'http://test.com/foo' }))
@@ -25,7 +24,7 @@ describe('An UrlContainerManager', (): void => {
   });
 
   it('errors when the root of an URl is reached that does not match the input root.', async(): Promise<void> => {
-    const manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' }));
+    const manager = new UrlContainerManager('http://test.com/foo/');
     await expect(manager.getContainer({ path: 'http://test.com/' }))
       .rejects.toThrow('URL root reached.');
   });