From 6cd2806c2ffe29411d0d7939b0e9c66f8e6f06cd Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Fri, 30 Apr 2021 12:34:02 +0200 Subject: [PATCH] test: Remove node-mocks-http references from integration tests --- test/assets/permanent.txt | 1 - test/assets/testfile0.txt | 1 - test/assets/testfile1.txt | 1 - test/assets/testfile2.txt | 1 - test/integration/LdpHandlerWithAuth.test.ts | 2 +- .../RedisResourceLockerIntegration.test.ts | 3 +- test/integration/SparqlStorage.test.ts | 46 ++-- test/util/AclHelper.ts | 60 +++++ test/util/TestHelpers.ts | 210 ------------------ test/util/Util.ts | 39 +--- 10 files changed, 98 insertions(+), 266 deletions(-) delete mode 100644 test/assets/permanent.txt delete mode 100644 test/assets/testfile0.txt delete mode 100644 test/assets/testfile1.txt delete mode 100644 test/assets/testfile2.txt create mode 100644 test/util/AclHelper.ts delete mode 100644 test/util/TestHelpers.ts diff --git a/test/assets/permanent.txt b/test/assets/permanent.txt deleted file mode 100644 index 2a02d41ce..000000000 --- a/test/assets/permanent.txt +++ /dev/null @@ -1 +0,0 @@ -TEST diff --git a/test/assets/testfile0.txt b/test/assets/testfile0.txt deleted file mode 100644 index 368534e7c..000000000 --- a/test/assets/testfile0.txt +++ /dev/null @@ -1 +0,0 @@ -TESTFILE0 diff --git a/test/assets/testfile1.txt b/test/assets/testfile1.txt deleted file mode 100644 index e1e3b3d69..000000000 --- a/test/assets/testfile1.txt +++ /dev/null @@ -1 +0,0 @@ -TESTFILE1 diff --git a/test/assets/testfile2.txt b/test/assets/testfile2.txt deleted file mode 100644 index 8dd26c0d5..000000000 --- a/test/assets/testfile2.txt +++ /dev/null @@ -1 +0,0 @@ -TESTFILE2 diff --git a/test/integration/LdpHandlerWithAuth.test.ts b/test/integration/LdpHandlerWithAuth.test.ts index f67d4c0e6..4d88dd2af 100644 --- a/test/integration/LdpHandlerWithAuth.test.ts +++ b/test/integration/LdpHandlerWithAuth.test.ts @@ -3,8 +3,8 @@ import fetch from 'cross-fetch'; import type { Initializer, ResourceStore } from '../../src/'; import { BasicRepresentation } from '../../src/'; import type { HttpServerFactory } from '../../src/server/HttpServerFactory'; +import { AclHelper } from '../util/AclHelper'; import { deleteResource, getResource, postResource, putResource } from '../util/FetchUtil'; -import { AclHelper } from '../util/TestHelpers'; import { getPort } from '../util/Util'; import { getTestFolder, instantiateFromConfig, removeFolder } from './Config'; diff --git a/test/integration/RedisResourceLockerIntegration.test.ts b/test/integration/RedisResourceLockerIntegration.test.ts index 8fd704f50..51a461863 100644 --- a/test/integration/RedisResourceLockerIntegration.test.ts +++ b/test/integration/RedisResourceLockerIntegration.test.ts @@ -3,8 +3,7 @@ import fetch from 'cross-fetch'; import type { RedisResourceLocker } from '../../src'; import { joinFilePath } from '../../src'; import type { HttpServerFactory } from '../../src/server/HttpServerFactory'; -import { describeIf } from '../util/TestHelpers'; -import { getPort } from '../util/Util'; +import { describeIf, getPort } from '../util/Util'; import { instantiateFromConfig } from './Config'; /** diff --git a/test/integration/SparqlStorage.test.ts b/test/integration/SparqlStorage.test.ts index 01ee11614..925e52a48 100644 --- a/test/integration/SparqlStorage.test.ts +++ b/test/integration/SparqlStorage.test.ts @@ -1,15 +1,24 @@ -import type { HttpHandler, Initializer, ResourceStore } from '../../src/'; -import { describeIf, ResourceHelper } from '../util/TestHelpers'; -import { BASE, instantiateFromConfig } from './Config'; +import { promises as fs } from 'fs'; +import type { Server } from 'http'; +import { joinFilePath } from '../../src/'; +import type { Initializer, ResourceStore } from '../../src/'; +import type { HttpServerFactory } from '../../src/server/HttpServerFactory'; +import { putResource } from '../util/FetchUtil'; +import { describeIf, getPort } from '../util/Util'; +import { instantiateFromConfig } from './Config'; + +const port = getPort('SparqlStorage'); +const baseUrl = `http://localhost:${port}/`; describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => { - let handler: HttpHandler; - let resourceHelper: ResourceHelper; + let server: Server; + let initializer: Initializer; + let factory: HttpServerFactory; beforeAll(async(): Promise => { - // Set up the internal store const variables: Record = { - 'urn:solid-server:default:variable:baseUrl': BASE, + 'urn:solid-server:default:variable:port': port, + 'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql', }; const internalStore = await instantiateFromConfig( @@ -19,24 +28,29 @@ describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => { ) as ResourceStore; variables['urn:solid-server:default:variable:store'] = internalStore; - // Create and initialize the HTTP handler and related components - let initializer: Initializer; + // Create and initialize the server const instances = await instantiateFromConfig( 'urn:solid-server:test:Instances', 'ldp-with-auth.json', variables, ) as Record; - ({ handler, initializer } = instances); - await initializer.handleSafe(); + ({ factory, initializer } = instances); - // Create test helpers for manipulating the components - resourceHelper = new ResourceHelper(handler, BASE); + await initializer.handleSafe(); + server = factory.startServer(port); }); - it('can add a Turtle file to the store.', async(): - Promise => { + afterAll(async(): Promise => { + await new Promise((resolve, reject): void => { + server.close((error): void => error ? reject(error) : resolve()); + }); + }); + + it('can add a Turtle file to the store.', async(): Promise => { // PUT - const response = await resourceHelper.createResource('../assets/person.ttl', 'person', 'text/turtle'); + const documentUrl = `${baseUrl}person`; + const body = (await fs.readFile(joinFilePath(__dirname, '../assets/person.ttl'))).toString('utf-8'); + const response = await putResource(documentUrl, { contentType: 'text/turtle', body }); expect(response).toBeTruthy(); }); }); diff --git a/test/util/AclHelper.ts b/test/util/AclHelper.ts new file mode 100644 index 000000000..b717e18f2 --- /dev/null +++ b/test/util/AclHelper.ts @@ -0,0 +1,60 @@ +import type { ResourceStore, PermissionSet } from '../../src/'; +import { BasicRepresentation } from '../../src/'; + +export class AclHelper { + public readonly store: ResourceStore; + + public constructor(store: ResourceStore) { + this.store = store; + } + + public async setSimpleAcl( + resource: string, + options: { + permissions: Partial; + agentClass?: 'agent' | 'authenticated'; + agent?: string; + accessTo?: boolean; + default?: boolean; + }, + ): Promise { + if (!options.agentClass && !options.agent) { + throw new Error('At least one of agentClass or agent have to be provided.'); + } + if (!options.accessTo && !options.default) { + throw new Error('At least one of accessTo or default have to be true.'); + } + + const acl: string[] = [ + '@prefix acl: .\n', + '@prefix foaf: .\n', + ' a acl:Authorization', + ]; + + for (const perm of [ 'Read', 'Append', 'Write', 'Control' ]) { + if (options.permissions[perm.toLowerCase() as keyof PermissionSet]) { + acl.push(`;\n acl:mode acl:${perm}`); + } + } + if (options.accessTo) { + acl.push(`;\n acl:accessTo <${resource}>`); + } + if (options.default) { + acl.push(`;\n acl:default <${resource}>`); + } + if (options.agentClass) { + acl.push( + `;\n acl:agentClass ${ + options.agentClass === 'agent' ? 'foaf:Agent' : 'foaf:AuthenticatedAgent' + }`, + ); + } + if (options.agent) { + acl.push(`;\n acl:agent ${options.agent}`); + } + + acl.push('.'); + + await this.store.setRepresentation({ path: `${resource}.acl` }, new BasicRepresentation(acl, 'text/turtle')); + } +} diff --git a/test/util/TestHelpers.ts b/test/util/TestHelpers.ts deleted file mode 100644 index 6d5ab477a..000000000 --- a/test/util/TestHelpers.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { EventEmitter } from 'events'; -import { promises as fs } from 'fs'; -import type { IncomingHttpHeaders } from 'http'; -import { Readable } from 'stream'; -import type { MockResponse } from 'node-mocks-http'; -import { createResponse } from 'node-mocks-http'; -import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/'; -import { BasicRepresentation, joinFilePath } from '../../src/'; -import { performRequest } from './Util'; - -/* eslint-disable jest/no-standalone-expect */ -export class AclHelper { - public readonly store: ResourceStore; - - public constructor(store: ResourceStore) { - this.store = store; - } - - public async setSimpleAcl( - resource: string, - options: { - permissions: Partial; - agentClass?: 'agent' | 'authenticated'; - agent?: string; - accessTo?: boolean; - default?: boolean; - }, - ): Promise { - if (!options.agentClass && !options.agent) { - throw new Error('At least one of agentClass or agent have to be provided for this to make sense.'); - } - if (!options.accessTo && !options.default) { - throw new Error('At least one of accessTo or default have to be true for this to make sense.'); - } - - const acl: string[] = [ - '@prefix acl: .\n', - '@prefix foaf: .\n', - ' a acl:Authorization', - ]; - - for (const perm of [ 'Read', 'Append', 'Write', 'Control' ]) { - if (options.permissions[perm.toLowerCase() as keyof PermissionSet]) { - acl.push(`;\n acl:mode acl:${perm}`); - } - } - if (options.accessTo) { - acl.push(`;\n acl:accessTo <${resource}>`); - } - if (options.default) { - acl.push(`;\n acl:default <${resource}>`); - } - if (options.agentClass) { - acl.push( - `;\n acl:agentClass ${ - options.agentClass === 'agent' ? 'foaf:Agent' : 'foaf:AuthenticatedAgent' - }`, - ); - } - if (options.agent) { - acl.push(`;\n acl:agent ${options.agent}`); - } - - acl.push('.'); - - await this.store.setRepresentation({ path: `${resource}.acl` }, new BasicRepresentation(acl, 'text/turtle')); - } -} - -export class ResourceHelper { - public readonly handler: HttpHandler; - public readonly baseUrl: URL; - - public constructor(handler: HttpHandler, baseUrl: string) { - this.handler = handler; - this.baseUrl = new URL(baseUrl); - } - - public async performRequest( - requestUrl: URL, - method: string, - headers: IncomingHttpHeaders, - ): Promise> { - return performRequest(this.handler, requestUrl, method, headers, []); - } - - public async performRequestWithBody( - requestUrl: URL, - method: string, - headers: IncomingHttpHeaders, - data: Buffer, - ): Promise> { - const request = Readable.from([ data ]) as HttpRequest; - request.url = `${requestUrl.pathname}${requestUrl.search}`; - request.method = method; - request.headers = headers; - request.headers.host = requestUrl.host; - const response: MockResponse = createResponse({ - eventEmitter: EventEmitter, - }); - - const endPromise = new Promise((resolve): void => { - response.on('end', (): void => { - expect(response._isEndCalled()).toBeTruthy(); - resolve(); - }); - }); - - await this.handler.handleSafe({ request, response }); - await endPromise; - - return response; - } - - public async createResource(fileLocation: string, path: string, contentType: string, mayFail = false): - Promise> { - const fileData = await fs.readFile( - joinFilePath(__dirname, fileLocation), - ); - - const response: MockResponse = await this.performRequestWithBody( - new URL(path, this.baseUrl), - 'PUT', - { 'content-type': contentType, - 'transfer-encoding': 'chunked' }, - fileData, - ); - if (!mayFail) { - expect(response.statusCode).toBe(205); - expect(response._getData()).toHaveLength(0); - } - return response; - } - - public async replaceResource(fileLocation: string, requestUrl: string, contentType: string): - Promise> { - const fileData = await fs.readFile( - joinFilePath(__dirname, fileLocation), - ); - - const putUrl = new URL(requestUrl); - - const response: MockResponse = await this.performRequestWithBody( - putUrl, - 'PUT', - { 'content-type': contentType, 'transfer-encoding': 'chunked' }, - fileData, - ); - expect(response.statusCode).toBe(205); - expect(response._getData()).toHaveLength(0); - return response; - } - - public async getResource(requestUrl: string): Promise> { - const getUrl = new URL(requestUrl); - - const response = await this.performRequest(getUrl, 'GET', { accept: '*/*' }); - expect(response.statusCode).toBe(200); - return response; - } - - public async deleteResource(requestUrl: string, mayFail = false): Promise> { - const deleteUrl = new URL(requestUrl); - - const response = await this.performRequest(deleteUrl, 'DELETE', {}); - if (!mayFail) { - expect(response.statusCode).toBe(205); - expect(response._getData()).toHaveLength(0); - } - return response; - } - - public async createContainer(path: string): Promise> { - const response: MockResponse = await this.performRequest( - new URL(path, this.baseUrl), - 'PUT', - { - link: '; rel="type"', - 'content-type': 'text/turtle', - 'transfer-encoding': 'chunked', - }, - ); - expect(response.statusCode).toBe(205); - expect(response._getData()).toHaveLength(0); - return response; - } - - public async getContainer(requestUrl: string): Promise> { - const getUrl = new URL(requestUrl); - - // `n-quads` allow for easy testing if a triple is present - return await this.performRequest(getUrl, 'GET', { accept: 'application/n-quads' }); - } - - public async shouldNotExist(requestUrl: string): Promise> { - const getUrl = new URL(requestUrl); - - const response = await this.performRequest(getUrl, 'GET', { accept: '*/*' }); - expect(response.statusCode).toBe(404); - expect(response._getData()).toContain('NotFoundHttpError'); - return response; - } -} - -export function describeIf(envFlag: string, name: string, fn: () => void): void { - const flag = `TEST_${envFlag.toUpperCase()}`; - const enabled = !/^(|0|false)$/iu.test(process.env[flag] ?? ''); - // eslint-disable-next-line jest/valid-describe, jest/valid-title, jest/no-disabled-tests - return enabled ? describe(name, fn) : describe.skip(name, fn); -} diff --git a/test/util/Util.ts b/test/util/Util.ts index 0759c6e5b..92a880333 100644 --- a/test/util/Util.ts +++ b/test/util/Util.ts @@ -1,12 +1,6 @@ -import { EventEmitter } from 'events'; import type { Stats } from 'fs'; -import type { IncomingHttpHeaders } from 'http'; import { PassThrough } from 'stream'; -import type { MockResponse } from 'node-mocks-http'; -import { createResponse } from 'node-mocks-http'; import streamifyArray from 'streamify-array'; -import type { HttpHandler } from '../../src/server/HttpHandler'; -import type { HttpRequest } from '../../src/server/HttpRequest'; import type { SystemError } from '../../src/util/errors/SystemError'; /* eslint-disable @typescript-eslint/naming-convention */ @@ -18,6 +12,7 @@ const portNames = [ 'PodCreation', 'RedisResourceLocker', 'ServerFetch', + 'SparqlStorage', 'Subdomains', 'WebSocketsProtocol', ] as const; @@ -31,33 +26,11 @@ export function getPort(name: typeof portNames[number]): number { return 6000 + idx; } -export async function performRequest( - handler: HttpHandler, - requestUrl: URL, - method: string, - headers: IncomingHttpHeaders, - data: string[], -): Promise> { - const request = streamifyArray(data) as HttpRequest; - request.url = `${requestUrl.pathname}${requestUrl.search}`; - request.method = method; - request.headers = headers; - request.headers.host = requestUrl.host; - const response: MockResponse = createResponse({ - eventEmitter: EventEmitter, - }); - - const endPromise = new Promise((resolve): void => { - response.on('end', (): void => { - expect(response._isEndCalled()).toBeTruthy(); - resolve(); - }); - }); - - await handler.handleSafe({ request, response }); - await endPromise; - - return response; +export function describeIf(envFlag: string, name: string, fn: () => void): void { + const flag = `TEST_${envFlag.toUpperCase()}`; + const enabled = !/^(|0|false)$/iu.test(process.env[flag] ?? ''); + // eslint-disable-next-line jest/valid-describe, jest/valid-title, jest/no-disabled-tests + return enabled ? describe(name, fn) : describe.skip(name, fn); } /**