Added old tests and fixed the ErrorResult test

main
Jackson Morgan 6 months ago
parent 2fa3e52246
commit 9ae7dea357
  1. 31
      packages/connected-solid/test/.ldo/post.context.ts
  2. 155
      packages/connected-solid/test/.ldo/post.schema.ts
  3. 19
      packages/connected-solid/test/.ldo/post.shapeTypes.ts
  4. 45
      packages/connected-solid/test/.ldo/post.typings.ts
  5. 2264
      packages/connected-solid/test/Integration.test.ts
  6. 256
      packages/connected-solid/test/LeafRequester.test.ts
  7. 112
      packages/connected-solid/test/RequestBatcher.test.ts
  8. 48
      packages/connected-solid/test/Websocket2023NotificationSubscription.test.ts
  9. 112
      packages/connected-solid/test/authFetch.helper.ts
  10. 44
      packages/connected-solid/test/configs/server-config-without-websocket.json
  11. 43
      packages/connected-solid/test/configs/server-config.json
  12. 9
      packages/connected-solid/test/configs/solid-css-seed.json
  13. 8
      packages/connected-solid/test/guaranteeFetch.test.ts
  14. 3
      packages/connected-solid/test/setup-tests.ts
  15. 40
      packages/connected-solid/test/solidServer.helper.ts
  16. 7
      packages/connected-solid/test/uriTypes.test.ts
  17. 3
      packages/connected-solid/test/utils.helper.ts
  18. 1
      packages/connected/jest.config.js
  19. 0
      packages/connected/src/test.ts
  20. 66
      packages/connected/test/ErrorResult.test.ts
  21. 65
      packages/connected/test/MockResource.ts

@ -0,0 +1,31 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* postContext: JSONLD Context for post
* =============================================================================
*/
export const postContext: ContextDefinition = {
type: {
"@id": "@type",
},
SocialMediaPosting: "http://schema.org/SocialMediaPosting",
CreativeWork: "http://schema.org/CreativeWork",
Thing: "http://schema.org/Thing",
articleBody: {
"@id": "http://schema.org/articleBody",
"@type": "http://www.w3.org/2001/XMLSchema#string",
},
uploadDate: {
"@id": "http://schema.org/uploadDate",
"@type": "http://www.w3.org/2001/XMLSchema#date",
},
image: {
"@id": "http://schema.org/image",
"@type": "@id",
},
publisher: {
"@id": "http://schema.org/publisher",
"@type": "@id",
},
};

@ -0,0 +1,155 @@
import { Schema } from "shexj";
/**
* =============================================================================
* postSchema: ShexJ Schema for post
* =============================================================================
*/
export const postSchema: Schema = {
type: "Schema",
shapes: [
{
id: "https://example.com/PostSh",
type: "ShapeDecl",
shapeExpr: {
type: "Shape",
expression: {
type: "EachOf",
expressions: [
{
type: "TripleConstraint",
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
valueExpr: {
type: "NodeConstraint",
values: [
"http://schema.org/SocialMediaPosting",
"http://schema.org/CreativeWork",
"http://schema.org/Thing",
],
},
},
{
type: "TripleConstraint",
predicate: "http://schema.org/articleBody",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#string",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "articleBody",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The actual body of the article. ",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/uploadDate",
valueExpr: {
type: "NodeConstraint",
datatype: "http://www.w3.org/2001/XMLSchema#date",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "uploadDate",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"Date when this media object was uploaded to this site.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/image",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
min: 0,
max: 1,
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "image",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A media object that encodes this CreativeWork. This property is a synonym for encoding.",
},
},
],
},
{
type: "TripleConstraint",
predicate: "http://schema.org/publisher",
valueExpr: {
type: "NodeConstraint",
nodeKind: "iri",
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "publisher",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value: "The publisher of the creative work.",
},
},
],
},
],
},
annotations: [
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#label",
object: {
value: "SocialMediaPost",
},
},
{
type: "Annotation",
predicate: "http://www.w3.org/2000/01/rdf-schema#comment",
object: {
value:
"A post to a social media platform, including blog posts, tweets, Facebook posts, etc.",
},
},
],
},
},
],
};

@ -0,0 +1,19 @@
import { ShapeType } from "@ldo/ldo";
import { postSchema } from "./post.schema";
import { postContext } from "./post.context";
import { PostSh } from "./post.typings";
/**
* =============================================================================
* LDO ShapeTypes post
* =============================================================================
*/
/**
* PostSh ShapeType
*/
export const PostShShapeType: ShapeType<PostSh> = {
schema: postSchema,
shape: "https://example.com/PostSh",
context: postContext,
};

@ -0,0 +1,45 @@
import { ContextDefinition } from "jsonld";
/**
* =============================================================================
* Typescript Typings for post
* =============================================================================
*/
/**
* PostSh Type
*/
export interface PostSh {
"@id"?: string;
"@context"?: ContextDefinition;
type:
| {
"@id": "SocialMediaPosting";
}
| {
"@id": "CreativeWork";
}
| {
"@id": "Thing";
};
/**
* The actual body of the article.
*/
articleBody?: string;
/**
* Date when this media object was uploaded to this site.
*/
uploadDate: string;
/**
* A media object that encodes this CreativeWork. This property is a synonym for encoding.
*/
image?: {
"@id": string;
};
/**
* The publisher of the creative work.
*/
publisher: {
"@id": string;
};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,256 @@
// import type { App } from "@solid/community-server";
// import { getAuthenticatedFetch, ROOT_COONTAINER } from "./solidServer.helper";
// import type { SolidLdoDataset } from "../src/SolidLdoDataset";
// import { createSolidLdoDataset } from "../src/createSolidLdoDataset";
// import { LeafRequester } from "../src/requester/LeafRequester";
// import { namedNode, quad as createQuad } from "@rdfjs/data-model";
describe("Leaf Requester", () => {
it("trivial", () => {
expect(true).toBe(true);
});
});
// describe.skip("Leaf Requester", () => {
// let _app: App;
// let authFetch: typeof fetch;
// let fetchMock: typeof fetch;
// let solidLdoDataset: SolidLdoDataset;
// beforeAll(async () => {
// // Start up the server
// // app = await createApp();
// // await app.start();
// authFetch = await getAuthenticatedFetch();
// });
// beforeEach(async () => {
// fetchMock = jest.fn(authFetch);
// solidLdoDataset = createSolidLdoDataset({ fetch: fetchMock });
// // Create a new document called sample.ttl
// await Promise.all([
// authFetch(`${ROOT_COONTAINER}test_leaf/`, {
// method: "POST",
// headers: { "content-type": "text/turtle", slug: "sample.ttl" },
// body: `@base <http://example.org/> .
// @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
// @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
// @prefix foaf: <http://xmlns.com/foaf/0.1/> .
// @prefix rel: <http://www.perceive.net/schemas/relationship/> .
// <#green-goblin>
// rel:enemyOf <#spiderman> ;
// a foaf:Person ; # in the context of the Marvel universe
// foaf:name "Green Goblin" .
// <#spiderman>
// rel:enemyOf <#green-goblin> ;
// a foaf:Person ;
// foaf:name "Spiderman", "Человек-паук"@ru .`,
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/`, {
// method: "PUT",
// headers: { "content-type": "text/plain", slug: "sample.txt" },
// body: `some text.`,
// }),
// ]);
// });
// afterEach(async () => {
// await Promise.all([
// authFetch(`${ROOT_COONTAINER}test_leaf/sample.ttl`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample2.ttl`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample.txt`, {
// method: "DELETE",
// }),
// authFetch(`${ROOT_COONTAINER}test_leaf/sample2.txt`, {
// method: "DELETE",
// }),
// ]);
// });
// /**
// * ===========================================================================
// * Read
// * ===========================================================================
// */
// it("reads data", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.read();
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.match(
// null,
// null,
// null,
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// ).size,
// ).toBe(7);
// });
// it("reads data that doesn't exist", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/doesnotexist.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.read();
// expect(result.type).toBe("absent");
// });
// /**
// * ===========================================================================
// * Create
// * ===========================================================================
// */
// it("creates a data resource that doesn't exist while not overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample2.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource();
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample2.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// it("creates a data resource that doesn't exist while overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample2.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource(true);
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample2.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// it("creates a data resource that does exist while not overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource();
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode("http://example.org/#spiderman"),
// namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
// namedNode("http://example.org/#green-goblin"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// ),
// ),
// ).toBe(true);
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// it("creates a data resource that does exist while overwriting", async () => {
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.createDataResource(true);
// expect(result.type).toBe("data");
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode("http://example.org/#spiderman"),
// namedNode("http://www.perceive.net/schemas/relationship/enemyOf"),
// namedNode("http://example.org/#green-goblin"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// ),
// ),
// ).toBe(false);
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}test_leaf/`),
// ),
// ),
// ).toBe(true);
// });
// /**
// * ===========================================================================
// * Delete
// * ===========================================================================
// */
// it("deletes data", async () => {
// solidLdoDataset.add(
// createQuad(
// namedNode("a"),
// namedNode("b"),
// namedNode("c"),
// namedNode(`${ROOT_COONTAINER}/test_leaf/sample.ttl`),
// ),
// );
// solidLdoDataset.add(
// createQuad(
// namedNode(`${ROOT_COONTAINER}/test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}/test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}/test_leaf/`),
// ),
// );
// const leafRequester = new LeafRequester(
// `${ROOT_COONTAINER}/test_leaf/sample.ttl`,
// solidLdoDataset.context,
// );
// const result = await leafRequester.delete();
// expect(result.type).toBe("absent");
// expect(
// solidLdoDataset.match(
// null,
// null,
// null,
// namedNode(`${ROOT_COONTAINER}/test_leaf/sample.ttl`),
// ).size,
// ).toBe(0);
// expect(
// solidLdoDataset.has(
// createQuad(
// namedNode(`${ROOT_COONTAINER}/test_leaf/`),
// namedNode("http://www.w3.org/ns/ldp#contains"),
// namedNode(`${ROOT_COONTAINER}/test_leaf/sample.ttl`),
// namedNode(`${ROOT_COONTAINER}/test_leaf/`),
// ),
// ),
// ).toBe(false);
// });
// });

@ -0,0 +1,112 @@
import type { WaitingProcess } from "../src/util/RequestBatcher";
import { RequestBatcher } from "../src/util/RequestBatcher";
describe("RequestBatcher", () => {
type ReadWaitingProcess = WaitingProcess<[string], string>;
it("Batches a request", async () => {
const requestBatcher = new RequestBatcher({ batchMillis: 500 });
const perform = async (input: string): Promise<string> => {
await wait(100);
return `Hello ${input}`;
};
const perform1 = jest.fn(perform);
const perform2 = jest.fn(perform);
const perform3 = jest.fn((input: string): Promise<string> => {
expect(requestBatcher.isLoading("read")).toBe(true);
return perform(input);
});
const perform4 = jest.fn(perform);
const modifyQueue = (queue, currentlyProcessing, input: [string]) => {
const last = queue[queue.length - 1];
if (last?.name === "read") {
(last as ReadWaitingProcess).args[0] += input;
return last;
}
return undefined;
};
let return1: string = "";
let return2: string = "";
let return3: string = "";
let return4: string = "";
expect(requestBatcher.isLoading("read")).toBe(false);
await Promise.all([
requestBatcher
.queueProcess<[string], string>({
name: "read",
args: ["a"],
perform: perform1,
modifyQueue,
})
.then((val) => (return1 = val)),
requestBatcher
.queueProcess<[string], string>({
name: "read",
args: ["b"],
perform: perform2,
modifyQueue,
})
.then((val) => (return2 = val)),
,
requestBatcher
.queueProcess<[string], string>({
name: "read",
args: ["c"],
perform: perform3,
modifyQueue,
})
.then((val) => (return3 = val)),
,
requestBatcher
.queueProcess<[string], string>({
name: "read",
args: ["d"],
perform: perform4,
modifyQueue,
})
.then((val) => (return4 = val)),
,
]);
expect(return1).toBe("Hello a");
expect(return2).toBe("Hello bcd");
expect(return3).toBe("Hello bcd");
expect(return4).toBe("Hello bcd");
expect(perform1).toHaveBeenCalledTimes(1);
expect(perform1).toHaveBeenCalledWith("a");
expect(perform2).toHaveBeenCalledTimes(1);
expect(perform2).toHaveBeenCalledWith("bcd");
expect(perform3).toHaveBeenCalledTimes(0);
expect(perform4).toHaveBeenCalledTimes(0);
});
it("sets a default batch millis", () => {
const requestBatcher = new RequestBatcher();
expect(requestBatcher.batchMillis).toBe(1000);
});
it("handles an error being thrown in the process", () => {
const requestBatcher = new RequestBatcher({ batchMillis: 500 });
const perform = async (_input: string): Promise<string> => {
throw new Error("Test Error");
};
const perform1 = jest.fn(perform);
expect(() =>
requestBatcher.queueProcess<[string], string>({
name: "read",
args: ["a"],
perform: perform1,
modifyQueue: () => undefined,
}),
).rejects.toThrowError("Test Error");
});
});
function wait(millis: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, millis));
}

@ -0,0 +1,48 @@
import type { WebSocket, Event, ErrorEvent } from "ws";
import { Websocket2023NotificationSubscription } from "../src/resource/notifications/Websocket2023NotificationSubscription";
import type { SolidLdoDatasetContext } from "../src";
import { Leaf } from "../src";
import type { NotificationChannel } from "@solid-notifications/types";
describe("Websocket2023NotificationSubscription", () => {
it("returns an error when websockets have an error", async () => {
const WebSocketMock: WebSocket = {} as WebSocket;
const subscription = new Websocket2023NotificationSubscription(
new Leaf("https://example.com", {
fetch,
} as unknown as SolidLdoDatasetContext),
() => {},
{} as unknown as SolidLdoDatasetContext,
() => WebSocketMock,
);
const subPromise = subscription.subscribeToWebsocket({
receiveFrom: "http://example.com",
} as unknown as NotificationChannel);
WebSocketMock.onopen?.({} as Event);
await subPromise;
WebSocketMock.onerror?.({ error: new Error("Test Error") } as ErrorEvent);
});
it("returns an error when websockets have an error at the beginning", async () => {
const WebSocketMock: WebSocket = {} as WebSocket;
const subscription = new Websocket2023NotificationSubscription(
new Leaf("https://example.com", {
fetch,
} as unknown as SolidLdoDatasetContext),
() => {},
{} as unknown as SolidLdoDatasetContext,
() => WebSocketMock,
);
const subPromise = subscription.subscribeToWebsocket({
receiveFrom: "http://example.com",
} as unknown as NotificationChannel);
WebSocketMock.onerror?.({ error: new Error("Test Error") } as ErrorEvent);
await subPromise;
});
});

@ -0,0 +1,112 @@
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 });
}

@ -0,0 +1,44 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^7.0.0/components/context.jsonld",
"import": [
"css:config/app/init/initialize-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/webhooks.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/no-accounts.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/file.json",
"css:config/storage/key-value/resource-store.json",
"css:config/storage/location/root.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/file.json",
"css:config/util/variables/default.json"
],
"@graph": [
{
"comment": [
"A Solid server that stores its resources on disk and uses WAC for authorization.",
"No registration and the root container is initialized to allow full access for everyone so make sure to change this."
]
}
]
}

@ -0,0 +1,43 @@
{
"@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."
}
]
}

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

@ -0,0 +1,8 @@
import { guaranteeFetch } from "../src/util/guaranteeFetch";
import crossFetch from "cross-fetch";
describe("guaranteeFetch", () => {
it("returns crossfetch when no fetch is provided", () => {
expect(guaranteeFetch()).toBe(crossFetch);
});
});

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

@ -0,0 +1,40 @@
// 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";
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}`;
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"),
},
});
}

@ -0,0 +1,7 @@
import { isLeafUri } from "../src";
describe("isLeafUri", () => {
it("returns true if the given value is a leaf URI", () => {
expect(isLeafUri("https://example.com/index.ttl")).toBe(true);
});
});

@ -0,0 +1,3 @@
export async function wait(millis: number) {
return new Promise((resolve) => setTimeout(resolve, millis));
}

@ -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",

@ -0,0 +1,66 @@
import {
AggregateError,
ErrorResult,
ResourceError,
UnexpectedResourceError,
} from "../src/results/error/ErrorResult";
import { InvalidUriError } from "../src/results/error/InvalidUriError";
import { MockResouce } from "./MockResource";
const mockResource = new MockResouce("https://example.com/");
describe("ErrorResult", () => {
describe("fromThrown", () => {
it("returns an UnexpecteResourceError if a string is provided", () => {
expect(
UnexpectedResourceError.fromThrown(mockResource, "hello").message,
).toBe("hello");
});
it("returns an UnexpecteResourceError if an odd valud is provided", () => {
expect(UnexpectedResourceError.fromThrown(mockResource, 5).message).toBe(
"Error of type number thrown: 5",
);
});
});
describe("AggregateError", () => {
it("flattens aggregate errors provided to the constructor", () => {
const err1 = UnexpectedResourceError.fromThrown(mockResource, "1");
const err2 = UnexpectedResourceError.fromThrown(mockResource, "2");
const err3 = UnexpectedResourceError.fromThrown(mockResource, "3");
const err4 = UnexpectedResourceError.fromThrown(mockResource, "4");
const aggErr1 = new AggregateError([err1, err2]);
const aggErr2 = new AggregateError([err3, err4]);
const finalAgg = new AggregateError([aggErr1, aggErr2]);
expect(finalAgg.errors.length).toBe(4);
});
});
describe("default messages", () => {
class ConcreteResourceError extends ResourceError<MockResouce> {
readonly type = "concreteResourceError" as const;
}
class ConcreteErrorResult extends ErrorResult {
readonly type = "concreteErrorResult" as const;
}
it("ResourceError fallsback to a default message if none is provided", () => {
expect(new ConcreteResourceError(mockResource).message).toBe(
"An unkown error for https://example.com/",
);
});
it("ErrorResult fallsback to a default message if none is provided", () => {
expect(new ConcreteErrorResult().message).toBe(
"An unkown error was encountered.",
);
});
it("InvalidUriError fallsback to a default message if none is provided", () => {
expect(new InvalidUriError(mockResource).message).toBe(
"https://example.com/ is an invalid uri.",
);
});
});
});

@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import EventEmitter from "events";
import {
Unfetched,
type ConnectedResult,
type Resource,
type ResourceEventEmitter,
type ResourceResult,
} from "../src";
export class MockResouce
extends (EventEmitter as new () => ResourceEventEmitter)
implements Resource
{
isError = false as const;
uri: string;
type = "mock" as const;
status: ConnectedResult;
constructor(uri: string) {
super();
this.uri = uri;
this.status = new Unfetched(this);
}
isLoading(): boolean {
throw new Error("Method not implemented.");
}
isFetched(): boolean {
throw new Error("Method not implemented.");
}
isUnfetched(): boolean {
throw new Error("Method not implemented.");
}
isDoingInitialFetch(): boolean {
throw new Error("Method not implemented.");
}
isPresent(): boolean | undefined {
throw new Error("Method not implemented.");
}
isAbsent(): boolean | undefined {
throw new Error("Method not implemented.");
}
isSubscribedToNotifications(): boolean {
throw new Error("Method not implemented.");
}
read(): Promise<ResourceResult<any>> {
throw new Error("Method not implemented.");
}
readIfAbsent(): Promise<ResourceResult<any>> {
throw new Error("Method not implemented.");
}
subscribeToNotifications(_callbacks?: {
onNotification: (message: any) => void;
onNotificationError: (err: Error) => void;
}): Promise<string> {
throw new Error("Method not implemented.");
}
unsubscribeFromNotifications(_subscriptionId: string): Promise<void> {
throw new Error("Method not implemented.");
}
unsubscribeFromAllNotifications(): Promise<void> {
throw new Error("Method not implemented.");
}
}
Loading…
Cancel
Save