@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
771 lines (709 loc) • 25.5 kB
text/typescript
// Copyright Inrupt Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import {
jest,
describe,
it,
expect,
afterEach,
beforeEach,
} from "@jest/globals";
import type { SolidDataset, WithServerResourceInfo } from "..";
import {
buildThing,
createSolidDataset,
createThing,
getSourceIri,
getThingAll,
mockSolidDatasetFrom,
setStringNoLocale,
setThing,
} from "..";
import { foaf, pim, rdf, rdfs } from "../constants";
import { triplesToTurtle } from "../formats/turtle";
import { toRdfJsQuads } from "../rdfjs.internal";
import {
getAltProfileUrlAllFrom,
getPodUrlAll,
getPodUrlAllFrom,
getProfileAll,
getWebIdDataset,
} from "./webid";
import { mockResponse } from "../tests.internal";
const MOCK_WEBID = "https://example.org/some.webid";
const MOCK_PROFILE = setThing(
createSolidDataset(),
setStringNoLocale(
createThing({ url: MOCK_WEBID }),
"https://example.org/ns#somePredicate",
"Some value",
),
);
describe("getAltProfileUrlAllFrom", () => {
it("returns no alt profiles if the WebID profile contains no triples with the rdfs:seeAlso or foaf:primaryTopic/foaf:isPrimaryTopicOf predicate", async () => {
const webIdProfile = mockSolidDatasetFrom(MOCK_WEBID);
await expect(
getProfileAll(MOCK_WEBID, { webIdProfile }),
).resolves.toStrictEqual({
webIdProfile,
altProfileAll: [],
});
});
it("returns an array of the IRI of subject of triples of the WebID doc with the foaf:primaryTopic predicate not matching the WebID", () => {
const profileContent = buildThing({ url: "https://some.profile" })
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
const otherProfileContent = buildThing({
url: "https://some.other.profile",
})
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
let webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
webIdProfile = setThing(webIdProfile, otherProfileContent);
const result = getAltProfileUrlAllFrom(MOCK_WEBID, webIdProfile);
expect(result).toHaveLength(2);
expect(result).toContain("https://some.profile");
expect(result).toContain("https://some.other.profile");
});
it("returns an array of the IRI of objects of triples of the WebID doc such as <webid, rdfs:seeAlso, ?object>", () => {
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(rdfs.seeAlso, "https://some.profile")
.addIri(rdfs.seeAlso, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = getAltProfileUrlAllFrom(MOCK_WEBID, webIdProfile);
expect(result).toHaveLength(2);
expect(result).toContain("https://some.profile");
expect(result).toContain("https://some.other.profile");
});
it("returns an array of the IRI of objects of triples of the WebID doc such as <webid, foaf:isPrimaryTopicOf, ?object>", () => {
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.addIri(foaf.isPrimaryTopicOf, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = getAltProfileUrlAllFrom(MOCK_WEBID, webIdProfile);
expect(result).toHaveLength(2);
expect(result).toContain("https://some.profile");
expect(result).toContain("https://some.other.profile");
});
it("returns an array of the IRI of objects of triples of the WebID doc such as <webid, foaf:isPrimaryTopicOf, ?object>, <webid, foaf:isPrimaryTopicOf, ?object>, in the correct order prioritizing rdfs:seeAlso", () => {
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.addIri(rdfs.seeAlso, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = getAltProfileUrlAllFrom(MOCK_WEBID, webIdProfile);
expect(result).toHaveLength(2);
expect(result[0]).toBe("https://some.other.profile");
expect(result[1]).toBe("https://some.profile");
});
it("deduplicates profile values", () => {
// The profile document will have two triples <profile, foaf:primaryTopic, webid>...
const profileContent = buildThing({ url: "https://some.profile" })
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
// and <webid, foaf:isPrimaryTopicOf, profile>.
const webidData = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.build();
let webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
webIdProfile = setThing(webIdProfile, webidData);
const result = getAltProfileUrlAllFrom(MOCK_WEBID, webIdProfile);
// 'profile' should appear only once in the result set.
expect(result).toHaveLength(1);
expect(result).toContain("https://some.profile");
});
});
const profileFn: typeof fetch = async (url) => {
const profileContent = buildThing({ url: "https://example.org/some.profile" })
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const mapping = {
"https://example.org/some.profile": MOCK_PROFILE,
[MOCK_WEBID]: webIdProfile,
};
if (!Object.keys(mapping).includes(url.toString())) {
throw new Error(`Unexpected URL: ${url.toString()}`);
}
return new Response(
await triplesToTurtle(
toRdfJsQuads(mapping[url.toString() as keyof typeof mapping]),
),
{
headers: {
"Content-Type": "text/turtle",
},
},
);
};
describe("getProfileAll", () => {
let fetchSpy: jest.SpiedFunction<typeof fetch>;
beforeEach(() => {
fetchSpy = jest.spyOn(globalThis, "fetch").mockImplementation(profileFn);
});
afterEach(() => {
jest.resetAllMocks();
});
it("defaults to the embeded fetch if available to fetch alt profiles", async () => {
await getProfileAll(MOCK_WEBID);
// The embedded fetch should have been used to fetch the WebID Profile first,
// and then the alt profile.
expect(fetchSpy).toHaveBeenCalledTimes(2);
expect(fetchSpy).toHaveBeenNthCalledWith(1, MOCK_WEBID, expect.anything());
expect(fetchSpy).toHaveBeenNthCalledWith(
2,
"https://example.org/some.profile",
expect.anything(),
);
});
it("uses the provided fetch to fetch alt profiles, but not the WebID", async () => {
// Mock the alt profile authenticated fetch
const mockedAuthFetcher = jest.fn<typeof fetch>(profileFn);
await getProfileAll(MOCK_WEBID, { fetch: mockedAuthFetcher });
// The embedded fetch should have been used.
expect(mockedAuthFetcher).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledTimes(1);
});
it("does not fetch the WebID profile document if provided", async () => {
const mockedFetch = jest.fn<typeof fetch>();
const webIdProfile = mockSolidDatasetFrom(MOCK_WEBID);
await expect(
getProfileAll(MOCK_WEBID, { fetch: mockedFetch, webIdProfile }),
).resolves.toStrictEqual({
webIdProfile,
altProfileAll: [],
});
expect(mockedFetch).not.toHaveBeenCalled();
});
it("returns an array of the subject of triples of the WebID doc with the foaf:primaryTopic predicate not matching the WebID", async () => {
const mockedFetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
)
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.other.profile",
),
);
const profileContent = buildThing({ url: "https://some.profile" })
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
const otherProfileContent = buildThing({
url: "https://some.other.profile",
})
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
let webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
webIdProfile = setThing(webIdProfile, otherProfileContent);
const result = await getProfileAll(MOCK_WEBID, {
fetch: mockedFetch,
webIdProfile,
});
// The provided fetch should have been used.
expect(mockedFetch).toHaveBeenCalled();
expect(result.altProfileAll).toHaveLength(2);
expect(getThingAll(result.altProfileAll[0])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
expect(getThingAll(result.altProfileAll[1])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
});
it("returns an array of the objects of triples of the WebID doc such as <webid, rdfs:seeAlso, ?object>", async () => {
const mockedFetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
)
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.other.profile",
),
);
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(rdfs.seeAlso, "https://some.profile")
.addIri(rdfs.seeAlso, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = await getProfileAll(MOCK_WEBID, {
fetch: mockedFetch,
webIdProfile,
});
expect(result.altProfileAll).toHaveLength(2);
expect(getThingAll(result.altProfileAll[0])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
expect(getThingAll(result.altProfileAll[1])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
});
it("returns an array of the objects of triples of the WebID doc such as <webid, foaf:isPrimaryTopicOf, ?object>", async () => {
const mockedFetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
)
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.other.profile",
),
);
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.addIri(foaf.isPrimaryTopicOf, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = await getProfileAll(MOCK_WEBID, {
fetch: mockedFetch,
webIdProfile,
});
expect(result.altProfileAll).toHaveLength(2);
expect(getThingAll(result.altProfileAll[0])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
expect(getThingAll(result.altProfileAll[1])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
});
it("deduplicates profile values", async () => {
const mockedFetch = jest.fn<typeof fetch>().mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
);
// The profile document will have two triples <profile, foaf:primaryTopic, webid>...
const profileContent = buildThing({ url: "https://some.profile" })
.addIri(foaf.primaryTopic, MOCK_WEBID)
.build();
// and <webid, foaf:isPrimaryTopicOf, profile>.
const webidData = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.addIri(rdfs.seeAlso, "https://some.profile")
.build();
let webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
webIdProfile = setThing(webIdProfile, webidData);
const result = await getProfileAll(MOCK_WEBID, {
fetch: mockedFetch,
webIdProfile,
});
// 'profile' should appear only once in the result set.
expect(result.altProfileAll).toHaveLength(1);
expect(mockedFetch).toHaveBeenCalledTimes(1);
});
it("handles gracefully fetch errors on alternative profiles", async () => {
const mockedFetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
)
// Fetching one of the alt profiles fails.
.mockResolvedValueOnce(
mockResponse(undefined, {
status: 401,
}),
);
const profileContent = buildThing({ url: MOCK_WEBID })
.addIri(foaf.isPrimaryTopicOf, "https://some.profile")
.addIri(foaf.isPrimaryTopicOf, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
const result = await getProfileAll(MOCK_WEBID, {
fetch: mockedFetch,
webIdProfile,
});
expect(result.altProfileAll).toHaveLength(1);
expect(getThingAll(result.altProfileAll[0])).toStrictEqual(
getThingAll(MOCK_PROFILE),
);
});
});
const mockProfileDoc = (
iri: string,
webId: string,
content: Partial<{ altProfiles: string[]; pods: string[] }>,
): SolidDataset & WithServerResourceInfo => {
const profileContent = buildThing({ url: webId }).addIri(
rdf.type,
foaf.Agent,
);
content.altProfiles?.forEach((altProfileIri) => {
profileContent.addIri(foaf.isPrimaryTopicOf, altProfileIri);
});
content.pods?.forEach((podIri) => {
profileContent.addIri(pim.storage, podIri);
});
return setThing(mockSolidDatasetFrom(iri), profileContent.build());
};
describe("getPodUrlAll", () => {
let fetchSpy: jest.SpiedFunction<typeof fetch>;
beforeEach(() => {
fetchSpy = jest.spyOn(globalThis, "fetch").mockImplementation(profileFn);
});
afterEach(() => {
jest.resetAllMocks();
});
it("does not use the provided fetch to dereference the WebID", async () => {
const mockedFetch = jest.fn<typeof fetch>();
await getPodUrlAll("https://example.org/some.profile", {
fetch: mockedFetch,
});
expect(mockedFetch).not.toHaveBeenCalled();
expect(fetchSpy).toHaveBeenCalled();
});
it("uses the embedded fetch if solid-client-authn-browser is in the dependencies", async () => {
const profileContent = buildThing({ url: MOCK_WEBID })
// This will point to an alt profile, prompting the authenticated fetch.
.addIri(rdfs.seeAlso, "https://some.profile")
.addIri(foaf.isPrimaryTopicOf, "https://some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
// The WebID is explicitly fetched using the unauthenticated fetch.
fetchSpy.mockImplementation(
async () =>
new Response(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
}),
);
await getPodUrlAll(MOCK_WEBID);
// The embedded fetch should have been used.
expect(fetchSpy).toHaveBeenCalled();
});
it("uses the provided fetch to fetch alt profiles, but not the WebID", async () => {
const mockedAuthFetch = jest.fn<typeof fetch>();
mockedAuthFetch.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(MOCK_PROFILE)),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.profile",
),
);
const profileContent = buildThing({ url: MOCK_WEBID })
// This will point to an alt profile, prompting the authenticated fetch.
.addIri(rdfs.seeAlso, "https://example.org/some.profile")
.addIri(foaf.isPrimaryTopicOf, "https://example.org/some.other.profile")
.build();
const webIdProfile = setThing(
mockSolidDatasetFrom(MOCK_WEBID),
profileContent,
);
fetchSpy.mockResolvedValueOnce(
new Response(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
url: MOCK_WEBID,
} as ResponseInit),
);
await getPodUrlAll(MOCK_WEBID, { fetch: mockedAuthFetch });
// The provided authenticated fetch should have been used to fetch the alt profile.
expect(mockedAuthFetch).toHaveBeenCalledTimes(2);
expect(mockedAuthFetch).toHaveBeenCalledWith(
"https://example.org/some.profile",
expect.anything(),
);
expect(mockedAuthFetch).toHaveBeenCalledWith(
"https://example.org/some.other.profile",
expect.anything(),
);
// The unauthenticated fetch should have been used to fetch the webid profile.
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(fetchSpy).toHaveBeenCalledWith(MOCK_WEBID, expect.anything());
});
it("returns Pod URLs found in the fetched WebId profile", async () => {
const MOCK_STORAGE = "https://some.storage";
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
pods: [MOCK_STORAGE],
});
fetchSpy.mockResolvedValueOnce(
mockResponse(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
}),
);
await expect(getPodUrlAll(MOCK_WEBID)).resolves.toStrictEqual([
MOCK_STORAGE,
]);
});
it("returns all Pod URLs found in fetched alternative profiles", async () => {
const ALT_MOCK_STORAGE_1 = "https://some.storage";
const ALT_MOCK_STORAGE_2 = "https://some-other.storage";
const altProfileAll = [
mockProfileDoc("https://some.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE_1],
}),
mockProfileDoc("https://some.other.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE_2],
}),
];
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
altProfiles: altProfileAll.map(getSourceIri) as string[],
});
fetchSpy.mockResolvedValueOnce(
new Response(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
}),
);
// The alternative profiles are Solid resources, and require authentication.
const mockedFetch = jest
.fn<typeof fetch>()
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(altProfileAll[0])),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.alt-profile",
),
)
.mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(altProfileAll[1])),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.other.alt-profile",
),
);
await expect(
getPodUrlAll(MOCK_WEBID, { fetch: mockedFetch }),
).resolves.toStrictEqual([ALT_MOCK_STORAGE_1, ALT_MOCK_STORAGE_2]);
});
it("returns Pod URLs from both the fetched WebID profile and fetched alternative profiles when applicable", async () => {
const MOCK_STORAGE = "https://some.storage";
const ALT_MOCK_STORAGE = "https://some-other.storage";
const altProfileAll = [
mockProfileDoc("https://some.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE],
}),
];
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
altProfiles: altProfileAll.map(getSourceIri) as string[],
pods: [MOCK_STORAGE],
});
fetchSpy.mockResolvedValueOnce(
new Response(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
}),
);
const mockedFetch = jest.fn<typeof fetch>().mockResolvedValueOnce(
mockResponse(
await triplesToTurtle(toRdfJsQuads(altProfileAll[0])),
{
headers: {
"Content-Type": "text/turtle",
},
},
"https://some.alt-profile",
),
);
await expect(
getPodUrlAll(MOCK_WEBID, { fetch: mockedFetch }),
).resolves.toStrictEqual([MOCK_STORAGE, ALT_MOCK_STORAGE]);
});
});
describe("getPodUrlAllFrom", () => {
it("returns an empty result if the given resources doesn't have the WebID as a subject", () => {
const MOCK_STORAGE = "https://some.storage";
const webIdProfile = mockProfileDoc(
"https://some.profile",
"https://some.different.webid",
{
pods: [MOCK_STORAGE],
},
);
expect(
getPodUrlAllFrom({ webIdProfile, altProfileAll: [] }, MOCK_WEBID),
).toStrictEqual([]);
});
it("returns Pod URLs found in the WebId profile", () => {
const MOCK_STORAGE = "https://some.storage";
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
pods: [MOCK_STORAGE],
});
expect(
getPodUrlAllFrom({ webIdProfile, altProfileAll: [] }, MOCK_WEBID),
).toStrictEqual([MOCK_STORAGE]);
});
it("returns all Pod URLs found in alternative profiles", () => {
const ALT_MOCK_STORAGE_1 = "https://some.storage";
const ALT_MOCK_STORAGE_2 = "https://some-other.storage";
const altProfileAll = [
mockProfileDoc("https://some.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE_1],
}),
mockProfileDoc("https://some.other.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE_2],
}),
];
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
altProfiles: altProfileAll.map(getSourceIri) as string[],
});
expect(
getPodUrlAllFrom({ webIdProfile, altProfileAll }, MOCK_WEBID),
).toStrictEqual([ALT_MOCK_STORAGE_1, ALT_MOCK_STORAGE_2]);
});
it("returns Pod URLs from both the WebID profile and alternative profiles when applicable", () => {
const MOCK_STORAGE = "https://some.storage";
const ALT_MOCK_STORAGE = "https://some-other.storage";
const altProfileAll = [
mockProfileDoc("https://some.alt-profile", MOCK_WEBID, {
pods: [ALT_MOCK_STORAGE],
}),
];
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {
altProfiles: altProfileAll.map(getSourceIri) as string[],
pods: [MOCK_STORAGE],
});
expect(
getPodUrlAllFrom({ webIdProfile, altProfileAll }, MOCK_WEBID),
).toStrictEqual([MOCK_STORAGE, ALT_MOCK_STORAGE]);
});
});
describe("getWebIdDataset", () => {
afterEach(() => {
jest.restoreAllMocks();
});
it("returns a Solid Dataset for a given WebID", async () => {
const webIdProfile = mockProfileDoc("https://some.profile", MOCK_WEBID, {});
jest.spyOn(globalThis, "fetch").mockResolvedValueOnce(
mockResponse(await triplesToTurtle(toRdfJsQuads(webIdProfile)), {
headers: {
"Content-Type": "text/turtle",
},
}),
);
const result = await getWebIdDataset(MOCK_WEBID);
expect(result?.graphs).toEqual(webIdProfile.graphs);
});
it("throws an error if fetching fails", async () => {
jest.spyOn(globalThis, "fetch").mockRejectedValueOnce(new Error("error"));
await expect(getWebIdDataset(MOCK_WEBID)).rejects.toThrow("error");
});
});