Skip to content

Commit

Permalink
chore: make event-emitter-promisify a dev dependency and extend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jeswr committed Nov 21, 2023
1 parent 1813b49 commit e95c2d8
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 72 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"dotenv-flow": "^3.2.0",
"eslint": "^8.18.0",
"eslint-config-next": "^13.0.6",
"event-emitter-promisify": "^1.1.0",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"license-checker": "^25.0.1",
Expand Down
1 change: 0 additions & 1 deletion src/common/getters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import {
import type { AccessGrant, AccessRequest } from "../gConsent";
import { GC_FOR_PERSONAL_DATA } from "../gConsent/constants";
import { TYPE, cred, gc } from "./constants";

Check failure on line 47 in src/common/getters.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Expected 1 empty line after import statement not followed by another import

const { quad, namedNode, literal, blankNode } = DataFactory;

jest.mock("@inrupt/universal-fetch", () => ({
Expand Down
118 changes: 51 additions & 67 deletions src/gConsent/manage/getAccessGrant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,22 @@ import type * as CrossFetch from "@inrupt/universal-fetch";
import { Response } from "@inrupt/universal-fetch";
import { beforeAll, describe, expect, it, jest } from "@jest/globals";

import { mockAccessApiEndpoint } from "../request/request.mock";
import {
mockAccessGrantObject,
mockAccessGrantVc,
mockAccessRequestVc,
} from "../util/access.mock";
import { toBeEqual } from "../util/toBeEqual.mock";
import { getAccessGrant } from "./getAccessGrant";
import { verifiableCredentialToDataset } from "@inrupt/solid-client-vc";

Check failure on line 33 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

`@inrupt/solid-client-vc` import should occur before import of `../util/access.mock`

Check failure on line 33 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

'verifiableCredentialToDataset' is defined but never used

jest.mock("@inrupt/universal-fetch", () => {
const crossFetch = jest.requireActual(
"@inrupt/universal-fetch",
) as jest.Mocked<typeof CrossFetch>;
return {
// Do no mock the globals such as Response.
...crossFetch,
Response: crossFetch.Response,
fetch: jest.fn<(typeof crossFetch)["fetch"]>(),
};
});
Expand All @@ -51,7 +51,6 @@ describe("getAccessGrant", () => {
});

it("uses the provided fetch if any", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest.fn<typeof fetch>().mockResolvedValueOnce(
new Response(JSON.stringify(mockAccessGrantObject()), {
headers: new Headers([["content-type", "application/json"]]),
Expand All @@ -65,48 +64,31 @@ describe("getAccessGrant", () => {
});

it("throws if resolving the IRI results in an HTTP error", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest
.fn(global.fetch)
.mockResolvedValueOnce(
new Response("Not Found", { status: 404, statusText: "Not Found" }),
);

await expect(
getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
fetch: async () => new Response("Not Found", { status: 404, statusText: "Not Found" }),

Check failure on line 69 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `⏎·········`
}),
).rejects.toThrow(
/Could not resolve \[https:\/\/some.vc.url\].*404 Not Found/,
);
});

it("throws if the given IRI does not resolve to a Verifiable Credential", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest
.fn(global.fetch)
.mockResolvedValueOnce(new Response("{'someKey': 'someValue'}"));

await expect(
getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
fetch: async () => new Response("{'someKey': 'someValue'}"),
}),
).rejects.toThrow(
/Unexpected response.*\[https:\/\/some.vc.url\].*not a Verifiable Credential/,
);
});

it("throws if the given IRI does not resolve to a access grant Verifiable Credential", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest.fn(global.fetch).mockResolvedValueOnce(
new Response(JSON.stringify(await mockAccessRequestVc()), {
headers: new Headers([["content-type", "application/json"]]),
}),
);

await expect(
getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
fetch: async () => new Response(JSON.stringify(await mockAccessRequestVc()), {

Check failure on line 89 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `⏎·········`
headers: new Headers([["content-type", "application/json"]]),

Check failure on line 90 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `··`
}),

Check failure on line 91 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `··`
}),
).rejects.toThrow(/not an Access Grant/);
});
Expand All @@ -115,18 +97,14 @@ describe("getAccessGrant", () => {
// but the linter doesn't pick up on this.
// eslint-disable-next-line jest/expect-expect
it("supports denied access grants with a given IRI", async () => {
mockAccessApiEndpoint();
const mockedAccessGrant = mockAccessGrantObject();
mockedAccessGrant.credentialSubject.providedConsent.hasStatus =
"https://w3id.org/GConsent#ConsentStatusDenied";
const mockedFetch = jest.fn(global.fetch).mockResolvedValueOnce(
new Response(JSON.stringify(mockedAccessGrant), {
headers: new Headers([["content-type", "application/json"]]),
}),
);

const accessGrant = await getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
fetch: async () => new Response(JSON.stringify(mockedAccessGrant), {

Check failure on line 105 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `⏎·······`
headers: new Headers([["content-type", "application/json"]]),

Check failure on line 106 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `··`
}),

Check failure on line 107 in src/gConsent/manage/getAccessGrant.test.ts

View workflow job for this annotation

GitHub Actions / lint / lint

Insert `··`
});
toBeEqual(accessGrant, mockedAccessGrant);
});
Expand All @@ -135,15 +113,10 @@ describe("getAccessGrant", () => {
// but the linter doesn't pick up on this.
// eslint-disable-next-line jest/expect-expect
it("returns the access grant with the given IRI", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest.fn(global.fetch).mockResolvedValueOnce(
new Response(JSON.stringify(mockAccessGrantObject()), {
const accessGrant = await getAccessGrant("https://some.vc.url", {
fetch: async () => new Response(JSON.stringify(mockAccessGrantObject()), {
headers: new Headers([["content-type", "application/json"]]),
}),
);

const accessGrant = await getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
});
toBeEqual(accessGrant, mockAccessGrant);
});
Expand All @@ -152,36 +125,31 @@ describe("getAccessGrant", () => {
// but the linter doesn't pick up on this.
// eslint-disable-next-line jest/expect-expect
it("normalizes equivalent JSON-LD VCs", async () => {
mockAccessApiEndpoint();
const normalizedAccessGrant = mockAccessGrantObject();
// The server returns an equivalent JSON-LD with a different frame:
const mockedFetch = jest.fn(global.fetch).mockResolvedValueOnce(
new Response(
JSON.stringify({
...normalizedAccessGrant,
credentialSubject: {
...normalizedAccessGrant.credentialSubject,
providedConsent: {
...normalizedAccessGrant.credentialSubject.providedConsent,
// The 1-value array is replaced by the literal value.
forPersonalData:
normalizedAccessGrant.credentialSubject.providedConsent
.forPersonalData[0],
mode: normalizedAccessGrant.credentialSubject.providedConsent
.mode[0],
inherit: "true",
},
},
}),
{
headers: new Headers([["content-type", "application/json"]]),
},
),
);

toBeEqual(
await getAccessGrant("https://some.vc.url", {
fetch: mockedFetch,
// The server returns an equivalent JSON-LD with a different frame:
fetch: async () => new Response(
JSON.stringify({
...normalizedAccessGrant,
credentialSubject: {
...normalizedAccessGrant.credentialSubject,
providedConsent: {
...normalizedAccessGrant.credentialSubject.providedConsent,
// The 1-value array is replaced by the literal value.
forPersonalData:
normalizedAccessGrant.credentialSubject.providedConsent
.forPersonalData[0],
mode: normalizedAccessGrant.credentialSubject.providedConsent
.mode[0],
inherit: "true",
},
},
}),
{
headers: new Headers([["content-type", "application/json"]]),
},
),
}),
mockAccessGrant,
);
Expand All @@ -191,7 +159,6 @@ describe("getAccessGrant", () => {
// but the linter doesn't pick up on this.
// eslint-disable-next-line jest/expect-expect
it("returns the access grant with the given URL object", async () => {
mockAccessApiEndpoint();
const mockedFetch = jest.fn(global.fetch).mockResolvedValueOnce(
new Response(JSON.stringify(mockAccessGrantObject()), {
headers: new Headers([["content-type", "application/json"]]),
Expand All @@ -203,4 +170,21 @@ describe("getAccessGrant", () => {
});
toBeEqual(accessGrant, mockAccessGrant);
});

it("errors if the response is not a full access grant", async () => {
expect(getAccessGrant(new URL("https://some.vc.url"), {
fetch: async () => new Response(JSON.stringify({
"@context": "https://www.w3.org/2018/credentials/v1",
id: "https://some.credential",
})),
})).rejects.toThrow('the result is not a Verifiable Credential');
});

it("errors if the response is an empty json object", async () => {
expect(getAccessGrant(new URL("https://some.vc.url"), {
fetch: async () => new Response(JSON.stringify({}), {
headers: new Headers([["content-type", "application/json"]]),
}),
})).rejects.toThrow('the result is not a Verifiable Credential');
});
});
7 changes: 3 additions & 4 deletions src/gConsent/manage/getAccessGrant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,17 @@ export async function getAccessGrant(
`Could not resolve [${vcUrl}]: ${response.status} ${response.statusText}`,
);
}
const responseErrorClone = response.clone();
const responseErrorClone = await response.text();
let data;
try {
data = await verifiableCredentialToDataset(await response.json(), {
data = await verifiableCredentialToDataset(normalizeAccessGrant(JSON.parse(responseErrorClone)), {
baseIRI: accessGrantVcUrl.toString(),
});
} catch (e) {
throw new Error(
`Unexpected response when resolving [${vcUrl}], the result is not a Verifiable Credential: ${await responseErrorClone.text()}.\n\nError details: ${e}`,
`Unexpected response when resolving [${vcUrl}], the result is not a Verifiable Credential: ${responseErrorClone}.\n\nError details: ${e}`,
);
}
data = normalizeAccessGrant(data);
if (
!isVerifiableCredential(data) ||
!isBaseAccessGrantVerifiableCredential(data) ||
Expand Down

0 comments on commit e95c2d8

Please sign in to comment.