Yaffs site version 1.1
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Base / Bean.php
1 <?php
2 namespace RedUNIT\Base;
3
4 use RedUNIT\Base as Base;
5 use RedBeanPHP\Facade as R;
6
7 /**
8  * Bean
9  *
10  * One of the core test suites of RedBeanPHP, tests the
11  * bean interface and all basic bean functions, including
12  * deletion, updating, inserting, importing and more...
13  *
14  * @file    RedUNIT/Base/Bean.php
15  * @desc    Tests list manipulations of bean.
16  * @author  Gabor de Mooij and the RedBeanPHP Community
17  * @license New BSD/GPLv2
18  *
19  * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
20  * This source file is subject to the New BSD/GPLv2 License that is bundled
21  * with this source code in the file license.txt.
22  */
23 class Bean extends Base
24 {
25         /**
26          * Tests whether we can send results of a query to meta data
27          * when converting to bean.
28          */
29         public function testImportMeta()
30         {
31                 R::nuke();
32                 $book = R::dispense( array(
33                         '_type'  => 'book',
34                         'title'  => 'Bean Recipes',
35                         'author' => 'Meastro de la Bean'
36                 ) );
37                 $pages = R::dispenseAll( 'page*2' );
38                 $book->ownPageList = reset( $pages );
39                 R::store( $book );
40                 $data = R::getRow('SELECT book.*,
41                                                                   COUNT(page.id) AS meta_count,
42                                                                   1234 AS meta_extra
43                                                    FROM book
44                                                    LEFT JOIN page ON page.book_id = book.id
45                                                    GROUP BY book.id
46                                                    ');
47                 $bean = R::convertToBean( 'book', $data, 'meta_' );
48                 asrt( isset( $bean->title ), TRUE );
49                 asrt( isset( $bean->author ), TRUE );
50                 asrt( isset( $bean->meta_count ), FALSE );
51                 asrt( isset( $bean->meta_extra ), FALSE );
52                 $data = $bean->getMeta( 'data.bundle' );
53                 asrt( intval( $data['meta_count'] ), 2);
54                 asrt( intval( $data['meta_extra'] ), 1234);
55                 //now with multiple beans
56                 $book = R::dispense( array(
57                         '_type'  => 'book',
58                         'title'  => 'Bean Adventures',
59                         'author' => 'Mr Adventure'
60                 ) );
61                 $pages = R::dispenseAll( 'page*3' );
62                 $book->ownPageList = reset( $pages );
63                 R::store( $book );
64                 $data = R::getAll('SELECT book.*,
65                                                                   COUNT(page.id) AS meta_pages
66                                                    FROM book
67                                                    LEFT JOIN page ON page.book_id = book.id
68                                                    GROUP BY book.id
69                                                    ');
70                 $books = R::convertToBeans( 'book', $data, 'meta_' );
71                 $found = 0;
72                 foreach( $books as $book ) {
73                         if ( $book->title == 'Bean Recipes' ) {
74                                 $found++;
75                                 asrt( isset( $book->title ), TRUE );
76                                 asrt( isset( $book->author ), TRUE );
77                                 asrt( isset( $book->meta_count ), FALSE );
78                                 asrt( isset( $book->meta_extra ), FALSE );
79                                 $data = $book->getMeta( 'data.bundle' );
80                                 asrt( intval( $data['meta_pages'] ), 2);
81                         }
82                         if ( $book->title == 'Bean Adventures' ) {
83                                 $found++;
84                                 asrt( isset( $book->title ), TRUE );
85                                 asrt( isset( $book->author ), TRUE );
86                                 asrt( isset( $book->meta_pages ), FALSE );
87                                 asrt( isset( $book->meta_extra ), FALSE );
88                                 $data = $book->getMeta( 'data.bundle' );
89                                 asrt( intval( $data['meta_pages'] ), 3);
90                         }
91                 }
92                 asrt( $found, 2 );
93         }
94
95         /**
96          * Test beautification conflicts...
97          * Issue #418
98          *
99          * @return void
100          */
101         public function testBeau()
102         {
103                 R::nuke();
104                 $book = R::dispense( 'book' );
105                 $book->ownerId = 2;
106                 $book->ownerCritic = 'a';
107                 $book->sharedbyReader = 'b';
108                 $id = R::store( $book );
109                 $columns = R::inspect( 'book' );
110                 asrt( isset( $columns['owner_id'] ), TRUE );
111                 asrt( isset( $columns['owner_critic'] ), TRUE );
112                 asrt( isset( $columns['sharedby_reader'] ), TRUE );
113                 asrt( isset( $columns['ownerId'] ), FALSE );
114                 asrt( isset( $columns['ownerCritic'] ), FALSE );
115                 asrt( isset( $columns['sharedbyReader'] ), FALSE );
116                 R::nuke();
117                 $book = R::dispense( 'book' );
118                 $book->xownerId = 2;
119                 $book->xownerCritic = 'a';
120                 $book->sharedbyReader = 'b';
121                 $id = R::store( $book );
122                 $columns = R::inspect( 'book' );
123                 asrt( isset( $columns['xowner_id'] ), TRUE );
124                 asrt( isset( $columns['xowner_critic'] ), TRUE );
125                 asrt( isset( $columns['sharedby_reader'] ), TRUE );
126                 asrt( isset( $columns['xownerId'] ), FALSE );
127                 asrt( isset( $columns['xownerCritic'] ), FALSE );
128                 asrt( isset( $columns['sharedbyReader'] ), FALSE );
129         }
130
131         /**
132          * Other tests...
133          */
134         public function testMisc()
135         {
136                 R::nuke();
137                 $book = R::dispense( 'book' );
138                 $book->ownPage[] = R::dispense( 'page' );
139                 R::store( $book );
140                 R::nuke();
141                 R::store( $book );
142                 asrt( R::count( 'book' ), 0 );
143                 $book->ownPage;
144                 R::store( $book );
145                 asrt( R::count( 'book' ), 0 );
146                 $book->title = 'x';
147                 R::store( $book );
148                 asrt( R::count( 'book' ), 0 );
149         }
150
151         /**
152          * Only fire update query if the bean really contains different
153          * values. But make sure beans several 'parents' away still get
154          * saved.
155          *
156          * @return void
157          */
158         public function testBeanTainting()
159         {
160                 $logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
161                 list( $i, $k, $c, $s ) = R::dispenseAll( 'invoice,customer,city,state' );
162                 $i->customer = $k;
163                 $i->status = 0;
164                 $k->city = $c;
165                 $c->state = $s;
166                 $s->name = 'x';
167                 R::store( $i );
168                 $i = $i->fresh();
169                 asrt( $i->customer->city->state->name, 'x' );
170                 $i->status = 1;
171                 R::freeze( true );
172                 $logger = R::debug( 1, 1 );
173                 //do we properly skip unmodified but tainted parent beans?
174                 R::store( $i );
175                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
176                 asrt( count( $numberOfUpdateQueries ), 1 );
177                 //does cascade update still work?
178                 $i = $i->fresh();
179                 $i->customer->city->state->name = 'y';
180                 R::store( $i );
181                 $i = $i->fresh();
182                 asrt( $i->customer->city->state->name, 'y' );
183                 $i = $i->fresh();
184                 $differentCity = R::dispense( 'city' );
185                 R::store( $differentCity );
186                 $i->customer->city = $differentCity;
187                 R::store( $i );
188                 $i = $i->fresh();
189                 asrt( ( $i->customer->city->id != $c->id ), TRUE );
190                 asrt( is_null( $i->customer->city->state ), TRUE );
191                 $i->customer->city = NULL;
192                 R::store( $i );
193                 $i = $i->fresh();
194                 asrt( is_null( $i->customer->city ), TRUE );
195                 $i->customer = $k;
196                 $i->status = 0;
197                 $k->city = $c;
198                 $c->state = $s;
199                 $s->name = 'x';
200                 R::store( $i );
201                 R::freeze( FALSE );
202                 $i = $i->fresh();
203                 //can we still change remote parent?
204                 $i->customer->city->name = 'q';
205                 $logger->clear();
206                 R::store($i);
207                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
208                 //print_r($logger->getLogs());
209                 asrt( count( $numberOfUpdateQueries ), 1 );
210                 $i = $i->fresh();
211                 asrt( $i->customer->city->name, 'q' );
212                 //do we properly skip unmodified but tainted parent beans?
213                 $i->status = 3;
214                 $logger->clear();
215                 R::store( $i );
216                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
217                 asrt( count( $numberOfUpdateQueries ), 1 );
218         }
219
220         /**
221          * Test whether the number of update queries
222          * executed is limited to the ones that are absolutely
223          * necessary to sync the database.
224          *
225          * @return void
226          */
227         public function testUpdateQueries()
228         {
229                 $book = R::dispense( 'book' );
230                 $book->title = 'Eye of Wight';
231                 $book->xownPageList = R::dispense( 'page', 10 );
232                 $book->sharedCategoryList = R::dispense( 'category', 2 );
233                 $n = 1;
234                 foreach( $book->xownPageList as $page ) {
235                         $page->number = $n++;
236                 }
237                 $book->sharedCategory[0]->name = 'adventure';
238                 $book->sharedCategory[1]->name = 'puzzle';
239                 $book->author = R::dispense( 'author' );
240                 $book->author->name = 'John';
241                 $book->map = R::dispense( 'map' );
242                 $book->map->name = 'Wight';
243                 $book->map->xownLocationList = R::dispense( 'location', 3 );
244                 asrt( $book->getMeta('tainted'), TRUE );
245                 asrt( $book->getMeta('changed'), TRUE );
246                 R::store( $book );
247                 asrt( $book->getMeta('tainted'), FALSE );
248                 asrt( $book->getMeta('changed'), FALSE );
249                 $logger = R::debug( 1, 1 );
250                 $book = $book->fresh();
251                 asrt( $book->getMeta('tainted'), FALSE );
252                 asrt( $book->getMeta('changed'), FALSE );
253                 $book->author;
254                 asrt( $book->getMeta('tainted'), TRUE );
255                 asrt( $book->getMeta('changed'), FALSE );
256                 $logger->clear();
257                 R::store( $book );
258                 //read only, no updates
259                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
260                 asrt( count( $numberOfUpdateQueries ), 0 );
261                 $book->title = 'Spirit of the Stones';
262                 R::store( $book );
263                 //changed title, 1 update
264                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
265                 asrt( count( $numberOfUpdateQueries ), 1 );
266                 $logger->clear();
267                 //store again, no changes, no updates
268                 R::store( $book );
269                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
270                 asrt( count( $numberOfUpdateQueries ), 0 );
271                 $logger->clear();
272                 $book = $book->fresh();
273                 $book->xownPageList;
274                 asrt( $book->getMeta('tainted'), TRUE );
275                 asrt( $book->getMeta('changed'), FALSE );
276                 R::store( $book );
277                 //access only, no update
278                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
279                 asrt( count( $numberOfUpdateQueries ), 0 );
280                 $logger->clear();
281                 $book = $book->fresh();
282                 $book->sharedCategoryList;
283                 asrt( $book->getMeta('tainted'), TRUE );
284                 asrt( $book->getMeta('changed'), FALSE );
285                 R::store( $book );
286                 //access only, no update
287                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
288                 asrt( count( $numberOfUpdateQueries ), 0 );
289                 $logger->clear();
290                 $book = $book->fresh();
291                 unset($book->xownPageList[5]);
292                 asrt( $book->getMeta('tainted'), TRUE );
293                 asrt( $book->getMeta('changed'), FALSE );
294                 R::store( $book );
295                 //remove only, no update, just 1 delete
296                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
297                 asrt( count( $numberOfUpdateQueries ), 0 );
298                 $numberOfUpdateQueries = $logger->grep( 'DELETE' );
299                 asrt( count( $numberOfUpdateQueries ), 1 );
300                 $book = $book->fresh();
301                 asrt( count( $book->xownPageList ), 9 );
302                 $logger->clear();
303                 $book = $book->fresh();
304                 $book->xownPageList[] = R::dispense('page');
305                 asrt( $book->getMeta('tainted'), TRUE );
306                 asrt( $book->getMeta('changed'), FALSE );
307                 R::store( $book );
308                 //no update, 1 insert, just adding
309                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
310                 asrt( count( $numberOfUpdateQueries ), 0 );
311                 $numberOfUpdateQueries = $logger->grep( 'INSERT' );
312                 asrt( count( $numberOfUpdateQueries ), 1 );
313                 $book = $book->fresh();
314                 asrt( count( $book->xownPageList ), 10 );
315                 $logger->clear();
316                 $book = $book->fresh();
317                 $book->map->xownLocationList[1]->name = 'Horshoe Bay';
318                 asrt( $book->getMeta('tainted'), TRUE );
319                 asrt( $book->getMeta('changed'), FALSE );
320                 asrt( $book->map->getMeta('tainted'), TRUE );
321                 asrt( $book->map->getMeta('changed'), FALSE );
322                 asrt( $book->map->xownLocationList[1]->getMeta('tainted'), TRUE );
323                 asrt( $book->map->xownLocationList[1]->getMeta('changed'), TRUE );
324                 R::store( $book );
325                 //1 update for child of parent, no other updates
326                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
327                 asrt( count( $numberOfUpdateQueries ), 1 );
328                 $book = $book->fresh();
329                 asrt( $book->map->xownLocationList[1]->name, 'Horshoe Bay' );
330                 $logger->clear();
331                 R::store( $book );
332                 //just access, no updates
333                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
334                 asrt( count( $numberOfUpdateQueries ), 0 );
335                 $logger->clear();
336                 $book = $book->fresh();
337                 $book->ownPageList[2]->number = 99;
338                 R::store( $book );
339                 //1 update, do not update rest of pages or book itself
340                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
341                 asrt( count( $numberOfUpdateQueries ), 1 );
342                 $book = $book->fresh();
343                 $book->author->name = 'Worsley';
344                 $logger->clear();
345                 R::store( $book );
346                 //1 update for parent
347                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
348                 asrt( count( $numberOfUpdateQueries ), 1 );
349                 $author = R::dispense('author');
350                 $author->name = 'J.W.';
351                 R::store( $author );
352                 $book = $book->fresh();
353                 $book->author = $author;
354                 $author->name = 'JW';
355                 $logger->clear();
356                 R::store( $book );
357                 //2 updates, one for author, one for link field: author_id needs update.
358                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
359                 asrt( count( $numberOfUpdateQueries ), 2 );
360                 $author->country = R::dispense( 'country' )->setAttr( 'name', 'England' );
361                 R::store( $author );
362                 $book = $book->fresh();
363                 $book->author->country->name = 'Wight';
364                 $logger->clear();
365                 R::store( $book );
366                 //1 update, country only, dont update for intermediate parents: book -> author -> ...
367                 $numberOfUpdateQueries = $logger->grep( 'UPDATE' );
368                 asrt( count( $numberOfUpdateQueries ), 1 );
369         }
370
371         /**
372          * Tests effects of importFrom and setProperty.
373          *
374          * @return void
375          */
376         public function testImportFromAndSetProp()
377         {
378                 $bean = R::dispense( 'bean' );
379                 asrt( $bean->getMeta( 'tainted' ), TRUE );
380                 asrt( $bean->getMeta( 'changed' ), TRUE );
381                 $bean->setMeta( 'tainted', FALSE );
382                 $bean->setMeta( 'changed', FALSE );
383                 asrt( $bean->getMeta( 'tainted' ), FALSE );
384                 asrt( $bean->getMeta( 'changed' ), FALSE );
385                 $bean->importFrom( R::dispense( 'bean' ) );
386                 asrt( $bean->getMeta( 'tainted' ), TRUE );
387                 asrt( $bean->getMeta( 'changed' ), TRUE );
388                 $bean->setMeta( 'tainted', FALSE );
389                 $bean->setMeta( 'changed', FALSE );
390                 asrt( $bean->getMeta( 'tainted' ), FALSE );
391                 asrt( $bean->getMeta( 'changed' ), FALSE );
392                 $bean->setProperty( 'id', 0, TRUE, TRUE );
393                 asrt( $bean->getMeta( 'tainted' ), TRUE );
394                 asrt( $bean->getMeta( 'changed' ), TRUE );
395                 $bean->setMeta( 'tainted', FALSE );
396                 $bean->setMeta( 'changed', FALSE );
397                 asrt( $bean->getMeta( 'tainted' ), FALSE );
398                 asrt( $bean->getMeta( 'changed' ), FALSE );
399                 $bean->setProperty( 'id', 0, TRUE, FALSE );
400                 asrt( $bean->getMeta( 'tainted' ), FALSE );
401                 asrt( $bean->getMeta( 'changed' ), FALSE );
402                 $bean->name = 'x';
403                 asrt( $bean->getMeta( 'tainted' ), TRUE );
404                 asrt( $bean->getMeta( 'changed' ), TRUE );
405         }
406
407         /**
408          * Setup
409          *
410          * @return void
411          */
412         private function _createBook()
413         {
414                 R::nuke();
415                 $book = R::dispense( 'book' );
416                 $pages = R::dispense( 'page', 2 );
417                 $ads = R::dispense('ad', 3 );
418                 $tags = R::dispense( 'tag', 2 );
419                 $author = R::dispense( 'author' );
420                 $coauthor = R::dispense( 'author' );
421                 $book->alias( 'magazine' )->ownAd = $ads;
422                 $book->ownPage = $pages;
423                 $book->sharedTag = $tags;
424                 $book->via( 'connection' )->sharedUser = array( R::dispense( 'user' ) );
425                 $book->author = $author;
426                 $book->coauthor = $coauthor;
427                 R::store( $book );
428                 return $book->fresh();
429         }
430
431         /*
432          * Can we add a bean to a list?
433          *
434          * @return void
435          */
436         public function testWhetherWeCanAddToLists()
437         {
438                 $book = $this->_createBook();
439                 $book->ownPage[] = R::dispense( 'page' );
440                 R::store( $book );
441                 asrt( R::count('page'), 3 );
442                 $book = $this->_createBook();
443                 $book->ownPageList[] = R::dispense('page');
444                 R::store( $book );
445                 asrt( R::count('page'), 3 );
446                 $book = $this->_createBook();
447                 $book->xownPage[] = R::dispense('page');
448                 R::store( $book );
449                 asrt( R::count('page'), 3 );
450                 $book = $this->_createBook();
451                 $book->xownPageList[] = R::dispense('page');
452                 R::store( $book );
453                 asrt( R::count('page'), 3 );
454
455                 $ads = R::dispense('ad', 3 );
456                 $book = $this->_createBook();
457                 $book->alias('magazine')->ownAd = $ads;
458                 $book->ownPage[] = R::dispense('page');
459                 R::store( $book );
460                 asrt( R::count('ad'), 6 );
461                 asrt( R::count('page'), 3 );
462                 $ads = R::dispense('ad', 3 );
463                 $book = $this->_createBook();
464                 $book->alias('magazine')->ownAdList = $ads;
465                 $book->ownPageList[] = R::dispense('page');
466                 R::store( $book );
467                 asrt( R::count('ad'), 6 );
468                 asrt( R::count('page'), 3 );
469                 $ads = R::dispense('ad', 3 );
470                 $book = $this->_createBook();
471                 $book->alias('magazine')->xownAd = $ads;
472                 $book->xownPage[] = R::dispense('page');
473                 R::store( $book );
474                 asrt( R::count('ad'), 3 );
475                 asrt( R::count('page'), 3 );
476                 $ads = R::dispense('ad', 3 );
477                 $book = $this->_createBook();
478                 $book->alias('magazine')->xownAdList = $ads;
479                 $book->xownPageList[] = R::dispense('page');
480                 R::store( $book );
481                 asrt( R::count('ad'), 3 );
482                 asrt( R::count('page'), 3 );
483
484                 $book = $this->_createBook();
485                 $book->sharedTag[] = R::dispense('tag');
486                 R::store( $book );
487                 asrt( R::count('tag'), 3 );
488                 $book = $this->_createBook();
489                 $book->sharedTagList[] = R::dispense('tag');
490                 R::store( $book );
491                 asrt( R::count('tag'), 3 );
492         }
493
494         /**
495          * Can we delete a bean in a list by its ID?
496          * Only the UNSET() variant should work.
497          *
498          * @return void
499          */
500         public function testDeleteByIDs()
501         {
502                 $book = $this->_createBook();
503                 $firstPage = reset( $book->ownPageList );
504                 $book->ownPage[ $firstPage->id ] = NULL;
505                 try { R::store( $book ); fail(); }catch(\Exception $e) { pass(); }
506                 $book = $this->_createBook();
507                 asrt( count( $book->ownPage ), 2 );
508                 $firstPage = reset( $book->ownPageList );
509                 unset( $book->ownPage[ $firstPage->id ] );
510                 R::store( $book );
511                 $book = $book->fresh();
512                 asrt( count( $book->ownPage ), 1 );
513                 $firstPage = reset( $book->ownPageList );
514                 $book->ownPage[ $firstPage->id ] = FALSE;
515                 try { R::store( $book ); fail(); }catch(\Exception $e) { pass(); }
516                 $book = $book->fresh();
517                 asrt( count( $book->ownPage ), 0 );
518
519                 $book = $this->_createBook();
520                 $firstAd = reset( $book->alias('magazine')->ownAd );
521                 $book->alias('magazine')->ownAd[ $firstAd->id ] = NULL;
522                 try { R::store( $book ); fail(); }catch(\Exception $e) { pass(); }
523                 $book = $this->_createBook();
524                 asrt( count( $book->alias('magazine')->ownAd ), 3 );
525                 $firstAd = reset( $book->alias('magazine')->ownAdList );
526                 unset( $book->alias('magazine')->ownAdList[ $firstAd->id ] );
527                 R::store( $book );
528                 $book = $book->fresh();
529                 asrt( count( $book->alias('magazine')->ownAd ), 2 );
530                 $firstAd = reset( $book->alias('magazine')->ownAd );
531                 $book->alias('magazine')->ownAd[ $firstAd->id ] = FALSE;
532                 try { R::store( $book ); fail(); }catch(\Exception $e) { pass(); }
533                 $book = $book->fresh();
534                 asrt( count( $book->alias('magazine')->ownAd ), 1 );
535
536         }
537
538         /**
539          * You CAN delete an own-list by assiging an empty array.
540          *
541          * @return void
542          */
543         public function testDeleteOwnListWithEmptyArray()
544         {
545                 $book = $this->_createBook();
546                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
547                 asrt( count( $book->ownPage ), 2 ); //when loaded has 2
548                 $book->ownPage = array(); //remove all
549                 R::store( $book );
550                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
551                 asrt( count( $book->ownPage ), 0 );
552         }
553
554         /**
555          * You cannot delete an own-list by assigning NULL.
556          *
557          * @return void
558          */
559         public function testCANTDeleteOwnListWithNULL()
560         {
561                 $book = $this->_createBook();
562                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
563                 asrt( count( $book->ownPage ), 2 ); //when loaded has 2
564                 $book->ownPage = NULL; //remove all
565                 R::store( $book );
566                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
567                 asrt( count( $book->ownPage ), 2 );
568         }
569
570         /**
571          * You cannot delete an own-list by assigning FALSE.
572          *
573          * @return void
574          */
575         public function testCANTDeleteOwnListWithFalse()
576         {
577                 $book = $this->_createBook();
578                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
579                 asrt( count( $book->ownPage ), 2 ); //when loaded has 2
580                 $book->ownPage = FALSE; //remove all
581                 R::store( $book );
582                 asrt( isset($book->ownPage), TRUE ); //not loaded yet, lazy loading
583                 asrt( $book->ownPage, '0' );
584         }
585
586         /**
587          * You cannot delete an own-list by unsetting it.
588          */
589         public function testCANTDeleteOwnListWithUnset()
590         {
591                 $book = $this->_createBook();
592                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
593                 asrt( count( $book->ownPage ), 2 ); //when loaded has 2
594                 unset( $book->ownPage ); //does NOT remove all
595                 R::store( $book );
596                 asrt( isset($book->ownPage), FALSE ); //not loaded yet, lazy loading
597                 asrt( count( $book->ownPage ), 2 );
598         }
599
600         /**
601          * You CAN delete an aliased own-list by assiging an empty array.
602          *
603          * @return void
604          */
605         public function testDeleteAliasedOwnListWithEmptyArray()
606         {
607                 $book = $this->_createBook();
608                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
609                 asrt( count( $book->alias('magazine')->ownAd ), 3 ); //when loaded has 2
610                 $book->alias('magazine')->ownAd = array(); //remove all
611                 $book->ownPage[] = R::dispense('page');
612                 R::store( $book );
613                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
614                 asrt( count( $book->alias('magazine')->ownAd ), 0 );
615                 asrt( count( $book->alias('magazine')->ownPage ), 0 ); //also test possible confusion
616                 asrt( count( $book->all()->ownPageList ), 3 );
617         }
618
619         /**
620          * You cannot delete an aliased own-list by assigning NULL.
621          *
622          * @return void
623          */
624         public function testCANTDeleteAliasedOwnListWithNULL()
625         {
626                 $book = $this->_createBook();
627                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
628                 asrt( count( $book->alias('magazine')->ownAd ), 3 ); //when loaded has 2
629                 $book->alias('magazine')->ownAd = NULL; //remove all
630                 R::store( $book );
631                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
632                 asrt( count( $book->alias('magazine')->ownAd ), 3 );
633         }
634
635         /**
636          * You cannot delete an aliased own-list by assigning FALSE.
637          *
638          * @return void
639          */
640         public function testCANTDeleteAliasedOwnListWithFalse()
641         {
642                 $book = $this->_createBook();
643                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
644                 asrt( count( $book->alias('magazine')->ownAd ), 3 ); //when loaded has 2
645                 $book->alias('magazine')->ownAd = FALSE; //remove all
646                 R::store( $book );
647                 asrt( isset($book->alias('magazine')->ownAd), TRUE ); //not loaded yet, lazy loading
648                 asrt( $book->alias('magazine')->ownAd, '0' );
649         }
650
651         /**
652          * You cannot delete an aliased own-list by unsetting it.
653          *
654          * @return void
655          */
656         public function testCANTDeleteAliasedOwnListWithUnset()
657         {
658                 $book = $this->_createBook();
659                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
660                 asrt( count( $book->alias('magazine')->ownAd ), 3 ); //when loaded has 2
661                 unset( $book->alias('magazine')->ownAd ); //does NOT remove all
662                 R::store( $book );
663                 asrt( isset($book->alias('magazine')->ownAd), FALSE ); //not loaded yet, lazy loading
664                 asrt( count( $book->alias('magazine')->ownAd ), 3 );
665         }
666
667         /**
668          * You CAN delete an x-own-list by assiging an empty array.
669          *
670          * @return void
671          */
672         public function testDeleteXOwnListWithEmptyArray()
673         {
674                 $book = $this->_createBook();
675                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
676                 asrt( count( $book->xownPage ), 2 ); //when loaded has 2
677                 $book->xownPage = array(); //remove all
678                 R::store( $book );
679                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
680                 asrt( count( $book->xownPage ), 0 );
681         }
682
683         /**
684          * You cannot delete an x-own-list by assigning NULL.
685          *
686          * @return  void
687          */
688         public function testCANTDeleteXOwnListWithNULL()
689         {
690                 $book = $this->_createBook();
691                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
692                 asrt( count( $book->xownPage ), 2 ); //when loaded has 2
693                 $book->xownPage = NULL; //remove all
694                 R::store( $book );
695                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
696                 asrt( count( $book->xownPage ), 2 );
697         }
698
699         /**
700          * You cannot delete an x-own-list by assigning FALSE.
701          *
702          * @return void
703          */
704         public function testCANTDeleteXOwnListWithFalse()
705         {
706                 $book = $this->_createBook();
707                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
708                 asrt( count( $book->xownPage ), 2 ); //when loaded has 2
709                 $book->xownPage = FALSE; //remove all
710                 R::store( $book );
711                 asrt( isset($book->xownPage), TRUE ); //not loaded yet, lazy loading
712                 asrt( $book->xownPage, '0' );
713         }
714
715         /**
716          * You cannot delete an x-own-list by unsetting it.
717          *
718          * @return void
719          */
720         public function testCANTDeleteXOwnListWithUnset()
721         {
722                 $book = $this->_createBook();
723                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
724                 asrt( count( $book->xownPage ), 2 ); //when loaded has 2
725                 unset( $book->xownPage ); //does NOT remove all
726                 R::store( $book );
727                 asrt( isset($book->xownPage), FALSE ); //not loaded yet, lazy loading
728                 asrt( count( $book->xownPage ), 2 );
729         }
730
731         /**
732          * You CAN delete a shared-list by assiging an empty array.
733          *
734          * @return void
735          */
736         public function testDeleteSharedListWithEmptyArray()
737         {
738                 $book = $this->_createBook();
739                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
740                 asrt( count( $book->sharedTag ), 2 ); //when loaded has 2
741                 $book->sharedTag = array(); //remove all
742                 R::store( $book );
743                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
744                 asrt( count( $book->sharedTag ), 0 );
745         }
746
747         /**
748          * You cannot delete a shared list by assigning NULL.
749          *
750          * @return void
751          */
752         public function testCANTDeleteSharedListWithNULL()
753         {
754                 $book = $this->_createBook();
755                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
756                 asrt( count( $book->sharedTag ), 2 ); //when loaded has 2
757                 $book->sharedTag = NULL; //remove all
758                 R::store( $book );
759                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
760                 asrt( count( $book->sharedTag ), 2 );
761         }
762
763         /**
764          * You cannot delete a shared-list by assigning FALSE.
765          *
766          * @return void
767          */
768         public function testCANTDeleteSharedListWithFalse()
769         {
770                 $book = $this->_createBook();
771                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
772                 asrt( count( $book->sharedTag ), 2 ); //when loaded has 2
773                 $book->sharedTag = FALSE; //remove all
774                 R::store( $book );
775                 asrt( isset($book->sharedTag), TRUE ); //not loaded yet, lazy loading
776                 asrt( $book->sharedTag, '0' );
777         }
778
779         /**
780          * You cannot delete a shared-list by unsetting it.
781          *
782          * @return void
783          */
784         public function testCANTDeleteSharedWithUnset()
785         {
786                 $book = $this->_createBook();
787                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
788                 asrt( count( $book->sharedTag ), 2 ); //when loaded has 2
789                 unset( $book->sharedTag ); //does NOT remove all
790                 R::store( $book );
791                 asrt( isset($book->sharedTag), FALSE ); //not loaded yet, lazy loading
792                 asrt( count( $book->sharedTag ), 2 );
793         }
794
795         /**
796          * You CAN delete a shared-list by assiging an empty array.
797          *
798          * @return void
799          */
800         public function testDeleteViaSharedListWithEmptyArray()
801         {
802                 $book = $this->_createBook();
803                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
804                 asrt( count( $book->via('connection')->sharedUser ), 1 ); //when loaded has 2
805                 $book->via('connection')->sharedUser = array(); //remove all
806                 R::store( $book );
807                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
808                 asrt( count( $book->via('connection')->sharedUser ), 0 );
809         }
810
811         /**
812          * You cannot delete a shared-list by assigning NULL.
813          *
814          * @return void
815          */
816         public function testCANTDeleteViaSharedListWithNULL()
817         {
818                 $book = $this->_createBook();
819                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
820                 asrt( count( $book->via('connection')->sharedUser ), 1 ); //when loaded has 2
821                 $book->via('connection')->sharedUser = NULL; //remove all
822                 R::store( $book );
823                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
824                 asrt( count( $book->via('connection')->sharedUser ), 1 );
825         }
826
827         /**
828          * You cannot delete a shared list by assigning FALSE.
829          *
830          * @return void
831          */
832         public function testCANTDeleteViaSharedListWithFalse()
833         {
834                 $book = $this->_createBook();
835                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
836                 asrt( count( $book->via('connection')->sharedUser ), 1 ); //when loaded has 1
837                 $book->via('connection')->sharedUser = FALSE; //remove all
838                 R::store( $book );
839                 asrt( isset($book->via('connection')->sharedUser), TRUE ); //not loaded yet, lazy loading
840                 asrt( count( $book->via('connection')->sharedUser ), 1 ); //when loaded has 1
841
842         }
843
844         /**
845          * You cannot delete a shared-list by unsetting it.
846          *
847          * @return void
848          */
849         public function testCANTDeleteViaSharedWithUnset()
850         {
851                 $book = $this->_createBook();
852                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
853                 asrt( count( $book->via('connection')->sharedUser ), 1 ); //when loaded has 2
854                 unset( $book->via('connection')->sharedUser ); //does NOT remove all
855                 R::store( $book );
856                 asrt( isset($book->via('connection')->sharedUser), FALSE ); //not loaded yet, lazy loading
857                 asrt( count( $book->via('connection')->sharedUser ), 1 );
858         }
859
860         /**
861          * You cannot delete a parent bean by unsetting it.
862          *
863          * @return void
864          */
865         public function testYouCANTDeleteParentBeanWithUnset()
866         {
867                 $book = $this->_createBook();
868                 asrt( isset($book->author), FALSE );
869                 asrt( (boolean) ($book->author), TRUE );
870                 unset( $book->author );
871                 R::store( $book );
872                 $book = $book->fresh();
873                 asrt( isset($book->author), FALSE );
874                 asrt( (boolean) ($book->author), TRUE );
875         }
876
877         /**
878          * You cannot delete a parent bean by setting it to NULL.
879          *
880          * @return void
881          */
882         public function testYouCANDeleteParentBeanWithNULL()
883         {
884                 $book = $this->_createBook();
885                 asrt( isset($book->author), FALSE );
886                 asrt( (boolean) ($book->author), TRUE );
887                 $book->author = NULL;
888                 R::store( $book );
889                 $book = $book->fresh();
890                 asrt( isset($book->author), FALSE );
891                 asrt( (boolean) ($book->author), FALSE );
892         }
893
894         /**
895          * You CAN delete a parent bean by setting it to FALSE.
896          *
897          * @return void
898          */
899         public function testYouCANDeleteParentBeanWithFALSE()
900         {
901                 $book = $this->_createBook();
902                 asrt( isset($book->author), FALSE );
903                 asrt( (boolean) ($book->author), TRUE );
904                 $book->author = FALSE;
905                 R::store( $book );
906                 $book = $book->fresh();
907                 asrt( isset($book->author), FALSE );
908                 asrt( (boolean) ($book->author), FALSE );
909         }
910
911         /**
912          * You cannot delete an aliased parent bean by unsetting it.
913          *
914          * @return void
915          */
916         public function testYouCANTDeleteAliasedParentBeanWithUnset()
917         {
918                 $book = $this->_createBook();
919                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
920                 asrt( (boolean) ($book->fetchAs('author')->coauthor), TRUE );
921                 unset( $book->fetchAs('author')->coauthor );
922                 R::store( $book );
923                 $book = $book->fresh();
924                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
925                 asrt( (boolean) ($book->fetchAs('author')->coauthor), TRUE );
926         }
927
928         /**
929          * You CAN delete an aliased parent bean by setting it to NULL.
930          *
931          * @return void
932          */
933         public function testYouCANDeleteAliasedParentBeanWithNULL()
934         {
935                 $book = $this->_createBook();
936                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
937                 asrt( (boolean) ($book->fetchAs('author')->coauthor), TRUE );
938                 $book->fetchAs('author')->coauthor = NULL;
939                 R::store( $book );
940                 $book = $book->fresh();
941                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
942                 asrt( (boolean) ($book->fetchAs('author')->coauthor), FALSE );
943         }
944
945         /**
946          * You cannot delete an aliased parent bean by setting it to FALSE.
947          *
948          * @return void
949          */
950         public function testYouCANDeleteAliasedParentBeanWithFALSE()
951         {
952                 $book = $this->_createBook();
953                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
954                 asrt( (boolean) ($book->fetchAs('author')->coauthor), TRUE );
955                 $book->fetchAs('author')->coauthor = FALSE;
956                 R::store( $book );
957                 $book = $book->fresh();
958                 asrt( isset($book->fetchAs('author')->coauthor), FALSE );
959                 asrt( (boolean) ($book->fetchAs('author')->coauthor), FALSE );
960         }
961
962         /**
963          * Tests the effects of unsetting on the shadow of a list.
964          *
965          * @return void
966          */
967         public function testUnsettingAListAndShadow()
968         {
969                 $book = $this->_createBook();
970                 //should work with ownPage and ownPageList as well...
971                 unset( $book->ownPageList );
972                 R::store( $book );
973                 $book = $book->fresh();
974                 asrt( count( $book->ownPage ), 2 );
975                 unset( $book->ownPage );
976                 //shadow should be reloaded as well...
977                 $book->with(' LIMIT 1 ')->ownPage;
978                 R::store( $book );
979                 $book = $book->fresh();
980                 asrt( count( $book->ownPage ), 2 );
981                 asrt( count( $book->getMeta('sys.shadow.ownPage') ), 2 );
982                 unset( $book->ownPage );
983                 asrt( $book->getMeta('sys.shadow.ownPage'), NULL );
984                 //no load must clear shadow as well...
985                 $book->noLoad()->ownPage[] = R::dispense( 'page' );
986                 asrt( count( $book->getMeta('sys.shadow.ownPage') ), 0 );
987                 R::store( $book );
988                 $book = $book->fresh();
989                 asrt( count( $book->ownPage ), 3 );
990                 $lists = array( 'ownPage', 'ownPageList', 'xownPage', 'xownPageList', 'sharedPage', 'sharedPageList' );
991                 foreach( $lists as $list ) {
992                         $book = R::dispense( 'book' );
993                         $book->$list;
994                         $shadowKey = $list;
995                         if ( strpos( $list, 'x' ) === 0) $shadowKey = substr( $shadowKey, 1 );
996                         $shadowKey = preg_replace( '/List$/', '', $shadowKey );
997                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
998                         unset( $book->$list );
999                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1000                         $book->$list; //reloading brings back shadow
1001                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1002                         $book->$list = array(); //keeps shadow (very important to compare deletions!)
1003                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1004                         R::store( $book ); //clears shadow
1005                         $book->alias('magazine')->$list; //reloading with alias also brings back shadow
1006                         unset( $book->$list );
1007                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1008                         $book = $book->fresh(); //clears shadow, reload
1009                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1010                         $book->noLoad()->$list; //reloading with noload also brings back shadow
1011                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1012                         asrt( count( $book->getMeta('sys.shadow.'.$shadowKey) ), 0 );
1013                         $book = $book->fresh(); //clears shadow, reload
1014                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1015                         $book->all()->$list; //reloading with all also brings back shadow
1016                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1017                         $book = $book->fresh(); //clears shadow, reload
1018                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1019                         $book->with(' LIMIT 1 ')->$list; //reloading with with- all also brings back shadow
1020                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1021                         $book = $book->fresh(); //clears shadow, reload
1022                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1023                         $book->$list = array(); //keeps shadow (very important to compare deletions!)
1024                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1025                         $book = $book->fresh(); //clears shadow, reload
1026                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1027                         $book->$list = array(); //keeps shadow (very important to compare deletions!)
1028                         asrt( is_array( $book->getMeta('sys.shadow.'.$shadowKey) ), TRUE );
1029                         R::trash( $book );
1030                         asrt( $book->getMeta('sys.shadow.'.$shadowKey), NULL );
1031                 }
1032
1033                 //no shadow for parent bean
1034                 $book = $book->fresh();
1035                 $book->author = R::dispense( 'author' );
1036                 asrt( $book->getMeta('sys.shadow.author'), NULL );
1037                 R::store( $book );
1038                 $book = $book->fresh();
1039                 unset( $book->author ); //we can unset and it does not remove
1040                 R::store( $book );
1041                 $book = $book->fresh();
1042                 asrt( is_object( $book->author ),TRUE );
1043                 //but we can also remove
1044                 $book->author = NULL;
1045                 R::store( $book );
1046                 $book = $book->fresh();
1047                 asrt( $book->author, NULL );
1048
1049         }
1050
1051         /**
1052          * Test whether the tainted flag gets set correctly.
1053          *
1054          * @return void
1055          */
1056         public function testAccessingTainting()
1057         {
1058                 $book = $this->_createBook();
1059                 asrt( $book->isTainted(), FALSE );
1060                 $book->ownPage;
1061                 asrt( $book->isTainted(), TRUE );
1062                 $book = $book->fresh();
1063                 asrt( $book->isTainted(), FALSE );
1064                 $book->author;
1065                 asrt( $book->isTainted(), TRUE );
1066                 $book = $book->fresh();
1067                 asrt( $book->isTainted(), FALSE );
1068                 $book->fetchAs('author')->coauthor;
1069                 asrt( $book->isTainted(), TRUE );
1070                 $book = $book->fresh();
1071                 asrt( $book->isTainted(), FALSE );
1072                 $book->alias('magazine')->xownAdList;
1073                 asrt( $book->isTainted(), TRUE );
1074                 $book = $book->fresh();
1075                 asrt( $book->isTainted(), FALSE );
1076                 $book->title = 'Hello';
1077                 asrt( $book->isTainted(), TRUE );
1078                 $book = $book->fresh();
1079                 asrt( $book->isTainted(), FALSE );
1080                 $book->sharedTag;
1081                 asrt( $book->isTainted(), TRUE );
1082                 $book = $book->fresh();
1083                 asrt( $book->isTainted(), FALSE );
1084                 $book->via('connection')->sharedUser;
1085                 asrt( $book->isTainted(), TRUE );
1086                 $book = $book->fresh();
1087                 asrt( $book->isTainted(), FALSE );
1088                 $book->coauthor;
1089                 asrt( $book->isTainted(), TRUE );
1090                 $book = $book->fresh();
1091                 asrt( $book->isTainted(), FALSE );
1092                 $book->ownFakeList;
1093                 asrt( $book->isTainted(), TRUE );
1094                 $book = $book->fresh();
1095                 asrt( $book->isTainted(), FALSE );
1096                 $book->sharedFakeList;
1097                 asrt( $book->isTainted(), TRUE );
1098                 $book = $book->fresh();
1099                 asrt( $book->isTainted(), FALSE );
1100                 $book->alias('fake')->ownFakeList;
1101                 asrt( $book->isTainted(), TRUE );
1102                 $book = $book->fresh();
1103                 asrt( $book->isTainted(), FALSE );
1104                 $book->title;
1105                 asrt( $book->isTainted(), FALSE );
1106                 $book = $book->fresh();
1107                 asrt( $book->isTainted(), FALSE );
1108                 $book->title = 1;
1109                 $book->setMeta( 'tainted', FALSE );
1110                 asrt( $book->isTainted(), FALSE );
1111         }
1112 }