Repository: solid/community-server
Pull request #437 (2020-12-20 14:47:36)

Use Components.js to set up integration tests
Closes https://github.com/solid/community-server/issues/305


Patch:
diff --git a/config/config-default.json b/config/config-default.json
index dd507f89b..bd4f4fbc9 100644
--- a/config/config-default.json
+++ b/config/config-default.json
@@ -17,7 +17,6 @@
     "files-scs:config/presets/pod-management.json",
     "files-scs:config/presets/representation-conversion.json",
     "files-scs:config/presets/storage/backend/storage-memory.json",
-    "files-scs:config/presets/storage/routing/no-routing.json",
     "files-scs:config/presets/storage-wrapper.json",
     "files-scs:config/presets/cli-params.json"
   ],
@@ -25,6 +24,7 @@
     {
       "comment": "Use a memory based store as backend with no additional routing.",
       "@id": "urn:solid-server:default:RoutingResourceStore",
+      "@type": "PassthroughStore",
       "PassthroughStore:_source": {
         "@id": "urn:solid-server:default:MemoryResourceStore"
       }
diff --git a/config/config-file.json b/config/config-file.json
index e0cec9af0..74945ce22 100644
--- a/config/config-file.json
+++ b/config/config-file.json
@@ -17,7 +17,6 @@
     "files-scs:config/presets/pod-management.json",
     "files-scs:config/presets/representation-conversion.json",
     "files-scs:config/presets/storage/backend/storage-filesystem.json",
-    "files-scs:config/presets/storage/routing/no-routing.json",
     "files-scs:config/presets/storage-wrapper.json",
     "files-scs:config/presets/cli-params.json"
   ],
@@ -25,6 +24,7 @@
     {
       "comment": "Use a file based store as backend with no additional routing.",
       "@id": "urn:solid-server:default:RoutingResourceStore",
+      "@type": "PassthroughStore",
       "PassthroughStore:_source": {
         "@id": "urn:solid-server:default:FileResourceStore"
       }
diff --git a/config/config-sparql-endpoint.json b/config/config-sparql-endpoint.json
index 2f809eeec..7a14b124c 100644
--- a/config/config-sparql-endpoint.json
+++ b/config/config-sparql-endpoint.json
@@ -17,7 +17,6 @@
     "files-scs:config/presets/pod-management.json",
     "files-scs:config/presets/representation-conversion.json",
     "files-scs:config/presets/storage/backend/storage-sparql-endpoint.json",
-    "files-scs:config/presets/storage/routing/no-routing.json",
     "files-scs:config/presets/storage-wrapper.json",
     "files-scs:config/presets/cli-params.json"
   ],
@@ -25,6 +24,7 @@
     {
       "comment": "Use a SPARQL based store as backend with no additional routing.",
       "@id": "urn:solid-server:default:RoutingResourceStore",
+      "@type": "PassthroughStore",
       "PassthroughStore:_source": {
         "@id": "urn:solid-server:default:SparqlResourceStore"
       }
diff --git a/config/presets/init.json b/config/presets/init.json
index 901aaa480..9743df183 100644
--- a/config/presets/init.json
+++ b/config/presets/init.json
@@ -6,12 +6,14 @@
       "@type": "SequenceHandler",
       "SequenceHandler:_handlers": [
         {
+          "@id": "urn:solid-server:default:LoggerInitializer",
           "@type": "LoggerInitializer",
           "LoggerInitializer:_loggerFactory": {
             "@id": "urn:solid-server:default:LoggerFactory"
           }
         },
         {
+          "@id": "urn:solid-server:default:RootContainerInitializer",
           "@type": "RootContainerInitializer",
           "RootContainerInitializer:_baseUrl": {
             "@id": "urn:solid-server:default:variable:baseUrl"
@@ -21,6 +23,7 @@
           }
         },
         {
+          "@id": "urn:solid-server:default:AclInitializer",
           "@type": "AclInitializer",
           "AclInitializer:_baseUrl": {
             "@id": "urn:solid-server:default:variable:baseUrl"
@@ -33,6 +36,7 @@
           }
         },
         {
+          "@id": "urn:solid-server:default:ServerInitializer",
           "@type": "ServerInitializer",
           "ServerInitializer:_serverFactory": {
             "@id": "urn:solid-server:default:ServerFactory"
diff --git a/config/presets/ldp/response-writer.json b/config/presets/ldp/response-writer.json
index fd0f7a3d3..2e26b1b12 100644
--- a/config/presets/ldp/response-writer.json
+++ b/config/presets/ldp/response-writer.json
@@ -20,10 +20,10 @@
         },
         {
           "@type": "LinkRelMetadataWriter",
-          "LinkRelMetadataWriter:_headerMap": [
+          "LinkRelMetadataWriter:_linkRelMap": [
             {
-              "LinkRelMetadataWriter:_headerMap_key": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
-              "LinkRelMetadataWriter:_headerMap_value": "type"
+              "LinkRelMetadataWriter:_linkRelMap_key": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
+              "LinkRelMetadataWriter:_linkRelMap_value": "type"
             }
           ]
         }
diff --git a/config/presets/storage/routing/no-routing.json b/config/presets/storage/routing/no-routing.json
deleted file mode 100644
index 3c6c80fa9..000000000
--- a/config/presets/storage/routing/no-routing.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
-  "@graph": [
-    {
-      "@id": "urn:solid-server:default:RoutingResourceStore",
-      "@type": "PassthroughStore",
-      "PassthroughStore:_source": {
-        "@id": "urn:solid-server:default:BackendResourceStore"
-      }
-    }
-  ]
-}
diff --git a/src/authorization/WebAclAuthorizer.ts b/src/authorization/WebAclAuthorizer.ts
index 0e4b2b102..9e8e3ae22 100644
--- a/src/authorization/WebAclAuthorizer.ts
+++ b/src/authorization/WebAclAuthorizer.ts
@@ -131,7 +131,7 @@ export class WebAclAuthorizer extends Authorizer {
       if (error instanceof NotFoundHttpError) {
         this.logger.debug(`No direct ACL document found for ${id.path}`);
       } else {
-        this.logger.error(`Error reading ACL for ${id.path}`, { error });
+        this.logger.error(`Error reading ACL for ${id.path}: ${(error as Error).message}`, { error });
         throw error;
       }
     }
diff --git a/src/index.ts b/src/index.ts
index a81b04b3d..4d9ee40f5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -190,6 +190,7 @@ export * from './util/AsyncHandler';
 export * from './util/HeaderUtil';
 export * from './util/PathUtil';
 export * from './util/QuadUtil';
+export * from './util/RecordObject';
 export * from './util/SequenceHandler';
 export * from './util/StreamUtil';
 export * from './util/WaterfallHandler';
diff --git a/src/init/AclInitializer.ts b/src/init/AclInitializer.ts
index 69cc24da6..0ad6accc4 100644
--- a/src/init/AclInitializer.ts
+++ b/src/init/AclInitializer.ts
@@ -5,6 +5,7 @@ import { getLoggerFor } from '../logging/LogUtil';
 import type { ResourceStore } from '../storage/ResourceStore';
 import { TEXT_TURTLE } from '../util/ContentTypes';
 import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
+import { ensureTrailingSlash } from '../util/PathUtil';
 import { guardedStreamFrom } from '../util/StreamUtil';
 import { CONTENT_TYPE } from '../util/UriConstants';
 import { Initializer } from './Initializer';
@@ -24,7 +25,7 @@ export class AclInitializer extends Initializer {
     aclManager: AclManager,
   ) {
     super();
-    this.baseUrl = baseUrl;
+    this.baseUrl = ensureTrailingSlash(baseUrl);
     this.store = store;
     this.aclManager = aclManager;
   }
diff --git a/src/ldp/http/metadata/LinkRelMetadataWriter.ts b/src/ldp/http/metadata/LinkRelMetadataWriter.ts
index a87d86e74..e0d590616 100644
--- a/src/ldp/http/metadata/LinkRelMetadataWriter.ts
+++ b/src/ldp/http/metadata/LinkRelMetadataWriter.ts
@@ -1,3 +1,4 @@
+import { getLoggerFor } from '../../../logging/LogUtil';
 import type { HttpResponse } from '../../../server/HttpResponse';
 import { addHeader } from '../../../util/HeaderUtil';
 import type { RepresentationMetadata } from '../../representation/RepresentationMetadata';
@@ -9,6 +10,7 @@ import { MetadataWriter } from './MetadataWriter';
  */
 export class LinkRelMetadataWriter extends MetadataWriter {
   private readonly linkRelMap: Record<string, string>;
+  protected readonly logger = getLoggerFor(this);
 
   public constructor(linkRelMap: Record<string, string>) {
     super();
@@ -16,9 +18,12 @@ export class LinkRelMetadataWriter extends MetadataWriter {
   }
 
   public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
-    for (const key of Object.keys(this.linkRelMap)) {
+    const keys = Object.keys(this.linkRelMap);
+    this.logger.debug(`Available link relations: ${keys.length}`);
+    for (const key of keys) {
       const values = input.metadata.getAll(key).map((term): string => `<${term.value}>; rel="${this.linkRelMap[key]}"`);
       if (values.length > 0) {
+        this.logger.debug(`Adding Link header ${values}`);
         addHeader(input.response, 'link', values);
       }
     }
diff --git a/src/ldp/representation/RepresentationMetadata.ts b/src/ldp/representation/RepresentationMetadata.ts
index bb6d8a529..4cbe46f66 100644
--- a/src/ldp/representation/RepresentationMetadata.ts
+++ b/src/ldp/representation/RepresentationMetadata.ts
@@ -8,6 +8,13 @@ import { isResourceIdentifier } from './ResourceIdentifier';
 export type MetadataIdentifier = ResourceIdentifier | NamedNode | BlankNode;
 export type MetadataOverrideValue = NamedNode | Literal | string | (NamedNode | Literal | string)[];
 
+/**
+ * Determines whether the object is a `RepresentationMetadata`.
+ */
+export const isRepresentationMetadata = function(object: any): object is RepresentationMetadata {
+  return typeof object?.setMetadata === 'function';
+};
+
 /**
  * Stores the metadata triples and provides methods for easy access.
  * Most functions return the metadata object to allow for chaining.
@@ -49,7 +56,7 @@ export class RepresentationMetadata {
       this.id = DataFactory.namedNode(input.path);
     } else if (isTerm(input)) {
       this.id = input;
-    } else if (input instanceof RepresentationMetadata) {
+    } else if (isRepresentationMetadata(input)) {
       this.id = input.identifier;
       this.addQuads(input.quads());
     } else {
diff --git a/src/util/RecordObject.ts b/src/util/RecordObject.ts
new file mode 100644
index 000000000..ad8d26872
--- /dev/null
+++ b/src/util/RecordObject.ts
@@ -0,0 +1,11 @@
+/**
+ * Helper class for instantiating multiple objects with Components.js.
+ * See https://github.com/LinkedSoftwareDependencies/Components.js/issues/26
+ */
+// eslint-disable-next-line @typescript-eslint/no-extraneous-class
+export class RecordObject implements Record<string, any> {
+  public constructor(record: Record<string, string> = {}) {
+    // eslint-disable-next-line no-constructor-return
+    return record;
+  }
+}
diff --git a/src/util/identifiers/SingleRootIdentifierStrategy.ts b/src/util/identifiers/SingleRootIdentifierStrategy.ts
index bb16c2e01..d5625cd07 100644
--- a/src/util/identifiers/SingleRootIdentifierStrategy.ts
+++ b/src/util/identifiers/SingleRootIdentifierStrategy.ts
@@ -1,4 +1,5 @@
 import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
+import { getLoggerFor } from '../../logging/LogUtil';
 import { InternalServerError } from '../errors/InternalServerError';
 import { ensureTrailingSlash } from '../PathUtil';
 import type { IdentifierStrategy } from './IdentifierStrategy';
@@ -8,13 +9,18 @@ import type { IdentifierStrategy } from './IdentifierStrategy';
  */
 export class SingleRootIdentifierStrategy implements IdentifierStrategy {
   private readonly baseUrl: string;
+  protected readonly logger = getLoggerFor(this);
 
   public constructor(baseUrl: string) {
     this.baseUrl = ensureTrailingSlash(baseUrl);
   }
 
   public supportsIdentifier(identifier: ResourceIdentifier): boolean {
-    return identifier.path.startsWith(this.baseUrl);
+    const supported = identifier.path.startsWith(this.baseUrl);
+    this.logger.debug(supported ?
+      `Identifier ${identifier.path} is part of ${this.baseUrl}` :
+      `Identifier ${identifier.path} is not part of ${this.baseUrl}`);
+    return supported;
   }
 
   public getParentContainer(identifier: ResourceIdentifier): ResourceIdentifier {


Test patch:
diff --git a/test/.eslintrc.js b/test/.eslintrc.js
index c81eb2d3b..585742262 100644
--- a/test/.eslintrc.js
+++ b/test/.eslintrc.js
@@ -13,8 +13,10 @@ module.exports = {
 
     // We are not using Mocha
     'mocha/no-exports': 'off',
+    'mocha/no-nested-tests': 'off',
     'mocha/no-skipped-tests': 'off',
     'mocha/no-synchronous-tests': 'off',
+    'mocha/no-top-level-hooks': 'off',
 
     // Need these 2 to run tests for throwing non-Error objects
     '@typescript-eslint/no-throw-literal': 'off',
diff --git a/test/configs/AuthenticatedDataAccessorBasedConfig.ts b/test/configs/AuthenticatedDataAccessorBasedConfig.ts
deleted file mode 100644
index 1c4ccd92b..000000000
--- a/test/configs/AuthenticatedDataAccessorBasedConfig.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import type {
-  DataAccessor,
-  HttpHandler,
-  ResourceStore,
-} from '../../src/index';
-import {
-  AuthenticatedLdpHandler,
-  EmptyCredentialsExtractor,
-  MethodPermissionsExtractor,
-  RdfToQuadConverter,
-  QuadToRdfConverter,
-  WaterfallHandler,
-} from '../../src/index';
-import type { ServerConfig } from './ServerConfig';
-import {
-  getConvertingStore,
-  getBasicRequestParser,
-  getOperationHandler,
-  getWebAclAuthorizer,
-  getDataAccessorStore,
-  getResponseWriter,
-} from './Util';
-
-/**
- * AuthenticatedFileResourceStoreConfig works with
- * - a WebAclAuthorizer
- * - a FileResourceStore wrapped in a converting store (rdf to quad & quad to rdf)
- * - GET, POST, PUT & DELETE operation handlers
- */
-export class AuthenticatedDataAccessorBasedConfig implements ServerConfig {
-  public base: string;
-  public store: ResourceStore;
-
-  public constructor(base: string, dataAccessor: DataAccessor) {
-    this.base = base;
-    this.store = getConvertingStore(
-      getDataAccessorStore(base, dataAccessor),
-      [ new QuadToRdfConverter(),
-        new RdfToQuadConverter() ],
-    );
-  }
-
-  public getHttpHandler(): HttpHandler {
-    const requestParser = getBasicRequestParser();
-
-    const credentialsExtractor = new EmptyCredentialsExtractor();
-    const permissionsExtractor = new WaterfallHandler([
-      new MethodPermissionsExtractor(),
-    ]);
-
-    const operationHandler = getOperationHandler(this.store);
-
-    const responseWriter = getResponseWriter();
-    const authorizer = getWebAclAuthorizer(this.store);
-
-    const handler = new AuthenticatedLdpHandler({
-      requestParser,
-      credentialsExtractor,
-      permissionsExtractor,
-      authorizer,
-      operationHandler,
-      responseWriter,
-    });
-
-    return handler;
-  }
-}
diff --git a/test/configs/BasicConfig.ts b/test/configs/BasicConfig.ts
deleted file mode 100644
index 0cebb8084..000000000
--- a/test/configs/BasicConfig.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import type {
-  HttpHandler,
-  ResourceStore,
-} from '../../src/index';
-import {
-  AllowEverythingAuthorizer,
-  AuthenticatedLdpHandler,
-  EmptyCredentialsExtractor,
-  MethodPermissionsExtractor,
-} from '../../src/index';
-import type { ServerConfig } from './ServerConfig';
-import { getOperationHandler, getInMemoryResourceStore, getBasicRequestParser, getResponseWriter } from './Util';
-
-/**
- * BasicConfig works with
- * - an AllowEverythingAuthorizer (no acl)
- * - an InMemoryResourceStore
- * - GET, POST & DELETE operation handlers
- */
-
-export class BasicConfig implements ServerConfig {
-  public store: ResourceStore;
-
-  public constructor() {
-    this.store = getInMemoryResourceStore();
-  }
-
-  public getHttpHandler(): HttpHandler {
-    const requestParser = getBasicRequestParser();
-
-    const credentialsExtractor = new EmptyCredentialsExtractor();
-    const permissionsExtractor = new MethodPermissionsExtractor();
-    const authorizer = new AllowEverythingAuthorizer();
-
-    const operationHandler = getOperationHandler(this.store);
-
-    const responseWriter = getResponseWriter();
-
-    const handler = new AuthenticatedLdpHandler({
-      requestParser,
-      credentialsExtractor,
-      permissionsExtractor,
-      authorizer,
-      operationHandler,
-      responseWriter,
-    });
-
-    return handler;
-  }
-}
diff --git a/test/configs/BasicHandlersConfig.ts b/test/configs/BasicHandlersConfig.ts
deleted file mode 100644
index 2412c4943..000000000
--- a/test/configs/BasicHandlersConfig.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import type {
-  HttpHandler,
-  ResourceStore,
-} from '../../src/index';
-import {
-  AllowEverythingAuthorizer,
-  AuthenticatedLdpHandler,
-  EmptyCredentialsExtractor,
-  MethodPermissionsExtractor,
-  QuadToRdfConverter,
-  RawBodyParser,
-  RdfToQuadConverter,
-  SparqlUpdateBodyParser,
-  SparqlPatchPermissionsExtractor,
-  WaterfallHandler,
-} from '../../src/index';
-
-import type { ServerConfig } from './ServerConfig';
-import {
-  getInMemoryResourceStore,
-  getOperationHandler,
-  getConvertingStore,
-  getPatchingStore,
-  getBasicRequestParser,
-  getResponseWriter,
-} from './Util';
-
-/**
- * BasicHandlersConfig works with
- * - an AllowEverythingAuthorizer (no acl)
- * - an InMemoryResourceStore wrapped in a converting store & wrapped in a patching store
- * - GET, POST, PUT, PATCH & DELETE operation handlers
- */
-
-export class BasicHandlersConfig implements ServerConfig {
-  public store: ResourceStore;
-
-  public constructor() {
-    const convertingStore = getConvertingStore(
-      getInMemoryResourceStore(),
-      [ new QuadToRdfConverter(), new RdfToQuadConverter() ],
-    );
-    this.store = getPatchingStore(convertingStore);
-  }
-
-  public getHttpHandler(): HttpHandler {
-    const requestParser = getBasicRequestParser([
-      new SparqlUpdateBodyParser(),
-      new RawBodyParser(),
-    ]);
-
-    const credentialsExtractor = new EmptyCredentialsExtractor();
-    const permissionsExtractor = new WaterfallHandler([
-      new MethodPermissionsExtractor(),
-      new SparqlPatchPermissionsExtractor(),
-    ]);
-    const authorizer = new AllowEverythingAuthorizer();
-
-    const operationHandler = getOperationHandler(this.store);
-
-    const responseWriter = getResponseWriter();
-
-    const handler = new AuthenticatedLdpHandler({
-      requestParser,
-      credentialsExtractor,
-      permissionsExtractor,
-      authorizer,
-      operationHandler,
-      responseWriter,
-    });
-
-    return handler;
-  }
-}
diff --git a/test/configs/BasicHandlersWithAclConfig.ts b/test/configs/BasicHandlersWithAclConfig.ts
deleted file mode 100644
index 561f873f9..000000000
--- a/test/configs/BasicHandlersWithAclConfig.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import type {
-  HttpHandler,
-  ResourceStore,
-} from '../../src/index';
-import {
-  AuthenticatedLdpHandler,
-  EmptyCredentialsExtractor,
-  MethodPermissionsExtractor,
-  RdfToQuadConverter,
-  QuadToRdfConverter,
-  WaterfallHandler,
-} from '../../src/index';
-import type { ServerConfig } from './ServerConfig';
-import {
-  getInMemoryResourceStore,
-  getConvertingStore,
-  getBasicRequestParser,
-  getOperationHandler,
-  getWebAclAuthorizer,
-  getResponseWriter,
-} from './Util';
-
-/**
- * BasicHandlersWithAclConfig works with
- * - an WebAclAuthorizer
- * - an InMemoryResourceStore wrapped in a converting store & wrapped in a patching store
- * - GET, POST, PUT, PATCH & DELETE operation handlers
- */
-
-export class BasicHandlersWithAclConfig implements ServerConfig {
-  public store: ResourceStore;
-
-  public constructor() {
-    this.store = getConvertingStore(
-      getInMemoryResourceStore(),
-      [ new QuadToRdfConverter(),
-        new RdfToQuadConverter() ],
-    );
-  }
-
-  public getHttpHandler(): HttpHandler {
-    const requestParser = getBasicRequestParser();
-
-    const credentialsExtractor = new EmptyCredentialsExtractor();
-    const permissionsExtractor = new WaterfallHandler([
-      new MethodPermissionsExtractor(),
-    ]);
-
-    const operationHandler = getOperationHandler(this.store);
-
-    const responseWriter = getResponseWriter();
-    const authorizer = getWebAclAuthorizer(this.store);
-
-    const handler = new AuthenticatedLdpHandler({
-      requestParser,
-      credentialsExtractor,
-      permissionsExtractor,
-      authorizer,
-      operationHandler,
-      responseWriter,
-    });
-
-    return handler;
-  }
-}
diff --git a/test/configs/DataAccessorBasedConfig.ts b/test/configs/DataAccessorBasedConfig.ts
deleted file mode 100644
index fc421463a..000000000
--- a/test/configs/DataAccessorBasedConfig.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import type {
-  DataAccessor,
-  HttpHandler,
-  ResourceStore,
-} from '../../src/index';
-import {
-  AllowEverythingAuthorizer,
-  AuthenticatedLdpHandler,
-  EmptyCredentialsExtractor,
-  MethodPermissionsExtractor,
-  QuadToRdfConverter,
-  RawBodyParser,
-  RdfToQuadConverter,
-  WaterfallHandler,
-} from '../../src/index';
-import type { ServerConfig } from './ServerConfig';
-import {
-  getOperationHandler,
-  getConvertingStore,
-  getBasicRequestParser,
-  getDataAccessorStore,
-  getResponseWriter,
-} from './Util';
-
-/**
- * DataAccessorBasedConfig works with
- * - an AllowEverythingAuthorizer (no acl)
- * - a DataAccessorBasedStore with a FileDataAccessor wrapped in a converting store (rdf to quad & quad to rdf)
- * - GET, POST, PUT & DELETE operation handlers
- */
-export class DataAccessorBasedConfig implements ServerConfig {
-  public store: ResourceStore;
-
-  public constructor(base: string, dataAccessor: DataAccessor, inType?: string) {
-    this.store = getConvertingStore(
-      getDataAccessorStore(base, dataAccessor),
-      [ new QuadToRdfConverter(), new RdfToQuadConverter() ],
-      inType,
-    );
-  }
-
-  public getHttpHandler(): HttpHandler {
-    // This is for the sake of test coverage, as it could also be just getBasicRequestParser()
-    const requestParser = getBasicRequestParser([ new RawBodyParser() ]);
-
-    const credentialsExtractor = new EmptyCredentialsExtractor();
-    const permissionsExtractor = new WaterfallHandler([
-      new MethodPermissionsExtractor(),
-    ]);
-    const authorizer = new AllowEverythingAuthorizer();
-
-    const operationHandler = getOperationHandler(this.store);
-    const responseWriter = getResponseWriter();
-
-    const handler = new AuthenticatedLdpHandler({
-      requestParser,
-      credentialsExtractor,
-      permissionsExtractor,
-      authorizer,
-      operationHandler,
-      responseWriter,
-    });
-
-    return handler;
-  }
-}
diff --git a/test/configs/ServerConfig.ts b/test/configs/ServerConfig.ts
deleted file mode 100644
index 13081c77c..000000000
--- a/test/configs/ServerConfig.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { HttpHandler } from '../../src/server/HttpHandler';
-import type { ResourceStore } from '../../src/storage/ResourceStore';
-
-export interface ServerConfig {
-  store: ResourceStore;
-  getHttpHandler: () => HttpHandler;
-}
diff --git a/test/configs/Util.ts b/test/configs/Util.ts
deleted file mode 100644
index e5706d2b9..000000000
--- a/test/configs/Util.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import type { Server } from 'http';
-import { join } from 'path';
-import * as Path from 'path';
-import { Loader } from 'componentsjs';
-import fetch from 'cross-fetch';
-import type {
-  BodyParser,
-  DataAccessor,
-  Operation,
-  RepresentationConverter,
-  ResourceStore,
-  ResponseDescription,
-  HttpResponse,
-  ResponseWriter,
-  OperationHandler,
-} from '../../src/index';
-import {
-  AcceptPreferenceParser,
-  BasicMetadataExtractor,
-  BasicRequestParser,
-  BasicResponseWriter,
-  BasicTargetExtractor,
-  ContentTypeParser,
-  DataAccessorBasedStore,
-  DeleteOperationHandler,
-  ErrorResponseWriter,
-  GetOperationHandler,
-  HeadOperationHandler,
-  HttpError,
-  InMemoryDataAccessor,
-  LinkRelMetadataWriter,
-  LinkTypeParser,
-  MappedMetadataWriter,
-  PatchingStore,
-  PatchOperationHandler,
-  PostOperationHandler,
-  PutOperationHandler,
-  RawBodyParser,
-  RepresentationConvertingStore,
-  SequenceHandler,
-  SingleRootIdentifierStrategy,
-  SingleThreadedResourceLocker,
-  SlugParser,
-  SparqlUpdatePatchHandler,
-  UrlBasedAclManager,
-  WaterfallHandler,
-  WebAclAuthorizer,
-} from '../../src/index';
-import { CONTENT_TYPE, HTTP, RDF } from '../../src/util/UriConstants';
-
-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 getRootFilePath = (subfolder: string): string => join(__dirname, '../testData', subfolder);
-
-/**
- * Gives a data accessor store with the given data accessor.
- * @param base - Base URL.
- * @param dataAccessor - DataAccessor to use.
- *
- * @returns The data accessor based store.
- */
-export const getDataAccessorStore = (base: string, dataAccessor: DataAccessor): DataAccessorBasedStore =>
-  new DataAccessorBasedStore(dataAccessor, new SingleRootIdentifierStrategy(base));
-
-/**
- * Gives an in memory resource store based on (default) base url.
- * @param base - Optional base parameter for the run time config.
- *
- * @returns The in memory resource store.
- */
-export const getInMemoryResourceStore = (base = BASE): DataAccessorBasedStore =>
-  getDataAccessorStore(base, new InMemoryDataAccessor(BASE));
-
-/**
- * Gives a converting store given some converters.
- * @param store - Initial store.
- * @param converters - Converters to be used.
- *
- * @returns The converting store.
- */
-export const getConvertingStore =
-(store: ResourceStore, converters: RepresentationConverter[], inType?: string):
-RepresentationConvertingStore =>
-  new RepresentationConvertingStore(store, {
-    inConverter: new WaterfallHandler(converters),
-    outConverter: new WaterfallHandler(converters),
-    inType,
-  });
-
-/**
- * Gives a patching store based on initial store.
- * @param store - Initial resource store.
- *
- * @returns The patching store.
- */
-export const getPatchingStore = (store: ResourceStore): PatchingStore => {
-  const locker = new SingleThreadedResourceLocker();
-  const patcher = new SparqlUpdatePatchHandler(store, locker);
-  return new PatchingStore(store, patcher);
-};
-
-/**
- * Gives an operation handler given a store with all the common operation handlers.
- * @param store - Initial resource store.
- *
- * @returns The operation handler.
- */
-export const getOperationHandler = (store: ResourceStore): OperationHandler => {
-  const handlers = [
-    new GetOperationHandler(store),
-    new HeadOperationHandler(store),
-    new PostOperationHandler(store),
-    new PutOperationHandler(store),
-    new PatchOperationHandler(store),
-    new DeleteOperationHandler(store),
-  ];
-  return new WaterfallHandler<Operation, ResponseDescription>(handlers);
-};
-
-export const getResponseWriter = (): ResponseWriter => {
-  const serializer = new SequenceHandler([
-    new MappedMetadataWriter({
-      [CONTENT_TYPE]: 'content-type',
-      [HTTP.location]: 'location',
-    }),
-    new LinkRelMetadataWriter({
-      [RDF.type]: 'type',
-    }),
-  ]);
-
-  return new WaterfallHandler<{ response: HttpResponse; result: ResponseDescription | Error }, void>([
-    new ErrorResponseWriter(),
-    new BasicResponseWriter(serializer),
-  ]);
-};
-
-/**
- * Creates a BasicMetadataExtractor with parsers for content-type, slugs and link types.
- */
-export const getBasicMetadataExtractor = (): BasicMetadataExtractor => new BasicMetadataExtractor([
-  new ContentTypeParser(),
-  new SlugParser(),
-  new LinkTypeParser(),
-]);
-
-/**
- * Gives a basic request parser based on some body parses.
- * @param bodyParsers - Optional list of body parsers, default is RawBodyParser.
- *
- * @returns The request parser.
- */
-export const getBasicRequestParser = (bodyParsers: BodyParser[] = []): BasicRequestParser => {
-  let bodyParser: BodyParser;
-  if (bodyParsers.length === 1) {
-    bodyParser = bodyParsers[0];
-  } else if (bodyParsers.length === 0) {
-    // If no body parser is given (array is empty), default to RawBodyParser
-    bodyParser = new RawBodyParser();
-  } else {
-    bodyParser = new WaterfallHandler(bodyParsers);
-  }
-  return new BasicRequestParser({
-    targetExtractor: new BasicTargetExtractor(),
-    preferenceParser: new AcceptPreferenceParser(),
-    metadataExtractor: getBasicMetadataExtractor(),
-    bodyParser,
-  });
-};
-
-/**
- * Gives a web acl authorizer based on a (default) runtimeConfig.
- * @param store - Initial resource store.
- * @param aclManager - Optional acl manager, default is UrlBasedAclManager.
- *
- * @returns The acl authorizer.
- */
-export const getWebAclAuthorizer = (store: ResourceStore, aclManager = new UrlBasedAclManager()): WebAclAuthorizer =>
-  new WebAclAuthorizer(aclManager, store, new SingleRootIdentifierStrategy(BASE));
-
-/**
- * Returns a component instantiated from a Components.js configuration.
- */
-export const instantiateFromConfig = async(componentUrl: string, configFile: string,
-  variables?: Record<string, any>): Promise<any> => {
-  // Initialize the Components.js loader
-  const mainModulePath = Path.join(__dirname, '../../');
-  const loader = new Loader({ mainModulePath });
-  await loader.registerAvailableModuleResources();
-
-  // Instantiate the component from the config
-  const configPath = Path.join(__dirname, configFile);
-  return loader.instantiateFromUrl(componentUrl, configPath, undefined, { variables });
-};
-
-/**
- * Initializes the root container of the server.
- * Useful for when the RootContainerInitializer was not instantiated.
- */
-export const initServerStore = async(server: Server, baseUrl: string,
-  headers: Record<string, string> = {}): Promise<void> => {
-  const res = await fetch(baseUrl, {
-    method: 'PUT',
-    headers: {
-      ...headers,
-      'content-type': 'text/turtle',
-    },
-    body: '',
-  });
-  if (res.status >= 400) {
-    throw new HttpError(res.status, 'Error', res.statusText);
-  }
-};
diff --git a/test/integration/AuthenticatedLdpHandler.test.ts b/test/integration/AuthenticatedLdpHandler.test.ts
deleted file mode 100644
index f80f8c07e..000000000
--- a/test/integration/AuthenticatedLdpHandler.test.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-import 'jest-rdf';
-import * as url from 'url';
-import { namedNode, quad } from '@rdfjs/data-model';
-import { Parser } from 'n3';
-import type { MockResponse } from 'node-mocks-http';
-import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
-import { LDP } from '../../src/util/UriConstants';
-import { BasicConfig } from '../configs/BasicConfig';
-import { BasicHandlersConfig } from '../configs/BasicHandlersConfig';
-import { BASE } from '../configs/Util';
-import { call } from '../util/Util';
-
-describe('An integrated AuthenticatedLdpHandler', (): void => {
-  describe('with simple handlers', (): void => {
-    const config = new BasicConfig();
-    const handler = config.getHttpHandler();
-
-    beforeAll(async(): Promise<void> => {
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-    });
-
-    it('can add, read and delete data based on incoming requests.', async(): Promise<void> => {
-      // POST
-      let requestUrl = new URL('http://test.com/');
-      let response: MockResponse<any> = await call(
-        handler,
-        requestUrl,
-        'POST',
-        { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
-        [ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
-      );
-      expect(response.statusCode).toBe(201);
-      expect(response._getData()).toHaveLength(0);
-      const id = response._getHeaders().location;
-      expect(id).toContain(url.format(requestUrl));
-
-      // GET
-      requestUrl = new URL(id);
-      response = await call(
-        handler,
-        requestUrl,
-        'GET',
-        { accept: 'text/turtle' },
-        [],
-      );
-      expect(response.statusCode).toBe(200);
-      expect(response._getData()).toContain(
-        '<http://test.com/s> <http://test.com/p> <http://test.com/o>.',
-      );
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // DELETE
-      response = await call(handler, requestUrl, 'DELETE', {}, []);
-      expect(response.statusCode).toBe(205);
-      expect(response._getData()).toHaveLength(0);
-
-      // GET
-      response = await call(
-        handler,
-        requestUrl,
-        'GET',
-        { accept: 'text/turtle' },
-        [],
-      );
-      expect(response.statusCode).toBe(404);
-      expect(response._getData()).toContain('NotFoundHttpError');
-    });
-  });
-
-  describe('with simple PATCH handlers', (): void => {
-    const config = new BasicHandlersConfig();
-    const handler = config.getHttpHandler();
-
-    beforeAll(async(): Promise<void> => {
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-    });
-
-    it('can handle simple SPARQL updates.', async(): Promise<void> => {
-      // POST
-      let requestUrl = new URL('http://test.com/');
-      let response: MockResponse<any> = await call(
-        handler,
-        requestUrl,
-        'POST',
-        { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
-        [ '<http://test.com/s1> <http://test.com/p1> <http://test.com/o1>.',
-          '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.' ],
-      );
-      expect(response.statusCode).toBe(201);
-      expect(response._getData()).toHaveLength(0);
-      const id = response._getHeaders().location;
-      expect(id).toContain(url.format(requestUrl));
-
-      // PATCH
-      requestUrl = new URL(id);
-      response = await call(
-        handler,
-        requestUrl,
-        'PATCH',
-        { 'content-type': 'application/sparql-update', 'transfer-encoding': 'chunked' },
-        [ 'DELETE { <http://test.com/s1> <http://test.com/p1> <http://test.com/o1> }',
-          'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
-          'WHERE {}',
-        ],
-      );
-      expect(response.statusCode).toBe(205);
-      expect(response._getData()).toHaveLength(0);
-
-      // GET
-      requestUrl = new URL(id);
-      response = await call(
-        handler,
-        requestUrl,
-        'GET',
-        { accept: 'text/turtle' },
-        [],
-      );
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain(
-        '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.',
-      );
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-      const parser = new Parser();
-      const triples = parser.parse(response._getBuffer().toString());
-      expect(triples).toBeRdfIsomorphic([
-        quad(
-          namedNode('http://test.com/s2'),
-          namedNode('http://test.com/p2'),
-          namedNode('http://test.com/o2'),
-        ),
-        quad(
-          namedNode('http://test.com/s3'),
-          namedNode('http://test.com/p3'),
-          namedNode('http://test.com/o3'),
-        ),
-      ]);
-    });
-  });
-
-  describe('with simple PUT handlers', (): void => {
-    const config = new BasicHandlersConfig();
-    const handler = config.getHttpHandler();
-
-    beforeAll(async(): Promise<void> => {
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-    });
-
-    it('should overwrite the content on PUT request.', async(): Promise<void> => {
-      // POST
-      let requestUrl = new URL('http://test.com/');
-      let response: MockResponse<any> = await call(
-        handler,
-        requestUrl,
-        'POST',
-        { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
-        [
-          '<http://test.com/s1> <http://test.com/p1> <http://test.com/o1>.',
-          '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.',
-        ],
-      );
-      expect(response.statusCode).toBe(201);
-      expect(response._getData()).toHaveLength(0);
-      const id = response._getHeaders().location;
-      expect(id).toContain(url.format(requestUrl));
-
-      // PUT
-      requestUrl = new URL(id);
-      response = await call(
-        handler,
-        requestUrl,
-        'PUT',
-        { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
-        [ '<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>.' ],
-      );
-      expect(response.statusCode).toBe(205);
-      expect(response._getData()).toHaveLength(0);
-
-      // GET
-      requestUrl = new URL(id);
-      response = await call(
-        handler,
-        requestUrl,
-        'GET',
-        { accept: 'text/turtle' },
-        [],
-      );
-      expect(response.statusCode).toBe(200);
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-      const parser = new Parser();
-      const triples = parser.parse(response._getData());
-      expect(triples).toBeRdfIsomorphic([
-        quad(
-          namedNode('http://test.com/s3'),
-          namedNode('http://test.com/p3'),
-          namedNode('http://test.com/o3'),
-        ),
-      ]);
-    });
-  });
-});
diff --git a/test/integration/Config.ts b/test/integration/Config.ts
new file mode 100644
index 000000000..14e036169
--- /dev/null
+++ b/test/integration/Config.ts
@@ -0,0 +1,33 @@
+import { mkdirSync } from 'fs';
+import { join } from 'path';
+import * as Path from 'path';
+import { Loader } from 'componentsjs';
+import * as rimraf from 'rimraf';
+
+export const BASE = 'http://test.com';
+
+/**
+ * Returns a component instantiated from a Components.js configuration.
+ */
+export const instantiateFromConfig = async(componentUrl: string, configFile: string,
+  variables?: Record<string, any>): Promise<any> => {
+  // Initialize the Components.js loader
+  const mainModulePath = Path.join(__dirname, '../../');
+  const loader = new Loader({ mainModulePath });
+  await loader.registerAvailableModuleResources();
+
+  // Instantiate the component from the config
+  const configPath = Path.join(__dirname, 'config', configFile);
+  return loader.instantiateFromUrl(componentUrl, configPath, undefined, { variables });
+};
+
+export const getTestFolder = (name: string): string =>
+  join(__dirname, '../tmp', name);
+
+export const createFolder = (folder: string): void => {
+  mkdirSync(folder, { recursive: true });
+};
+
+export const removeFolder = (folder: string): void => {
+  rimraf.sync(folder, { glob: false });
+};
diff --git a/test/integration/FullConfig.acl.test.ts b/test/integration/FullConfig.acl.test.ts
deleted file mode 100644
index 89e73846b..000000000
--- a/test/integration/FullConfig.acl.test.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { createReadStream, mkdirSync } from 'fs';
-import { join } from 'path';
-import * as rimraf from 'rimraf';
-import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
-import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
-import { FileDataAccessor } from '../../src/storage/accessors/FileDataAccessor';
-import { InMemoryDataAccessor } from '../../src/storage/accessors/InMemoryDataAccessor';
-import { ExtensionBasedMapper } from '../../src/storage/mapping/ExtensionBasedMapper';
-import { guardStream } from '../../src/util/GuardedStream';
-import { ensureTrailingSlash } from '../../src/util/PathUtil';
-import { CONTENT_TYPE, LDP } from '../../src/util/UriConstants';
-import { AuthenticatedDataAccessorBasedConfig } from '../configs/AuthenticatedDataAccessorBasedConfig';
-import type { ServerConfig } from '../configs/ServerConfig';
-import { BASE, getRootFilePath } from '../configs/Util';
-import { AclTestHelper, FileTestHelper } from '../util/TestHelpers';
-
-const dataAccessorStore: [string, (rootFilePath: string) => ServerConfig] = [
-  'AuthenticatedFileDataAccessorBasedStore',
-  (rootFilePath: string): ServerConfig => new AuthenticatedDataAccessorBasedConfig(BASE,
-    new FileDataAccessor(new ExtensionBasedMapper(BASE, rootFilePath))),
-];
-const inMemoryDataAccessorStore: [string, (rootFilePath: string) => ServerConfig] = [
-  'AuthenticatedInMemoryDataAccessorBasedStore',
-  (): ServerConfig => new AuthenticatedDataAccessorBasedConfig(BASE, new InMemoryDataAccessor(BASE)),
-];
-
-describe.each([ dataAccessorStore, inMemoryDataAccessorStore ])('A server using a %s', (name, configFn): void => {
-  describe('with acl', (): void => {
-    let config: ServerConfig;
-    let aclHelper: AclTestHelper;
-    let fileHelper: FileTestHelper;
-    let rootFilePath: string;
-
-    beforeAll(async(): Promise<void> => {
-      rootFilePath = getRootFilePath(name);
-      mkdirSync(rootFilePath, { recursive: true });
-      config = configFn(rootFilePath);
-      aclHelper = new AclTestHelper(config.store, ensureTrailingSlash(BASE));
-      fileHelper = new FileTestHelper(config.getHttpHandler(), new URL(ensureTrailingSlash(BASE)));
-
-      // Make sure the root directory exists
-      mkdirSync(rootFilePath, { recursive: true });
-
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-
-      // Use store instead of file access so tests also work for non-file backends
-      await config.store.setRepresentation({ path: `${BASE}/permanent.txt` }, {
-        binary: true,
-        data: guardStream(createReadStream(join(__dirname, '../assets/permanent.txt'))),
-        metadata: new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }),
-      });
-    });
-
-    afterAll(async(): Promise<void> => {
-      rimraf.sync(rootFilePath, { glob: false });
-    });
-
-    it('can add a file to the store, read it and delete it if allowed.', async(): Promise<
-    void
-    > => {
-      // Set acl
-      await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'agent');
-
-      // Create file
-      let response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain');
-      const id = response._getHeaders().location;
-
-      // Get file
-      response = await fileHelper.getFile(id);
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain('TESTFILE2');
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // DELETE file
-      await fileHelper.deleteResource(id);
-      await fileHelper.shouldNotExist(id);
-    });
-
-    it('can not add a file to the store if not allowed.', async():
-    Promise<void> => {
-      // Set acl
-      await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'authenticated');
-
-      // Try to create file
-      const response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain', true);
-      expect(response.statusCode).toBe(401);
-    });
-
-    it('can not add/delete, but only read files if allowed.', async():
-    Promise<void> => {
-      // Set acl
-      await aclHelper.setSimpleAcl({ read: true, write: false, append: false }, 'agent');
-
-      // Try to create file
-      let response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain', true);
-      expect(response.statusCode).toBe(401);
-
-      // GET permanent file
-      response = await fileHelper.getFile('http://test.com/permanent.txt');
-      expect(response._getBuffer().toString()).toContain('TEST');
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // Try to delete permanent file
-      response = await fileHelper.deleteResource('http://test.com/permanent.txt', true);
-      expect(response.statusCode).toBe(401);
-    });
-  });
-});
diff --git a/test/integration/FullConfig.noAuth.test.ts b/test/integration/FullConfig.noAuth.test.ts
deleted file mode 100644
index 4b83a4ba1..000000000
--- a/test/integration/FullConfig.noAuth.test.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-import { mkdirSync } from 'fs';
-import * as rimraf from 'rimraf';
-import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
-import type { HttpHandler } from '../../src/server/HttpHandler';
-import { FileDataAccessor } from '../../src/storage/accessors/FileDataAccessor';
-import { InMemoryDataAccessor } from '../../src/storage/accessors/InMemoryDataAccessor';
-import { ExtensionBasedMapper } from '../../src/storage/mapping/ExtensionBasedMapper';
-import { LDP } from '../../src/util/UriConstants';
-import { DataAccessorBasedConfig } from '../configs/DataAccessorBasedConfig';
-import type { ServerConfig } from '../configs/ServerConfig';
-import { BASE, getRootFilePath } from '../configs/Util';
-import { FileTestHelper } from '../util/TestHelpers';
-
-const fileDataAccessorStore: [string, (rootFilePath: string) => ServerConfig] = [
-  'FileDataAccessorBasedStore',
-  (rootFilePath: string): ServerConfig => new DataAccessorBasedConfig(BASE,
-    new FileDataAccessor(new ExtensionBasedMapper(BASE, rootFilePath))),
-];
-const inMemoryDataAccessorStore: [string, (rootFilePath: string) => ServerConfig] = [
-  'InMemoryDataAccessorBasedStore',
-  (): ServerConfig => new DataAccessorBasedConfig(BASE, new InMemoryDataAccessor(BASE)),
-];
-
-const configs = [ fileDataAccessorStore, inMemoryDataAccessorStore ];
-
-describe.each(configs)('A server using a %s', (name, configFn): void => {
-  describe('without acl', (): void => {
-    let rootFilePath: string;
-    let config: ServerConfig;
-    let handler: HttpHandler;
-    let fileHelper: FileTestHelper;
-
-    beforeAll(async(): Promise<void> => {
-      rootFilePath = getRootFilePath(name);
-      mkdirSync(rootFilePath, { recursive: true });
-      config = configFn(rootFilePath);
-      handler = config.getHttpHandler();
-      fileHelper = new FileTestHelper(handler, new URL(BASE));
-
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-    });
-
-    afterAll(async(): Promise<void> => {
-      rimraf.sync(rootFilePath, { glob: false });
-    });
-
-    it('can add a file to the store, read it and delete it.', async():
-    Promise<void> => {
-      // POST
-      let response = await fileHelper.createFile('../assets/testfile0.txt', 'testfile0.txt', 'text/plain');
-      const id = response._getHeaders().location;
-
-      // GET
-      response = await fileHelper.getFile(id);
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain('TESTFILE0');
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // DELETE
-      await fileHelper.deleteResource(id);
-      await fileHelper.shouldNotExist(id);
-    });
-
-    it('can add and overwrite a file.', async(): Promise<void> => {
-      let response = await fileHelper.createFile('../assets/testfile0.txt', 'file.txt', 'text/plain');
-      const id = response._getHeaders().location;
-
-      // GET
-      response = await fileHelper.getFile(id);
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain('TESTFILE0');
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // PUT
-      response = await fileHelper.overwriteFile('../assets/testfile1.txt', id, 'text/plain');
-
-      // GET
-      response = await fileHelper.getFile(id);
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain('TESTFILE1');
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // DELETE
-      await fileHelper.deleteResource(id);
-      await fileHelper.shouldNotExist(id);
-    });
-
-    it('can create a folder and delete it.', async(): Promise<void> => {
-      // POST
-      let response = await fileHelper.createFolder('secondfolder/');
-      const id = response._getHeaders().location;
-
-      // GET
-      response = await fileHelper.getFolder(id);
-      expect(response.statusCode).toBe(200);
-      expect(response.getHeaders().link).toEqual(
-        [ `<${LDP.Container}>; rel="type"`, `<${LDP.BasicContainer}>; rel="type"`, `<${LDP.Resource}>; rel="type"` ],
-      );
-
-      // DELETE
-      await fileHelper.deleteResource(id);
-      await fileHelper.shouldNotExist(id);
-    });
-
-    it('can make a folder and put a file in it.', async(): Promise<void> => {
-      // Create folder
-      await fileHelper.createFolder('testfolder0/');
-
-      // Create file
-      let response = await fileHelper.createFile('../assets/testfile0.txt', 'testfolder0/testfile0.txt', 'text/plain');
-      const id = response._getHeaders().location;
-
-      // GET File
-      response = await fileHelper.getFile(id);
-      expect(response.statusCode).toBe(200);
-      expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
-
-      // DELETE
-      await fileHelper.deleteResource(id);
-      await fileHelper.shouldNotExist(id);
-      await fileHelper.deleteResource('http://test.com/testfolder0/');
-      await fileHelper.shouldNotExist('http://test.com/testfolder0/');
-    });
-
-    it('cannot remove a folder when the folder contains a file.', async(): Promise<void> => {
-      // Create folder
-      let response = await fileHelper.createFolder('testfolder1/');
-      const folderId = response._getHeaders().location;
-
-      // Create file
-      await fileHelper.createFile('../assets/testfile0.txt', 'testfolder1/testfile0.txt', 'text/plain');
-
-      // Try DELETE folder
-      response = await fileHelper.simpleCall(new URL(folderId), 'DELETE', {});
-      expect(response.statusCode).toBe(409);
-      expect(response._getData()).toContain('ConflictHttpError: Can only delete empty containers.');
-
-      // DELETE
-      await fileHelper.deleteResource('http://test.com/testfolder1/testfile0.txt');
-      await fileHelper.shouldNotExist('http://test.com/testfolder1/testfile0.txt');
-      await fileHelper.deleteResource(folderId);
-      await fileHelper.shouldNotExist(folderId);
-    });
-
-    it('cannot remove a folder when the folder contains a subfolder.', async(): Promise<void> => {
-      // Create folder
-      let response = await fileHelper.createFolder('testfolder2/');
-      const folderId = response._getHeaders().location;
-
-      // Create subfolder
-      response = await fileHelper.createFolder('testfolder2/subfolder0');
-      const subFolderId = response._getHeaders().location;
-
-      // Try DELETE folder
-      response = await fileHelper.simpleCall(new URL(folderId), 'DELETE', {});
-      expect(response.statusCode).toBe(409);
-      expect(response._getData()).toContain('ConflictHttpError: Can only delete empty containers.');
-
-      // DELETE
-      await fileHelper.deleteResource(subFolderId);
-      await fileHelper.shouldNotExist(subFolderId);
-      await fileHelper.deleteResource(folderId);
-      await fileHelper.shouldNotExist(folderId);
-    });
-
-    it('can read the contents of a folder.', async(): Promise<void> => {
-      // Create folder
-      let response = await fileHelper.createFolder('testfolder3/');
-      const folderId = response._getHeaders().location;
-
-      // Create subfolder
-      response = await fileHelper.createFolder('testfolder3/subfolder0/');
-      const subFolderId = response._getHeaders().location;
-
-      // Create file
-      response = await fileHelper.createFile('../assets/testfile0.txt', 'testfolder3/testfile0.txt', 'text/plain');
-      const fileId = response._getHeaders().location;
-
-      response = await fileHelper.getFolder(folderId);
-      expect(response.statusCode).toBe(200);
-      expect(response._getBuffer().toString()).toContain('<http://www.w3.org/ns/ldp#contains> <http://test.com/testfolder3/subfolder0/> .');
-      expect(response._getBuffer().toString()).toContain('<http://www.w3.org/ns/ldp#contains> <http://test.com/testfolder3/testfile0.txt> .');
-      expect(response.getHeaders().link).toEqual(
-        [ `<${LDP.Container}>; rel="type"`, `<${LDP.BasicContainer}>; rel="type"`, `<${LDP.Resource}>; rel="type"` ],
-      );
-
-      // DELETE
-      await fileHelper.deleteResource(fileId);
-      await fileHelper.shouldNotExist(fileId);
-      await fileHelper.deleteResource(subFolderId);
-      await fileHelper.shouldNotExist(subFolderId);
-      await fileHelper.deleteResource(folderId);
-      await fileHelper.shouldNotExist(folderId);
-    });
-
-    it('can upload and delete a image.', async(): Promise<void> => {
-      let response = await fileHelper.createFile('../assets/testimage.png', 'image.png', 'image/png');
-      const fileId = response._getHeaders().location;
-
-      // GET
-      response = await fileHelper.getFile(fileId);
-      expect(response.statusCode).toBe(200);
-      expect(response._getHeaders()['content-type']).toBe('image/png');
-
-      // DELETE
-      await fileHelper.deleteResource(fileId);
-      await fileHelper.shouldNotExist(fileId);
-    });
-  });
-});
diff --git a/test/integration/LdpHandlerWithAuth.test.ts b/test/integration/LdpHandlerWithAuth.test.ts
new file mode 100644
index 000000000..1ed60b285
--- /dev/null
+++ b/test/integration/LdpHandlerWithAuth.test.ts
@@ -0,0 +1,118 @@
+import { createReadStream } from 'fs';
+import { join } from 'path';
+import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
+import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
+import { guardStream } from '../../src/util/GuardedStream';
+import { CONTENT_TYPE, LDP } from '../../src/util/UriConstants';
+import { AclTestHelper, FileTestHelper } from '../util/TestHelpers';
+import { BASE, getTestFolder, createFolder, removeFolder, instantiateFromConfig } from './Config';
+
+const rootFilePath = getTestFolder('full-config-acl');
+const stores: [string, any][] = [
+  [ 'in-memory storage', {
+    storeUrn: 'urn:solid-server:default:MemoryResourceStore',
+    setup: jest.fn(),
+    teardown: jest.fn(),
+  }],
+  [ 'on-disk storage', {
+    storeUrn: 'urn:solid-server:default:FileResourceStore',
+    setup: (): void => createFolder(rootFilePath),
+    teardown: (): void => removeFolder(rootFilePath),
+  }],
+];
+
+describe.each(stores)('An LDP handler with auth using %s', (name, { storeUrn, setup, teardown }): void => {
+  let handler: HttpHandler;
+  let aclHelper: AclTestHelper;
+  let fileHelper: FileTestHelper;
+
+  beforeAll(async(): Promise<void> => {
+    // Set up the internal store
+    await setup();
+    const variables: Record<string, any> = {
+      'urn:solid-server:default:variable:baseUrl': BASE,
+      'urn:solid-server:default:variable:rootFilePath': rootFilePath,
+    };
+    const internalStore = await instantiateFromConfig(
+      storeUrn,
+      'ldp-with-auth.json',
+      variables,
+    ) as ResourceStore;
+    variables['urn:solid-server:default:variable:store'] = internalStore;
+
+    // Create and initialize the HTTP handler and related components
+    let initializer: Initializer;
+    let store: ResourceStore;
+    const instances = await instantiateFromConfig(
+      'urn:solid-server:test:Instances',
+      'ldp-with-auth.json',
+      variables,
+    ) as Record<string, any>;
+    ({ handler, store, initializer } = instances);
+    await initializer.handleSafe();
+
+    // Create test helpers for manipulating the components
+    aclHelper = new AclTestHelper(store, BASE);
+    fileHelper = new FileTestHelper(handler, new URL(BASE));
+
+    // Write test resource
+    await store.setRepresentation({ path: `${BASE}/permanent.txt` }, {
+      binary: true,
+      data: guardStream(createReadStream(join(__dirname, '../assets/permanent.txt'))),
+      metadata: new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }),
+    });
+  });
+
+  afterAll(async(): Promise<void> => {
+    await teardown();
+  });
+
+  it('can add a file to the store, read it and delete it if allowed.', async():
+  Promise<void> => {
+    // Set acl
+    await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'agent');
+
+    // Create file
+    let response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain');
+    const id = response._getHeaders().location;
+
+    // Get file
+    response = await fileHelper.getFile(id);
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain('TESTFILE2');
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // DELETE file
+    await fileHelper.deleteResource(id);
+    await fileHelper.shouldNotExist(id);
+  });
+
+  it('can not add a file to the store if not allowed.', async():
+  Promise<void> => {
+    // Set acl
+    await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'authenticated');
+
+    // Try to create file
+    const response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain', true);
+    expect(response.statusCode).toBe(401);
+  });
+
+  it('can not add/delete, but only read files if allowed.', async():
+  Promise<void> => {
+    // Set acl
+    await aclHelper.setSimpleAcl({ read: true, write: false, append: false }, 'agent');
+
+    // Try to create file
+    let response = await fileHelper.createFile('../assets/testfile2.txt', 'testfile2.txt', 'text/plain', true);
+    expect(response.statusCode).toBe(401);
+
+    // GET permanent file
+    response = await fileHelper.getFile('http://test.com/permanent.txt');
+    expect(response._getBuffer().toString()).toContain('TEST');
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // Try to delete permanent file
+    response = await fileHelper.deleteResource('http://test.com/permanent.txt', true);
+    expect(response.statusCode).toBe(401);
+  });
+});
diff --git a/test/integration/LdpHandlerWithoutAuth.test.ts b/test/integration/LdpHandlerWithoutAuth.test.ts
new file mode 100644
index 000000000..ec533924e
--- /dev/null
+++ b/test/integration/LdpHandlerWithoutAuth.test.ts
@@ -0,0 +1,218 @@
+import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
+import { LDP } from '../../src/util/UriConstants';
+import { FileTestHelper } from '../util/TestHelpers';
+import { BASE, getTestFolder, createFolder, removeFolder, instantiateFromConfig } from './Config';
+
+const rootFilePath = getTestFolder('full-config-no-auth');
+const stores: [string, any][] = [
+  [ 'in-memory storage', {
+    storeUrn: 'urn:solid-server:default:MemoryResourceStore',
+    setup: jest.fn(),
+    teardown: jest.fn(),
+  }],
+  [ 'on-disk storage', {
+    storeUrn: 'urn:solid-server:default:FileResourceStore',
+    setup: (): void => createFolder(rootFilePath),
+    teardown: (): void => removeFolder(rootFilePath),
+  }],
+];
+
+describe.each(stores)('An LDP handler without auth using %s', (name, { storeUrn, setup, teardown }): void => {
+  let handler: HttpHandler;
+  let fileHelper: FileTestHelper;
+
+  beforeAll(async(): Promise<void> => {
+    // Set up the internal store
+    await setup();
+    const variables: Record<string, any> = {
+      'urn:solid-server:default:variable:baseUrl': BASE,
+      'urn:solid-server:default:variable:rootFilePath': rootFilePath,
+    };
+    const internalStore = await instantiateFromConfig(
+      storeUrn,
+      'ldp-with-auth.json',
+      variables,
+    ) as ResourceStore;
+    variables['urn:solid-server:default:variable:store'] = internalStore;
+
+    // Create and initialize the HTTP handler and related components
+    let initializer: Initializer;
+    const instances = await instantiateFromConfig(
+      'urn:solid-server:test:Instances',
+      'ldp-with-auth.json',
+      variables,
+    ) as Record<string, any>;
+    ({ handler, initializer } = instances);
+    await initializer.handleSafe();
+
+    // Create test helpers for manipulating the components
+    fileHelper = new FileTestHelper(handler, new URL(BASE));
+  });
+
+  afterAll(async(): Promise<void> => {
+    await teardown();
+  });
+
+  it('can add a file to the store, read it and delete it.', async():
+  Promise<void> => {
+    // POST
+    let response = await fileHelper.createFile('../assets/testfile0.txt', 'testfile0.txt', 'text/plain');
+    const id = response._getHeaders().location;
+
+    // GET
+    response = await fileHelper.getFile(id);
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain('TESTFILE0');
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // DELETE
+    await fileHelper.deleteResource(id);
+    await fileHelper.shouldNotExist(id);
+  });
+
+  it('can add and overwrite a file.', async(): Promise<void> => {
+    let response = await fileHelper.createFile('../assets/testfile0.txt', 'file.txt', 'text/plain');
+    const id = response._getHeaders().location;
+
+    // GET
+    response = await fileHelper.getFile(id);
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain('TESTFILE0');
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // PUT
+    response = await fileHelper.overwriteFile('../assets/testfile1.txt', id, 'text/plain');
+
+    // GET
+    response = await fileHelper.getFile(id);
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain('TESTFILE1');
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // DELETE
+    await fileHelper.deleteResource(id);
+    await fileHelper.shouldNotExist(id);
+  });
+
+  it('can create a folder and delete it.', async(): Promise<void> => {
+    // POST
+    let response = await fileHelper.createFolder('secondfolder/');
+    const id = response._getHeaders().location;
+
+    // GET
+    response = await fileHelper.getFolder(id);
+    expect(response.statusCode).toBe(200);
+    expect(response.getHeaders().link).toEqual(
+      [ `<${LDP.Container}>; rel="type"`, `<${LDP.BasicContainer}>; rel="type"`, `<${LDP.Resource}>; rel="type"` ],
+    );
+
+    // DELETE
+    await fileHelper.deleteResource(id);
+    await fileHelper.shouldNotExist(id);
+  });
+
+  it('can make a folder and put a file in it.', async(): Promise<void> => {
+    // Create folder
+    await fileHelper.createFolder('testfolder0/');
+
+    // Create file
+    let response = await fileHelper.createFile('../assets/testfile0.txt', 'testfolder0/testfile0.txt', 'text/plain');
+    const id = response._getHeaders().location;
+
+    // GET File
+    response = await fileHelper.getFile(id);
+    expect(response.statusCode).toBe(200);
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // DELETE
+    await fileHelper.deleteResource(id);
+    await fileHelper.shouldNotExist(id);
+    await fileHelper.deleteResource('http://test.com/testfolder0/');
+    await fileHelper.shouldNotExist('http://test.com/testfolder0/');
+  });
+
+  it('cannot remove a folder when the folder contains a file.', async(): Promise<void> => {
+    // Create folder
+    let response = await fileHelper.createFolder('testfolder1/');
+    const folderId = response._getHeaders().location;
+
+    // Create file
+    await fileHelper.createFile('../assets/testfile0.txt', 'testfolder1/testfile0.txt', 'text/plain');
+
+    // Try DELETE folder
+    response = await fileHelper.simpleCall(new URL(folderId), 'DELETE', {});
+    expect(response.statusCode).toBe(409);
+    expect(response._getData()).toContain('ConflictHttpError: Can only delete empty containers.');
+
+    // DELETE
+    await fileHelper.deleteResource('http://test.com/testfolder1/testfile0.txt');
+    await fileHelper.shouldNotExist('http://test.com/testfolder1/testfile0.txt');
+    await fileHelper.deleteResource(folderId);
+    await fileHelper.shouldNotExist(folderId);
+  });
+
+  it('cannot remove a folder when the folder contains a subfolder.', async(): Promise<void> => {
+    // Create folder
+    let response = await fileHelper.createFolder('testfolder2/');
+    const folderId = response._getHeaders().location;
+
+    // Create subfolder
+    response = await fileHelper.createFolder('testfolder2/subfolder0');
+    const subFolderId = response._getHeaders().location;
+
+    // Try DELETE folder
+    response = await fileHelper.simpleCall(new URL(folderId), 'DELETE', {});
+    expect(response.statusCode).toBe(409);
+    expect(response._getData()).toContain('ConflictHttpError: Can only delete empty containers.');
+
+    // DELETE
+    await fileHelper.deleteResource(subFolderId);
+    await fileHelper.shouldNotExist(subFolderId);
+    await fileHelper.deleteResource(folderId);
+    await fileHelper.shouldNotExist(folderId);
+  });
+
+  it('can read the contents of a folder.', async(): Promise<void> => {
+    // Create folder
+    let response = await fileHelper.createFolder('testfolder3/');
+    const folderId = response._getHeaders().location;
+
+    // Create subfolder
+    response = await fileHelper.createFolder('testfolder3/subfolder0/');
+    const subFolderId = response._getHeaders().location;
+
+    // Create file
+    response = await fileHelper.createFile('../assets/testfile0.txt', 'testfolder3/testfile0.txt', 'text/plain');
+    const fileId = response._getHeaders().location;
+
+    response = await fileHelper.getFolder(folderId);
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain('<http://www.w3.org/ns/ldp#contains> <http://test.com/testfolder3/subfolder0/> .');
+    expect(response._getBuffer().toString()).toContain('<http://www.w3.org/ns/ldp#contains> <http://test.com/testfolder3/testfile0.txt> .');
+    expect(response.getHeaders().link).toEqual(
+      [ `<${LDP.Container}>; rel="type"`, `<${LDP.BasicContainer}>; rel="type"`, `<${LDP.Resource}>; rel="type"` ],
+    );
+
+    // DELETE
+    await fileHelper.deleteResource(fileId);
+    await fileHelper.shouldNotExist(fileId);
+    await fileHelper.deleteResource(subFolderId);
+    await fileHelper.shouldNotExist(subFolderId);
+    await fileHelper.deleteResource(folderId);
+    await fileHelper.shouldNotExist(folderId);
+  });
+
+  it('can upload and delete a image.', async(): Promise<void> => {
+    let response = await fileHelper.createFile('../assets/testimage.png', 'image.png', 'image/png');
+    const fileId = response._getHeaders().location;
+
+    // GET
+    response = await fileHelper.getFile(fileId);
+    expect(response.statusCode).toBe(200);
+    expect(response._getHeaders()['content-type']).toBe('image/png');
+
+    // DELETE
+    await fileHelper.deleteResource(fileId);
+    await fileHelper.shouldNotExist(fileId);
+  });
+});
diff --git a/test/integration/LockingResourceStore.test.ts b/test/integration/LockingResourceStore.test.ts
index f60ae002e..5e61fbb60 100644
--- a/test/integration/LockingResourceStore.test.ts
+++ b/test/integration/LockingResourceStore.test.ts
@@ -14,7 +14,7 @@ import { SingleThreadedResourceLocker } from '../../src/util/locking/SingleThrea
 import { WrappedExpiringResourceLocker } from '../../src/util/locking/WrappedExpiringResourceLocker';
 import { guardedStreamFrom } from '../../src/util/StreamUtil';
 import { CONTENT_TYPE } from '../../src/util/UriConstants';
-import { BASE } from '../configs/Util';
+import { BASE } from './Config';
 
 describe('A LockingResourceStore', (): void => {
   let path: string;
diff --git a/test/integration/LpdHandlerOperations.test.ts b/test/integration/LpdHandlerOperations.test.ts
new file mode 100644
index 000000000..b3552c1e2
--- /dev/null
+++ b/test/integration/LpdHandlerOperations.test.ts
@@ -0,0 +1,181 @@
+import 'jest-rdf';
+import * as url from 'url';
+import { namedNode, quad } from '@rdfjs/data-model';
+import { Parser } from 'n3';
+import type { MockResponse } from 'node-mocks-http';
+import type { HttpHandler } from '../../src/server/HttpHandler';
+import { LDP } from '../../src/util/UriConstants';
+import { call } from '../util/Util';
+import { BASE, instantiateFromConfig } from './Config';
+
+describe('An integrated AuthenticatedLdpHandler', (): void => {
+  let handler: HttpHandler;
+
+  beforeAll(async(): Promise<void> => {
+    handler = await instantiateFromConfig(
+      'urn:solid-server:default:LdpHandler', 'server-without-auth.json', {
+        'urn:solid-server:default:variable:baseUrl': BASE,
+      },
+    ) as HttpHandler;
+  });
+
+  it('can add, read and delete data based on incoming requests.', async(): Promise<void> => {
+    // POST
+    let requestUrl = new URL('http://test.com/');
+    let response: MockResponse<any> = await call(
+      handler,
+      requestUrl,
+      'POST',
+      { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
+      [ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ],
+    );
+    expect(response.statusCode).toBe(201);
+    expect(response._getData()).toHaveLength(0);
+    const id = response._getHeaders().location;
+    expect(id).toContain(url.format(requestUrl));
+
+    // GET
+    requestUrl = new URL(id);
+    response = await call(
+      handler,
+      requestUrl,
+      'GET',
+      { accept: 'text/turtle' },
+      [],
+    );
+    expect(response.statusCode).toBe(200);
+    expect(response._getData()).toContain(
+      '<http://test.com/s> <http://test.com/p> <http://test.com/o>.',
+    );
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+
+    // DELETE
+    response = await call(handler, requestUrl, 'DELETE', {}, []);
+    expect(response.statusCode).toBe(205);
+    expect(response._getData()).toHaveLength(0);
+
+    // GET
+    response = await call(
+      handler,
+      requestUrl,
+      'GET',
+      { accept: 'text/turtle' },
+      [],
+    );
+    expect(response.statusCode).toBe(404);
+    expect(response._getData()).toContain('NotFoundHttpError');
+  });
+
+  it('can handle simple SPARQL updates.', async(): Promise<void> => {
+    // POST
+    let requestUrl = new URL('http://test.com/');
+    let response: MockResponse<any> = await call(
+      handler,
+      requestUrl,
+      'POST',
+      { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
+      [ '<http://test.com/s1> <http://test.com/p1> <http://test.com/o1>.',
+        '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.' ],
+    );
+    expect(response.statusCode).toBe(201);
+    expect(response._getData()).toHaveLength(0);
+    const id = response._getHeaders().location;
+    expect(id).toContain(url.format(requestUrl));
+
+    // PATCH
+    requestUrl = new URL(id);
+    response = await call(
+      handler,
+      requestUrl,
+      'PATCH',
+      { 'content-type': 'application/sparql-update', 'transfer-encoding': 'chunked' },
+      [ 'DELETE { <http://test.com/s1> <http://test.com/p1> <http://test.com/o1> }',
+        'INSERT {<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>}',
+        'WHERE {}',
+      ],
+    );
+    expect(response.statusCode).toBe(205);
+    expect(response._getData()).toHaveLength(0);
+
+    // GET
+    requestUrl = new URL(id);
+    response = await call(
+      handler,
+      requestUrl,
+      'GET',
+      { accept: 'text/turtle' },
+      [],
+    );
+    expect(response.statusCode).toBe(200);
+    expect(response._getBuffer().toString()).toContain(
+      '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.',
+    );
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+    const parser = new Parser();
+    const triples = parser.parse(response._getBuffer().toString());
+    expect(triples).toBeRdfIsomorphic([
+      quad(
+        namedNode('http://test.com/s2'),
+        namedNode('http://test.com/p2'),
+        namedNode('http://test.com/o2'),
+      ),
+      quad(
+        namedNode('http://test.com/s3'),
+        namedNode('http://test.com/p3'),
+        namedNode('http://test.com/o3'),
+      ),
+    ]);
+  });
+
+  it('should overwrite the content on PUT request.', async(): Promise<void> => {
+    // POST
+    let requestUrl = new URL('http://test.com/');
+    let response: MockResponse<any> = await call(
+      handler,
+      requestUrl,
+      'POST',
+      { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
+      [
+        '<http://test.com/s1> <http://test.com/p1> <http://test.com/o1>.',
+        '<http://test.com/s2> <http://test.com/p2> <http://test.com/o2>.',
+      ],
+    );
+    expect(response.statusCode).toBe(201);
+    expect(response._getData()).toHaveLength(0);
+    const id = response._getHeaders().location;
+    expect(id).toContain(url.format(requestUrl));
+
+    // PUT
+    requestUrl = new URL(id);
+    response = await call(
+      handler,
+      requestUrl,
+      'PUT',
+      { 'content-type': 'text/turtle', 'transfer-encoding': 'chunked' },
+      [ '<http://test.com/s3> <http://test.com/p3> <http://test.com/o3>.' ],
+    );
+    expect(response.statusCode).toBe(205);
+    expect(response._getData()).toHaveLength(0);
+
+    // GET
+    requestUrl = new URL(id);
+    response = await call(
+      handler,
+      requestUrl,
+      'GET',
+      { accept: 'text/turtle' },
+      [],
+    );
+    expect(response.statusCode).toBe(200);
+    expect(response.getHeaders().link).toBe(`<${LDP.Resource}>; rel="type"`);
+    const parser = new Parser();
+    const triples = parser.parse(response._getData());
+    expect(triples).toBeRdfIsomorphic([
+      quad(
+        namedNode('http://test.com/s3'),
+        namedNode('http://test.com/p3'),
+        namedNode('http://test.com/o3'),
+      ),
+    ]);
+  });
+});
diff --git a/test/integration/Middleware.test.ts b/test/integration/Middleware.test.ts
index c60085552..660040a0a 100644
--- a/test/integration/Middleware.test.ts
+++ b/test/integration/Middleware.test.ts
@@ -4,8 +4,8 @@ import type { ExpressHttpServerFactory } from '../../src/server/ExpressHttpServe
 import { HttpHandler } from '../../src/server/HttpHandler';
 import type { HttpRequest } from '../../src/server/HttpRequest';
 import type { HttpResponse } from '../../src/server/HttpResponse';
-import { instantiateFromConfig } from '../configs/Util';
 import { StaticAsyncHandler } from '../util/StaticAsyncHandler';
+import { instantiateFromConfig } from './Config';
 
 const port = 6002;
 
@@ -21,7 +21,7 @@ describe('An Express server with middleware', (): void => {
 
   beforeAll(async(): Promise<void> => {
     const factory = await instantiateFromConfig(
-      'urn:solid-server:default:ExpressHttpServerFactory', 'middleware.json', {
+      'urn:solid-server:default:ExpressHttpServerFactory', 'server-middleware.json', {
         'urn:solid-server:default:PodManagerHandler': new StaticAsyncHandler(false, null),
         'urn:solid-server:default:LdpHandler': new SimpleHttpHandler(),
         'urn:solid-server:default:variable:port': port,
diff --git a/test/integration/PodCreation.test.ts b/test/integration/PodCreation.test.ts
index 9250d1008..68951044f 100644
--- a/test/integration/PodCreation.test.ts
+++ b/test/integration/PodCreation.test.ts
@@ -3,7 +3,7 @@ import { join } from 'path';
 import fetch from 'cross-fetch';
 import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
 import { readableToString } from '../../src/util/StreamUtil';
-import { initServerStore, instantiateFromConfig } from '../configs/Util';
+import { instantiateFromConfig } from './Config';
 
 const port = 6003;
 const baseUrl = `http://localhost:${port}/`;
@@ -14,14 +14,13 @@ describe('A server with a pod handler', (): void => {
 
   beforeAll(async(): Promise<void> => {
     const factory = await instantiateFromConfig(
-      'urn:solid-server:default:ServerFactory', 'auth-allow-all.json', {
+      'urn:solid-server:default:ServerFactory', 'server-without-auth.json', {
         'urn:solid-server:default:variable:port': port,
         'urn:solid-server:default:variable:baseUrl': baseUrl,
         'urn:solid-server:default:variable:podTemplateFolder': join(__dirname, '../assets/templates'),
       },
     ) as HttpServerFactory;
     server = factory.startServer(port);
-    await initServerStore(server, baseUrl);
   });
 
   afterAll(async(): Promise<void> => {
diff --git a/test/integration/Authorization.test.ts b/test/integration/ServerWithAuth.test.ts
similarity index 63%
rename from test/integration/Authorization.test.ts
rename to test/integration/ServerWithAuth.test.ts
index 65019d85f..21a48f8c5 100644
--- a/test/integration/Authorization.test.ts
+++ b/test/integration/ServerWithAuth.test.ts
@@ -1,20 +1,38 @@
 import type { MockResponse } from 'node-mocks-http';
-import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
-import { BasicHandlersWithAclConfig } from '../configs/BasicHandlersWithAclConfig';
-import { BASE } from '../configs/Util';
+import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
 import { AclTestHelper } from '../util/TestHelpers';
 import { call } from '../util/Util';
+import { BASE, instantiateFromConfig } from './Config';
 
 describe('A server with authorization', (): void => {
-  const config = new BasicHandlersWithAclConfig();
-  const handler = config.getHttpHandler();
-  const { store } = config;
-  const aclHelper = new AclTestHelper(store, 'http://test.com/');
+  let handler: HttpHandler;
+  let aclHelper: AclTestHelper;
 
   beforeAll(async(): Promise<void> => {
-    // Initialize store
-    const initializer = new RootContainerInitializer(BASE, config.store);
+    // Set up the internal store
+    const variables: Record<string, any> = {
+      'urn:solid-server:default:variable:baseUrl': BASE,
+    };
+    const internalStore = await instantiateFromConfig(
+      'urn:solid-server:default:MemoryResourceStore',
+      'ldp-with-auth.json',
+      variables,
+    ) as ResourceStore;
+    variables['urn:solid-server:default:variable:store'] = internalStore;
+
+    // Create and initialize the HTTP handler and related components
+    let initializer: Initializer;
+    let store: ResourceStore;
+    const instances = await instantiateFromConfig(
+      'urn:solid-server:test:Instances',
+      'ldp-with-auth.json',
+      variables,
+    ) as Record<string, any>;
+    ({ handler, store, initializer } = instances);
     await initializer.handleSafe();
+
+    // Create test helpers for manipulating the components
+    aclHelper = new AclTestHelper(store, BASE);
   });
 
   it('can create new entries.', async(): Promise<void> => {
@@ -43,7 +61,7 @@ describe('A server with authorization', (): void => {
     expect(response.statusCode).toBe(205);
   });
 
-  it('can not create new entries if not allowed.', async(): Promise<void> => {
+  it('cannot create new entries if not allowed.', async(): Promise<void> => {
     await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'authenticated');
 
     // POST
diff --git a/test/integration/SparqlStorage.test.ts b/test/integration/SparqlStorage.test.ts
index c3dc69b46..431efacaa 100644
--- a/test/integration/SparqlStorage.test.ts
+++ b/test/integration/SparqlStorage.test.ts
@@ -1,31 +1,43 @@
-import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
-import { SparqlDataAccessor } from '../../src/storage/accessors/SparqlDataAccessor';
-import { INTERNAL_QUADS } from '../../src/util/ContentTypes';
-import { SingleRootIdentifierStrategy } from '../../src/util/identifiers/SingleRootIdentifierStrategy';
-import { DataAccessorBasedConfig } from '../configs/DataAccessorBasedConfig';
-import { BASE } from '../configs/Util';
+import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
 import { describeIf, FileTestHelper } from '../util/TestHelpers';
+import { BASE, instantiateFromConfig } from './Config';
 
-describeIf('docker', 'a server with a SPARQL endpoint as storage', (): void => {
-  describe('without acl', (): void => {
-    const config = new DataAccessorBasedConfig(BASE,
-      new SparqlDataAccessor('http://localhost:4000/sparql', new SingleRootIdentifierStrategy(BASE)),
-      INTERNAL_QUADS);
-    const handler = config.getHttpHandler();
-    const fileHelper = new FileTestHelper(handler, new URL(BASE));
+describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
+  let handler: HttpHandler;
+  let fileHelper: FileTestHelper;
 
-    beforeAll(async(): Promise<void> => {
-      // Initialize store
-      const initializer = new RootContainerInitializer(BASE, config.store);
-      await initializer.handleSafe();
-    });
+  beforeAll(async(): Promise<void> => {
+    // Set up the internal store
+    const variables: Record<string, any> = {
+      'urn:solid-server:default:variable:baseUrl': BASE,
+      'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
+    };
+    const internalStore = await instantiateFromConfig(
+      'urn:solid-server:default:SparqlResourceStore',
+      'ldp-with-auth.json',
+      variables,
+    ) as ResourceStore;
+    variables['urn:solid-server:default:variable:store'] = internalStore;
 
-    it('can add a Turtle file to the store.', async():
-    Promise<void> => {
-      // POST
-      const response = await fileHelper.createFile('../assets/person.ttl', 'person', 'text/turtle');
-      const id = response._getHeaders().location;
-      expect(id).toBeTruthy();
-    });
+    // Create and initialize the HTTP handler and related components
+    let initializer: Initializer;
+    const instances = await instantiateFromConfig(
+      'urn:solid-server:test:Instances',
+      'ldp-with-auth.json',
+      variables,
+    ) as Record<string, any>;
+    ({ handler, initializer } = instances);
+    await initializer.handleSafe();
+
+    // Create test helpers for manipulating the components
+    fileHelper = new FileTestHelper(handler, new URL(BASE));
+  });
+
+  it('can add a Turtle file to the store.', async():
+  Promise<void> => {
+    // POST
+    const response = await fileHelper.createFile('../assets/person.ttl', 'person', 'text/turtle');
+    const id = response._getHeaders().location;
+    expect(id).toBeTruthy();
   });
 });
diff --git a/test/integration/WebSocketsProtocol.test.ts b/test/integration/WebSocketsProtocol.test.ts
index 83132bc6e..f8a7e3866 100644
--- a/test/integration/WebSocketsProtocol.test.ts
+++ b/test/integration/WebSocketsProtocol.test.ts
@@ -2,7 +2,7 @@ import type { Server } from 'http';
 import fetch from 'cross-fetch';
 import WebSocket from 'ws';
 import type { HttpServerFactory } from '../../src/server/HttpServerFactory';
-import { initServerStore, instantiateFromConfig } from '../configs/Util';
+import { instantiateFromConfig } from './Config';
 
 const port = 6001;
 const serverUrl = `http://localhost:${port}/`;
@@ -13,14 +13,13 @@ describe('A server with the Solid WebSockets API behind a proxy', (): void => {
 
   beforeAll(async(): Promise<void> => {
     const factory = await instantiateFromConfig(
-      'urn:solid-server:default:ServerFactory', 'auth-allow-all.json', {
+      'urn:solid-server:default:ServerFactory', 'server-without-auth.json', {
         'urn:solid-server:default:variable:port': port,
         'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
         'urn:solid-server:default:variable:podTemplateFolder': 'templates',
       },
     ) as HttpServerFactory;
     server = factory.startServer(port);
-    await initServerStore(server, serverUrl, headers);
   });
 
   afterAll(async(): Promise<void> => {
diff --git a/test/integration/config/ldp-with-auth.json b/test/integration/config/ldp-with-auth.json
new file mode 100644
index 000000000..245abe00c
--- /dev/null
+++ b/test/integration/config/ldp-with-auth.json
@@ -0,0 +1,57 @@
+{
+  "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
+  "import": [
+    "files-scs:config/presets/acl.json",
+    "files-scs:config/presets/init.json",
+    "files-scs:config/presets/ldp.json",
+    "files-scs:config/presets/ldp/credentials-extractor.json",
+    "files-scs:config/presets/ldp/metadata-handler.json",
+    "files-scs:config/presets/ldp/operation-handler.json",
+    "files-scs:config/presets/ldp/permissions-extractor.json",
+    "files-scs:config/presets/ldp/response-writer.json",
+    "files-scs:config/presets/ldp/request-parser.json",
+    "files-scs:config/presets/representation-conversion.json",
+    "files-scs:config/presets/storage/backend/storage-memory.json",
+    "files-scs:config/presets/storage/backend/storage-filesystem.json",
+    "files-scs:config/presets/storage/backend/storage-sparql-endpoint.json",
+    "files-scs:config/presets/storage-wrapper.json",
+    "files-scs:config/presets/cli-params.json"
+  ],
+  "@graph": [
+    {
+      "@id": "urn:solid-server:test:Instances",
+      "@type": "RecordObject",
+      "RecordObject:_record": [
+        {
+          "RecordObject:_record_key": "initializer",
+          "RecordObject:_record_value": {
+            "@type": "SequenceHandler",
+            "SequenceHandler:_handlers": [
+              { "@id": "urn:solid-server:default:RootContainerInitializer" },
+              { "@id": "urn:solid-server:default:AclInitializer" }
+            ]
+          }
+        },
+        {
+          "RecordObject:_record_key": "handler",
+          "RecordObject:_record_value": { "@id": "urn:solid-server:default:LdpHandler" }
+        },
+        {
+          "RecordObject:_record_key": "store",
+          "RecordObject:_record_value": { "@id": "urn:solid-server:default:ResourceStore" }
+        }
+      ]
+    },
+    {
+      "@id": "urn:solid-server:default:RoutingResourceStore",
+      "@type": "PassthroughStore",
+      "PassthroughStore:_source": {
+        "@id": "urn:solid-server:default:variable:store"
+      }
+    },
+    {
+      "@id": "urn:solid-server:default:variable:store",
+      "@type": "Variable"
+    }
+  ]
+}
diff --git a/test/configs/middleware.json b/test/integration/config/server-middleware.json
similarity index 100%
rename from test/configs/middleware.json
rename to test/integration/config/server-middleware.json
diff --git a/test/configs/auth-allow-all.json b/test/integration/config/server-without-auth.json
similarity index 96%
rename from test/configs/auth-allow-all.json
rename to test/integration/config/server-without-auth.json
index 3efda7a98..52d700041 100644
--- a/test/configs/auth-allow-all.json
+++ b/test/integration/config/server-without-auth.json
@@ -13,7 +13,6 @@
     "files-scs:config/presets/pod-management.json",
     "files-scs:config/presets/representation-conversion.json",
     "files-scs:config/presets/storage/backend/storage-memory.json",
-    "files-scs:config/presets/storage/routing/no-routing.json",
     "files-scs:config/presets/storage-wrapper.json",
     "files-scs:config/presets/cli-params.json"
   ],
@@ -42,6 +41,7 @@
     },
     {
       "@id": "urn:solid-server:default:RoutingResourceStore",
+      "@type": "PassthroughStore",
       "PassthroughStore:_source": {
         "@id": "urn:solid-server:default:MemoryResourceStore"
       }
diff --git a/test/testData/.gitkeep b/test/testData/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/test/unit/util/RecordObject.test.ts b/test/unit/util/RecordObject.test.ts
new file mode 100644
index 000000000..cc9582854
--- /dev/null
+++ b/test/unit/util/RecordObject.test.ts
@@ -0,0 +1,12 @@
+import { RecordObject } from '../../../src/util/RecordObject';
+
+describe('RecordObject', (): void => {
+  it('returns an empty record when created without parameters.', async(): Promise<void> => {
+    expect(new RecordObject()).toStrictEqual({});
+  });
+
+  it('returns the passed record.', async(): Promise<void> => {
+    const record = { abc: 'def' };
+    expect(new RecordObject(record)).toBe(record);
+  });
+});
diff --git a/test/util/TestHelpers.ts b/test/util/TestHelpers.ts
index 8f21a245e..67eb1ea94 100644
--- a/test/util/TestHelpers.ts
+++ b/test/util/TestHelpers.ts
@@ -6,8 +6,8 @@ import { Readable } from 'stream';
 import * as url from 'url';
 import type { MockResponse } from 'node-mocks-http';
 import { createResponse } from 'node-mocks-http';
-import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/index';
-import { guardedStreamFrom, RepresentationMetadata } from '../../src/index';
+import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/';
+import { guardedStreamFrom, RepresentationMetadata, ensureTrailingSlash } from '../../src/';
 import { CONTENT_TYPE } from '../../src/util/UriConstants';
 import { call } from './Util';
 
@@ -18,7 +18,7 @@ export class AclTestHelper {
 
   public constructor(store: ResourceStore, id: string) {
     this.store = store;
-    this.id = id;
+    this.id = ensureTrailingSlash(id);
   }
 
   public async setSimpleAcl(