Repository: solid/community-server (ts)

Issue:
Initialize DataAccessor/ResourceStore
We should create an initializer that creates the correct metadata for the root container when starting the server. This should only happen if the container does not exist yet (which in practice would mean that this will never happen with the file backend if the folder already exists).

One of the triples should be indicating the root is a `pim:Storage` in preparation of #411. Since we probably really want to set this triple on the root of our storage we might want to use a PATCH for this once #384 is resolved.

This initializer could either target a DataAccessor or a ResourceStore. The problem with using a ResourceStore is that currently there are some assumptions that the root container is always going to exist, so if we want to target the root that assumption will have to be removed (from `DataAccessorBasedStore`).

For SPARQL this also depends on #410 being resolved.

Pull request description:
Create ResourceStoreInitializer for initializing root containers
Depends on #418 and #419 .

Closes #412 (except that we perhaps still want to update this to PATCH in the future).

The change was easy to do (once the related issues were fixed), most of the work was updating the integration tests. Partially due to #305 but also because there are assumptions in the code that the root container is going to exist while only testing part of the code (and thus excluding the store initializer).

There is some overlap with the `AclInitializer` but it didn't seem necessary to generalize that part as these classes are quite simple. But something to think of if this paradigm occurs more times in the future.

Patch:
diff --git a/config/presets/init.json b/config/presets/init.json
index a7aff9a01..901aaa480 100644
--- a/config/presets/init.json
+++ b/config/presets/init.json
@@ -11,6 +11,15 @@
             "@id": "urn:solid-server:default:LoggerFactory"
           }
         },
+        {
+          "@type": "RootContainerInitializer",
+          "RootContainerInitializer:_baseUrl": {
+            "@id": "urn:solid-server:default:variable:baseUrl"
+          },
+          "RootContainerInitializer:_store": {
+            "@id": "urn:solid-server:default:ResourceStore"
+          }
+        },
         {
           "@type": "AclInitializer",
           "AclInitializer:_baseUrl": {
diff --git a/src/index.ts b/src/index.ts
index 38d8bfd36..a81b04b3d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -17,6 +17,7 @@ export * from './init/AclInitializer';
 export * from './init/CliRunner';
 export * from './init/Initializer';
 export * from './init/LoggerInitializer';
+export * from './init/RootContainerInitializer';
 export * from './init/ServerInitializer';
 
 // LDP/HTTP/Metadata
diff --git a/src/init/RootContainerInitializer.ts b/src/init/RootContainerInitializer.ts
new file mode 100644
index 000000000..b9ba9a8f5
--- /dev/null
+++ b/src/init/RootContainerInitializer.ts
@@ -0,0 +1,72 @@
+import { DataFactory } from 'n3';
+import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
+import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
+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 { generateResourceQuads } from '../util/ResourceUtil';
+import { guardedStreamFrom } from '../util/StreamUtil';
+import { PIM, RDF } from '../util/UriConstants';
+import { toNamedNode } from '../util/UriUtil';
+import { Initializer } from './Initializer';
+import namedNode = DataFactory.namedNode;
+
+/**
+ * Initializes ResourceStores by creating a root container if it didn't exist yet.
+ */
+export class RootContainerInitializer extends Initializer {
+  protected readonly logger = getLoggerFor(this);
+  private readonly baseId: ResourceIdentifier;
+  private readonly store: ResourceStore;
+
+  public constructor(baseUrl: string, store: ResourceStore) {
+    super();
+    this.baseId = { path: ensureTrailingSlash(baseUrl) };
+    this.store = store;
+  }
+
+  public async handle(): Promise<void> {
+    if (!await this.hasRootContainer()) {
+      await this.createRootContainer();
+    }
+  }
+
+  /**
+   * Verify if a root container already exists in a ResourceStore.
+   */
+  protected async hasRootContainer(): Promise<boolean> {
+    try {
+      const result = await this.store.getRepresentation(this.baseId, {});
+      this.logger.debug(`Existing root container found at ${this.baseId.path}`);
+      result.data.destroy();
+      return true;
+    } catch (error: unknown) {
+      if (!(error instanceof NotFoundHttpError)) {
+        throw error;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Create a root container in a ResourceStore.
+   */
+  protected async createRootContainer(): Promise<void> {
+    const metadata = new RepresentationMetadata(this.baseId);
+    metadata.addQuads(generateResourceQuads(namedNode(this.baseId.path), true));
+
+    // Make sure the root container is a pim:Storage
+    // This prevents deletion of the root container as storage root containers can not be deleted
+    metadata.add(RDF.type, toNamedNode(PIM.Storage));
+
+    metadata.contentType = TEXT_TURTLE;
+
+    await this.store.setRepresentation(this.baseId, {
+      binary: true,
+      data: guardedStreamFrom([]),
+      metadata,
+    });
+  }
+}
diff --git a/src/storage/DataAccessorBasedStore.ts b/src/storage/DataAccessorBasedStore.ts
index e71b7544a..296e3f853 100644
--- a/src/storage/DataAccessorBasedStore.ts
+++ b/src/storage/DataAccessorBasedStore.ts
@@ -218,7 +218,8 @@ export class DataAccessorBasedStore implements ResourceStore {
       await this.handleContainerData(representation);
     }
 
-    if (createContainers) {
+    // Root container should not have a parent container
+    if (createContainers && !this.identifierStrategy.isRootContainer(identifier)) {
       await this.createRecursiveContainers(this.identifierStrategy.getParentContainer(identifier));
     }
 
diff --git a/src/storage/accessors/SparqlDataAccessor.ts b/src/storage/accessors/SparqlDataAccessor.ts
index 3bb9f13fe..0b3b60f12 100644
--- a/src/storage/accessors/SparqlDataAccessor.ts
+++ b/src/storage/accessors/SparqlDataAccessor.ts
@@ -27,7 +27,6 @@ import { guardStream } from '../../util/GuardedStream';
 import type { Guarded } from '../../util/GuardedStream';
 import type { IdentifierStrategy } from '../../util/identifiers/IdentifierStrategy';
 import { isContainerIdentifier } from '../../util/PathUtil';
-import { generateResourceQuads } from '../../util/ResourceUtil';
 import { CONTENT_TYPE, LDP } from '../../util/UriConstants';
 import { toNamedNode } from '../../util/UriUtil';
 import type { DataAccessor } from './DataAccessor';
@@ -90,8 +89,7 @@ export class SparqlDataAccessor implements DataAccessor {
     const stream = await this.sendSparqlConstruct(query);
     const quads = await arrayifyStream(stream);
 
-    // Root container will not have metadata if there are no containment triples
-    if (quads.length === 0 && !this.identifierStrategy.isRootContainer(identifier)) {
+    if (quads.length === 0) {
       throw new NotFoundHttpError();
     }
 
@@ -100,11 +98,6 @@ export class SparqlDataAccessor implements DataAccessor {
       metadata.contentType = INTERNAL_QUADS;
     }
 
-    // Need to generate type metadata for the root container since it's not stored
-    if (this.identifierStrategy.isRootContainer(identifier)) {
-      metadata.addQuads(generateResourceQuads(name, true));
-    }
-
     return metadata;
   }
 
diff --git a/src/util/UriConstants.ts b/src/util/UriConstants.ts
index 861ba1e91..58058f887 100644
--- a/src/util/UriConstants.ts
+++ b/src/util/UriConstants.ts
@@ -46,6 +46,11 @@ export const MA = {
   format: MA_PREFIX('format'),
 };
 
+const PIM_PREFIX = createNamespace('http://www.w3.org/ns/pim/space#');
+export const PIM = {
+  Storage: PIM_PREFIX('Storage'),
+};
+
 const POSIX_PREFIX = createNamespace('http://www.w3.org/ns/posix/stat#');
 export const POSIX = {
   mtime: POSIX_PREFIX('mtime'),


Test patch:
diff --git a/test/configs/Util.ts b/test/configs/Util.ts
index a39f87fe1..13a7b2589 100644
--- a/test/configs/Util.ts
+++ b/test/configs/Util.ts
@@ -1,6 +1,8 @@
+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,
@@ -24,6 +26,7 @@ import {
   ErrorResponseWriter,
   GetOperationHandler,
   HeadOperationHandler,
+  HttpError,
   InMemoryDataAccessor,
   LinkRelMetadataWriter,
   LinkTypeParser,
@@ -192,3 +195,21 @@ export const instantiateFromConfig = async(componentUrl: string, configFile: str
   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: HeadersInit = {}): 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
index 2723dcddb..f80f8c07e 100644
--- a/test/integration/AuthenticatedLdpHandler.test.ts
+++ b/test/integration/AuthenticatedLdpHandler.test.ts
@@ -3,14 +3,23 @@ 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 handler = new BasicConfig().getHttpHandler();
+    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
@@ -61,7 +70,14 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
   });
 
   describe('with simple PATCH handlers', (): void => {
-    const handler = new BasicHandlersConfig().getHttpHandler();
+    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
@@ -126,7 +142,14 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
   });
 
   describe('with simple PUT handlers', (): void => {
-    const handler = new BasicHandlersConfig().getHttpHandler();
+    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
diff --git a/test/integration/Authorization.test.ts b/test/integration/Authorization.test.ts
index 480179a12..65019d85f 100644
--- a/test/integration/Authorization.test.ts
+++ b/test/integration/Authorization.test.ts
@@ -1,5 +1,7 @@
 import type { MockResponse } from 'node-mocks-http';
+import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
 import { BasicHandlersWithAclConfig } from '../configs/BasicHandlersWithAclConfig';
+import { BASE } from '../configs/Util';
 import { AclTestHelper } from '../util/TestHelpers';
 import { call } from '../util/Util';
 
@@ -9,6 +11,12 @@ describe('A server with authorization', (): void => {
   const { store } = config;
   const aclHelper = new AclTestHelper(store, 'http://test.com/');
 
+  beforeAll(async(): Promise<void> => {
+    // Initialize store
+    const initializer = new RootContainerInitializer(BASE, config.store);
+    await initializer.handleSafe();
+  });
+
   it('can create new entries.', async(): Promise<void> => {
     await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'agent');
 
diff --git a/test/integration/FullConfig.acl.test.ts b/test/integration/FullConfig.acl.test.ts
index 9a747a568..89e73846b 100644
--- a/test/integration/FullConfig.acl.test.ts
+++ b/test/integration/FullConfig.acl.test.ts
@@ -1,6 +1,7 @@
 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';
@@ -40,6 +41,10 @@ describe.each([ dataAccessorStore, inMemoryDataAccessorStore ])('A server using
       // 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,
diff --git a/test/integration/FullConfig.noAuth.test.ts b/test/integration/FullConfig.noAuth.test.ts
index e0a912368..4b83a4ba1 100644
--- a/test/integration/FullConfig.noAuth.test.ts
+++ b/test/integration/FullConfig.noAuth.test.ts
@@ -1,5 +1,6 @@
 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';
@@ -35,6 +36,10 @@ describe.each(configs)('A server using a %s', (name, configFn): void => {
       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> => {
diff --git a/test/integration/LockingResourceStore.test.ts b/test/integration/LockingResourceStore.test.ts
index 3c04fd9a3..f60ae002e 100644
--- a/test/integration/LockingResourceStore.test.ts
+++ b/test/integration/LockingResourceStore.test.ts
@@ -1,4 +1,5 @@
 import streamifyArray from 'streamify-array';
+import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
 import type { Representation } from '../../src/ldp/representation/Representation';
 import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
 import { InMemoryDataAccessor } from '../../src/storage/accessors/InMemoryDataAccessor';
@@ -13,6 +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';
 
 describe('A LockingResourceStore', (): void => {
   let path: string;
@@ -28,6 +30,10 @@ describe('A LockingResourceStore', (): void => {
     path = `${base}path`;
     source = new DataAccessorBasedStore(new InMemoryDataAccessor(base), new SingleRootIdentifierStrategy(base));
 
+    // Initialize store
+    const initializer = new RootContainerInitializer(BASE, source);
+    await initializer.handleSafe();
+
     locker = new SingleThreadedResourceLocker();
     expiringLocker = new WrappedExpiringResourceLocker(locker, 1000);
 
diff --git a/test/integration/PodCreation.test.ts b/test/integration/PodCreation.test.ts
index f6737f384..9250d1008 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 { instantiateFromConfig } from '../configs/Util';
+import { initServerStore, instantiateFromConfig } from '../configs/Util';
 
 const port = 6003;
 const baseUrl = `http://localhost:${port}/`;
@@ -21,6 +21,7 @@ describe('A server with a pod handler', (): void => {
       },
     ) as HttpServerFactory;
     server = factory.startServer(port);
+    await initServerStore(server, baseUrl);
   });
 
   afterAll(async(): Promise<void> => {
diff --git a/test/integration/SparqlStorage.test.ts b/test/integration/SparqlStorage.test.ts
index e161cfad7..c3dc69b46 100644
--- a/test/integration/SparqlStorage.test.ts
+++ b/test/integration/SparqlStorage.test.ts
@@ -1,3 +1,4 @@
+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';
@@ -13,6 +14,12 @@ describeIf('docker', 'a server with a SPARQL endpoint as storage', (): void => {
     const handler = config.getHttpHandler();
     const fileHelper = new FileTestHelper(handler, new URL(BASE));
 
+    beforeAll(async(): Promise<void> => {
+      // Initialize store
+      const initializer = new RootContainerInitializer(BASE, config.store);
+      await initializer.handleSafe();
+    });
+
     it('can add a Turtle file to the store.', async():
     Promise<void> => {
       // POST
diff --git a/test/integration/WebSocketsProtocol.test.ts b/test/integration/WebSocketsProtocol.test.ts
index a118c9ce1..83132bc6e 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 { instantiateFromConfig } from '../configs/Util';
+import { initServerStore, instantiateFromConfig } from '../configs/Util';
 
 const port = 6001;
 const serverUrl = `http://localhost:${port}/`;
@@ -20,6 +20,7 @@ describe('A server with the Solid WebSockets API behind a proxy', (): void => {
       },
     ) as HttpServerFactory;
     server = factory.startServer(port);
+    await initServerStore(server, serverUrl, headers);
   });
 
   afterAll(async(): Promise<void> => {
diff --git a/test/unit/init/RootContainerInitializer.test.ts b/test/unit/init/RootContainerInitializer.test.ts
new file mode 100644
index 000000000..ba7942f82
--- /dev/null
+++ b/test/unit/init/RootContainerInitializer.test.ts
@@ -0,0 +1,41 @@
+import { RootContainerInitializer } from '../../../src/init/RootContainerInitializer';
+import type { ResourceStore } from '../../../src/storage/ResourceStore';
+import { NotFoundHttpError } from '../../../src/util/errors/NotFoundHttpError';
+
+describe('A RootContainerInitializer', (): void => {
+  const baseUrl = 'http://test.com/';
+  const store: jest.Mocked<ResourceStore> = {
+    getRepresentation: jest.fn().mockRejectedValue(new NotFoundHttpError()),
+    setRepresentation: jest.fn(),
+  } as any;
+  const initializer = new RootContainerInitializer(baseUrl, store);
+
+  afterEach((): void => {
+    jest.clearAllMocks();
+  });
+
+  it('invokes ResourceStore initialization.', async(): Promise<void> => {
+    await initializer.handle();
+
+    expect(store.getRepresentation).toHaveBeenCalledTimes(1);
+    expect(store.getRepresentation).toHaveBeenCalledWith({ path: baseUrl }, {});
+    expect(store.setRepresentation).toHaveBeenCalledTimes(1);
+  });
+
+  it('does not invoke ResourceStore initialization when a root container already exists.', async(): Promise<void> => {
+    store.getRepresentation.mockReturnValueOnce(Promise.resolve({
+      data: { destroy: jest.fn() },
+    } as any));
+
+    await initializer.handle();
+
+    expect(store.getRepresentation).toHaveBeenCalledTimes(1);
+    expect(store.getRepresentation).toHaveBeenCalledWith({ path: 'http://test.com/' }, {});
+    expect(store.setRepresentation).toHaveBeenCalledTimes(0);
+  });
+
+  it('errors when the store errors writing the root container.', async(): Promise<void> => {
+    store.getRepresentation.mockRejectedValueOnce(new Error('Fatal'));
+    await expect(initializer.handle()).rejects.toThrow('Fatal');
+  });
+});
diff --git a/test/unit/storage/DataAccessorBasedStore.test.ts b/test/unit/storage/DataAccessorBasedStore.test.ts
index 29a14c642..3512dc413 100644
--- a/test/unit/storage/DataAccessorBasedStore.test.ts
+++ b/test/unit/storage/DataAccessorBasedStore.test.ts
@@ -343,6 +343,21 @@ describe('A DataAccessorBasedStore', (): void => {
         new ConflictHttpError(`Creating container ${root}a/ conflicts with an existing resource.`),
       );
     });
+
+    it('can write to root if it does not exist.', async(): Promise<void> => {
+      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+      delete accessor.data[root];
+      const resourceID = { path: `${root}` };
+
+      // Generate based on URI
+      representation.metadata.removeAll(RDF.type);
+      representation.metadata.contentType = 'text/turtle';
+      representation.data = guardedStreamFrom([]);
+      await expect(store.setRepresentation(resourceID, representation)).resolves.toBeUndefined();
+      expect(accessor.data[resourceID.path]).toBeTruthy();
+      expect(Object.keys(accessor.data)).toHaveLength(1);
+      expect(accessor.data[resourceID.path].metadata.contentType).toBeUndefined();
+    });
   });
 
   describe('modifying a Representation', (): void => {
diff --git a/test/unit/storage/accessors/InMemoryDataAccessor.test.ts b/test/unit/storage/accessors/InMemoryDataAccessor.test.ts
index 8c61102d9..a83bafec1 100644
--- a/test/unit/storage/accessors/InMemoryDataAccessor.test.ts
+++ b/test/unit/storage/accessors/InMemoryDataAccessor.test.ts
@@ -18,6 +18,9 @@ describe('An InMemoryDataAccessor', (): void => {
   beforeEach(async(): Promise<void> => {
     accessor = new InMemoryDataAccessor(base);
 
+    // Create default root container
+    await accessor.writeContainer({ path: `${base}` }, new RepresentationMetadata());
+
     metadata = new RepresentationMetadata({ [CONTENT_TYPE]: APPLICATION_OCTET_STREAM });
 
     data = guardedStreamFrom([ 'data' ]);
diff --git a/test/unit/storage/accessors/SparqlDataAccessor.test.ts b/test/unit/storage/accessors/SparqlDataAccessor.test.ts
index 4e4f3f8bb..0d838dbc1 100644
--- a/test/unit/storage/accessors/SparqlDataAccessor.test.ts
+++ b/test/unit/storage/accessors/SparqlDataAccessor.test.ts
@@ -121,26 +121,6 @@ describe('A SparqlDataAccessor', (): void => {
     ]));
   });
 
-  it('generates resource metadata for the root container.', async(): Promise<void> => {
-    metadata = await accessor.getMetadata({ path: base });
-    expect(metadata.quads()).toBeRdfIsomorphic([
-      quad(namedNode('this'), namedNode('a'), namedNode('triple')),
-      quad(namedNode(base), toNamedNode(RDF.type), toNamedNode(LDP.Container)),
-      quad(namedNode(base), toNamedNode(RDF.type), toNamedNode(LDP.BasicContainer)),
-      quad(namedNode(base), toNamedNode(RDF.type), toNamedNode(LDP.Resource)),
-    ]);
-
-    expect(fetchTriples).toHaveBeenCalledTimes(1);
-    expect(fetchTriples.mock.calls[0][0]).toBe(endpoint);
-    expect(simplifyQuery(fetchTriples.mock.calls[0][1])).toBe(simplifyQuery([
-      'CONSTRUCT { ?s ?p ?o. } WHERE {',
-      `  { GRAPH <${base}> { ?s ?p ?o. } }`,
-      '  UNION',
-      `  { GRAPH <meta:${base}> { ?s ?p ?o. } }`,
-      '}',
-    ]));
-  });
-
   it('throws 404 if no metadata was found.', async(): Promise<void> => {
     // Clear triples array
     triples = [];