mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: More integration tests and test configs (#154)
* add todo * Added configurations folder * Added config to index * changed /bin/server to use configfiles * initiate acl with Setup component * add last changes * reset serverconfig * move authenticatedLdpHandler configs to config files * failed to read testss * failed to read testss * removed import part * Fix merge conflicts * add first FileResTests * fix: fix fileresourcestore metadata error * fix unit tests * remove test files * Added test and changed callFile * Fix: metadata file error in FileResourceStore * fix: ensure full test coverage * added tests * Fix get tests * added testfiles * changed config to use PasstrueStore * to continue work on * refactor fileresourcestore config * refactor tests * fix content-types, update tests * replace sync methods with async methods * move acl function to util * added testfiles for Fileserver with acl * update tests * add first acl filestore test * refactor * add resource mapper * refactor config files * add more fileresourcestore acl tests * add locking resource store test files * move file mapping logic to resourcemapper * added beforeAll for a permanent file in Auht tests * make filestore dependent of resource mapper * moved configs to test/configs * set default contenttype * refactor fileresourcemapper * fix map function * refactor * fixed foldercreationtest * changed some tests so the files are cleaned up when done testing * add normalized parser * Auhtenticationtests clean up acl file * refactor unit test * lost changes added again * fix metadata problem * refactor names * reverse change * add getters * configs and start util * add comments * add comments, move code * added acl helper and changed tests * linter 7.7.0 -> 7.0.0 * moved test/tesfiles -> test/assets * removed configs/**/*.ts from tsconfig.json * Temporary changed threshold so cli test is ignored and commiting goes easier, will revert later * added FileResourceStore to index * Changed imports * Changed imports for all configs * Removed comment * Changed names of configs * added 'Config' to name and removed comment * removed unused testfile and added testfile 0 * changed beforeAll to just copy permanent file * change text/turtle to constant * fix converter issue * getHandler -> getHttpHandler, and updates to config * removed ',' * removed trailing / * changed imports for index.d.js problem * removed duplicate file and added line that got removed in mergeconflicts * add jest global teardown * add ignore for CliRunner * add changes * fix copyfile error * remove unused testfiles * adding test with image * add first util functions * relative paths to absolute paths * added 3 FileStoreTests * more refactoring * more absolute paths * fix mkdir path * added test * add util for easy configs * add comments * added some testhelpers and refactor first test * fix converter test error * refactor FileResTests * solved failing test because new converters * removed afterAll() * removed setAcl from util * removed config from Authorization.test.ts * changed strange linting * refactored AuthenticatedFileResourceStore tests * fixed unclear root variable * fix: Use absolute test paths * Mock fs correctly and remove teardown * Clean up after tests Co-authored-by: freyavs <freyavanspeybroeck@outlook.com> Co-authored-by: thdossch <dossche.thor@gmail.com> Co-authored-by: Freya <56410697+freyavs@users.noreply.github.com> Co-authored-by: thdossch <49074469+thdossch@users.noreply.github.com> Co-authored-by: Ruben Verborgh <ruben@verborgh.org>
This commit is contained in:
parent
383da24601
commit
b1991cb08a
@ -46,6 +46,7 @@ module.exports = {
|
||||
}
|
||||
}],
|
||||
'unused-imports/no-unused-imports-ts': 'error',
|
||||
'import/no-extraneous-dependencies': 'error'
|
||||
'import/no-extraneous-dependencies': 'error',
|
||||
'unicorn/import-index': 'off'
|
||||
},
|
||||
};
|
||||
|
3
index.ts
3
index.ts
@ -79,6 +79,7 @@ export * from './src/storage/patch/SparqlUpdatePatchHandler';
|
||||
export * from './src/storage/AtomicResourceStore';
|
||||
export * from './src/storage/Conditions';
|
||||
export * from './src/storage/ContainerManager';
|
||||
export * from './src/storage/FileResourceStore';
|
||||
export * from './src/storage/InMemoryResourceStore';
|
||||
export * from './src/storage/Lock';
|
||||
export * from './src/storage/LockingResourceStore';
|
||||
@ -103,4 +104,6 @@ export * from './src/util/errors/UnsupportedMediaTypeHttpError';
|
||||
export * from './src/util/AcceptParser';
|
||||
export * from './src/util/AsyncHandler';
|
||||
export * from './src/util/CompositeAsyncHandler';
|
||||
export * from './src/util/InteractionController';
|
||||
export * from './src/util/MetadataController';
|
||||
export * from './src/util/Util';
|
||||
|
@ -16,7 +16,8 @@ module.exports = {
|
||||
"setupFilesAfterEnv": ["jest-rdf"],
|
||||
"collectCoverage": true,
|
||||
"coveragePathIgnorePatterns": [
|
||||
"/node_modules/"
|
||||
"/node_modules/",
|
||||
"/src/init/CliRunner.ts"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"./src": {
|
||||
|
43
package-lock.json
generated
43
package-lock.json
generated
@ -1324,6 +1324,16 @@
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/minimatch": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/graceful-fs": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz",
|
||||
@ -1411,6 +1421,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz",
|
||||
"integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM="
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
|
||||
@ -1471,6 +1487,16 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/rimraf": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.0.tgz",
|
||||
"integrity": "sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz",
|
||||
@ -4349,6 +4375,17 @@
|
||||
"flatted": "^2.0.0",
|
||||
"rimraf": "2.6.3",
|
||||
"write": "1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flatted": {
|
||||
@ -8780,9 +8817,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
|
@ -91,6 +91,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.0",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/supertest": "^2.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
@ -108,6 +109,7 @@
|
||||
"manual-git-changelog": "^1.0.1",
|
||||
"node-mocks-http": "^1.8.1",
|
||||
"nodemon": "^2.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"stream-to-string": "^1.1.0",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-jest": "^26.0.0",
|
||||
|
1
test/assets/permanent.txt
Normal file
1
test/assets/permanent.txt
Normal file
@ -0,0 +1 @@
|
||||
TEST
|
1
test/assets/testfile0.txt
Normal file
1
test/assets/testfile0.txt
Normal file
@ -0,0 +1 @@
|
||||
TESTFILE0
|
1
test/assets/testfile1.txt
Normal file
1
test/assets/testfile1.txt
Normal file
@ -0,0 +1 @@
|
||||
TESTFILE1
|
1
test/assets/testfile2.txt
Normal file
1
test/assets/testfile2.txt
Normal file
@ -0,0 +1 @@
|
||||
TESTFILE2
|
BIN
test/assets/testimage.png
Normal file
BIN
test/assets/testimage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 169 B |
66
test/configs/AuthenticatedFileResourceStoreConfig.ts
Normal file
66
test/configs/AuthenticatedFileResourceStoreConfig.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import {
|
||||
AuthenticatedLdpHandler,
|
||||
BasicResponseWriter,
|
||||
CompositeAsyncHandler,
|
||||
HttpHandler,
|
||||
MethodPermissionsExtractor,
|
||||
RdfToQuadConverter,
|
||||
ResourceStore,
|
||||
UnsecureWebIdExtractor,
|
||||
QuadToRdfConverter,
|
||||
RuntimeConfig,
|
||||
} from '../../index';
|
||||
import { ServerConfig } from './ServerConfig';
|
||||
import {
|
||||
getFileResourceStore,
|
||||
getConvertingStore,
|
||||
getBasicRequestParser,
|
||||
getOperationHandler,
|
||||
getWebAclAuthorizer,
|
||||
} 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 AuthenticatedFileResourceStoreConfig implements ServerConfig {
|
||||
private readonly runtimeConfig: RuntimeConfig;
|
||||
public store: ResourceStore;
|
||||
|
||||
public constructor(runtimeConfig: RuntimeConfig) {
|
||||
this.runtimeConfig = runtimeConfig;
|
||||
this.store = getConvertingStore(
|
||||
getFileResourceStore(runtimeConfig),
|
||||
[ new QuadToRdfConverter(),
|
||||
new RdfToQuadConverter() ],
|
||||
);
|
||||
}
|
||||
|
||||
public getHttpHandler(): HttpHandler {
|
||||
const requestParser = getBasicRequestParser();
|
||||
|
||||
const credentialsExtractor = new UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new CompositeAsyncHandler([
|
||||
new MethodPermissionsExtractor(),
|
||||
]);
|
||||
|
||||
const operationHandler = getOperationHandler(this.store);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
const authorizer = getWebAclAuthorizer(this.store, this.runtimeConfig.base);
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
49
test/configs/BasicConfig.ts
Normal file
49
test/configs/BasicConfig.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
AllowEverythingAuthorizer,
|
||||
AuthenticatedLdpHandler,
|
||||
BasicResponseWriter,
|
||||
HttpHandler,
|
||||
MethodPermissionsExtractor,
|
||||
ResourceStore,
|
||||
UnsecureWebIdExtractor,
|
||||
} from '../../index';
|
||||
import { ServerConfig } from './ServerConfig';
|
||||
import { getOperationHandler, getInMemoryResourceStore, getBasicRequestParser } 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 UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new MethodPermissionsExtractor();
|
||||
const authorizer = new AllowEverythingAuthorizer();
|
||||
|
||||
const operationHandler = getOperationHandler(this.store);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
69
test/configs/BasicHandlersConfig.ts
Normal file
69
test/configs/BasicHandlersConfig.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
AllowEverythingAuthorizer,
|
||||
AuthenticatedLdpHandler,
|
||||
BasicResponseWriter,
|
||||
CompositeAsyncHandler,
|
||||
HttpHandler,
|
||||
MethodPermissionsExtractor,
|
||||
QuadToRdfConverter,
|
||||
RawBodyParser,
|
||||
RdfToQuadConverter,
|
||||
ResourceStore,
|
||||
SparqlUpdateBodyParser,
|
||||
SparqlPatchPermissionsExtractor,
|
||||
UnsecureWebIdExtractor,
|
||||
} from '../../index';
|
||||
|
||||
import { ServerConfig } from './ServerConfig';
|
||||
import { getInMemoryResourceStore,
|
||||
getOperationHandler,
|
||||
getConvertingStore,
|
||||
getPatchingStore, getBasicRequestParser } 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 UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new CompositeAsyncHandler([
|
||||
new MethodPermissionsExtractor(),
|
||||
new SparqlPatchPermissionsExtractor(),
|
||||
]);
|
||||
const authorizer = new AllowEverythingAuthorizer();
|
||||
|
||||
const operationHandler = getOperationHandler(this.store);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
63
test/configs/BasicHandlersWithAclConfig.ts
Normal file
63
test/configs/BasicHandlersWithAclConfig.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
AuthenticatedLdpHandler,
|
||||
BasicResponseWriter,
|
||||
CompositeAsyncHandler,
|
||||
HttpHandler,
|
||||
MethodPermissionsExtractor,
|
||||
RdfToQuadConverter,
|
||||
ResourceStore,
|
||||
UnsecureWebIdExtractor,
|
||||
QuadToRdfConverter,
|
||||
} from '../../index';
|
||||
import { ServerConfig } from './ServerConfig';
|
||||
import {
|
||||
getInMemoryResourceStore,
|
||||
getConvertingStore,
|
||||
getBasicRequestParser,
|
||||
getOperationHandler,
|
||||
getWebAclAuthorizer,
|
||||
} 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 UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new CompositeAsyncHandler([
|
||||
new MethodPermissionsExtractor(),
|
||||
]);
|
||||
|
||||
const operationHandler = getOperationHandler(this.store);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
const authorizer = getWebAclAuthorizer(this.store);
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
59
test/configs/FileResourceStoreConfig.ts
Normal file
59
test/configs/FileResourceStoreConfig.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {
|
||||
AllowEverythingAuthorizer,
|
||||
AuthenticatedLdpHandler,
|
||||
BasicResponseWriter,
|
||||
CompositeAsyncHandler,
|
||||
HttpHandler,
|
||||
MethodPermissionsExtractor,
|
||||
QuadToRdfConverter,
|
||||
RawBodyParser,
|
||||
RdfToQuadConverter,
|
||||
ResourceStore,
|
||||
RuntimeConfig,
|
||||
UnsecureWebIdExtractor,
|
||||
} from '../../index';
|
||||
import { ServerConfig } from './ServerConfig';
|
||||
import { getFileResourceStore, getOperationHandler, getConvertingStore, getBasicRequestParser } from './Util';
|
||||
|
||||
/**
|
||||
* FileResourceStoreConfig works with
|
||||
* - an AllowEverythingAuthorizer (no acl)
|
||||
* - a FileResourceStore wrapped in a converting store (rdf to quad & quad to rdf)
|
||||
* - GET, POST, PUT & DELETE operation handlers
|
||||
*/
|
||||
|
||||
export class FileResourceStoreConfig implements ServerConfig {
|
||||
public store: ResourceStore;
|
||||
|
||||
public constructor(runtimeConfig: RuntimeConfig) {
|
||||
this.store = getConvertingStore(
|
||||
getFileResourceStore(runtimeConfig),
|
||||
[ new QuadToRdfConverter(), new RdfToQuadConverter() ],
|
||||
);
|
||||
}
|
||||
|
||||
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 UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new CompositeAsyncHandler([
|
||||
new MethodPermissionsExtractor(),
|
||||
]);
|
||||
const authorizer = new AllowEverythingAuthorizer();
|
||||
|
||||
const operationHandler = getOperationHandler(this.store);
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
5
test/configs/ServerConfig.ts
Normal file
5
test/configs/ServerConfig.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||
|
||||
export interface ServerConfig {
|
||||
getHttpHandler(): HttpHandler;
|
||||
}
|
143
test/configs/Util.ts
Normal file
143
test/configs/Util.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import { join } from 'path';
|
||||
import {
|
||||
AcceptPreferenceParser,
|
||||
BasicRequestParser,
|
||||
BasicTargetExtractor,
|
||||
BodyParser,
|
||||
CompositeAsyncHandler,
|
||||
DeleteOperationHandler,
|
||||
FileResourceStore,
|
||||
GetOperationHandler,
|
||||
HttpRequest,
|
||||
InMemoryResourceStore,
|
||||
InteractionController,
|
||||
MetadataController,
|
||||
Operation,
|
||||
PatchingStore,
|
||||
PatchOperationHandler,
|
||||
PostOperationHandler,
|
||||
PutOperationHandler,
|
||||
RawBodyParser,
|
||||
Representation,
|
||||
RepresentationConverter,
|
||||
RepresentationConvertingStore,
|
||||
ResourceStore,
|
||||
ResponseDescription,
|
||||
RuntimeConfig,
|
||||
SingleThreadedResourceLocker,
|
||||
SparqlUpdatePatchHandler,
|
||||
UrlBasedAclManager,
|
||||
UrlContainerManager,
|
||||
WebAclAuthorizer,
|
||||
} from '../../index';
|
||||
import { ExtensionBasedMapper } from '../../src/storage/ExtensionBasedMapper';
|
||||
|
||||
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 getRuntimeConfig = (subfolder: string): RuntimeConfig => new RuntimeConfig({
|
||||
base: BASE,
|
||||
rootFilepath: join(__dirname, '../testData', subfolder),
|
||||
});
|
||||
|
||||
/**
|
||||
* Gives a file resource store based on (default) runtime config.
|
||||
* @param runtimeConfig - Optional runtime config.
|
||||
*
|
||||
* @returns The file resource store.
|
||||
*/
|
||||
export const getFileResourceStore = (runtimeConfig: RuntimeConfig): FileResourceStore =>
|
||||
new FileResourceStore(
|
||||
new ExtensionBasedMapper(runtimeConfig),
|
||||
new InteractionController(),
|
||||
new MetadataController(),
|
||||
);
|
||||
|
||||
/**
|
||||
* 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): InMemoryResourceStore =>
|
||||
new InMemoryResourceStore(new RuntimeConfig({ 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[]): RepresentationConvertingStore =>
|
||||
new RepresentationConvertingStore(store, new CompositeAsyncHandler(converters));
|
||||
|
||||
/**
|
||||
* Gives a patching store based on initial store.
|
||||
* @param store - Inital 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): CompositeAsyncHandler<Operation, ResponseDescription> => {
|
||||
const handlers = [
|
||||
new GetOperationHandler(store),
|
||||
new PostOperationHandler(store),
|
||||
new PutOperationHandler(store),
|
||||
new PatchOperationHandler(store),
|
||||
new DeleteOperationHandler(store),
|
||||
];
|
||||
return new CompositeAsyncHandler<Operation, ResponseDescription>(handlers);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 CompositeAsyncHandler<HttpRequest, Representation | undefined>(bodyParsers);
|
||||
}
|
||||
return new BasicRequestParser({
|
||||
targetExtractor: new BasicTargetExtractor(),
|
||||
preferenceParser: new AcceptPreferenceParser(),
|
||||
bodyParser,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gives a web acl authorizer, using a UrlContainerManager & based on a (default) runtimeConfig.
|
||||
* @param store - Initial resource store.
|
||||
* @param base - Base URI of the pod.
|
||||
* @param aclManager - Optional acl manager, default is UrlBasedAclManager.
|
||||
*
|
||||
* @returns The acl authorizer.
|
||||
*/
|
||||
export const getWebAclAuthorizer =
|
||||
(store: ResourceStore, base = BASE, aclManager = new UrlBasedAclManager()): WebAclAuthorizer => {
|
||||
const containerManager = new UrlContainerManager(new RuntimeConfig({ base }));
|
||||
return new WebAclAuthorizer(aclManager, containerManager, store);
|
||||
};
|
86
test/integration/AuthenticatedFileResourceStore.test.ts
Normal file
86
test/integration/AuthenticatedFileResourceStore.test.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { copyFileSync, mkdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
import { HttpHandler, ResourceStore, RuntimeConfig } from '../../index';
|
||||
import { AuthenticatedFileResourceStoreConfig } from '../configs/AuthenticatedFileResourceStoreConfig';
|
||||
import { getRuntimeConfig } from '../configs/Util';
|
||||
import { AclTestHelper, FileTestHelper } from '../util/TestHelpers';
|
||||
|
||||
describe('A server using a AuthenticatedFileResourceStore', (): void => {
|
||||
let config: AuthenticatedFileResourceStoreConfig;
|
||||
let handler: HttpHandler;
|
||||
let store: ResourceStore;
|
||||
let aclHelper: AclTestHelper;
|
||||
let fileHelper: FileTestHelper;
|
||||
let runtimeConfig: RuntimeConfig;
|
||||
|
||||
beforeAll(async(): Promise<void> => {
|
||||
runtimeConfig = getRuntimeConfig('AuthenticatedFileResourceStore');
|
||||
config = new AuthenticatedFileResourceStoreConfig(runtimeConfig);
|
||||
const { base, rootFilepath } = runtimeConfig;
|
||||
handler = config.getHttpHandler();
|
||||
({ store } = config);
|
||||
aclHelper = new AclTestHelper(store, base);
|
||||
fileHelper = new FileTestHelper(handler, new URL('http://test.com/'));
|
||||
|
||||
// Make sure the root directory exists
|
||||
mkdirSync(rootFilepath, { recursive: true });
|
||||
copyFileSync(join(__dirname, '../assets/permanent.txt'), `${rootFilepath}/permanent.txt`);
|
||||
});
|
||||
|
||||
afterAll(async(): Promise<void> => {
|
||||
rimraf.sync(runtimeConfig.rootFilepath, { glob: false });
|
||||
});
|
||||
|
||||
describe('with acl', (): void => {
|
||||
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');
|
||||
const id = response._getHeaders().location;
|
||||
|
||||
// Get file
|
||||
response = await fileHelper.getFile(id);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
expect(response._getBuffer().toString()).toContain('TESTFILE2');
|
||||
|
||||
// DELETE file
|
||||
await fileHelper.deleteFile(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', 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', true);
|
||||
expect(response.statusCode).toBe(401);
|
||||
|
||||
// GET permanent file
|
||||
response = await fileHelper.getFile('http://test.com/permanent.txt');
|
||||
expect(response._getHeaders().location).toBe('http://test.com/permanent.txt');
|
||||
expect(response._getBuffer().toString()).toContain('TEST');
|
||||
|
||||
// Try to delete permanent file
|
||||
response = await fileHelper.deleteFile('http://test.com/permanent.txt', true);
|
||||
expect(response.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
});
|
@ -2,66 +2,13 @@ import * as url from 'url';
|
||||
import { namedNode, quad } from '@rdfjs/data-model';
|
||||
import { Parser } from 'n3';
|
||||
import { MockResponse } from 'node-mocks-http';
|
||||
import { UnsecureWebIdExtractor } from '../../src/authentication/UnsecureWebIdExtractor';
|
||||
import { AllowEverythingAuthorizer } from '../../src/authorization/AllowEverythingAuthorizer';
|
||||
import { RuntimeConfig } from '../../src/init/RuntimeConfig';
|
||||
import { AuthenticatedLdpHandler } from '../../src/ldp/AuthenticatedLdpHandler';
|
||||
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
|
||||
import { BasicRequestParser } from '../../src/ldp/http/BasicRequestParser';
|
||||
import { BasicResponseWriter } from '../../src/ldp/http/BasicResponseWriter';
|
||||
import { BasicTargetExtractor } from '../../src/ldp/http/BasicTargetExtractor';
|
||||
import { BodyParser } from '../../src/ldp/http/BodyParser';
|
||||
import { RawBodyParser } from '../../src/ldp/http/RawBodyParser';
|
||||
import { SparqlUpdateBodyParser } from '../../src/ldp/http/SparqlUpdateBodyParser';
|
||||
import { DeleteOperationHandler } from '../../src/ldp/operations/DeleteOperationHandler';
|
||||
import { GetOperationHandler } from '../../src/ldp/operations/GetOperationHandler';
|
||||
import { Operation } from '../../src/ldp/operations/Operation';
|
||||
import { PatchOperationHandler } from '../../src/ldp/operations/PatchOperationHandler';
|
||||
import { PostOperationHandler } from '../../src/ldp/operations/PostOperationHandler';
|
||||
import { ResponseDescription } from '../../src/ldp/operations/ResponseDescription';
|
||||
import { MethodPermissionsExtractor } from '../../src/ldp/permissions/MethodPermissionsExtractor';
|
||||
import { SparqlPatchPermissionsExtractor } from '../../src/ldp/permissions/SparqlPatchPermissionsExtractor';
|
||||
import { Representation } from '../../src/ldp/representation/Representation';
|
||||
import { HttpRequest } from '../../src/server/HttpRequest';
|
||||
import { QuadToTurtleConverter } from '../../src/storage/conversion/QuadToTurtleConverter';
|
||||
import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter';
|
||||
import { InMemoryResourceStore } from '../../src/storage/InMemoryResourceStore';
|
||||
import { SparqlUpdatePatchHandler } from '../../src/storage/patch/SparqlUpdatePatchHandler';
|
||||
import { PatchingStore } from '../../src/storage/PatchingStore';
|
||||
import { RepresentationConvertingStore } from '../../src/storage/RepresentationConvertingStore';
|
||||
import { SingleThreadedResourceLocker } from '../../src/storage/SingleThreadedResourceLocker';
|
||||
import { CompositeAsyncHandler } from '../../src/util/CompositeAsyncHandler';
|
||||
import { BasicConfig } from '../configs/BasicConfig';
|
||||
import { BasicHandlersConfig } from '../configs/BasicHandlersConfig';
|
||||
import { call } from '../util/Util';
|
||||
|
||||
describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||
describe('with simple handlers', (): void => {
|
||||
const requestParser = new BasicRequestParser({
|
||||
targetExtractor: new BasicTargetExtractor(),
|
||||
preferenceParser: new AcceptPreferenceParser(),
|
||||
bodyParser: new RawBodyParser(),
|
||||
});
|
||||
|
||||
const credentialsExtractor = new UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new MethodPermissionsExtractor();
|
||||
const authorizer = new AllowEverythingAuthorizer();
|
||||
|
||||
const store = new InMemoryResourceStore(new RuntimeConfig({ base: 'http://test.com/' }));
|
||||
const operationHandler = new CompositeAsyncHandler<Operation, ResponseDescription>([
|
||||
new GetOperationHandler(store),
|
||||
new PostOperationHandler(store),
|
||||
new DeleteOperationHandler(store),
|
||||
]);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
const handler = new BasicConfig().getHttpHandler();
|
||||
|
||||
it('can add, read and delete data based on incoming requests.', async(): Promise<void> => {
|
||||
// POST
|
||||
@ -80,9 +27,17 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||
|
||||
// GET
|
||||
requestUrl = new URL(id);
|
||||
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
||||
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._getData()).toContain(
|
||||
'<http://test.com/s> <http://test.com/p> <http://test.com/o>.',
|
||||
);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
|
||||
// DELETE
|
||||
@ -92,57 +47,20 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||
expect(response._getHeaders().location).toBe(url.format(requestUrl));
|
||||
|
||||
// GET
|
||||
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
||||
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 bodyParser: BodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([
|
||||
new SparqlUpdateBodyParser(),
|
||||
new RawBodyParser(),
|
||||
]);
|
||||
const requestParser = new BasicRequestParser({
|
||||
targetExtractor: new BasicTargetExtractor(),
|
||||
preferenceParser: new AcceptPreferenceParser(),
|
||||
bodyParser,
|
||||
});
|
||||
|
||||
const credentialsExtractor = new UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new CompositeAsyncHandler([
|
||||
new MethodPermissionsExtractor(),
|
||||
new SparqlPatchPermissionsExtractor(),
|
||||
]);
|
||||
const authorizer = new AllowEverythingAuthorizer();
|
||||
|
||||
const store = new InMemoryResourceStore(new RuntimeConfig({ base: 'http://test.com/' }));
|
||||
const converter = new CompositeAsyncHandler([
|
||||
new QuadToTurtleConverter(),
|
||||
new TurtleToQuadConverter(),
|
||||
]);
|
||||
const convertingStore = new RepresentationConvertingStore(store, converter);
|
||||
const locker = new SingleThreadedResourceLocker();
|
||||
const patcher = new SparqlUpdatePatchHandler(convertingStore, locker);
|
||||
const patchingStore = new PatchingStore(convertingStore, patcher);
|
||||
|
||||
const operationHandler = new CompositeAsyncHandler<Operation, ResponseDescription>([
|
||||
new GetOperationHandler(patchingStore),
|
||||
new PostOperationHandler(patchingStore),
|
||||
new DeleteOperationHandler(patchingStore),
|
||||
new PatchOperationHandler(patchingStore),
|
||||
]);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
const handler = new BasicHandlersConfig().getHttpHandler();
|
||||
|
||||
it('can handle simple SPARQL updates.', async(): Promise<void> => {
|
||||
// POST
|
||||
@ -169,7 +87,8 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||
{ '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 {}' ],
|
||||
'WHERE {}',
|
||||
],
|
||||
);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
@ -177,18 +96,89 @@ describe('An integrated AuthenticatedLdpHandler', (): void => {
|
||||
|
||||
// GET
|
||||
requestUrl = new URL(id);
|
||||
response = await call(handler, requestUrl, 'GET', { accept: 'text/turtle' }, []);
|
||||
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().location).toBe(id);
|
||||
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 handler = new BasicHandlersConfig().getHttpHandler();
|
||||
|
||||
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(200);
|
||||
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(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
|
||||
// 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/s2> <http://test.com/p2> <http://test.com/o2>.');
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
const parser = new Parser();
|
||||
const triples = parser.parse(response._getData());
|
||||
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')),
|
||||
],
|
||||
);
|
||||
expect(triples).toBeRdfIsomorphic([
|
||||
quad(
|
||||
namedNode('http://test.com/s3'),
|
||||
namedNode('http://test.com/p3'),
|
||||
namedNode('http://test.com/o3'),
|
||||
),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,127 +1,16 @@
|
||||
import { MockResponse } from 'node-mocks-http';
|
||||
import streamifyArray from 'streamify-array';
|
||||
import { UnsecureWebIdExtractor } from '../../src/authentication/UnsecureWebIdExtractor';
|
||||
import { UrlBasedAclManager } from '../../src/authorization/UrlBasedAclManager';
|
||||
import { WebAclAuthorizer } from '../../src/authorization/WebAclAuthorizer';
|
||||
import { RuntimeConfig } from '../../src/init/RuntimeConfig';
|
||||
import { AuthenticatedLdpHandler } from '../../src/ldp/AuthenticatedLdpHandler';
|
||||
import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser';
|
||||
import { BasicRequestParser } from '../../src/ldp/http/BasicRequestParser';
|
||||
import { BasicResponseWriter } from '../../src/ldp/http/BasicResponseWriter';
|
||||
import { BasicTargetExtractor } from '../../src/ldp/http/BasicTargetExtractor';
|
||||
import { BodyParser } from '../../src/ldp/http/BodyParser';
|
||||
import { RawBodyParser } from '../../src/ldp/http/RawBodyParser';
|
||||
import { DeleteOperationHandler } from '../../src/ldp/operations/DeleteOperationHandler';
|
||||
import { GetOperationHandler } from '../../src/ldp/operations/GetOperationHandler';
|
||||
import { Operation } from '../../src/ldp/operations/Operation';
|
||||
import { PostOperationHandler } from '../../src/ldp/operations/PostOperationHandler';
|
||||
import { PutOperationHandler } from '../../src/ldp/operations/PutOperationHandler';
|
||||
import { ResponseDescription } from '../../src/ldp/operations/ResponseDescription';
|
||||
import { MethodPermissionsExtractor } from '../../src/ldp/permissions/MethodPermissionsExtractor';
|
||||
import { PermissionSet } from '../../src/ldp/permissions/PermissionSet';
|
||||
import { QuadToTurtleConverter } from '../../src/storage/conversion/QuadToTurtleConverter';
|
||||
import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter';
|
||||
import { InMemoryResourceStore } from '../../src/storage/InMemoryResourceStore';
|
||||
import { RepresentationConvertingStore } from '../../src/storage/RepresentationConvertingStore';
|
||||
import { ResourceStore } from '../../src/storage/ResourceStore';
|
||||
import { UrlContainerManager } from '../../src/storage/UrlContainerManager';
|
||||
import { CompositeAsyncHandler } from '../../src/util/CompositeAsyncHandler';
|
||||
import { BasicHandlersWithAclConfig } from '../configs/BasicHandlersWithAclConfig';
|
||||
import { AclTestHelper } from '../util/TestHelpers';
|
||||
import { call } from '../util/Util';
|
||||
|
||||
const setAcl = async(store: ResourceStore, id: string, permissions: PermissionSet, control: boolean,
|
||||
access: boolean, def: boolean, agent?: string, agentClass?: 'agent' | 'authenticated'): Promise<void> => {
|
||||
const acl: string[] = [
|
||||
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.\n',
|
||||
'@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n',
|
||||
'<http://test.com/#auth> a acl:Authorization',
|
||||
];
|
||||
|
||||
for (const perm of [ 'Read', 'Append', 'Write', 'Delete' ]) {
|
||||
if (permissions[perm.toLowerCase() as keyof PermissionSet]) {
|
||||
acl.push(`;\n acl:mode acl:${perm}`);
|
||||
}
|
||||
}
|
||||
if (control) {
|
||||
acl.push(';\n acl:mode acl:Control');
|
||||
}
|
||||
if (access) {
|
||||
acl.push(`;\n acl:accessTo <${id}>`);
|
||||
}
|
||||
if (def) {
|
||||
acl.push(`;\n acl:default <${id}>`);
|
||||
}
|
||||
if (agent) {
|
||||
acl.push(`;\n acl:agent <${agent}>`);
|
||||
}
|
||||
if (agentClass) {
|
||||
acl.push(`;\n acl:agentClass ${agentClass === 'agent' ? 'foaf:Agent' : 'foaf:AuthenticatedAgent'}`);
|
||||
}
|
||||
|
||||
acl.push('.');
|
||||
|
||||
const representation = {
|
||||
binary: true,
|
||||
data: streamifyArray(acl),
|
||||
metadata: {
|
||||
raw: [],
|
||||
profiles: [],
|
||||
contentType: 'text/turtle',
|
||||
},
|
||||
};
|
||||
|
||||
return store.setRepresentation({ path: `${id}.acl` }, representation);
|
||||
};
|
||||
|
||||
describe('A server with authorization', (): void => {
|
||||
const bodyParser: BodyParser = new RawBodyParser();
|
||||
const requestParser = new BasicRequestParser({
|
||||
targetExtractor: new BasicTargetExtractor(),
|
||||
preferenceParser: new AcceptPreferenceParser(),
|
||||
bodyParser,
|
||||
});
|
||||
|
||||
const store = new InMemoryResourceStore(new RuntimeConfig({ base: 'http://test.com/' }));
|
||||
const converter = new CompositeAsyncHandler([
|
||||
new QuadToTurtleConverter(),
|
||||
new TurtleToQuadConverter(),
|
||||
]);
|
||||
const convertingStore = new RepresentationConvertingStore(store, converter);
|
||||
|
||||
const credentialsExtractor = new UnsecureWebIdExtractor();
|
||||
const permissionsExtractor = new MethodPermissionsExtractor();
|
||||
const authorizer = new WebAclAuthorizer(
|
||||
new UrlBasedAclManager(),
|
||||
new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/' })),
|
||||
convertingStore,
|
||||
);
|
||||
|
||||
const operationHandler = new CompositeAsyncHandler<Operation, ResponseDescription>([
|
||||
new GetOperationHandler(convertingStore),
|
||||
new PostOperationHandler(convertingStore),
|
||||
new DeleteOperationHandler(convertingStore),
|
||||
new PutOperationHandler(convertingStore),
|
||||
]);
|
||||
|
||||
const responseWriter = new BasicResponseWriter();
|
||||
|
||||
const handler = new AuthenticatedLdpHandler({
|
||||
requestParser,
|
||||
credentialsExtractor,
|
||||
permissionsExtractor,
|
||||
authorizer,
|
||||
operationHandler,
|
||||
responseWriter,
|
||||
});
|
||||
const config = new BasicHandlersWithAclConfig();
|
||||
const handler = config.getHttpHandler();
|
||||
const { store } = config;
|
||||
const aclHelper = new AclTestHelper(store, 'http://test.com/');
|
||||
|
||||
it('can create new entries.', async(): Promise<void> => {
|
||||
await setAcl(convertingStore,
|
||||
'http://test.com/',
|
||||
{ read: true, write: true, append: true },
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
undefined,
|
||||
'agent');
|
||||
await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'agent');
|
||||
|
||||
// POST
|
||||
let requestUrl = new URL('http://test.com/');
|
||||
@ -147,14 +36,7 @@ describe('A server with authorization', (): void => {
|
||||
});
|
||||
|
||||
it('can not create new entries if not allowed.', async(): Promise<void> => {
|
||||
await setAcl(convertingStore,
|
||||
'http://test.com/',
|
||||
{ read: true, write: true, append: true },
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
undefined,
|
||||
'authenticated');
|
||||
await aclHelper.setSimpleAcl({ read: true, write: true, append: true }, 'authenticated');
|
||||
|
||||
// POST
|
||||
let requestUrl = new URL('http://test.com/');
|
||||
|
186
test/integration/FileResourceStore.test.ts
Normal file
186
test/integration/FileResourceStore.test.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import * as rimraf from 'rimraf';
|
||||
import { RuntimeConfig } from '../../src/init/RuntimeConfig';
|
||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||
import { FileResourceStoreConfig } from '../configs/FileResourceStoreConfig';
|
||||
import { getRuntimeConfig } from '../configs/Util';
|
||||
import { FileTestHelper } from '../util/TestHelpers';
|
||||
|
||||
describe('A server using a FileResourceStore', (): void => {
|
||||
describe('without acl', (): void => {
|
||||
let config: FileResourceStoreConfig;
|
||||
let handler: HttpHandler;
|
||||
let fileHelper: FileTestHelper;
|
||||
let runtimeConfig: RuntimeConfig;
|
||||
|
||||
beforeAll(async(): Promise<void> => {
|
||||
runtimeConfig = getRuntimeConfig('FileResourceStore');
|
||||
config = new FileResourceStoreConfig(runtimeConfig);
|
||||
handler = config.getHttpHandler();
|
||||
fileHelper = new FileTestHelper(handler, new URL(runtimeConfig.base));
|
||||
});
|
||||
|
||||
afterAll(async(): Promise<void> => {
|
||||
rimraf.sync(runtimeConfig.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');
|
||||
const id = response._getHeaders().location;
|
||||
|
||||
// GET
|
||||
response = await fileHelper.getFile(id);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
expect(response._getBuffer().toString()).toContain('TESTFILE0');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile(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');
|
||||
const id = response._getHeaders().location;
|
||||
|
||||
// GET
|
||||
response = await fileHelper.getFile(id);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
expect(response._getBuffer().toString()).toContain('TESTFILE0');
|
||||
|
||||
// PUT
|
||||
response = await fileHelper.overwriteFile('../assets/testfile1.txt', id);
|
||||
|
||||
// GET
|
||||
response = await fileHelper.getFile(id);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
expect(response._getBuffer().toString()).toContain('TESTFILE1');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile(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().location).toBe(id);
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFolder(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');
|
||||
const id = response._getHeaders().location;
|
||||
|
||||
// GET File
|
||||
response = await fileHelper.getFile(id);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(id);
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile(id);
|
||||
await fileHelper.shouldNotExist(id);
|
||||
await fileHelper.deleteFolder('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');
|
||||
|
||||
// Try DELETE folder
|
||||
response = await fileHelper.simpleCall(new URL(folderId), 'DELETE', {});
|
||||
expect(response.statusCode).toBe(409);
|
||||
expect(response._getData()).toContain('ConflictHttpError: Container is not empty.');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile('http://test.com/testfolder1/testfile0.txt');
|
||||
await fileHelper.shouldNotExist('http://test.com/testfolder1/testfile0.txt');
|
||||
await fileHelper.deleteFolder(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: Container is not empty.');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFolder(subFolderId);
|
||||
await fileHelper.shouldNotExist(subFolderId);
|
||||
await fileHelper.deleteFolder(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');
|
||||
const fileId = response._getHeaders().location;
|
||||
|
||||
response = await fileHelper.getFolder(folderId);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(folderId);
|
||||
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>.');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile(fileId);
|
||||
await fileHelper.shouldNotExist(fileId);
|
||||
await fileHelper.deleteFolder(subFolderId);
|
||||
await fileHelper.shouldNotExist(subFolderId);
|
||||
await fileHelper.deleteFolder(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');
|
||||
const fileId = response._getHeaders().location;
|
||||
|
||||
// GET
|
||||
response = await fileHelper.getFile(fileId);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getHeaders().location).toBe(fileId);
|
||||
expect(response._getHeaders()['content-type']).toBe('image/png');
|
||||
|
||||
// DELETE
|
||||
await fileHelper.deleteFile(fileId);
|
||||
await fileHelper.shouldNotExist(fileId);
|
||||
});
|
||||
});
|
||||
});
|
0
test/testData/.gitkeep
Normal file
0
test/testData/.gitkeep
Normal file
@ -25,12 +25,18 @@ const { join: joinPath } = posix;
|
||||
const base = 'http://test.com/';
|
||||
const rootFilepath = '/Users/default/home/public/';
|
||||
|
||||
fsPromises.rmdir = jest.fn();
|
||||
fsPromises.lstat = jest.fn();
|
||||
fsPromises.readdir = jest.fn();
|
||||
fsPromises.mkdir = jest.fn();
|
||||
fsPromises.unlink = jest.fn();
|
||||
fsPromises.access = jest.fn();
|
||||
jest.mock('fs', (): any => ({
|
||||
createReadStream: jest.fn(),
|
||||
createWriteStream: jest.fn(),
|
||||
promises: {
|
||||
rmdir: jest.fn(),
|
||||
lstat: jest.fn(),
|
||||
readdir: jest.fn(),
|
||||
mkdir: jest.fn(),
|
||||
unlink: jest.fn(),
|
||||
access: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('A FileResourceStore', (): void => {
|
||||
let store: FileResourceStore;
|
||||
@ -45,8 +51,6 @@ describe('A FileResourceStore', (): void => {
|
||||
namedNode('http://test.com/o'),
|
||||
);
|
||||
|
||||
fs.createReadStream = jest.fn();
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
@ -68,8 +72,6 @@ describe('A FileResourceStore', (): void => {
|
||||
mtime: new Date(),
|
||||
} as jest.Mocked<Stats>;
|
||||
|
||||
// Mock the fs functions for the createDataFile function.
|
||||
fs.createWriteStream = jest.fn();
|
||||
writeStream = {
|
||||
on: jest.fn((name: string, func: () => void): any => {
|
||||
if (name === 'finish') {
|
||||
|
211
test/util/TestHelpers.ts
Normal file
211
test/util/TestHelpers.ts
Normal file
@ -0,0 +1,211 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { promises as fs } from 'fs';
|
||||
import { IncomingHttpHeaders } from 'http';
|
||||
import { join } from 'path';
|
||||
import * as url from 'url';
|
||||
import { createResponse, MockResponse } from 'node-mocks-http';
|
||||
import streamifyArray from 'streamify-array';
|
||||
import { ResourceStore } from '../../index';
|
||||
import { PermissionSet } from '../../src/ldp/permissions/PermissionSet';
|
||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||
import { HttpRequest } from '../../src/server/HttpRequest';
|
||||
import { call } from './Util';
|
||||
|
||||
export class AclTestHelper {
|
||||
public readonly store: ResourceStore;
|
||||
public id: string;
|
||||
|
||||
public constructor(store: ResourceStore, id: string) {
|
||||
this.store = store;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public async setSimpleAcl(
|
||||
permissions: PermissionSet,
|
||||
agentClass: 'agent' | 'authenticated',
|
||||
): Promise<void> {
|
||||
const acl: string[] = [
|
||||
'@prefix acl: <http://www.w3.org/ns/auth/acl#>.\n',
|
||||
'@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n',
|
||||
'<http://test.com/#auth> a acl:Authorization',
|
||||
];
|
||||
|
||||
for (const perm of [ 'Read', 'Append', 'Write', 'Delete' ]) {
|
||||
if (permissions[perm.toLowerCase() as keyof PermissionSet]) {
|
||||
acl.push(`;\n acl:mode acl:${perm}`);
|
||||
}
|
||||
}
|
||||
acl.push(';\n acl:mode acl:Control');
|
||||
acl.push(`;\n acl:accessTo <${this.id}>`);
|
||||
acl.push(`;\n acl:default <${this.id}>`);
|
||||
acl.push(
|
||||
`;\n acl:agentClass ${
|
||||
agentClass === 'agent' ? 'foaf:Agent' : 'foaf:AuthenticatedAgent'
|
||||
}`,
|
||||
);
|
||||
|
||||
acl.push('.');
|
||||
|
||||
const representation = {
|
||||
binary: true,
|
||||
data: streamifyArray(acl),
|
||||
metadata: {
|
||||
raw: [],
|
||||
profiles: [],
|
||||
contentType: 'text/turtle',
|
||||
},
|
||||
};
|
||||
|
||||
return this.store.setRepresentation(
|
||||
{ path: `${this.id}.acl` },
|
||||
representation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class FileTestHelper {
|
||||
public readonly handler: HttpHandler;
|
||||
public readonly baseUrl: URL;
|
||||
|
||||
public constructor(handler: HttpHandler, baseUrl: URL) {
|
||||
this.handler = handler;
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public async simpleCall(
|
||||
requestUrl: URL,
|
||||
method: string,
|
||||
headers: IncomingHttpHeaders,
|
||||
): Promise<MockResponse<any>> {
|
||||
return call(this.handler, requestUrl, method, headers, []);
|
||||
}
|
||||
|
||||
public async callWithFile(
|
||||
requestUrl: URL,
|
||||
method: string,
|
||||
headers: IncomingHttpHeaders,
|
||||
data: Buffer,
|
||||
): Promise<MockResponse<any>> {
|
||||
const request = streamifyArray([ data ]) as HttpRequest;
|
||||
|
||||
request.url = requestUrl.pathname;
|
||||
request.method = method;
|
||||
request.headers = headers;
|
||||
request.headers.host = requestUrl.host;
|
||||
const response: MockResponse<any> = 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 createFile(fileLocation: string, slug: string, mayFail = false): Promise<MockResponse<any>> {
|
||||
const fileData = await fs.readFile(
|
||||
join(__dirname, fileLocation),
|
||||
);
|
||||
|
||||
const response: MockResponse<any> = await this.callWithFile(
|
||||
this.baseUrl,
|
||||
'POST',
|
||||
{ 'content-type': 'application/octet-stream',
|
||||
slug,
|
||||
'transfer-encoding': 'chunked' },
|
||||
fileData,
|
||||
);
|
||||
if (!mayFail) {
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toContain(url.format(this.baseUrl));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public async overwriteFile(fileLocation: string, requestUrl: string): Promise<MockResponse<any>> {
|
||||
const fileData = await fs.readFile(
|
||||
join(__dirname, fileLocation),
|
||||
);
|
||||
|
||||
const putUrl = new URL(requestUrl);
|
||||
|
||||
const response: MockResponse<any> = await this.callWithFile(
|
||||
putUrl,
|
||||
'PUT',
|
||||
{ 'content-type': 'application/octet-stream', 'transfer-encoding': 'chunked' },
|
||||
fileData,
|
||||
);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toContain(url.format(putUrl));
|
||||
return response;
|
||||
}
|
||||
|
||||
public async getFile(requestUrl: string): Promise<MockResponse<any>> {
|
||||
const getUrl = new URL(requestUrl);
|
||||
|
||||
return this.simpleCall(getUrl, 'GET', { accept: '*/*' });
|
||||
}
|
||||
|
||||
public async deleteFile(requestUrl: string, mayFail = false): Promise<MockResponse<any>> {
|
||||
const deleteUrl = new URL(requestUrl);
|
||||
|
||||
const response = await this.simpleCall(deleteUrl, 'DELETE', {});
|
||||
if (!mayFail) {
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toBe(url.format(requestUrl));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
public async createFolder(slug: string): Promise<MockResponse<any>> {
|
||||
const response: MockResponse<any> = await this.simpleCall(
|
||||
this.baseUrl,
|
||||
'POST',
|
||||
{
|
||||
slug,
|
||||
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
|
||||
'content-type': 'text/plain',
|
||||
'transfer-encoding': 'chunked',
|
||||
},
|
||||
);
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toContain(url.format(this.baseUrl));
|
||||
return response;
|
||||
}
|
||||
|
||||
public async getFolder(requestUrl: string): Promise<MockResponse<any>> {
|
||||
const getUrl = new URL(requestUrl);
|
||||
|
||||
return await this.simpleCall(getUrl, 'GET', { accept: 'text/turtle' });
|
||||
}
|
||||
|
||||
public async deleteFolder(requestUrl: string): Promise<MockResponse<any>> {
|
||||
const deleteUrl = new URL(requestUrl);
|
||||
|
||||
const response = await this.simpleCall(deleteUrl, 'DELETE', {});
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response._getData()).toHaveLength(0);
|
||||
expect(response._getHeaders().location).toBe(url.format(requestUrl));
|
||||
return response;
|
||||
}
|
||||
|
||||
public async shouldNotExist(requestUrl: string): Promise<MockResponse<any>> {
|
||||
const getUrl = new URL(requestUrl);
|
||||
|
||||
const response = await this.simpleCall(getUrl, 'GET', { accept: '*/*' });
|
||||
expect(response.statusCode).toBe(404);
|
||||
expect(response._getData()).toContain('NotFoundHttpError');
|
||||
return response;
|
||||
}
|
||||
}
|
@ -5,14 +5,21 @@ import streamifyArray from 'streamify-array';
|
||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||
import { HttpRequest } from '../../src/server/HttpRequest';
|
||||
|
||||
export const call = async(handler: HttpHandler, requestUrl: URL, method: string,
|
||||
headers: IncomingHttpHeaders, data: string[]): Promise<MockResponse<any>> => {
|
||||
export const call = async(
|
||||
handler: HttpHandler,
|
||||
requestUrl: URL,
|
||||
method: string,
|
||||
headers: IncomingHttpHeaders,
|
||||
data: string[],
|
||||
): Promise<MockResponse<any>> => {
|
||||
const request = streamifyArray(data) as HttpRequest;
|
||||
request.url = requestUrl.pathname;
|
||||
request.method = method;
|
||||
request.headers = headers;
|
||||
request.headers.host = requestUrl.host;
|
||||
const response: MockResponse<any> = createResponse({ eventEmitter: EventEmitter });
|
||||
const response: MockResponse<any> = createResponse({
|
||||
eventEmitter: EventEmitter,
|
||||
});
|
||||
|
||||
const endPromise = new Promise((resolve): void => {
|
||||
response.on('end', (): void => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user