PageRenderTime 33ms CodeModel.GetById 13ms app.highlight 16ms RepoModel.GetById 0ms app.codeStats 1ms

/OData Producer for PHP/Tests/UriProcessor/QueryProcessor/SkipTokenParser/SkipTokenParserTest.php

#
PHP | 569 lines | 438 code | 45 blank | 86 comment | 10 complexity | 9a700bf48deb8b3c3bfe76cf7642b5e0 MD5 | raw file
  1<?php
  2use ODataProducer\Providers\Metadata\ResourceProperty;
  3use ODataProducer\Configuration\EntitySetRights;
  4use ODataProducer\Providers\MetadataQueryProviderWrapper;
  5use ODataProducer\Configuration\DataServiceConfiguration;
  6use ODataProducer\Common\ODataException;
  7use ODataProducer\UriProcessor\QueryProcessor\OrderByParser\OrderByParser;
  8use ODataProducer\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenParser;
  9require_once 'PHPUnit\Framework\Assert.php';
 10require_once 'PHPUnit\Framework\Test.php';
 11require_once 'PHPUnit\Framework\SelfDescribing.php';
 12require_once 'PHPUnit\Framework\TestCase.php';
 13require_once 'PHPUnit\Framework\TestSuite.php';
 14require_once 'ODataProducer\Common\ClassAutoLoader.php';
 15require_once (dirname(__FILE__) . "\..\..\..\Resources\NorthWindMetadata.php");
 16ODataProducer\Common\ClassAutoLoader::register();
 17class TestSkipTokenParser extends PHPUnit_Framework_TestCase
 18{
 19    protected function setUp()
 20    {
 21    }
 22
 23    /**
 24     * Named values are not allowed in skip token     
 25     */
 26    public function testNamedValuesInSkipToken()
 27    {
 28        try {
 29            $northWindMetadata = CreateNorthWindMetadata3::Create();
 30            $configuration = new DataServiceConfiguration($northWindMetadata);
 31            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
 32            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
 33                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
 34                                        null, //IDataServiceQueryProvider implementation (set to null)
 35                                        $configuration, //Service configuuration
 36                                        false
 37                                        );
 38
 39            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
 40            $resourceType = $resourceSetWrapper->getResourceType();
 41            $orderBy = 'ShipName asc, Price';
 42            //Note: library will add prim key as last sort key
 43            $orderBy .= ', OrderID';
 44            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
 45            $skipToken = "'Antonio%20Moreno%20Taquer%C3%ADa',Price=22.0000M,10365";
 46            $skipToken = urldecode($skipToken);
 47            $thrownException = false;
 48            try {
 49                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
 50            } catch (ODataException $odataException) {
 51                $thrownException = true;
 52                $this->assertStringStartsWith('Bad Request - Error in the syntax of skiptoken', $odataException->getMessage());
 53            }
 54
 55            if (!$thrownException) {
 56                $this->fail('An expected ODataException for syntax error has not been raised (because of named value)');
 57            }
 58        } catch (\Exception $exception) {
 59            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
 60        }
 61    }
 62
 63    /**
 64     * Skiptoken should not contain leading and trailing commas, use
 65     * commas only to seperate token sub-values.
 66     */
 67    public function testSkipTokenWithLeadingAndTrailingCommas()
 68    {
 69            try {
 70            $northWindMetadata = CreateNorthWindMetadata3::Create();
 71            $configuration = new DataServiceConfiguration($northWindMetadata);
 72            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
 73            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
 74                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
 75                                        null, //IDataServiceQueryProvider implementation (set to null)
 76                                        $configuration, //Service configuuration
 77                                        false
 78                                        );
 79
 80            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
 81            $resourceType = $resourceSetWrapper->getResourceType();
 82            $orderBy = 'ShipName asc, Price';
 83            //Note: library will add prim key as last sort key
 84            $orderBy .= ', OrderID';
 85            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
 86            
 87            //Test with trailing comma
 88            $skipToken = "'Antonio%20Moreno%20Taquer%C3%ADa',22.000,10365,";
 89            $skipToken = urldecode($skipToken);
 90            $thrownException = false;
 91            try {
 92                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
 93            } catch (ODataException $odataException) {
 94                $thrownException = true;
 95                $this->assertStringStartsWith('Bad Request - Error in the syntax of skiptoken', $odataException->getMessage());
 96            }
 97
 98            if (!$thrownException) {
 99                $this->fail('An expected ODataException for syntax error has not been raised (because of trailing comma)');
100            }
101
102            //Test with leading comma
103            $skipToken = ",'Antonio%20Moreno%20Taquer%C3%ADa',22.000,10365,";
104            $skipToken = urldecode($skipToken);
105            $thrownException = false;
106            try {
107                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
108            } catch (ODataException $odataException) {
109                $thrownException = true;
110                $this->assertStringStartsWith('Bad Request - Error in the syntax of skiptoken', $odataException->getMessage());
111            }
112
113            if (!$thrownException) {
114                $this->fail('An expected ODataException for syntax error has not been raised (because of leading comma)');
115            }
116
117            //Test with extra comma between values
118            $skipToken = "'Antonio%20Moreno%20Taquer%C3%ADa',,22.000,10365,";
119            $skipToken = urldecode($skipToken);
120            $thrownException = false;
121            try {
122                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
123            } catch (ODataException $odataException) {
124                $thrownException = true;
125                $this->assertStringStartsWith('Bad Request - Error in the syntax of skiptoken', $odataException->getMessage());
126            }
127
128            if (!$thrownException) {
129                $this->fail('An expected ODataException for syntax error has not been raised (because of extra comma)');
130            }
131        } catch (\Exception $exception) {
132            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
133        }
134    }
135
136    /**
137     * Number of sub-values in skiptoken should match with number of
138     * ordering expressions.     
139     */
140    public function testSkipTokenHavingCountMismatchWithOrderBy()
141    {
142        try {
143            $northWindMetadata = CreateNorthWindMetadata3::Create();
144            $configuration = new DataServiceConfiguration($northWindMetadata);
145            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
146            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
147                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
148                                        null, //IDataServiceQueryProvider implementation (set to null)
149                                        $configuration, //Service configuuration
150                                        false
151                                        );
152
153            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
154            $resourceType = $resourceSetWrapper->getResourceType();
155            $orderBy = 'ShipName asc, Price';
156            //Note: library will add prim key as last sort key
157            $orderBy .= ', OrderID';
158            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
159            //zero skiptoken values
160            $skipToken = "";
161            $thrownException = false;
162            try {
163                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
164            } catch (ODataException $odataException) {
165                $thrownException = true;
166                $this->assertStringStartsWith('The number of keys \'0\' in skip token with value \'\' did not match the number of ordering constraints \'3\'', $odataException->getMessage());
167            }
168
169            if (!$thrownException) {
170                $this->fail('An expected ODataException for syntax error has not been raised for skiptoken and ordering constraint count mismatch');
171            }
172
173            //two skiptoken values            
174            $skipToken = "'Antonio%20Moreno%20Taquer%C3%ADa',22.000";
175            $skipToken = urldecode($skipToken);
176            $thrownException = false;
177            try {
178                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
179            } catch (ODataException $odataException) {
180                $thrownException = true;
181                $this->assertStringStartsWith('The number of keys \'2\' in skip token with value', $odataException->getMessage());
182            }
183
184            if (!$thrownException) {
185                $this->fail('An expected ODataException for syntax error has not been raised for skiptoken and ordering constraint count mismatch');
186            }
187        } catch (\Exception $exception) {
188            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
189        }
190    }
191
192    /**
193     * test skiptoken sub-values where type does not match with
194     * corrosponding type of orderby path
195     */
196    public function testSkipTokenTypeInCompatability()
197    {
198        try {
199            $northWindMetadata = CreateNorthWindMetadata3::Create();
200            $configuration = new DataServiceConfiguration($northWindMetadata);
201            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
202            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
203                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
204                                        null, //IDataServiceQueryProvider implementation (set to null)
205                                        $configuration, //Service configuuration
206                                        false
207                                        );
208
209            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
210            $resourceType = $resourceSetWrapper->getResourceType();
211            $orderBy = 'ShipName asc, Price';
212            //Note: library will add prim key as last sort key
213            $orderBy .= ', OrderID';
214            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
215            //ShipName is String, but in skiptoken uses datetime
216            $skipToken = "datetime'1996-07-12T03%3A58%3A58', 22.00, 1234";
217            //do decoding so token become "datetime'1996-07-12T03:58:58', 22.00, 1234";
218            $skipToken = urldecode($skipToken);
219            $thrownException = false;
220            try {
221                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
222            } catch (ODataException $odataException) {
223                $thrownException = true;
224                $this->assertStringStartsWith('The skiptoken value \'datetime\'1996-07-12T03:58:58\', 22.00, 1234\' contain a value of type \'Edm.DateTime\' at position 0 which is not compatible with the type \'Edm.String\' of corrosponding orderby constraint', $odataException->getMessage());
225            }
226
227            if (!$thrownException) {
228                $this->fail('An expected ODataException for type mismatch (datetime and string) ');
229            }
230
231            //Price is Double, but in skiptoken uses true
232            $skipToken = "'ANS', true, 1234";
233            $skipToken = urldecode($skipToken);
234            $thrownException = false;
235            try {
236                $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
237            } catch (ODataException $odataException) {
238                $thrownException = true;
239                $this->assertStringStartsWith('The skiptoken value \'\'ANS\', true, 1234\' contain a value of type \'Edm.Boolean\' at position 1 which is not compatible with the type \'Edm.Double\' of corrosponding orderby constraint', $odataException->getMessage());
240            }
241
242            if (!$thrownException) {
243                $this->fail('An expected ODataException for type mismatch (boolean and double) ');
244            }
245            
246            //null is allowed in skiptoken and compactable with all types
247            $skipToken = "null, null, null";            
248            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
249        } catch (\Exception $exception) {
250            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
251        }
252    }
253
254    /**
255     * Test the initialzation of orderbyinfo's dummy object from the skiptoken value.
256     * The orderby parser will create the dummy object, using the orderby path,
257     * we will do a negative testing on GetKeyObject, by explicitly unsetting property 
258     * set by orderby parser
259     */
260    public function testInitializationOfKeyObject1()
261    {
262        try {
263            $northWindMetadata = CreateNorthWindMetadata3::Create();
264            $configuration = new DataServiceConfiguration($northWindMetadata);
265            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
266            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
267                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
268                                        null, //IDataServiceQueryProvider implementation (set to null)
269                                        $configuration, //Service configuuration
270                                        false
271                                        );
272
273            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Order_Details');
274            $resourceType = $resourceSetWrapper->getResourceType();
275            $orderBy = 'Order/Customer/Rating';
276            //Note: library will add primery key as last sort key
277            $orderBy .= ', ProductID, OrderID';
278            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
279            $skipToken = "12, 1234, 4567";            
280            $dummyObject = $internalOrderByInfo->getDummyObject();
281            //we will remove Customer object createdby orderby parser, so that getKeyObject function will
282            //fail to acees the property 'Rating', since parent ($dummyObject->Order->Customer) is null
283            $this->assertTrue(!is_null($dummyObject));
284            $this->assertTrue(!is_null($dummyObject->Order));
285            $this->assertTrue(!is_null($dummyObject->Order->Customer));
286            $this->assertTrue(is_null($dummyObject->Order->Customer->Rating));
287            $dummyObject->Order->Customer = null;
288            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
289            $thrownException = false;
290            try {
291                $o = $internalSkipTokenInfo->getKeyObject();
292            } catch (ODataException $odataException) {
293                $thrownException = true;
294                $this->assertStringStartsWith('internalSkipTokenInfo failed to access or initialize the property Rating', $odataException->getMessage());
295            }
296
297            if (!$thrownException) {
298                $this->fail('An expected ODataException for failure of ReflectionProperty on dummy object');
299            }
300        } catch (\Exception $exception) {
301            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
302        }
303        //test with null values
304        //test with string value
305        //test with other values, double, guid, int, datetime        
306    }
307
308    /**
309     * Test the initialzation of orderbyinfo's dummy object from the skiptoken value.
310     * 1. The string properties in the key object should be utf8 decoded.
311     * 2. The lexer will consider all token text as string, but when the we populate the
312     *    dummy object with these values, it should be converted to appropriate type 
313     *  
314     */
315    public function testInitializationOfKeyObject2()
316    {
317        try {
318            $northWindMetadata = CreateNorthWindMetadata3::Create();
319            $configuration = new DataServiceConfiguration($northWindMetadata);
320            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
321            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
322                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
323                                        null, //IDataServiceQueryProvider implementation (set to null)
324                                        $configuration, //Service configuuration
325                                        false
326                                        );
327
328            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
329            $resourceType = $resourceSetWrapper->getResourceType();
330            $orderBy = 'ShipName asc, Price';
331            //Note: library will add prim key as last sort key
332            $orderBy .= ', OrderID';
333            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
334            $skipToken = "'Antonio%20Moreno%20Taquer%C3%ADa',22.001,10365";
335            //urldecode convert the skip token to - 'Antonio Moreno Taquería',22.000,10365
336            $skipToken = urldecode($skipToken);
337            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
338            //The getKeyObject should do utf8 encoding on Antonio Moreno Taquería
339            $keyObject = $internalSkipTokenInfo->getKeyObject();
340            $this->assertTrue(!is_null($keyObject->ShipName));
341            $shipName = (urldecode('Antonio%20Moreno%20Taquer%C3%ADa'));
342            $this->assertEquals($shipName, $keyObject->ShipName);
343
344            $this->assertTrue(!is_null($keyObject->Price));
345            $this->assertTrue(is_float($keyObject->Price));
346
347            $this->assertTrue(!is_null($keyObject->OrderID));
348            $this->assertTrue(is_int($keyObject->OrderID));
349            
350        } catch (\Exception $exception) {
351            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
352        }
353        //test with null values
354        //test with string value
355        //test with other values, double, guid, int, datetime        
356    }
357
358    /**
359     * Test the initialzation of orderbyinfo's dummy object from the skiptoken value.
360     * 1. Complex navigation can be also used in the skiptoken  
361     * 2. The lexer will consider all token text as string, but when the we populate the
362     *    dummy object with these values, it should be converted to appropriate type 
363     *  
364     */
365    public function testInitializationOfKeyObject3()
366    {
367        try {
368            $northWindMetadata = CreateNorthWindMetadata3::Create();
369            $configuration = new DataServiceConfiguration($northWindMetadata);
370            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
371            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
372                                    $northWindMetadata, //IDataServiceMetadataProvider implementation 
373                                    null, //IDataServiceQueryProvider implementation (set to null)
374                                    $configuration, //Service configuuration
375                                    false
376                                    );
377
378            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
379            $resourceType = $resourceSetWrapper->getResourceType();
380            $orderBy = 'Address/IsValid';
381            //Note: library will add primery key as last sort key
382            $orderBy .= ',CustomerID';
383            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
384            $skipToken = "true, 'ALFKI'";
385            $dummyObject = $internalOrderByInfo->getDummyObject();
386            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
387            $keyObject = $internalSkipTokenInfo->getKeyObject();
388            $this->assertTrue(!is_null($keyObject));
389            $this->assertTrue(!is_null($keyObject->Address));
390            $this->assertTrue(!is_null($keyObject->Address->IsValid));
391            $this->assertTrue($keyObject->Address->IsValid);
392            
393        } catch (\Exception $exception) {
394            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
395        }
396
397        //test with other values, double, guid, int, datetime        
398    }
399
400    /**
401     * test the creation of nextlink from an object.
402     * Test whether the buildNextPageLink function set skiptoken sub value as null when
403     * it found one of the ancestor of corrosponding ordering key property(ies) as null.     
404     */
405    public function testCreationOfNextLink1()
406    {
407        try {
408            $northWindMetadata = CreateNorthWindMetadata3::Create();
409            $configuration = new DataServiceConfiguration($northWindMetadata);
410            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
411            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
412                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
413                                        null, //IDataServiceQueryProvider implementation (set to null)
414                                        $configuration, //Service configuuration
415                                        false
416                                        );
417
418            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
419            $resourceType = $resourceSetWrapper->getResourceType();
420            $orderBy = 'Address/Address2/IsPrimary';
421            //Note: library will add primery key as last sort key
422            $orderBy .= ',CustomerID';
423            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
424            $skipToken = "true, 'ALFKI'";
425            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
426            //Here the Address2 property is null, the sort key property IsPrimary is a property of Address2
427            //so should emit null
428            $lastObject = new Customer2();
429            $lastObject->CustomerID = 'ALFKI';
430            $lastObject->Address = new Address4();
431            $lastObject->Address->Address2 = null;
432            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
433            $this->assertTrue(!is_null($nextLink));
434            $this->assertEquals($nextLink, "null, 'ALFKI'");
435
436            $lastObject = null;
437            $thrownException = false;
438            try {
439                $internalSkipTokenInfo->buildNextPageLink($lastObject);
440            } catch (ODataException $odataException) {
441                $thrownException = true;
442                $this->assertStringStartsWith('internalSkipTokenInfo failed to access or initialize the property Address', $odataException->getMessage());
443            }
444
445            if (!$thrownException) {
446                $this->fail('An expected ODataException for failure of ReflectionProperty on dummy object while building next link');
447            }
448        } catch (\Exception $exception) {
449            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
450        }
451    }
452
453    /**
454     * test the creation of nextlink from an object.
455     * Test building of link with string sub-value
456     */
457    public function testCreationOfNextLink2()
458    {
459        try {
460            $northWindMetadata = CreateNorthWindMetadata3::Create();
461            $configuration = new DataServiceConfiguration($northWindMetadata);
462            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
463            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
464                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
465                                        null, //IDataServiceQueryProvider implementation (set to null)
466                                        $configuration, //Service configuuration
467                                        false
468                                        );
469
470            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
471            $resourceType = $resourceSetWrapper->getResourceType();
472            $orderBy = 'ShipName asc, Price';
473            //Note: library will add prim key as last sort key
474            $orderBy .= ', OrderID';
475            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
476            $skipToken = "'ABY',22.000,10365";
477            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
478            $lastObject = new Order2();
479            //The string that is fetched from DB can be in utf8 format, so we are building
480            //such a string with utf8_decode
481            $lastObject->ShipName = utf8_decode(urldecode('Antonio%20Moreno%20Taquer%C3%ADa'));
482            $lastObject->Price = 23.56;
483            $lastObject->OrderID = 3456;
484            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
485            $this->assertTrue(!is_null($nextLink));
486            $thrownException = false;
487            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
488            $this->assertEquals($nextLink, "'Antonio+Moreno+Taquer%C3%ADa', 23.56D, 3456");
489        } catch (\Exception $exception) {
490            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
491        }
492    }
493    
494    /**
495     * test the creation of nextlink from an object.
496     * Test building of link with datetime sub-value
497     */
498    public function testCreationOfNextLink3()
499    {
500        try {
501            $northWindMetadata = CreateNorthWindMetadata3::Create();
502            $configuration = new DataServiceConfiguration($northWindMetadata);
503            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
504            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
505                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
506                                        null, //IDataServiceQueryProvider implementation (set to null)
507                                        $configuration, //Service configuuration
508                                        false
509                                        );
510
511            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Orders');
512            $resourceType = $resourceSetWrapper->getResourceType();
513            $orderBy = 'OrderDate';
514            //Note: library will add prim key as last sort key
515            $orderBy .= ', OrderID';
516            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
517            $skipToken = "null,10365";
518            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
519            $lastObject = new Order2();
520            $lastObject->OrderDate = '1996-07-12T00:00:00';
521            $lastObject->OrderID = 3456;
522            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
523            $this->assertTrue(!is_null($nextLink));            
524            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
525            $this->assertEquals($nextLink, "datetime'1996-07-12T00%3A00%3A00', 3456");
526        } catch (\Exception $exception) {
527            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
528        }
529    }
530
531	/**
532     * test the creation of nextlink from an object.
533     * Test building of link with guid sub-value
534     */
535    public function testCreationOfNextLink4()
536    {
537        try {
538            $northWindMetadata = CreateNorthWindMetadata3::Create();
539            $configuration = new DataServiceConfiguration($northWindMetadata);
540            $configuration->setEntitySetAccessRule('*', EntitySetRights::ALL);
541            $metaQueryProverWrapper = new MetadataQueryProviderWrapper(
542                                        $northWindMetadata, //IDataServiceMetadataProvider implementation 
543                                        null, //IDataServiceQueryProvider implementation (set to null)
544                                        $configuration, //Service configuuration
545                                        false
546                                        );
547
548            $resourceSetWrapper = $metaQueryProverWrapper->resolveResourceSet('Customers');
549            $resourceType = $resourceSetWrapper->getResourceType();
550            $orderBy = 'CustomerID, CustomerGuid';
551            $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $resourceType, $orderBy, $metaQueryProverWrapper);
552            $skipToken = "null, guid'05b242e752eb46bd8f0e6568b72cd9a5'";
553            $internalSkipTokenInfo = SkipTokenParser::parseSkipTokenClause($resourceType, $internalOrderByInfo, $skipToken);
554            $keyObject = $internalSkipTokenInfo->getKeyObject();
555            $lastObject = new Customer2();
556            $lastObject->CustomerID = 'ABC';
557            $lastObject->CustomerGuid = '{05b242e7-52eb-46bd-8f0e-6568b72cd9a5}';
558            $nextLink = $internalSkipTokenInfo->buildNextPageLink($lastObject);
559            $this->assertEquals($nextLink, "'ABC', guid'%7B05b242e7-52eb-46bd-8f0e-6568b72cd9a5%7D'");
560        } catch (\Exception $exception) {
561            $this->fail('An unexpected Exception has been raised.' . $exception->getMessage());
562        }
563    }
564    
565    protected function tearDown()
566    {
567    }
568}
569?>