@ -10,6 +10,7 @@ import type {
import { changeData , commitData , createSolidLdoDataset } from "../src" ;
import {
ROOT_CONTAINER ,
WEB_ID ,
createApp ,
getAuthenticatedFetch ,
} from "./solidServer.helper" ;
@ -20,9 +21,6 @@ import {
defaultGraph ,
} from "@rdfjs/data-model" ;
import type { CreateSuccess } from "../src/requester/results/success/CreateSuccess" ;
import type { DatasetChanges } from "@ldo/rdf-utils" ;
import { createDataset } from "@ldo/dataset" ;
import type { Quad } from "@rdfjs/types" ;
import type { AggregateSuccess } from "../src/requester/results/success/SuccessResult" ;
import type {
UpdateDefaultGraphSuccess ,
@ -91,6 +89,12 @@ const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
posix :size 522 .
< sample.txt > posix :mtime 1697810234 ;
posix :size 10 . ` ;
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>;
< 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 # mode > < http : / / www.w3.org / ns / auth / acl # Read > , < http : / / www.w3.org / ns / auth / acl # Write > , < http : / / www.w3.org / ns / auth / acl # Append > , < http : / / www.w3.org / ns / auth / acl # Control > ;
< http : / / www.w3.org / ns / auth / acl # agent > < $ { WEB_ID } > . ` ;
async function testRequestLoads < ReturnVal > (
request : ( ) = > Promise < ReturnVal > ,
@ -166,6 +170,13 @@ describe("Integration", () => {
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" ,
@ -330,8 +341,8 @@ describe("Integration", () => {
expect ( result . isError ) . toBe ( true ) ;
if ( ! result . isError ) return ;
expect ( result . type ) . toBe ( "noncompliantPodError" ) ;
expect ( result . message ) . toBe (
"Response from https://solidweb.me/jackson3/test_ldo/sample2.ttl is not compliant with the Solid Specification: Resource requests must return a content-type header." ,
expect ( result . message ) . toMatch (
/\Response from .* is not compliant with the Solid Specification: Resource requests must return a content-type header\./ ,
) ;
} ) ;
@ -351,8 +362,8 @@ describe("Integration", () => {
expect ( result . isError ) . toBe ( true ) ;
if ( ! result . isError ) return ;
expect ( result . type ) . toBe ( "noncompliantPodError" ) ;
expect ( result . message ) . toBe (
'Response from https://solidweb.me/jackson3/test_ldo/sample2.ttl is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "Error" on line 1.' ,
expect ( result . message ) . toMatch (
/\Response from .* is not compliant with the Solid Specification: Request returned noncompliant turtle: Unexpected "Error" on line 1\./ ,
) ;
} ) ;
@ -386,8 +397,8 @@ describe("Integration", () => {
expect ( result . isError ) . toBe ( true ) ;
if ( ! result . isError ) return ;
expect ( result . type ) . toBe ( "noncompliantPodError" ) ;
expect ( result . message ) . toBe (
"Response from https://solidweb.me/jackson3/test_ldo/ is not compliant with the Solid Specification: No link header present in request." ,
expect ( result . message ) . toMatch (
/\Response from .* is not compliant with the Solid Specification: No link header present in request\./ ,
) ;
} ) ;
@ -406,26 +417,22 @@ describe("Integration", () => {
resource . read ( ) ,
] ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( result . type ) . toBe ( "dataReadSuccess" ) ;
expect ( result1 . type ) . toBe ( "dataReadSuccess" ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 1 ) ;
} ) ;
it ( "batches the read request when a read request is in queue" , async ( ) = > {
const resource = solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ;
const [ , result , result1 ] = await Promise . all ( [
resource . update ( {
added : createDataset ( [
createQuad ( namedNode ( "a" ) , namedNode ( "b" ) , namedNode ( "c" ) ) ,
] ) ,
} ) ,
resource . createAndOverwrite ( ) ,
resource . read ( ) ,
resource . read ( ) ,
] ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 3 ) ;
expect ( result . type ) . toBe ( "dataReadSuccess" ) ;
expect ( result1 . type ) . toBe ( "dataReadSuccess" ) ;
expect ( fetchMock ) . toHaveBeenCalledTimes ( 2 ) ;
} ) ;
} ) ;
@ -546,8 +553,8 @@ describe("Integration", () => {
expect ( result . isError ) . toBe ( true ) ;
if ( ! result . isError ) return ;
expect ( result . type ) . toBe ( "noncompliantPodError" ) ;
expect ( result . message ) . toBe (
"Response from https://solidweb.me/jackson3/test_ldo/ is not compliant with the Solid Specification: No link header present in request." ,
expect ( result . message ) . toMatch (
/\Response from .* is not compliant with the Solid Specification: No link header present in request\./ ,
) ;
} ) ;
@ -983,28 +990,28 @@ describe("Integration", () => {
* Update
* /
describe ( "updateDataResource" , ( ) = > {
const changes : DatasetChanges < Quad > = {
added : createDataset ( [
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
] ) ,
removed : createDataset ( [
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Green Goblin" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
] ) ,
} ;
const normanQuad = createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ;
const goblinQuad = createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Green Goblin" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ;
it ( "applies changes to a Pod" , async ( ) = > {
const result = await testRequestLoads (
( ) = > solidLdoDataset . commitChangesToPod ( changes ) ,
( ) = > {
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . add ( normanQuad ) ;
transaction . delete ( goblinQuad ) ;
return transaction . commitToPod ( ) ;
} ,
solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ,
{
isLoading : true ,
@ -1017,41 +1024,17 @@ describe("Integration", () => {
> ;
expect ( aggregateSuccess . results . length ) . toBe ( 1 ) ;
expect ( aggregateSuccess . results [ 0 ] . type === "updateSuccess" ) . toBe ( true ) ;
expect (
solidLdoDataset . has (
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ,
) . toBe ( true ) ;
expect (
solidLdoDataset . has (
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Green Goblin" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ,
) . toBe ( false ) ;
expect ( solidLdoDataset . has ( normanQuad ) ) . toBe ( true ) ;
expect ( solidLdoDataset . has ( goblinQuad ) ) . toBe ( false ) ;
} ) ;
it ( "applies only remove changes to the Pod" , async ( ) = > {
const changes : DatasetChanges < Quad > = {
removed : createDataset ( [
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Green Goblin" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
] ) ,
} ;
const result = await testRequestLoads (
( ) = > solidLdoDataset . commitChangesToPod ( changes ) ,
( ) = > {
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . delete ( goblinQuad ) ;
return transaction . commitToPod ( ) ;
} ,
solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ,
{
isLoading : true ,
@ -1064,21 +1047,17 @@ describe("Integration", () => {
> ;
expect ( aggregateSuccess . results . length ) . toBe ( 1 ) ;
expect ( aggregateSuccess . results [ 0 ] . type === "updateSuccess" ) . toBe ( true ) ;
expect (
solidLdoDataset . has (
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Green Goblin" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ,
) . toBe ( false ) ;
expect ( solidLdoDataset . has ( goblinQuad ) ) . toBe ( false ) ;
} ) ;
it ( "handles an HTTP error" , async ( ) = > {
fetchMock . mockResolvedValueOnce ( new Response ( "Error" , { status : 500 } ) ) ;
const result = await solidLdoDataset . commitChangesToPod ( changes ) ;
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . add ( normanQuad ) ;
transaction . delete ( goblinQuad ) ;
const result = await transaction . commitToPod ( ) ;
expect ( result . isError ) . toBe ( true ) ;
expect ( result . type ) . toBe ( "aggregateError" ) ;
const aggregateError = result as AggregateError <
@ -1092,7 +1071,10 @@ describe("Integration", () => {
fetchMock . mockImplementationOnce ( ( ) = > {
throw new Error ( "Some Error" ) ;
} ) ;
const result = await solidLdoDataset . commitChangesToPod ( changes ) ;
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . add ( normanQuad ) ;
transaction . delete ( goblinQuad ) ;
const result = await transaction . commitToPod ( ) ;
expect ( result . isError ) . toBe ( true ) ;
expect ( result . type ) . toBe ( "aggregateError" ) ;
const aggregateError = result as AggregateError <
@ -1103,17 +1085,15 @@ describe("Integration", () => {
} ) ;
it ( "errors when trying to update a container" , async ( ) = > {
const changes : DatasetChanges < Quad > = {
added : createDataset ( [
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
namedNode ( SAMPLE_CONTAINER_URI ) ,
) ,
] ) ,
} ;
const result = await solidLdoDataset . commitChangesToPod ( changes ) ;
const badContainerQuad = createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
namedNode ( SAMPLE_CONTAINER_URI ) ,
) ;
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . add ( badContainerQuad ) ;
const result = await transaction . commitToPod ( ) ;
expect ( result . isError ) . toBe ( true ) ;
expect ( result . type ) . toBe ( "aggregateError" ) ;
const aggregateError = result as AggregateError <
@ -1124,17 +1104,15 @@ describe("Integration", () => {
} ) ;
it ( "writes to the default graph without fetching" , async ( ) = > {
const changes : DatasetChanges < Quad > = {
added : createDataset ( [
createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
defaultGraph ( ) ,
) ,
] ) ,
} ;
const result = await solidLdoDataset . commitChangesToPod ( changes ) ;
const defaultGraphQuad = createQuad (
namedNode ( "http://example.org/#green-goblin" ) ,
namedNode ( "http://xmlns.com/foaf/0.1/name" ) ,
literal ( "Norman Osborn" ) ,
defaultGraph ( ) ,
) ;
const transaction = solidLdoDataset . startTransaction ( ) ;
transaction . add ( defaultGraphQuad ) ;
const result = await transaction . commitToPod ( ) ;
expect ( result . type ) . toBe ( "aggregateSuccess" ) ;
const aggregateSuccess = result as AggregateSuccess <
ResourceSuccess < UpdateSuccess | UpdateDefaultGraphSuccess , Leaf >
@ -1158,10 +1136,15 @@ describe("Integration", () => {
it ( "batches data update changes" , async ( ) = > {
const resource = solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ;
const transaction1 = solidLdoDataset . startTransaction ( ) ;
transaction1 . delete ( goblinQuad ) ;
const transaction2 = solidLdoDataset . startTransaction ( ) ;
transaction2 . add ( normanQuad ) ;
const [ , updateResult1 , updateResult2 ] = await Promise . all ( [
resource . read ( ) ,
solidLdoDataset . commitChangesToPod ( { removed : changes.removed } ) ,
solidLdoDataset . commitChangesToPod ( { added : changes.added } ) ,
transaction1 . commitToPod ( ) ,
transaction2 . commitToPod ( ) ,
] ) ;
expect ( updateResult1 . type ) . toBe ( "aggregateSuccess" ) ;
expect ( updateResult2 . type ) . toBe ( "aggregateSuccess" ) ;
@ -1445,10 +1428,8 @@ describe("Integration", () => {
* === === === === === === === === === === === === === === === === === === === === === === === === ===
* /
describe ( "methods" , ( ) = > {
it ( "creates a data object for a specific subject" , ( ) = > {
const resource = solidLdoDataset . getResource (
"https://example.com/resource.ttl" ,
) ;
it ( "creates a data object for a specific subject" , async ( ) = > {
const resource = solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ;
const post = solidLdoDataset . createData (
PostShShapeType ,
"https://example.com/subject" ,
@ -1456,29 +1437,46 @@ describe("Integration", () => {
) ;
post . type = { "@id" : "CreativeWork" } ;
expect ( post . type [ "@id" ] ) . toBe ( "CreativeWork" ) ;
commitData ( post ) ;
const result = await commitData ( post ) ;
expect ( result . type ) . toBe ( "aggregateSuccess" ) ;
expect (
solidLdoDataset . has (
createQuad (
namedNode ( "https://example.com/subject" ) ,
namedNode ( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ) ,
namedNode ( "http://schema.org/CreativeWork" ) ,
namedNode ( "https://example.com/resource.ttl" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ,
) . toBe ( true ) ;
} ) ;
it ( "uses changeData to start a transaction" , ( ) = > {
const resource = solidLdoDataset . getResource (
"https://example.com/resource.ttl" ,
it ( "handles an error when committing data" , async ( ) = > {
fetchMock . mockResolvedValueOnce (
new Response ( SAMPLE_DATA_URI , {
status : 500 ,
} ) ,
) ;
const resource = solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ;
const post = solidLdoDataset . createData (
PostShShapeType ,
"https://example.com/subject" ,
resource ,
) ;
post . type = { "@id" : "CreativeWork" } ;
expect ( post . type [ "@id" ] ) . toBe ( "CreativeWork" ) ;
const result = await commitData ( post ) ;
expect ( result . isError ) . toBe ( true ) ;
} ) ;
it ( "uses changeData to start a transaction" , async ( ) = > {
const resource = solidLdoDataset . getResource ( SAMPLE_DATA_URI ) ;
solidLdoDataset . add (
createQuad (
namedNode ( "https://example.com/subject" ) ,
namedNode ( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ) ,
namedNode ( "http://schema.org/CreativeWork" ) ,
namedNode ( "https://example.com/resource.ttl" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ;
const post = solidLdoDataset
@ -1487,14 +1485,15 @@ describe("Integration", () => {
const cPost = changeData ( post , resource ) ;
cPost . type = { "@id" : "SocialMediaPosting" } ;
expect ( cPost . type [ "@id" ] ) . toBe ( "SocialMediaPosting" ) ;
commitData ( cPost ) ;
const result = await commitData ( cPost ) ;
expect ( result . isError ) . toBe ( false ) ;
expect (
solidLdoDataset . has (
createQuad (
namedNode ( "https://example.com/subject" ) ,
namedNode ( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ) ,
namedNode ( "http://schema.org/SocialMediaPosting" ) ,
namedNode ( "https://example.com/resource.ttl" ) ,
namedNode ( SAMPLE_DATA_URI ) ,
) ,
) ,
) . toBe ( true ) ;