@@ -4,14 +4,13 @@ import {
44 ForbiddenHttpError ,
55 KeyValueStorage ,
66 NotFoundHttpError ,
7- RDF
87} from '@solid/community-server' ;
98import { Parser , Store } from 'n3' ;
109import { ODRL } from 'odrl-evaluator' ;
1110import { Mocked } from 'vitest' ;
1211import { AccessRequestController } from '../../../src/controller/AccessRequestController' ;
1312import { UCRulesStorage } from '../../../src/ucp/storage/UCRulesStorage' ;
14- import { SOTW } from '../../../src/ucp/util/Vocabularies' ;
13+ import { RDF , SOTW } from '../../../src/ucp/util/Vocabularies' ;
1514
1615describe ( 'AccessRequestController' , ( ) : void => {
1716 const target = 'http://example.org/resource_id' ;
@@ -40,10 +39,15 @@ describe('AccessRequestController', (): void => {
4039 sotw:requestStatus sotw:requested ;
4140 sotw:requestedAction <${ scopes [ 1 ] } > ;
4241 odrl:constraint [
42+ a odrl:Constraint ;
43+ odrl:leftOperand odrl:dateTime ;
44+ odrl:operator odrl:gt ;
45+ odrl:rightOperand "2026-01-01T00:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>
46+ ], [
4347 a odrl:Constraint ;
4448 odrl:leftOperand odrl:purpose ;
45- odrl:operator odrl:eq ;
46- odrl:rightOperand <http://example.org /purpose>
49+ odrl:operator odrl:isAnyOf ;
50+ odrl:rightOperand ( <http://example.com /purpose-a> <http://example.com/purpose-b> )
4751 ] .
4852 ` ) ) ;
4953
@@ -118,6 +122,38 @@ describe('AccessRequestController', (): void => {
118122 ` ) ;
119123 expect ( request ) . toBeRdfIsomorphic ( expected ) ;
120124 } ) ;
125+
126+ it ( 'can add a request with a list of right operand values.' , async ( ) : Promise < void > => {
127+ const purposes = [ 'http://example.org/purpose1' , 'http://example.org/purpose2' ] ;
128+ const data = JSON . stringify ( {
129+ resource_id : target ,
130+ resource_scopes : scopes ,
131+ constraints : [
132+ [ 'http://www.w3.org/ns/odrl/2/purpose' , 'http://www.w3.org/ns/odrl/2/isAnyOf' , purposes ] ,
133+ ] ,
134+ } ) ;
135+
136+ const response = await controller . addEntity ( data , client ) ;
137+ expect ( response . status ) . toBe ( 201 ) ;
138+ expect ( store . addRule ) . toHaveBeenCalledTimes ( 1 ) ;
139+
140+ const request = store . addRule . mock . calls [ 0 ] [ 0 ] ;
141+ const expected = new Parser ( ) . parse ( `
142+ @prefix sotw: <https://w3id.org/force/sotw#> .
143+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
144+ <${ response . id } > a sotw:EvaluationRequest ;
145+ sotw:requestedTarget <${ target } > ;
146+ sotw:requestingParty <${ client } > ;
147+ sotw:requestStatus sotw:requested ;
148+ sotw:requestedAction <${ scopes [ 0 ] } > , <${ scopes [ 1 ] } > ;
149+ odrl:constraint <${ response . id } -constraint-1> .
150+ <${ response . id } -constraint-1> a odrl:Constraint ;
151+ odrl:leftOperand odrl:purpose ;
152+ odrl:operator odrl:isAnyOf ;
153+ odrl:rightOperand ( <${ purposes [ 0 ] } > <${ purposes [ 1 ] } > ) .
154+ ` ) ;
155+ expect ( request ) . toBeRdfIsomorphic ( expected ) ;
156+ } ) ;
121157 } ) ;
122158
123159 describe ( '#getEntities' , ( ) : void => {
@@ -149,10 +185,15 @@ describe('AccessRequestController', (): void => {
149185 sotw:requestStatus sotw:requested ;
150186 sotw:requestedAction <${ scopes [ 1 ] } > ;
151187 odrl:constraint [
188+ a odrl:Constraint ;
189+ odrl:leftOperand odrl:dateTime ;
190+ odrl:operator odrl:gt ;
191+ odrl:rightOperand "2026-01-01T00:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>
192+ ], [
152193 a odrl:Constraint ;
153194 odrl:leftOperand odrl:purpose ;
154- odrl:operator odrl:eq ;
155- odrl:rightOperand <http://example.org /purpose>
195+ odrl:operator odrl:isAnyOf ;
196+ odrl:rightOperand ( <http://example.com /purpose-a> <http://example.com/purpose-b> )
156197 ] .
157198 ` ) ) ;
158199 expect ( ownershipStore . get ) . toHaveBeenCalledExactlyOnceWith ( owner ) ;
@@ -191,10 +232,15 @@ describe('AccessRequestController', (): void => {
191232 sotw:requestStatus sotw:requested ;
192233 sotw:requestedAction <${ scopes [ 1 ] } > ;
193234 odrl:constraint [
235+ a odrl:Constraint ;
236+ odrl:leftOperand odrl:dateTime ;
237+ odrl:operator odrl:gt ;
238+ odrl:rightOperand "2026-01-01T00:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime>
239+ ], [
194240 a odrl:Constraint ;
195241 odrl:leftOperand odrl:purpose ;
196- odrl:operator odrl:eq ;
197- odrl:rightOperand <http://example.org /purpose>
242+ odrl:operator odrl:isAnyOf ;
243+ odrl:rightOperand ( <http://example.com /purpose-a> <http://example.com/purpose-b> )
198244 ] .
199245 ` ) ) ;
200246 } ) ;
@@ -227,7 +273,7 @@ describe('AccessRequestController', (): void => {
227273 const permissions = quads . getObjects ( policies [ 0 ] , ODRL . terms . permission , null ) ;
228274 expect ( permissions ) . toHaveLength ( 1 ) ;
229275 const constraints = quads . getObjects ( permissions [ 0 ] , ODRL . terms . constraint , null ) ;
230- expect ( constraints ) . toHaveLength ( 1 ) ;
276+ expect ( constraints ) . toHaveLength ( 2 ) ;
231277 expect ( quads . getQuads ( policies [ 0 ] , null , null , null ) ) . toBeRdfIsomorphic ( new Parser ( ) . parse ( `
232278 @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
233279 <${ policies [ 0 ] . value } > a odrl:Agreement ;
@@ -241,14 +287,27 @@ describe('AccessRequestController', (): void => {
241287 odrl:action <http://example.org/scope2> ;
242288 odrl:assignee <http://example.org/unknown> ;
243289 odrl:assigner <${ owner } > ;
244- odrl:constraint _:${ constraints [ 0 ] . value } .
290+ odrl:constraint _:${ constraints [ 0 ] . value } , _: ${ constraints [ 1 ] . value } .
245291 ` ) ) ;
246292 expect ( quads . getQuads ( constraints [ 0 ] , null , null , null ) ) . toBeRdfIsomorphic ( new Parser ( ) . parse ( `
247293 @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
248294 _:${ constraints [ 0 ] . value } a odrl:Constraint ;
295+ odrl:leftOperand odrl:dateTime ;
296+ odrl:operator odrl:gt ;
297+ odrl:rightOperand "2026-01-01T00:00:00Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
298+ ` ) ) ;
299+ const listConstraintQuads = [ ...quads . getQuads ( constraints [ 1 ] , null , null , null ) ] ;
300+ let listNode = quads . getObjects ( constraints [ 1 ] , ODRL . terms . rightOperand , null ) [ 0 ] ;
301+ while ( listNode && ! listNode . equals ( RDF . terms . nil ) ) {
302+ listConstraintQuads . push ( ...quads . getQuads ( listNode as any , null , null , null ) ) ;
303+ listNode = quads . getObjects ( listNode as any , RDF . terms . rest , null ) [ 0 ] ;
304+ }
305+ expect ( listConstraintQuads ) . toBeRdfIsomorphic ( new Parser ( ) . parse ( `
306+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
307+ _:${ constraints [ 1 ] . value } a odrl:Constraint ;
249308 odrl:leftOperand odrl:purpose ;
250- odrl:operator odrl:eq ;
251- odrl:rightOperand <http://example.org /purpose> .
309+ odrl:operator odrl:isAnyOf ;
310+ odrl:rightOperand ( <http://example.com /purpose-a> <http://example.com/purpose-b> ) .
252311 ` ) ) ;
253312 } ) ;
254313
0 commit comments