3 namespace RedUNIT\Base;
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7 use RedBeanPHP\Logger\RDefault as RDefault;
8 use RedBeanPHP\Logger as Logger;
9 use RedBeanPHP\OODBBean as OODBBean;
10 use RedBeanPHP\OODB as OODB;
11 use RedBeanPHP\Adapter as Adapter;
12 use RedBeanPHP\QueryWriter as QueryWriter;
13 use RedBeanPHP\RedException as RedException;
14 use RedBeanPHP\RedException\SQL as SQL;
15 use RedBeanPHP\Driver\RPDO as RPDO;
16 use RedBeanPHP\SimpleModel as SimpleModel;
21 * @file RedUNIT/Base/Misc.php
22 * @desc Various tests.
23 * @author Gabor de Mooij and the RedBeanPHP Community
24 * @license New BSD/GPLv2
26 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
27 * This source file is subject to the New BSD/GPLv2 License that is bundled
28 * with this source code in the file license.txt.
30 class Misc extends Base
33 * Test whether we can set the 'auto clear'
38 public function testAutoClearHistory()
40 testpack( 'Auto clear history' );
41 $book = R::dispense( 'book' );
43 $book->title = 'book';
45 $book = R::findOne( 'book' );
46 asrt( $book->hasChanged( 'title' ), FALSE );
49 asrt( $book->hasChanged( 'title' ), TRUE );
50 OODB::autoClearHistoryAfterStore( TRUE );
51 $book = R::findOne( 'book' );
52 asrt( $book->hasChanged( 'title' ), FALSE );
53 $book->title = 'yes2';
55 asrt( $book->hasChanged( 'title' ), FALSE );
56 OODB::autoClearHistoryAfterStore( FALSE );
57 $book = R::findOne( 'book' );
58 asrt( $book->hasChanged( 'title' ), FALSE );
61 asrt( $book->hasChanged( 'title' ), TRUE );
65 * Tests the R::inspect() method on the Facade.
69 public function testInspect() {
71 testpack( 'Test R::inspect() ' );
73 R::store( R::dispense( 'book' )->setAttr( 'title', 'book' ) );
75 asrt( count( $info ), 1 );
76 asrt( strtolower( $info[0] ), 'book' );
77 $info = R::inspect( 'book' );
78 asrt( count( $info ), 2 );
79 $keys = array_keys( $info );
81 asrt( strtolower( $keys[0] ), 'id' );
82 asrt( strtolower( $keys[1] ), 'title' );
86 * Test whether we can use the tableExist() method in OODB
87 * instances directly to help us determine
88 * the existance of a table.
92 public function testTableExist()
95 R::store( R::dispense( 'book' ) );
97 asrt( R::getRedBean()->tableExists( 'book' ), TRUE );
98 asrt( R::getRedBean()->tableExists( 'book2' ), FALSE );
100 asrt( R::getRedBean()->tableExists( 'book' ), TRUE );
101 asrt( R::getRedBean()->tableExists( 'book2' ), FALSE );
106 * Normally the check() method is always called indirectly when
107 * dealing with beans. This test ensures we can call check()
108 * directly. Even though frozen repositories do not rely on
109 * bean checking to improve performance the method should still
110 * offer the same functionality when called directly.
114 public function testCheckDirectly()
116 $bean = new OODBBean;
118 $bean->setMeta( 'type', 'book' );
119 R::getRedBean()->check( $bean );
120 $bean->setMeta( 'type', '.' );
122 R::getRedBean()->check( $bean );
124 } catch ( \Exception $e ) {
127 //check should remain the same even if frozen repo is used, method is public after all!
128 //we dont want to break the API!
131 R::getRedBean()->check( $bean );
133 } catch ( \Exception $e ) {
140 * Test Backward compatibility writer ESC-method.
144 public function testLegacyCode()
146 testpack( 'Test Backward compatibility methods in writer.' );
147 asrt( R::getWriter()->safeColumn( 'column', TRUE ), R::getWriter()->esc( 'column', TRUE ) );
148 asrt( R::getWriter()->safeColumn( 'column', FALSE ), R::getWriter()->esc( 'column', FALSE ) );
149 asrt( R::getWriter()->safeTable( 'table', TRUE ), R::getWriter()->esc( 'table', TRUE ) );
150 asrt( R::getWriter()->safeTable( 'table', FALSE ), R::getWriter()->esc( 'table', FALSE ) );
154 * Test beautification and array functions.
158 public function testBeauficationAndArrayFunctions()
160 $bean = R::dispense( 'bean' );
161 $bean->isReallyAwesome = TRUE;
162 asrt( isset( $bean->isReallyAwesome ), TRUE );
163 asrt( isset( $bean->is_really_awesome ), TRUE );
164 unset( $bean->is_really_awesome );
165 asrt( isset( $bean->isReallyAwesome ), FALSE );
166 asrt( isset( $bean->is_really_awesome ), FALSE );
170 * Test beautification of column names.
174 public function testBeautifulColumnNames()
176 testpack( 'Beautiful column names' );
177 $town = R::dispense( 'town' );
178 $town->isCapital = FALSE;
179 $town->hasTrainStation = TRUE;
180 $town->name = 'BeautyVille';
181 $houses = R::dispense( 'house', 2 );
182 $houses[0]->isForSale = TRUE;
183 $town->ownHouse = $houses;
185 $town = R::load( 'town', $town->id );
186 asrt( ( $town->isCapital == FALSE ), TRUE );
187 asrt( ( $town->hasTrainStation == TRUE ), TRUE );
188 asrt( ( $town->name == 'BeautyVille' ), TRUE );
189 testpack( 'Accept datetime objects.' );
190 $cal = R::dispense( 'calendar' );
191 $cal->when = new\DateTime( '2000-01-01', new\DateTimeZone( 'Pacific/Nauru' ) );
192 asrt( $cal->when, '2000-01-01 00:00:00' );
193 testpack( 'Affected rows test' );
194 $currentDriver = $this->currentlyActiveDriverID;
195 $toolbox = R::getToolBox();
196 $adapter = $toolbox->getDatabaseAdapter();
197 $writer = $toolbox->getWriter();
198 $redbean = $toolbox->getRedBean();
199 $pdo = $adapter->getDatabase();
200 $bean = $redbean->dispense( 'bean' );
201 $bean->prop = 3; //make test run with strict mode as well
202 $redbean->store( $bean );
203 $adapter->exec( 'UPDATE bean SET prop = 2' );
204 asrt( $adapter->getAffectedRows(), 1 );
205 testpack( 'Testing Logger' );
206 R::getDatabaseAdapter()->getDatabase()->setLogger( new RDefault );
207 asrt( ( R::getDatabaseAdapter()->getDatabase()->getLogger() instanceof Logger ), TRUE );
208 asrt( ( R::getDatabaseAdapter()->getDatabase()->getLogger() instanceof RDefault ), TRUE );
209 $bean = R::dispense( 'bean' );
211 $bean->unsetAll( array( 'property' ) );
212 asrt( $bean->property, NULL );
213 asrt( ( $bean->setAttr( 'property', 2 ) instanceof OODBBean ), TRUE );
214 asrt( $bean->property, 2 );
215 asrt( preg_match( '/\d\d\d\d\-\d\d\-\d\d/', R::isoDate() ), 1 );
216 asrt( preg_match( '/\d\d\d\d\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', R::isoDateTime() ), 1 );
217 $redbean = R::getRedBean();
218 $adapter = R::getDatabaseAdapter();
219 $writer = R::getWriter();
220 asrt( ( $redbean instanceof OODB ), TRUE );
221 asrt( ( $adapter instanceof Adapter ), TRUE );
222 asrt( ( $writer instanceof QueryWriter ), TRUE );
223 R::setRedBean( $redbean );
224 pass(); //cant really test this
225 R::setDatabaseAdapter( $adapter );
226 pass(); //cant really test this
227 R::setWriter( $writer );
228 pass(); //cant really test this
229 $u1 = R::dispense( 'user' );
232 $u2 = R::dispense( 'user' );
237 $list = R::getAssoc( 'select login,' . R::getWriter()->esc( 'name' ) . ' from ' . R::getWriter()->esc( 'user' ) . ' ' );
238 asrt( $list['e'], 'Eric' );
239 asrt( $list['g'], 'Gabor' );
240 $painting = R::dispense( 'painting' );
241 $painting->name = 'Nighthawks';
242 $id = R::store( $painting );
243 testpack( 'Testing SQL Error Types' );
244 foreach ( $writer->typeno_sqltype as $code => $text ) {
245 asrt( is_integer( $code ), TRUE );
246 asrt( is_string( $text ), TRUE );
248 foreach ( $writer->sqltype_typeno as $text => $code ) {
249 asrt( is_integer( $code ), TRUE );
250 asrt( is_string( $text ), TRUE );
252 testpack( 'Testing Nowhere Pt. 1 (unfrozen)' );
255 'exec', 'getAll', 'getCell', 'getAssoc', 'getRow', 'getCol'
258 R::$method( 'select * from nowhere' );
261 testpack( 'Testing Nowhere Pt. 2 (frozen)' );
265 'exec', 'getAll', 'getCell', 'getAssoc', 'getRow', 'getCol'
269 R::$method( 'select * from nowhere' );
279 * Test reflectional functions of database.
283 public function testDatabaseProperties()
285 testpack( 'Testing Database Properties' );
286 $adapter = R::getDatabaseAdapter();
287 if ( method_exists( R::getDatabaseAdapter()->getDatabase(), 'getPDO' ) ){
288 asrt( $adapter->getDatabase()->getPDO() instanceof \PDO, TRUE );
290 asrt( strlen( $adapter->getDatabase()->getDatabaseVersion() ) > 0, TRUE );
291 asrt( strlen( $adapter->getDatabase()->getDatabaseType() ) > 0, TRUE );
299 public function testTransactions()
301 testpack( 'transactions' );
303 $bean = R::dispense( 'bean' );
306 asrt( R::count( 'bean' ), 1 );
310 $bean = R::dispense( 'bean' );
313 asrt( R::count( 'bean' ), 0 );
315 testpack( 'genSlots' );
316 asrt( R::genSlots( array( 'a', 'b' ) ), '?,?' );
317 asrt( R::genSlots( array( 'a' ) ), '?' );
318 asrt( R::genSlots( array() ), '' );
322 * Test nested FUSE scenarios.
326 public function testFUSEnested()
328 testpack( 'FUSE models cant touch nested beans in update() - issue 106' );
329 $spoon = R::dispense( 'spoon' );
330 $spoon->name = 'spoon for test bean';
331 $deep = R::dispense( 'deep' );
332 $deep->name = 'deepbean';
333 $item = R::dispense( 'item' );
336 $test = R::dispense( 'test' );
338 $test->sharedSpoon[] = $spoon;
339 $test->isnowtainted = TRUE;
340 $id = R::store( $test );
341 $test = R::load( 'test', $id );
342 asrt( $test->item->val, 'Test2' );
343 $can = reset( $test->ownCan );
344 $spoon = reset( $test->sharedSpoon );
345 asrt( $can->name, 'can for bean' );
346 asrt( $spoon->name, 'S2' );
347 asrt( $test->item->deep->name, '123' );
348 asrt( count( $test->ownCan ), 1 );
349 asrt( count( $test->sharedSpoon ), 1 );
350 asrt( count( $test->sharedPeas ), 10 );
351 asrt( count( $test->ownChip ), 9 );
355 * Tests FUSE and lists, FUSE enforces no more than
356 * 3 sugar cubes in coffee.
360 public function testCoffeeWithSugarAndFUSE()
362 $coffee = R::dispense( 'coffee' );
363 $coffee->size = 'XL';
364 $coffee->ownSugar = R::dispense( 'sugar', 5 );
365 $id = R::store( $coffee );
366 $coffee = R::load( 'coffee', $id );
367 asrt( count( $coffee->ownSugar ), 3 );
368 $coffee->ownSugar = R::dispense( 'sugar', 2 );
369 $id = R::store( $coffee );
370 $coffee = R::load( 'coffee', $id );
371 asrt( count( $coffee->ownSugar ), 2 );
372 $cocoa = R::dispense( 'cocoa' );
373 $cocoa->name = 'Fair Cocoa';
374 list( $taste1, $taste2 ) = R::dispense( 'taste', 2 );
375 $taste1->name = 'sweet';
376 $taste2->name = 'bitter';
377 $cocoa->ownTaste = array( $taste1, $taste2 );
379 $cocoa->name = 'Koko';
381 if ( method_exists( R::getDatabaseAdapter()->getDatabase(), 'getPDO' ) ) {
382 $pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
383 $driver = new RPDO( $pdo );
385 asrt( $pdo->getAttribute(\PDO::ATTR_ERRMODE ),\PDO::ERRMODE_EXCEPTION );
386 asrt( $pdo->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE ),\PDO::FETCH_ASSOC );
387 asrt( strval( $driver->GetCell( 'select 123' ) ), '123' );
390 $a->setSqlState( 'test' );
392 asrt( ( strpos( $b, '[test] - ' ) === 0 ), TRUE );
400 public function testENUMBasics() {
401 asrt( R::enum( 'gender:male' )->name, 'MALE' );
402 asrt( R::enum( 'country:South-Africa' )->name, 'SOUTH_AFRICA' );
403 asrt( R::enum( 'tester:T@E S_t' )->name, 'T_E_S_T' );
407 * Test ENUM in Queries and with short hand notation.
411 public function testENUMInQuery()
413 testpack('Test ENUM in Query and test ENUM short notation');
415 $coffee = R::dispense( 'coffee' );
416 $coffee->taste = R::enum( 'flavour:mocca' );
418 $coffee = R::dispense( 'coffee' );
419 $coffee->taste = R::enum( 'flavour:banana' );
421 $coffee = R::dispense( 'coffee' );
422 $coffee->taste = R::enum( 'flavour:banana' );
424 //now we have two flavours
425 asrt( R::count('flavour'), 2 );
427 asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:mocca' )->id ) ), 1);
428 //use in quer with short notation
429 asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:mocca' ) ) ), 1);
431 asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:banana' )->id ) ), 2);
432 //use in quer with short notation
433 asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:banana' ) ) ), 2);
435 asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:strawberry' )->id ) ), 0);
436 //use in quer with short notation
437 asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:strawberry' ) ) ), 0);
441 * Test ENUM functionality offered by Label Maker.
445 public function testENUM() {
446 testpack('test ENUM');
447 $coffee = R::dispense( 'coffee' );
448 $coffee->taste = R::enum( 'flavour:mocca' );
449 //did we create an enum?
450 asrt( implode( '', R::gatherLabels( R::enum( 'flavour' ) ) ), 'MOCCA' );
452 $coffee = $coffee->fresh();
453 //test enum identity check - with alias
454 asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum('flavour:mocca') ), TRUE );
455 asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum('flavour:banana') ), FALSE );
456 //now we have two flavours
457 asrt( R::count( 'flavour' ), 2 );
458 asrt( implode( ',', R::gatherLabels( R::enum( 'flavour') ) ), 'BANANA,MOCCA' );
459 $coffee->flavour = R::enum( 'flavour:mocca' );
461 //same results, can we have multiple flavours?
462 asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum( 'flavour:mocca' ) ), TRUE );
463 asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum( 'flavour:banana' ) ), FALSE );
464 asrt( $coffee->flavour->equals( R::enum( 'flavour:mocca' ) ), TRUE );
465 //no additional mocca enum...
466 asrt( R::count( 'flavour' ), 2 );
467 $drink = R::dispense( 'drink' );
468 $drink->flavour = R::enum( 'flavour:choco' );
471 asrt( R::count('flavour'), 3 );
472 $drink = R::load( 'drink', $drink->id );
473 asrt( $drink->flavour->equals( R::enum('flavour:mint') ), FALSE );
474 asrt( $drink->flavour->equals( R::enum('flavour:choco') ), TRUE );
475 asrt( R::count( 'flavour' ), 4 );
476 //trash should not affect flavour!
478 asrt( R::count( 'flavour' ), 4 );
484 public function testMultiDeleteUpdate()
486 testpack( 'test multi delete and multi update' );
487 $beans = R::dispenseLabels( 'bean', array( 'a', 'b' ) );
488 $ids = R::storeAll( $beans );
489 asrt( (int) R::count( 'bean' ), 2 );
490 R::trashAll( R::batch( 'bean', $ids ) );
491 asrt( (int) R::count( 'bean' ), 0 );
492 testpack( 'test assocManager check' );
493 $rb = new OODB( R::getWriter() );
495 $rb->getAssociationManager();
497 } catch ( RedException $e ) {
503 * Test Bean identity equality.
505 public function testBeanIdentityEquality() {
506 $beanA = R::dispense( 'bean' );
507 $beanB = R::dispense( 'bean' );
510 asrt( $beanA->equals( $beanB ), TRUE );
511 asrt( $beanB->equals( $beanA ), TRUE );
512 asrt( $beanA->equals( $beanA ), TRUE );
513 asrt( $beanB->equals( $beanB ), TRUE );
515 asrt( $beanA->equals( $beanB ), FALSE );
516 asrt( $beanB->equals( $beanA ), FALSE );
518 asrt( $beanA->equals( $beanB ), TRUE );
519 asrt( $beanB->equals( $beanA ), TRUE );
520 $beanB = R::dispense( 'carrot' );
521 $beanB->id = $beanA->id;
522 asrt( $beanA->equals( $beanB ), FALSE );
523 asrt( $beanB->equals( $beanA ), FALSE );
527 * Test if adding SimpleModles to a shared list will auto unbox them.
529 public function testSharedListsAutoUnbox() {
530 $boxedBean = R::dispense( 'boxedbean' );
531 $bean = R::dispense( 'bean' );
532 $model = new SimpleModel();
533 $model->loadBean($boxedBean);
534 $bean->ownBoxedbeanList[] = $model;
538 } catch ( \Exception $e ) {