mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Create RootInitializer to set up root resources
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import { AclInitializer } from '../../../src/init/AclInitializer';
|
||||
import type { AuxiliaryIdentifierStrategy } from '../../../src/ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import { BasicRepresentation } from '../../../src/ldp/representation/BasicRepresentation';
|
||||
import type { ResourceStore } from '../../../src/storage/ResourceStore';
|
||||
import { InternalServerError } from '../../../src/util/errors/InternalServerError';
|
||||
import { joinFilePath } from '../../../src/util/PathUtil';
|
||||
|
||||
const createReadStream = jest.spyOn(fs, 'createReadStream').mockReturnValue('file contents' as any);
|
||||
|
||||
jest.mock('../../../src/ldp/representation/BasicRepresentation');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const RepresentationMock: jest.Mock<BasicRepresentation> = BasicRepresentation as any;
|
||||
|
||||
describe('AclInitializer', (): void => {
|
||||
const store: jest.Mocked<ResourceStore> = {
|
||||
setRepresentation: jest.fn(),
|
||||
resourceExists: jest.fn().mockImplementation((): any => false),
|
||||
} as any;
|
||||
const aclIdentifier = { path: 'http://test.com/.acl' };
|
||||
const aclStrategy: jest.Mocked<AuxiliaryIdentifierStrategy> = {
|
||||
getAuxiliaryIdentifier: jest.fn().mockReturnValue(aclIdentifier),
|
||||
} as any;
|
||||
const baseUrl = 'http://localhost:3000/';
|
||||
|
||||
afterEach((): void => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('sets the default ACL when none exists already.', async(): Promise<void> => {
|
||||
const initializer = new AclInitializer({ baseUrl, store, aclStrategy });
|
||||
await initializer.handle();
|
||||
|
||||
expect(aclStrategy.getAuxiliaryIdentifier).toHaveBeenCalledWith({ path: baseUrl });
|
||||
expect(store.resourceExists).toHaveBeenCalledTimes(1);
|
||||
expect(store.resourceExists).toHaveBeenCalledWith(aclIdentifier);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||
expect(store.setRepresentation).toHaveBeenCalledWith(
|
||||
{ path: 'http://test.com/.acl' }, RepresentationMock.mock.instances[0],
|
||||
);
|
||||
expect(createReadStream).toHaveBeenCalledTimes(1);
|
||||
expect(createReadStream).toHaveBeenCalledWith(joinFilePath(__dirname, '../../../templates/root/.acl'), 'utf8');
|
||||
expect(RepresentationMock).toHaveBeenCalledWith('file contents', aclIdentifier, 'text/turtle');
|
||||
});
|
||||
|
||||
it('sets the specific ACL when one was specified.', async(): Promise<void> => {
|
||||
const initializer = new AclInitializer({ baseUrl, store, aclStrategy, aclPath: '/path/doc.acl' });
|
||||
await initializer.handle();
|
||||
|
||||
expect(aclStrategy.getAuxiliaryIdentifier).toHaveBeenCalledWith({ path: baseUrl });
|
||||
expect(store.resourceExists).toHaveBeenCalledTimes(1);
|
||||
expect(store.resourceExists).toHaveBeenCalledWith(aclIdentifier);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||
expect(store.setRepresentation).toHaveBeenCalledWith(
|
||||
{ path: 'http://test.com/.acl' }, RepresentationMock.mock.instances[0],
|
||||
);
|
||||
expect(createReadStream).toHaveBeenCalledTimes(1);
|
||||
expect(createReadStream).toHaveBeenCalledWith('/path/doc.acl', 'utf8');
|
||||
expect(RepresentationMock).toHaveBeenCalledWith('file contents', aclIdentifier, 'text/turtle');
|
||||
});
|
||||
|
||||
it('does not invoke ACL initialization when a root ACL already exists.', async(): Promise<void> => {
|
||||
store.resourceExists.mockResolvedValueOnce(true);
|
||||
|
||||
const initializer = new AclInitializer({ baseUrl, store, aclStrategy });
|
||||
await initializer.handle();
|
||||
|
||||
expect(aclStrategy.getAuxiliaryIdentifier).toHaveBeenCalledWith({ path: baseUrl });
|
||||
expect(store.resourceExists).toHaveBeenCalledTimes(1);
|
||||
expect(store.resourceExists).toHaveBeenCalledWith(aclIdentifier);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('errors when the root ACL check errors.', async(): Promise<void> => {
|
||||
store.setRepresentation.mockRejectedValueOnce(new Error('Fatal'));
|
||||
|
||||
const initializer = new AclInitializer({ baseUrl, store, aclStrategy });
|
||||
const prom = initializer.handle();
|
||||
await expect(prom).rejects.toThrow('Issue initializing the root ACL resource: Fatal');
|
||||
await expect(prom).rejects.toThrow(InternalServerError);
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
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(),
|
||||
resourceExists: jest.fn(),
|
||||
} as any;
|
||||
const initializer = new RootContainerInitializer({ store, baseUrl });
|
||||
|
||||
afterEach((): void => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('invokes ResourceStore initialization.', async(): Promise<void> => {
|
||||
store.resourceExists.mockResolvedValueOnce(false);
|
||||
await initializer.handle();
|
||||
|
||||
expect(store.resourceExists).toHaveBeenCalledTimes(1);
|
||||
expect(store.resourceExists).toHaveBeenCalledWith({ path: baseUrl });
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not invoke ResourceStore initialization when a root container already exists.', async(): Promise<void> => {
|
||||
store.resourceExists.mockResolvedValueOnce(true);
|
||||
store.getRepresentation.mockReturnValueOnce(Promise.resolve({
|
||||
data: { destroy: jest.fn() },
|
||||
} as any));
|
||||
|
||||
await initializer.handle();
|
||||
|
||||
expect(store.resourceExists).toHaveBeenCalledTimes(1);
|
||||
expect(store.resourceExists).toHaveBeenCalledWith({ path: 'http://test.com/' });
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('errors when the store errors writing the root container.', async(): Promise<void> => {
|
||||
store.resourceExists.mockRejectedValueOnce(new Error('Fatal'));
|
||||
await expect(initializer.handle()).rejects.toThrow('Fatal');
|
||||
});
|
||||
});
|
||||
82
test/unit/init/RootInitializer.test.ts
Normal file
82
test/unit/init/RootInitializer.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { RootInitializer } from '../../../src/init/RootInitializer';
|
||||
import { BasicRepresentation } from '../../../src/ldp/representation/BasicRepresentation';
|
||||
import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
|
||||
import type { Logger } from '../../../src/logging/Logger';
|
||||
import { getLoggerFor } from '../../../src/logging/LogUtil';
|
||||
import type { Resource, ResourcesGenerator } from '../../../src/pods/generate/ResourcesGenerator';
|
||||
import type { ResourceStore } from '../../../src/storage/ResourceStore';
|
||||
import { NotFoundHttpError } from '../../../src/util/errors/NotFoundHttpError';
|
||||
import { PIM, RDF } from '../../../src/util/Vocabularies';
|
||||
|
||||
jest.mock('../../../src/logging/LogUtil', (): any => {
|
||||
const logger: Logger = { warn: jest.fn(), debug: jest.fn(), info: jest.fn() } as any;
|
||||
return { getLoggerFor: (): Logger => logger };
|
||||
});
|
||||
|
||||
describe('A RootInitializer', (): void => {
|
||||
const baseUrl = 'http://test.com/foo/';
|
||||
let store: jest.Mocked<ResourceStore>;
|
||||
let generatorData: Resource[];
|
||||
let generator: jest.Mocked<ResourcesGenerator>;
|
||||
let initializer: RootInitializer;
|
||||
let logger: jest.Mocked<Logger>;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
store = {
|
||||
getRepresentation: jest.fn().mockRejectedValue(new NotFoundHttpError()),
|
||||
setRepresentation: jest.fn(),
|
||||
} as any;
|
||||
|
||||
generatorData = [
|
||||
{ identifier: { path: '/.acl' }, representation: '/.acl' as any },
|
||||
{ identifier: { path: '/container/' }, representation: '/container/' as any },
|
||||
];
|
||||
generator = {
|
||||
generate: jest.fn(async function* (): any {
|
||||
yield* generatorData;
|
||||
}),
|
||||
} as any;
|
||||
|
||||
initializer = new RootInitializer(baseUrl, store, generator);
|
||||
logger = getLoggerFor(initializer) as any;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('does nothing is the root container already has pim:Storage metadata.', async(): Promise<void> => {
|
||||
const metadata = new RepresentationMetadata({ path: baseUrl }, { [RDF.type]: PIM.terms.Storage });
|
||||
store.getRepresentation.mockResolvedValueOnce(new BasicRepresentation('data', metadata));
|
||||
|
||||
await expect(initializer.handle()).resolves.toBeUndefined();
|
||||
expect(generator.generate).toHaveBeenCalledTimes(0);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('writes new resources if the container does not exist yet.', async(): Promise<void> => {
|
||||
await expect(initializer.handle()).resolves.toBeUndefined();
|
||||
expect(generator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('writes new resources if the container is not a pim:Storage.', async(): Promise<void> => {
|
||||
store.getRepresentation.mockResolvedValueOnce(new BasicRepresentation('data', 'text/string'));
|
||||
|
||||
await expect(initializer.handle()).resolves.toBeUndefined();
|
||||
expect(generator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('throws an error if there is a problem accessing the root container.', async(): Promise<void> => {
|
||||
store.getRepresentation.mockRejectedValueOnce(new Error('bad data'));
|
||||
await expect(initializer.handle()).rejects.toThrow('bad data');
|
||||
});
|
||||
|
||||
it('logs warnings if there was a problem creating a resource.', async(): Promise<void> => {
|
||||
store.setRepresentation.mockRejectedValueOnce(new Error('bad input'));
|
||||
|
||||
await expect(initializer.handle()).resolves.toBeUndefined();
|
||||
expect(generator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(store.setRepresentation).toHaveBeenCalledTimes(2);
|
||||
expect(logger.warn).toHaveBeenCalledTimes(1);
|
||||
expect(logger.warn).toHaveBeenLastCalledWith('Failed to create resource /.acl: bad input');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user