Refactored tests to use the test-solid-server library

main
Jackson Morgan 4 months ago
parent 5b7982cbce
commit a78a604a18
  1. 1
      packages/connected-solid/jest.config.js
  2. 3
      packages/connected-solid/package.json
  3. 330
      packages/connected-solid/test/Integration.test.ts
  4. 112
      packages/connected-solid/test/authFetch.helper.ts
  5. 43
      packages/connected-solid/test/configs/server-config.json
  6. 9
      packages/connected-solid/test/configs/solid-css-seed.json
  7. 3
      packages/connected-solid/test/setup-tests.ts
  8. 42
      packages/connected-solid/test/solidServer.helper.ts
  9. 5
      packages/test-solid-server/package.json
  10. 1
      packages/test-solid-server/src/index.ts
  11. 7
      packages/test-solid-server/src/resourceUtils.ts
  12. 14
      packages/test-solid-server/src/setupTestServer.ts

@ -3,7 +3,6 @@ const sharedConfig = require("../../jest.config.js");
module.exports = { module.exports = {
...sharedConfig, ...sharedConfig,
rootDir: "./", rootDir: "./",
setupFiles: ["<rootDir>/test/setup-tests.ts"],
transform: { transform: {
"^.+\\.(ts|tsx)?$": "ts-jest", "^.+\\.(ts|tsx)?$": "ts-jest",
"^.+\\.(js|jsx)$": "babel-jest", "^.+\\.(js|jsx)$": "babel-jest",

@ -26,6 +26,7 @@
"devDependencies": { "devDependencies": {
"@inrupt/solid-client-authn-core": "^2.2.6", "@inrupt/solid-client-authn-core": "^2.2.6",
"@ldo/cli": "^1.0.0-alpha.3", "@ldo/cli": "^1.0.0-alpha.3",
"@ldo/test-solid-server": "^1.0.0-alpha.8",
"@rdfjs/data-model": "^1.2.0", "@rdfjs/data-model": "^1.2.0",
"@rdfjs/types": "^1.0.1", "@rdfjs/types": "^1.0.1",
"@solid-notifications/types": "^0.1.2", "@solid-notifications/types": "^0.1.2",
@ -53,4 +54,4 @@
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }
} }

@ -1,5 +1,3 @@
import type { App } from "@solid/community-server";
import { ROOT_CONTAINER, WEB_ID, createApp } from "./solidServer.helper";
import { import {
namedNode, namedNode,
quad as createQuad, quad as createQuad,
@ -16,9 +14,7 @@ import type {
} from "../src/requester/results/error/HttpErrorResult"; } from "../src/requester/results/error/HttpErrorResult";
import type { NoncompliantPodError } from "../src/requester/results/error/NoncompliantPodError"; import type { NoncompliantPodError } from "../src/requester/results/error/NoncompliantPodError";
import type { GetStorageContainerFromWebIdSuccess } from "../src/requester/results/success/CheckRootContainerSuccess"; import type { GetStorageContainerFromWebIdSuccess } from "../src/requester/results/success/CheckRootContainerSuccess";
import { generateAuthFetch } from "./authFetch.helper";
import { wait } from "./utils.helper"; import { wait } from "./utils.helper";
import fs from "fs/promises";
import path from "path"; import path from "path";
import type { GetWacRuleSuccess, UpdateResultError, WacRule } from "../src"; import type { GetWacRuleSuccess, UpdateResultError, WacRule } from "../src";
import { import {
@ -45,7 +41,11 @@ import {
ConnectedLdoTransactionDataset, ConnectedLdoTransactionDataset,
} from "@ldo/connected"; } from "@ldo/connected";
import { getStorageFromWebId } from "../src/getStorageFromWebId"; import { getStorageFromWebId } from "../src/getStorageFromWebId";
import type { ResourceInfo } from "@ldo/test-solid-server";
import { createApp, setupServer } from "@ldo/test-solid-server";
const ROOT_CONTAINER = "http://localhost:3001/";
const WEB_ID = "http://localhost:3001/example/profile/card#me";
const TEST_CONTAINER_SLUG = "test_ldo/"; const TEST_CONTAINER_SLUG = "test_ldo/";
const TEST_CONTAINER_URI = const TEST_CONTAINER_URI =
`${ROOT_CONTAINER}${TEST_CONTAINER_SLUG}` as SolidContainerUri; `${ROOT_CONTAINER}${TEST_CONTAINER_SLUG}` as SolidContainerUri;
@ -93,7 +93,7 @@ const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
posix:size 522. posix:size 522.
<sample.txt> posix:mtime 1697810234; <sample.txt> posix:mtime 1697810234;
posix:size 10.`; posix:size 10.`;
const TEST_CONTAINER_ACL_URI = `${TEST_CONTAINER_URI}.acl`; const _TEST_CONTAINER_ACL_URI = `${TEST_CONTAINER_URI}.acl`;
const TEST_CONTAINER_ACL = `<#b30e3fd1-b5a8-4763-ad9d-e95de9cf7933> a <http://www.w3.org/ns/auth/acl#Authorization>; const TEST_CONTAINER_ACL = `<#b30e3fd1-b5a8-4763-ad9d-e95de9cf7933> a <http://www.w3.org/ns/auth/acl#Authorization>;
<http://www.w3.org/ns/auth/acl#accessTo> <${TEST_CONTAINER_URI}>; <http://www.w3.org/ns/auth/acl#accessTo> <${TEST_CONTAINER_URI}>;
<http://www.w3.org/ns/auth/acl#default> <${TEST_CONTAINER_URI}>; <http://www.w3.org/ns/auth/acl#default> <${TEST_CONTAINER_URI}>;
@ -106,6 +106,57 @@ const SAMPLE_PROFILE_TTL = `
<${SAMPLE_PROFILE_URI}> pim:storage <https://example.com/A/>, <https://example.com/B/> . <${SAMPLE_PROFILE_URI}> pim:storage <https://example.com/A/>, <https://example.com/B/> .
`; `;
const resourceInfo: ResourceInfo = {
slug: TEST_CONTAINER_SLUG,
isContainer: true,
contains: [
{
slug: ".acl",
isContainer: false,
mimeType: "text/turtle",
data: TEST_CONTAINER_ACL,
},
{
slug: "sample.ttl",
isContainer: false,
mimeType: "text/turtle",
data: SPIDER_MAN_TTL,
},
{
slug: "sample.txt",
isContainer: false,
mimeType: "text/plain",
data: "some text.",
},
{
slug: "profile.ttl",
isContainer: false,
mimeType: "text/turtle",
data: SAMPLE_PROFILE_TTL,
},
{
slug: "sample_container/",
isContainer: true,
shouldNotInit: true,
contains: [],
},
{
slug: SAMPLE2_DATA_SLUG,
isContainer: false,
shouldNotInit: true,
mimeType: "text/turtle",
data: "",
},
{
slug: SAMPLE2_BINARY_SLUG,
isContainer: false,
shouldNotInit: true,
mimeType: "text/plain",
data: "",
},
],
};
async function testRequestLoads<ReturnVal>( async function testRequestLoads<ReturnVal>(
request: () => Promise<ReturnVal>, request: () => Promise<ReturnVal>,
loadingResource: SolidLeaf | SolidContainer, loadingResource: SolidLeaf | SolidContainer,
@ -149,98 +200,13 @@ async function testRequestLoads<ReturnVal>(
} }
describe("Integration", () => { describe("Integration", () => {
let app: App;
let authFetch: typeof fetch;
let fetchMock: jest.Mock<
Promise<Response>,
[input: RequestInfo | URL, init?: RequestInit | undefined]
>;
let solidLdoDataset: ConnectedLdoDataset<SolidConnectedPlugin[]>; let solidLdoDataset: ConnectedLdoDataset<SolidConnectedPlugin[]>;
let previousJestId: string | undefined; const s = setupServer(3001, resourceInfo);
let previousNodeEnv: string | undefined;
beforeAll(async () => {
// Remove Jest ID so that community solid server doesn't use the Jest Import
previousJestId = process.env.JEST_WORKER_ID;
previousNodeEnv = process.env.NODE_ENV;
delete process.env.JEST_WORKER_ID;
process.env.NODE_ENV = "other_test";
// Start up the server
app = await createApp();
await app.start();
authFetch = await generateAuthFetch();
});
afterAll(async () => {
app.stop();
process.env.JEST_WORKER_ID = previousJestId;
process.env.NODE_ENV = previousNodeEnv;
const testDataPath = path.join(__dirname, "./data");
await fs.rm(testDataPath, { recursive: true, force: true });
});
beforeEach(async () => { beforeEach(async () => {
fetchMock = jest.fn(authFetch);
solidLdoDataset = createSolidLdoDataset(); solidLdoDataset = createSolidLdoDataset();
solidLdoDataset.setContext("solid", { fetch: fetchMock }); solidLdoDataset.setContext("solid", { fetch: s.fetchMock });
// Create a new document called sample.ttl
await authFetch(ROOT_CONTAINER, {
method: "POST",
headers: {
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
slug: TEST_CONTAINER_SLUG,
},
});
await authFetch(TEST_CONTAINER_ACL_URI, {
method: "PUT",
headers: {
"content-type": "text/turtle",
},
body: TEST_CONTAINER_ACL,
});
await Promise.all([
authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "sample.ttl" },
body: SPIDER_MAN_TTL,
}),
authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/plain", slug: "sample.txt" },
body: "some text.",
}),
authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "profile.ttl" },
body: SAMPLE_PROFILE_TTL,
}),
]);
});
afterEach(async () => {
await Promise.all([
authFetch(SAMPLE_DATA_URI, {
method: "DELETE",
}),
authFetch(SAMPLE2_DATA_URI, {
method: "DELETE",
}),
authFetch(SAMPLE_BINARY_URI, {
method: "DELETE",
}),
authFetch(SAMPLE2_BINARY_URI, {
method: "DELETE",
}),
authFetch(SAMPLE_PROFILE_URI, {
method: "DELETE",
}),
authFetch(SAMPLE_CONTAINER_URI, {
method: "DELETE",
}),
]);
await authFetch(TEST_CONTAINER_URI, {
method: "DELETE",
});
}); });
/** /**
@ -338,7 +304,7 @@ describe("Integration", () => {
}); });
it("Returns an ServerError when an 500 error is returned", async () => { it("Returns an ServerError when an 500 error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, { const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true, isLoading: true,
@ -350,7 +316,7 @@ describe("Integration", () => {
}); });
it("Returns an Unauthorized error if a 403 error is returned", async () => { it("Returns an Unauthorized error if a 403 error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 403 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 403 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, { const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true, isLoading: true,
@ -362,7 +328,7 @@ describe("Integration", () => {
}); });
it("Returns an UnauthenticatedError on an 401 error is returned", async () => { it("Returns an UnauthenticatedError on an 401 error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 401 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 401 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, { const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true, isLoading: true,
@ -374,7 +340,7 @@ describe("Integration", () => {
}); });
it("Returns an UnexpectedHttpError on a strange number error is returned", async () => { it("Returns an UnexpectedHttpError on a strange number error is returned", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 399 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 399 }));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, { const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true, isLoading: true,
@ -386,7 +352,7 @@ describe("Integration", () => {
}); });
it("Returns a NoncompliantPod error when no content type is returned", async () => { it("Returns a NoncompliantPod error when no content type is returned", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(undefined, { status: 200, headers: {} }), new Response(undefined, { status: 200, headers: {} }),
); );
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
@ -404,7 +370,7 @@ describe("Integration", () => {
}); });
it("Returns a NoncompliantPod error if invalid turtle is provided", async () => { it("Returns a NoncompliantPod error if invalid turtle is provided", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("Error", { new Response("Error", {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
@ -425,7 +391,7 @@ describe("Integration", () => {
}); });
it("Parses Turtle even when the content type contains parameters", async () => { it("Parses Turtle even when the content type contains parameters", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(SPIDER_MAN_TTL, { new Response(SPIDER_MAN_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle;charset=utf-8" }), headers: new Headers({ "content-type": "text/turtle;charset=utf-8" }),
@ -443,7 +409,7 @@ describe("Integration", () => {
}); });
it("Returns an UnexpectedResourceError if an unknown error is triggered", async () => { it("Returns an UnexpectedResourceError if an unknown error is triggered", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened.")); s.fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
const result = await testRequestLoads(() => resource.read(), resource, { const result = await testRequestLoads(() => resource.read(), resource, {
isLoading: true, isLoading: true,
@ -457,7 +423,7 @@ describe("Integration", () => {
}); });
it("Does not return an error if there is no link header for a container request", async () => { it("Does not return an error if there is no link header for a container request", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
@ -489,7 +455,7 @@ describe("Integration", () => {
resource.read(), resource.read(),
]); ]);
expect(fetchMock).toHaveBeenCalledTimes(1); expect(s.fetchMock).toHaveBeenCalledTimes(1);
expect(result.type).toBe("dataReadSuccess"); expect(result.type).toBe("dataReadSuccess");
expect(result1.type).toBe("dataReadSuccess"); expect(result1.type).toBe("dataReadSuccess");
}); });
@ -502,7 +468,7 @@ describe("Integration", () => {
resource.read(), resource.read(),
]); ]);
expect(fetchMock).toHaveBeenCalledTimes(3); expect(s.fetchMock).toHaveBeenCalledTimes(3);
expect(result.type).toBe("dataReadSuccess"); expect(result.type).toBe("dataReadSuccess");
expect(result1.type).toBe("dataReadSuccess"); expect(result1.type).toBe("dataReadSuccess");
}); });
@ -551,9 +517,9 @@ describe("Integration", () => {
it("returns a cached existing container", async () => { it("returns a cached existing container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
await resource.read(); await resource.read();
fetchMock.mockClear(); s.fetchMock.mockClear();
const result = await resource.readIfUnfetched(); const result = await resource.readIfUnfetched();
expect(fetchMock).not.toHaveBeenCalled(); expect(s.fetchMock).not.toHaveBeenCalled();
expect(result.type).toBe("containerReadSuccess"); expect(result.type).toBe("containerReadSuccess");
expect(resource.children().length).toBe(3); expect(resource.children().length).toBe(3);
}); });
@ -561,7 +527,7 @@ describe("Integration", () => {
it("returns a cached existing data leaf", async () => { it("returns a cached existing data leaf", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
await resource.read(); await resource.read();
fetchMock.mockClear(); s.fetchMock.mockClear();
const result = await resource.readIfUnfetched(); const result = await resource.readIfUnfetched();
expect(result.type).toBe("dataReadSuccess"); expect(result.type).toBe("dataReadSuccess");
expect( expect(
@ -576,7 +542,7 @@ describe("Integration", () => {
it("returns a cached existing binary leaf", async () => { it("returns a cached existing binary leaf", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI); const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI);
await resource.read(); await resource.read();
fetchMock.mockClear(); s.fetchMock.mockClear();
const result = await resource.readIfUnfetched(); const result = await resource.readIfUnfetched();
expect(result.type).toBe("binaryReadSuccess"); expect(result.type).toBe("binaryReadSuccess");
}); });
@ -584,18 +550,18 @@ describe("Integration", () => {
it("returns a cached absent container", async () => { it("returns a cached absent container", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_CONTAINER_URI); const resource = solidLdoDataset.getResource(SAMPLE_CONTAINER_URI);
await resource.read(); await resource.read();
fetchMock.mockClear(); s.fetchMock.mockClear();
const result = await resource.readIfUnfetched(); const result = await resource.readIfUnfetched();
expect(fetchMock).not.toHaveBeenCalled(); expect(s.fetchMock).not.toHaveBeenCalled();
expect(result.type).toBe("absentReadSuccess"); expect(result.type).toBe("absentReadSuccess");
}); });
it("returns a cached absent leaf", async () => { it("returns a cached absent leaf", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
await resource.read(); await resource.read();
fetchMock.mockClear(); s.fetchMock.mockClear();
const result = await resource.readIfUnfetched(); const result = await resource.readIfUnfetched();
expect(fetchMock).not.toHaveBeenCalled(); expect(s.fetchMock).not.toHaveBeenCalled();
expect(result.type).toBe("absentReadSuccess"); expect(result.type).toBe("absentReadSuccess");
}); });
}); });
@ -614,19 +580,19 @@ describe("Integration", () => {
}); });
it("Returns an error if there is no root container", async () => { it("Returns an error if there is no root container", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
}), }),
); );
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
}), }),
); );
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ "content-type": "text/turtle" }), headers: new Headers({ "content-type": "text/turtle" }),
@ -641,7 +607,7 @@ describe("Integration", () => {
}); });
it("An error to be returned if a common http error is encountered", async () => { it("An error to be returned if a common http error is encountered", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -653,7 +619,7 @@ describe("Integration", () => {
}); });
it("Returns an UnexpectedResourceError if an unknown error is triggered", async () => { it("Returns an UnexpectedResourceError if an unknown error is triggered", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened.")); s.fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
const result = await resource.getRootContainer(); const result = await resource.getRootContainer();
expect(result.isError).toBe(true); expect(result.isError).toBe(true);
@ -663,7 +629,7 @@ describe("Integration", () => {
}); });
it("returns a NonCompliantPodError when there is no root", async () => { it("returns a NonCompliantPodError when there is no root", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 200, status: 200,
headers: new Headers({ headers: new Headers({
@ -711,7 +677,7 @@ describe("Integration", () => {
}); });
it("Passes any errors returned from the read method", async () => { it("Passes any errors returned from the read method", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened.")); s.fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const result = await getStorageFromWebId( const result = await getStorageFromWebId(
SAMPLE_DATA_URI, SAMPLE_DATA_URI,
solidLdoDataset, solidLdoDataset,
@ -720,8 +686,8 @@ describe("Integration", () => {
}); });
it("Passes any errors returned from the getRootContainer method", async () => { it("Passes any errors returned from the getRootContainer method", async () => {
fetchMock.mockResolvedValueOnce(new Response("")); s.fetchMock.mockResolvedValueOnce(new Response(""));
fetchMock.mockRejectedValueOnce(new Error("Something happened.")); s.fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const result = await getStorageFromWebId( const result = await getStorageFromWebId(
SAMPLE_DATA_URI, SAMPLE_DATA_URI,
solidLdoDataset, solidLdoDataset,
@ -828,7 +794,7 @@ describe("Integration", () => {
it("returns and error if creating a container", async () => { it("returns and error if creating a container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -840,7 +806,7 @@ describe("Integration", () => {
it("returns a delete error if delete failed", async () => { it("returns a delete error if delete failed", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -852,10 +818,10 @@ describe("Integration", () => {
it("returns an error if the create fetch fails", async () => { it("returns an error if the create fetch fails", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockImplementationOnce(async (...args) => { s.fetchMock.mockImplementationOnce(async (...args) => {
return authFetch(...args); return s.authFetch(...args);
}); });
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -867,10 +833,10 @@ describe("Integration", () => {
it("returns an unexpected error if some unknown error is triggered", async () => { it("returns an unexpected error if some unknown error is triggered", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockImplementationOnce(async (...args) => { s.fetchMock.mockImplementationOnce(async (...args) => {
return authFetch(...args); return s.authFetch(...args);
}); });
fetchMock.mockImplementationOnce(async () => { s.fetchMock.mockImplementationOnce(async () => {
throw new Error("Some Unknown"); throw new Error("Some Unknown");
}); });
const result = await resource.createAndOverwrite(); const result = await resource.createAndOverwrite();
@ -889,7 +855,7 @@ describe("Integration", () => {
expect(result1.type).toBe("createSuccess"); expect(result1.type).toBe("createSuccess");
expect(result2.type).toBe("createSuccess"); expect(result2.type).toBe("createSuccess");
// 1 for read, 1 for delete in createAndOverwrite, 1 for create // 1 for read, 1 for delete in createAndOverwrite, 1 for create
expect(fetchMock).toHaveBeenCalledTimes(3); expect(s.fetchMock).toHaveBeenCalledTimes(3);
}); });
it("batches the create request while waiting on a similar request", async () => { it("batches the create request while waiting on a similar request", async () => {
@ -902,7 +868,7 @@ describe("Integration", () => {
expect(result1.type).toBe("createSuccess"); expect(result1.type).toBe("createSuccess");
expect(result2.type).toBe("createSuccess"); expect(result2.type).toBe("createSuccess");
// 1 for delete in createAndOverwrite, 1 for create // 1 for delete in createAndOverwrite, 1 for create
expect(fetchMock).toHaveBeenCalledTimes(2); expect(s.fetchMock).toHaveBeenCalledTimes(2);
}); });
}); });
@ -999,7 +965,7 @@ describe("Integration", () => {
it("returns an error if creating a container", async () => { it("returns an error if creating a container", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_CONTAINER_URI); const resource = solidLdoDataset.getResource(SAMPLE_CONTAINER_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(SAMPLE_CONTAINER_URI, { new Response(SAMPLE_CONTAINER_URI, {
status: 500, status: 500,
}), }),
@ -1011,7 +977,7 @@ describe("Integration", () => {
it("returns an error if creating a leaf", async () => { it("returns an error if creating a leaf", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE2_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(SAMPLE2_DATA_URI, { new Response(SAMPLE2_DATA_URI, {
status: 500, status: 500,
}), }),
@ -1028,7 +994,7 @@ describe("Integration", () => {
describe("deleteResource", () => { describe("deleteResource", () => {
it("returns an unexpected http error if an unexpected value is returned", async () => { it("returns an unexpected http error if an unexpected value is returned", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 214, status: 214,
}), }),
@ -1040,7 +1006,7 @@ describe("Integration", () => {
it("returns an unexpected resource error if an unknown error is triggered", async () => { it("returns an unexpected resource error if an unknown error is triggered", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockImplementationOnce(async () => { s.fetchMock.mockImplementationOnce(async () => {
throw new Error("Some unknwon"); throw new Error("Some unknwon");
}); });
const result = await resource.delete(); const result = await resource.delete();
@ -1056,7 +1022,7 @@ describe("Integration", () => {
it("returns an error on container read when deleting a container", async () => { it("returns an error on container read when deleting a container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockImplementation(async (input, init) => { s.fetchMock.mockImplementation(async (input, init) => {
if ( if (
(init?.method === "get" || !init?.method) && (init?.method === "get" || !init?.method) &&
input === TEST_CONTAINER_URI input === TEST_CONTAINER_URI
@ -1065,7 +1031,7 @@ describe("Integration", () => {
status: 500, status: 500,
}); });
} }
return authFetch(input, init); return s.authFetch(input, init);
}); });
const result = await resource.delete(); const result = await resource.delete();
expect(result.isError).toBe(true); expect(result.isError).toBe(true);
@ -1082,13 +1048,13 @@ describe("Integration", () => {
it("returns an error on child delete when deleting a container", async () => { it("returns an error on child delete when deleting a container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockImplementation(async (input, init) => { s.fetchMock.mockImplementation(async (input, init) => {
if (init?.method === "delete" && input === SAMPLE_DATA_URI) { if (init?.method === "delete" && input === SAMPLE_DATA_URI) {
return new Response(SAMPLE_DATA_URI, { return new Response(SAMPLE_DATA_URI, {
status: 500, status: 500,
}); });
} }
return authFetch(input, init); return s.authFetch(input, init);
}); });
const result = await resource.delete(); const result = await resource.delete();
expect(result.isError).toBe(true); expect(result.isError).toBe(true);
@ -1105,13 +1071,13 @@ describe("Integration", () => {
it("returns an error on container delete when deleting a container", async () => { it("returns an error on container delete when deleting a container", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockImplementation(async (input, init) => { s.fetchMock.mockImplementation(async (input, init) => {
if (init?.method === "delete" && input === TEST_CONTAINER_URI) { if (init?.method === "delete" && input === TEST_CONTAINER_URI) {
return new Response(SAMPLE_DATA_URI, { return new Response(SAMPLE_DATA_URI, {
status: 500, status: 500,
}); });
} }
return authFetch(input, init); return s.authFetch(input, init);
}); });
const result = await resource.delete(); const result = await resource.delete();
expect(result.isError).toBe(true); expect(result.isError).toBe(true);
@ -1184,7 +1150,7 @@ describe("Integration", () => {
}); });
it("handles an HTTP error", async () => { it("handles an HTTP error", async () => {
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const transaction = solidLdoDataset.startTransaction(); const transaction = solidLdoDataset.startTransaction();
transaction.add(normanQuad); transaction.add(normanQuad);
@ -1202,7 +1168,7 @@ describe("Integration", () => {
}); });
it("handles an unknown request", async () => { it("handles an unknown request", async () => {
fetchMock.mockImplementationOnce(() => { s.fetchMock.mockImplementationOnce(() => {
throw new Error("Some Error"); throw new Error("Some Error");
}); });
const transaction = solidLdoDataset.startTransaction(); const transaction = solidLdoDataset.startTransaction();
@ -1286,7 +1252,7 @@ describe("Integration", () => {
]); ]);
expect(updateResult1.type).toBe("aggregateSuccess"); expect(updateResult1.type).toBe("aggregateSuccess");
expect(updateResult2.type).toBe("aggregateSuccess"); expect(updateResult2.type).toBe("aggregateSuccess");
expect(fetchMock).toHaveBeenCalledTimes(2); expect(s.fetchMock).toHaveBeenCalledTimes(2);
expect( expect(
solidLdoDataset.has( solidLdoDataset.has(
createQuad( createQuad(
@ -1394,7 +1360,7 @@ describe("Integration", () => {
it("returns a delete error if delete failed", async () => { it("returns a delete error if delete failed", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI); const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -1409,10 +1375,10 @@ describe("Integration", () => {
it("returns an error if the create fetch fails", async () => { it("returns an error if the create fetch fails", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI); const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI);
fetchMock.mockImplementationOnce(async (...args) => { s.fetchMock.mockImplementationOnce(async (...args) => {
return authFetch(...args); return s.authFetch(...args);
}); });
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(TEST_CONTAINER_TTL, { new Response(TEST_CONTAINER_TTL, {
status: 500, status: 500,
}), }),
@ -1427,10 +1393,10 @@ describe("Integration", () => {
it("returns an unexpected error if some unknown error is triggered", async () => { it("returns an unexpected error if some unknown error is triggered", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI); const resource = solidLdoDataset.getResource(SAMPLE_BINARY_URI);
fetchMock.mockImplementationOnce(async (...args) => { s.fetchMock.mockImplementationOnce(async (...args) => {
return authFetch(...args); return s.authFetch(...args);
}); });
fetchMock.mockImplementationOnce(async () => { s.fetchMock.mockImplementationOnce(async () => {
throw new Error("Some Unknown"); throw new Error("Some Unknown");
}); });
const result = await resource.uploadAndOverwrite( const result = await resource.uploadAndOverwrite(
@ -1458,7 +1424,7 @@ describe("Integration", () => {
expect(result1.type).toBe("createSuccess"); expect(result1.type).toBe("createSuccess");
expect(result2.type).toBe("createSuccess"); expect(result2.type).toBe("createSuccess");
// 1 for read, 1 for delete in createAndOverwrite, 1 for create // 1 for read, 1 for delete in createAndOverwrite, 1 for create
expect(fetchMock).toHaveBeenCalledTimes(3); expect(s.fetchMock).toHaveBeenCalledTimes(3);
expect(resource.getBlob()?.toString()).toBe("some text 2."); expect(resource.getBlob()?.toString()).toBe("some text 2.");
}); });
@ -1478,7 +1444,7 @@ describe("Integration", () => {
expect(result1.type).toBe("createSuccess"); expect(result1.type).toBe("createSuccess");
expect(result2.type).toBe("createSuccess"); expect(result2.type).toBe("createSuccess");
// 1 for delete in createAndOverwrite, 1 for create // 1 for delete in createAndOverwrite, 1 for create
expect(fetchMock).toHaveBeenCalledTimes(2); expect(s.fetchMock).toHaveBeenCalledTimes(2);
expect(resource.getBlob()?.toString()).toBe("some text 2."); expect(resource.getBlob()?.toString()).toBe("some text 2.");
}); });
}); });
@ -1552,7 +1518,7 @@ describe("Integration", () => {
it("returns an error if an error is encountered", async () => { it("returns an error if an error is encountered", async () => {
const resource = solidLdoDataset.getResource(SAMPLE2_BINARY_URI); const resource = solidLdoDataset.getResource(SAMPLE2_BINARY_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(SAMPLE2_BINARY_URI, { new Response(SAMPLE2_BINARY_URI, {
status: 500, status: 500,
}), }),
@ -1596,7 +1562,7 @@ describe("Integration", () => {
}); });
it("handles an error when committing data", async () => { it("handles an error when committing data", async () => {
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response(SAMPLE_DATA_URI, { new Response(SAMPLE_DATA_URI, {
status: 500, status: 500,
}), }),
@ -1847,7 +1813,7 @@ describe("Integration", () => {
it("returns an error when an error is encountered fetching the aclUri", async () => { it("returns an error when an error is encountered fetching the aclUri", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.getWac(); const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError"); expect(wacResult.type).toBe("serverError");
@ -1862,7 +1828,7 @@ describe("Integration", () => {
it("returns a non-compliant error if a response is returned without a link header", async () => { it("returns a non-compliant error if a response is returned without a link header", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("Error", { new Response("Error", {
status: 200, status: 200,
}), }),
@ -1879,7 +1845,7 @@ describe("Integration", () => {
it("returns a non-compliant error if a response is returned without an ACL link", async () => { it("returns a non-compliant error if a response is returned without an ACL link", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("Error", { new Response("Error", {
status: 200, status: 200,
headers: { link: `<card.meta>; rel="describedBy"` }, headers: { link: `<card.meta>; rel="describedBy"` },
@ -1896,7 +1862,7 @@ describe("Integration", () => {
}); });
it("Returns an UnexpectedResourceError if an unknown error is triggered while getting the wac URI", async () => { it("Returns an UnexpectedResourceError if an unknown error is triggered while getting the wac URI", async () => {
fetchMock.mockRejectedValueOnce(new Error("Something happened.")); s.fetchMock.mockRejectedValueOnce(new Error("Something happened."));
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const result = await resource.getWac(); const result = await resource.getWac();
expect(result.isError).toBe(true); expect(result.isError).toBe(true);
@ -1907,13 +1873,13 @@ describe("Integration", () => {
it("Returns an error if the request to get the ACL fails", async () => { it("Returns an error if the request to get the ACL fails", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("", { new Response("", {
status: 200, status: 200,
headers: { link: `<card.acl>; rel="acl"` }, headers: { link: `<card.acl>; rel="acl"` },
}), }),
); );
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.getWac(); const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError"); expect(wacResult.type).toBe("serverError");
@ -1923,13 +1889,13 @@ describe("Integration", () => {
it("Returns an error if the request to the ACL resource returns invalid turtle", async () => { it("Returns an error if the request to the ACL resource returns invalid turtle", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("", { new Response("", {
status: 200, status: 200,
headers: { link: `<card.acl>; rel="acl"` }, headers: { link: `<card.acl>; rel="acl"` },
}), }),
); );
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("BAD TURTLE", { status: 200 }), new Response("BAD TURTLE", { status: 200 }),
); );
const wacResult = await resource.getWac(); const wacResult = await resource.getWac();
@ -1944,14 +1910,14 @@ describe("Integration", () => {
it("Returns an error if there was a problem getting the parent resource", async () => { it("Returns an error if there was a problem getting the parent resource", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("", { new Response("", {
status: 200, status: 200,
headers: { link: `<card.acl>; rel="acl"` }, headers: { link: `<card.acl>; rel="acl"` },
}), }),
); );
fetchMock.mockResolvedValueOnce(new Response("", { status: 404 })); s.fetchMock.mockResolvedValueOnce(new Response("", { status: 404 }));
fetchMock.mockResolvedValueOnce(new Response("", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("", { status: 500 }));
const wacResult = await resource.getWac(); const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError"); expect(wacResult.type).toBe("serverError");
@ -1959,13 +1925,13 @@ describe("Integration", () => {
it("returns a NonCompliantPodError when this is the root resource and it doesn't have an ACL", async () => { it("returns a NonCompliantPodError when this is the root resource and it doesn't have an ACL", async () => {
const resource = solidLdoDataset.getResource(ROOT_CONTAINER); const resource = solidLdoDataset.getResource(ROOT_CONTAINER);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("", { new Response("", {
status: 200, status: 200,
headers: { link: `<card.acl>; rel="acl"` }, headers: { link: `<card.acl>; rel="acl"` },
}), }),
); );
fetchMock.mockResolvedValueOnce(new Response("", { status: 404 })); s.fetchMock.mockResolvedValueOnce(new Response("", { status: 404 }));
const wacResult = await resource.getWac(); const wacResult = await resource.getWac();
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("noncompliantPodError"); expect(wacResult.type).toBe("noncompliantPodError");
@ -2039,7 +2005,7 @@ describe("Integration", () => {
it("returns an error when an error is encountered fetching the aclUri", async () => { it("returns an error when an error is encountered fetching the aclUri", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("Error", { status: 500 }));
const wacResult = await resource.setWac(newRules); const wacResult = await resource.setWac(newRules);
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError"); expect(wacResult.type).toBe("serverError");
@ -2047,13 +2013,13 @@ describe("Integration", () => {
it("Returns an error when the request to write the access rules throws an error", async () => { it("Returns an error when the request to write the access rules throws an error", async () => {
const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI); const resource = solidLdoDataset.getResource(TEST_CONTAINER_URI);
fetchMock.mockResolvedValueOnce( s.fetchMock.mockResolvedValueOnce(
new Response("", { new Response("", {
status: 200, status: 200,
headers: { link: `<card.acl>; rel="acl"` }, headers: { link: `<card.acl>; rel="acl"` },
}), }),
); );
fetchMock.mockResolvedValueOnce(new Response("", { status: 500 })); s.fetchMock.mockResolvedValueOnce(new Response("", { status: 500 }));
const wacResult = await resource.setWac(newRules); const wacResult = await resource.setWac(newRules);
expect(wacResult.isError).toBe(true); expect(wacResult.isError).toBe(true);
expect(wacResult.type).toBe("serverError"); expect(wacResult.type).toBe("serverError");
@ -2083,7 +2049,7 @@ describe("Integration", () => {
expect(resource.isSubscribedToNotifications()).toBe(true); expect(resource.isSubscribedToNotifications()).toBe(true);
await authFetch(SAMPLE_DATA_URI, { await s.authFetch(SAMPLE_DATA_URI, {
method: "PATCH", method: "PATCH",
body: 'INSERT DATA { <http://example.org/#spiderman> <http://xmlns.com/foaf/0.1/name> "Peter Parker" . }', body: 'INSERT DATA { <http://example.org/#spiderman> <http://xmlns.com/foaf/0.1/name> "Peter Parker" . }',
headers: { headers: {
@ -2105,7 +2071,7 @@ describe("Integration", () => {
spidermanCallback.mockClear(); spidermanCallback.mockClear();
await resource.unsubscribeFromNotifications(subscriptionId); await resource.unsubscribeFromNotifications(subscriptionId);
expect(resource.isSubscribedToNotifications()).toBe(false); expect(resource.isSubscribedToNotifications()).toBe(false);
await authFetch(SAMPLE_DATA_URI, { await s.authFetch(SAMPLE_DATA_URI, {
method: "PATCH", method: "PATCH",
body: 'INSERT DATA { <http://example.org/#spiderman> <http://xmlns.com/foaf/0.1/name> "Miles Morales" . }', body: 'INSERT DATA { <http://example.org/#spiderman> <http://xmlns.com/foaf/0.1/name> "Miles Morales" . }',
headers: { headers: {
@ -2143,7 +2109,7 @@ describe("Integration", () => {
await resource.subscribeToNotifications(); await resource.subscribeToNotifications();
await authFetch(SAMPLE_DATA_URI, { await s.authFetch(SAMPLE_DATA_URI, {
method: "DELETE", method: "DELETE",
}); });
await wait(1000); await wait(1000);
@ -2177,7 +2143,7 @@ describe("Integration", () => {
await testContainer.subscribeToNotifications(); await testContainer.subscribeToNotifications();
await authFetch(SAMPLE_DATA_URI, { await s.authFetch(SAMPLE_DATA_URI, {
method: "DELETE", method: "DELETE",
}); });
await wait(1000); await wait(1000);
@ -2211,7 +2177,7 @@ describe("Integration", () => {
await testContainer.subscribeToNotifications(); await testContainer.subscribeToNotifications();
await authFetch(TEST_CONTAINER_URI, { await s.authFetch(TEST_CONTAINER_URI, {
method: "POST", method: "POST",
headers: { "content-type": "text/turtle", slug: "sample2.ttl" }, headers: { "content-type": "text/turtle", slug: "sample2.ttl" },
body: SPIDER_MAN_TTL, body: SPIDER_MAN_TTL,
@ -2234,18 +2200,19 @@ describe("Integration", () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const onError = jest.fn(); const onError = jest.fn();
await app.stop(); await s.app.stop();
await resource.subscribeToNotifications({ onNotificationError: onError }); await resource.subscribeToNotifications({ onNotificationError: onError });
expect(onError).toHaveBeenCalledTimes(2); expect(onError).toHaveBeenCalledTimes(2);
await app.start(); await s.app.start();
}); });
it("returns an error when the server doesnt support websockets", async () => { it("returns an error when the server doesnt support websockets", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const onError = jest.fn(); const onError = jest.fn();
await app.stop(); await s.app.stop();
const disabledWebsocketsApp = await createApp( const disabledWebsocketsApp = await createApp(
3001,
path.join(__dirname, "./configs/server-config-without-websocket.json"), path.join(__dirname, "./configs/server-config-without-websocket.json"),
); );
await disabledWebsocketsApp.start(); await disabledWebsocketsApp.start();
@ -2254,15 +2221,16 @@ describe("Integration", () => {
expect(onError).toHaveBeenCalledTimes(2); expect(onError).toHaveBeenCalledTimes(2);
await disabledWebsocketsApp.stop(); await disabledWebsocketsApp.stop();
await app.start(); await s.app.start();
}); });
it("attempts to reconnect multiple times before giving up.", async () => { it("attempts to reconnect multiple times before giving up.", async () => {
const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI); const resource = solidLdoDataset.getResource(SAMPLE_DATA_URI);
const onError = jest.fn(); const onError = jest.fn();
await app.stop(); await s.app.stop();
const disabledWebsocketsApp = await createApp( const disabledWebsocketsApp = await createApp(
3001,
path.join(__dirname, "./configs/server-config-without-websocket.json"), path.join(__dirname, "./configs/server-config-without-websocket.json"),
); );
await disabledWebsocketsApp.start(); await disabledWebsocketsApp.start();
@ -2282,7 +2250,7 @@ describe("Integration", () => {
); );
await disabledWebsocketsApp.stop(); await disabledWebsocketsApp.stop();
await app.start(); await s.app.start();
}); });
it("causes no problems when unsubscribing when not subscribed", async () => { it("causes no problems when unsubscribing when not subscribed", async () => {

@ -1,112 +0,0 @@
import type { KeyPair } from "@inrupt/solid-client-authn-core";
import {
buildAuthenticatedFetch,
createDpopHeader,
generateDpopKeyPair,
} from "@inrupt/solid-client-authn-core";
import fetch from "cross-fetch";
const config = {
podName: process.env.USER_NAME || "example",
email: process.env.EMAIL || "hello@example.com",
password: process.env.PASSWORD || "abc123",
};
async function getAuthorization(): Promise<string> {
// First we request the account API controls to find out where we can log in
const indexResponse = await fetch("http://localhost:3001/.account/");
const { controls } = await indexResponse.json();
// And then we log in to the account API
const response = await fetch(controls.password.login, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
email: config.email,
password: config.password,
}),
});
// This authorization value will be used to authenticate in the next step
const result = await response.json();
return result.authorization;
}
async function getSecret(
authorization: string,
): Promise<{ id: string; secret: string; resource: string }> {
// Now that we are logged in, we need to request the updated controls from the server.
// These will now have more values than in the previous example.
const indexResponse = await fetch("http://localhost:3001/.account/", {
headers: { authorization: `CSS-Account-Token ${authorization}` },
});
const { controls } = await indexResponse.json();
// Here we request the server to generate a token on our account
const response = await fetch(controls.account.clientCredentials, {
method: "POST",
headers: {
authorization: `CSS-Account-Token ${authorization}`,
"content-type": "application/json",
},
// The name field will be used when generating the ID of your token.
// The WebID field determines which WebID you will identify as when using the token.
// Only WebIDs linked to your account can be used.
body: JSON.stringify({
name: "my-token",
webId: `http://localhost:3001/${config.podName}/profile/card#me`,
}),
});
// These are the identifier and secret of your token.
// Store the secret somewhere safe as there is no way to request it again from the server!
// The `resource` value can be used to delete the token at a later point in time.
const response2 = await response.json();
return response2;
}
async function getAccessToken(
id: string,
secret: string,
): Promise<{ accessToken: string; dpopKey: KeyPair }> {
try {
// A key pair is needed for encryption.
// This function from `solid-client-authn` generates such a pair for you.
const dpopKey = await generateDpopKeyPair();
// These are the ID and secret generated in the previous step.
// Both the ID and the secret need to be form-encoded.
const authString = `${encodeURIComponent(id)}:${encodeURIComponent(
secret,
)}`;
// This URL can be found by looking at the "token_endpoint" field at
// http://localhost:3001/.well-known/openid-configuration
// if your server is hosted at http://localhost:3000/.
const tokenUrl = "http://localhost:3001/.oidc/token";
const response = await fetch(tokenUrl, {
method: "POST",
headers: {
// The header needs to be in base64 encoding.
authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
"content-type": "application/x-www-form-urlencoded",
dpop: await createDpopHeader(tokenUrl, "POST", dpopKey),
},
body: "grant_type=client_credentials&scope=webid",
});
// This is the Access token that will be used to do an authenticated request to the server.
// The JSON also contains an "expires_in" field in seconds,
// which you can use to know when you need request a new Access token.
const response2 = await response.json();
return { accessToken: response2.access_token, dpopKey };
} catch (err) {
console.error(err);
throw err;
}
}
export async function generateAuthFetch() {
const authorization = await getAuthorization();
const { id, secret } = await getSecret(authorization);
const { accessToken, dpopKey } = await getAccessToken(id, secret);
return await buildAuthenticatedFetch(accessToken, { dpopKey });
}

@ -1,43 +0,0 @@
{
"@context": [
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld"
],
"import": [
"css:config/app/init/static-root.json",
"css:config/app/main/default.json",
"css:config/app/variables/default.json",
"css:config/http/handler/default.json",
"css:config/http/middleware/default.json",
"css:config/http/notifications/all.json",
"css:config/http/server-factory/http.json",
"css:config/http/static/default.json",
"css:config/identity/access/public.json",
"css:config/identity/email/default.json",
"css:config/identity/handler/default.json",
"css:config/identity/oidc/default.json",
"css:config/identity/ownership/token.json",
"css:config/identity/pod/static.json",
"css:config/ldp/authentication/dpop-bearer.json",
"css:config/ldp/authorization/webacl.json",
"css:config/ldp/handler/default.json",
"css:config/ldp/metadata-parser/default.json",
"css:config/ldp/metadata-writer/default.json",
"css:config/ldp/modes/default.json",
"css:config/storage/backend/memory.json",
"css:config/storage/key-value/resource-store.json",
"css:config/storage/location/pod.json",
"css:config/storage/middleware/default.json",
"css:config/util/auxiliary/acl.json",
"css:config/util/identifiers/suffix.json",
"css:config/util/index/default.json",
"css:config/util/logging/winston.json",
"css:config/util/representation-conversion/default.json",
"css:config/util/resource-locker/memory.json",
"css:config/util/variables/default.json"
],
"@graph": [
{
"comment": "A Solid server that stores its resources on disk and uses WAC for authorization."
}
]
}

@ -1,9 +0,0 @@
[
{
"email": "hello@example.com",
"password": "abc123",
"pods": [
{ "name": "example" }
]
}
]

@ -1,3 +0,0 @@
import { config } from "dotenv";
config();

@ -1,42 +0,0 @@
// Taken from https://github.com/comunica/comunica/blob/b237be4265c353a62a876187d9e21e3bc05123a3/engines/query-sparql/test/QuerySparql-solid-test.ts#L9
import * as path from "path";
import type { App } from "@solid/community-server";
import { AppRunner, resolveModulePath } from "@solid/community-server";
import "jest-rdf";
import type { SolidContainerUri } from "../src";
export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/";
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "";
export const ROOT_CONTAINER =
`${SERVER_DOMAIN}${ROOT_ROUTE}` as SolidContainerUri;
export const WEB_ID =
process.env.WEB_ID || `${SERVER_DOMAIN}example/profile/card#me`;
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
export async function createApp(customConfigPath?: string): Promise<App> {
if (process.env.SERVER) {
return {
start: () => {},
stop: () => {},
} as App;
}
const appRunner = new AppRunner();
return appRunner.create({
loaderProperties: {
mainModulePath: resolveModulePath(""),
typeChecking: false,
},
config: customConfigPath ?? resolveModulePath("config/file-root.json"),
variableBindings: {},
shorthand: {
port: 3_001,
loggingLevel: "off",
seedConfig: path.join(__dirname, "configs", "solid-css-seed.json"),
rootFilePath: path.join(__dirname, "./data"),
},
});
}

@ -1,11 +1,12 @@
{ {
"name": "@ldo/test-solid-server", "name": "@ldo/test-solid-server",
"version": "1.0.0-alpha.3", "version": "1.0.0-alpha.8",
"description": "A solid server to be used in jest tests", "description": "A solid server to be used in jest tests",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"build": "tsc --project tsconfig.build.json", "build": "tsc --project tsconfig.build.json && npm run copy-configs",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"copy-configs": "cp -r src/configs dist/configs",
"lint": "eslint src/** --fix --no-error-on-unmatched-pattern" "lint": "eslint src/** --fix --no-error-on-unmatched-pattern"
}, },
"repository": { "repository": {

@ -1,2 +1,3 @@
export * from "./createServer"; export * from "./createServer";
export * from "./setupTestServer"; export * from "./setupTestServer";
export * from "./resourceUtils";

@ -3,7 +3,7 @@ export type ResourceInfo = ContainerInfo | LeafInfo;
interface ContainerInfo { interface ContainerInfo {
slug: string; slug: string;
isContainer: true; isContainer: true;
shouldNotInit: boolean; shouldNotInit?: boolean;
contains: (ContainerInfo | LeafInfo)[]; contains: (ContainerInfo | LeafInfo)[];
} }
@ -39,11 +39,10 @@ export async function initResources(
), ),
); );
} else { } else {
authFetch(rootUri, { await authFetch(`${rootUri}${resourceInfo.slug}`, {
method: "POST", method: "PUT",
headers: { headers: {
"content-type": resourceInfo.mimeType, "content-type": resourceInfo.mimeType,
slug: resourceInfo.slug,
}, },
body: resourceInfo.data, body: resourceInfo.data,
}); });

@ -18,13 +18,11 @@ export function setupServer(
Promise<Response>, Promise<Response>,
[input: RequestInfo | URL, init?: RequestInit | undefined] [input: RequestInfo | URL, init?: RequestInit | undefined]
>; >;
authFetch: typeof fetch;
rootUri: string; rootUri: string;
rootContainer: string;
} = { } = {
rootUri: `https://localhost:${port}`, rootUri: `http://localhost:${port}/`,
rootContainer: `https://localhost:${port}/example/`,
} as any; } as any;
let authFetch: typeof fetch;
let previousJestId: string | undefined; let previousJestId: string | undefined;
let previousNodeEnv: string | undefined; let previousNodeEnv: string | undefined;
@ -37,7 +35,7 @@ export function setupServer(
// Start up the server // Start up the server
data.app = await createApp(port, customConfigPath); data.app = await createApp(port, customConfigPath);
await data.app.start(); await data.app.start();
authFetch = await generateAuthFetch(port); data.authFetch = await generateAuthFetch(port);
}); });
afterAll(async () => { afterAll(async () => {
@ -49,13 +47,13 @@ export function setupServer(
}); });
beforeEach(async () => { beforeEach(async () => {
data.fetchMock = jest.fn(authFetch); data.fetchMock = jest.fn(data.authFetch);
// Create a new document called sample.ttl // Create a new document called sample.ttl
await initResources(data.rootUri, resourceInfo, authFetch); await initResources(data.rootUri, resourceInfo, data.authFetch);
}); });
afterEach(async () => { afterEach(async () => {
await cleanResources(data.rootUri, resourceInfo, authFetch); await cleanResources(data.rootUri, resourceInfo, data.authFetch);
}); });
return data; return data;

Loading…
Cancel
Save