import { DataFactory } from 'n3'; import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier'; import { TemplatedResourcesGenerator } from '../../../../src/pods/generate/TemplatedResourcesGenerator'; import type { FileIdentifierMapper, FileIdentifierMapperFactory, ResourceLink, } from '../../../../src/storage/mapping/FileIdentifierMapper'; import { ensureTrailingSlash, trimTrailingSlashes } from '../../../../src/util/PathUtil'; import { readableToString } from '../../../../src/util/StreamUtil'; import { HandlebarsTemplateEngine } from '../../../../src/util/templates/HandlebarsTemplateEngine'; import { mockFileSystem } from '../../../util/Util'; const { namedNode } = DataFactory; jest.mock('fs'); class DummyFactory implements FileIdentifierMapperFactory { public async create(base: string, rootFilePath: string): Promise { const trimBase = trimTrailingSlashes(base); const trimRoot = trimTrailingSlashes(rootFilePath); return { async mapFilePathToUrl(filePath: string, isContainer: boolean): Promise { let path = `${trimBase}${filePath.slice(trimRoot.length)}`; const isMetadata = filePath.endsWith('.meta'); if (isMetadata) { path = path.slice(0, -'.meta'.length); } return { identifier: { path: isContainer ? ensureTrailingSlash(path) : path }, filePath, contentType: isContainer ? undefined : 'text/turtle', isMetadata, }; }, } as any; } } async function genToArray(iterable: AsyncIterable): Promise { const arr: T[] = []; for await (const result of iterable) { arr.push(result); } return arr; } describe('A TemplatedResourcesGenerator', (): void => { const rootFilePath = '/templates/pod'; // Using handlebars engine since it's smaller than any possible dummy const generator = new TemplatedResourcesGenerator(rootFilePath, new DummyFactory(), new HandlebarsTemplateEngine('http://test.com/')); let cache: { data: any }; const template = '<{{webId}}> a .'; const location = { path: 'http://test.com/alice/' }; const webId = 'http://alice/#profile'; beforeEach(async(): Promise => { cache = mockFileSystem(rootFilePath); }); it('fills in a template with the given options.', async(): Promise => { cache.data = { 'template.hbs': template }; const result = await genToArray(generator.generate(location, { webId })); const identifiers = result.map((res): ResourceIdentifier => res.identifier); const id = { path: `${location.path}template` }; expect(identifiers).toEqual([ location, id ]); const { representation } = result[1]; expect(representation.binary).toBe(true); expect(representation.metadata.contentType).toBe('text/turtle'); await expect(readableToString(representation.data)).resolves .toBe(`<${webId}> a .`); }); it('creates the necessary containers.', async(): Promise => { cache.data = { container: { container: { 'template.hbs': template }}}; const result = await genToArray(generator.generate(location, { webId })); const identifiers = result.map((res): ResourceIdentifier => res.identifier); const id = { path: `${location.path}container/container/template` }; expect(identifiers).toEqual([ location, { path: `${location.path}container/` }, { path: `${location.path}container/container/` }, id, ]); const { representation } = result[3]; await expect(readableToString(representation.data)).resolves .toBe(`<${webId}> a .`); }); it('copies the file stream directly if no template extension is found.', async(): Promise => { cache.data = { noTemplate: template }; const result = await genToArray(generator.generate(location, { webId })); const identifiers = result.map((res): ResourceIdentifier => res.identifier); const id = { path: `${location.path}noTemplate` }; expect(identifiers).toEqual([ location, id ]); const { representation } = result[1]; expect(representation.binary).toBe(true); expect(representation.metadata.contentType).toBe('text/turtle'); await expect(readableToString(representation.data)).resolves.toEqual(template); }); it('adds metadata from .meta files.', async(): Promise => { const meta = '<> "metadata".'; cache.data = { '.meta': meta, container: { 'template.meta': meta, template }}; // Not using options since our dummy template generator generates invalid turtle const result = await genToArray(generator.generate(location, { webId })); const identifiers = result.map((res): ResourceIdentifier => res.identifier); expect(identifiers).toEqual([ location, { path: `${location.path}container/` }, { path: `${location.path}container/template` }, ]); // Root has the 1 raw metadata triple (with <> changed to its identifier) and content-type const rootMetadata = result[0].representation.metadata; expect(rootMetadata.identifier.value).toBe(location.path); expect(rootMetadata.quads()).toHaveLength(2); expect(rootMetadata.get(namedNode('pre:has'))?.value).toBe('metadata'); expect(rootMetadata.contentType).toBe('text/turtle'); // Container has no metadata triples besides content-type const contMetadata = result[1].representation.metadata; expect(contMetadata.identifier.value).toBe(`${location.path}container/`); expect(contMetadata.quads()).toHaveLength(1); expect(contMetadata.contentType).toBe('text/turtle'); // Document has the 1 raw metadata triple (with <> changed to its identifier) and content-type const docMetadata = result[2].representation.metadata; expect(docMetadata.identifier.value).toBe(`${location.path}container/template`); expect(docMetadata.quads()).toHaveLength(2); expect(docMetadata.get(namedNode('pre:has'))?.value).toBe('metadata'); expect(docMetadata.contentType).toBe('text/turtle'); }); });