Begin Refactor

This commit is contained in:
color.diff=auto
2021-03-21 13:47:29 -06:00
parent 7622dc0d02
commit 018931d7ae
197 changed files with 47799 additions and 6 deletions

View File

@@ -0,0 +1,387 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Aliasing
*
* Tests aliasing functionality, i.e. fetching beans as,
* inferring correct type and retrieving lists as alias.
*
* @file RedUNIT/Base/Aliasing.php
* @desc Tests for nested beans with aliases, i.e. teacher alias for person etc.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Aliasing extends Base
{
/**
* Test for aliasing issue for LTS version.
*
* @return void
*/
public function testIssueAliasingForLTSVersion() {
$person = R::dispense('person');
$pro = R::dispense('project');
$c = R::dispense('course');
$person->name = 'x';
$person->alias('teacher')->ownProject[] = $pro;
$person->alias('student')->ownCourse[] = $c;
R::store($person);
asrt($c->fresh()->fetchAs('person')->student->name, 'x');
asrt($pro->fresh()->fetchAs('person')->teacher->name, 'x');
$person = $person->fresh();
$person->alias('teacher')->ownProject = array();
$person->alias('student')->ownCourse = array();
R::store($person);
asrt($c->fresh()->fetchAs('person')->student, NULL);
asrt($pro->fresh()->fetchAs('person')->teacher, NULL);
}
/**
* Describing how clearing state of bean works.
* Every method returning somthing (except getID)
* clears prefix-method-state (anything set by withCond,with,alias,fetchAs).
*
* @return void
*/
public function clearStateAdditionalTests()
{
list( $project1, $project2 ) = R::dispense( 'project', 2 );
list( $irene, $ilse ) = R::dispense('person', 2);
$project1->developer = $ilse;
$project1->designer = $irene;
$ilse->name = 'Ilse';
$irene->name = 'Irene';
$project2->developer = $ilse;
R::storeAll( array( $project1, $project2 ) );
$ilse = R::load( 'person', $ilse->id );
asrt( count( $ilse->alias( 'developer' )->ownProject ), 2);
//cached - same list
asrt( count( $ilse->ownProject ), 2);
asrt( count( $ilse->alias( 'designer' )->ownProject ), 0);
//cached - same list
asrt( count( $ilse->ownProject ), 0);
//now test state
asrt( count( $ilse->setAttr( 'a', 'b' )->alias( 'developer' )->ownProject ), 2);
//now test state
$ilse = $ilse->fresh();
//attr clears state...
asrt( count( $ilse->alias( 'developer' )->setAttr( 'a', 'b' )->ownProject ), 0);
//but getID() does not!
$ilse = $ilse->fresh();
$ilse->alias('developer');
$ilse->getID();
asrt( count( $ilse->ownProject ), 2 );
}
/**
* Can switch fetchAs().
* Also checks shadow by storing.
*
* @return void
*/
public function canSwitchParentBean()
{
list( $project1, $project2 ) = R::dispense( 'project', 2 );
list( $irene, $ilse ) = R::dispense('person', 2);
$project1->developer = $ilse;
$project1->designer = $irene;
$ilse->name = 'Ilse';
$irene->name = 'Irene';
$project2->developer = $ilse;
R::storeAll( array( $project1, $project2 ) );
$project1 = R::load( 'project', $project1->id );
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
R::store( $project1 );
$project1 = R::load( 'project', $project1->id );
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
R::store( $project1 );
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
asrt( $project1->fetchAs('person')->designer->name, 'Irene' );
asrt( $project1->fetchAs('person')->developer->name, 'Ilse' );
}
/**
* Switching aliases (->alias) should not change other list during
* storage.
*
* @return void
*/
public function testShadow()
{
list( $project1, $project2 ) = R::dispense( 'project', 2 );
list( $irene, $ilse ) = R::dispense('person', 2);
$project1->developer = $ilse;
$project1->designer = $irene;
$project2->developer = $ilse;
R::storeAll( array( $project1, $project2 ) );
$ilse = R::load( 'person', $ilse->id );
$irene = R::load( 'person', $irene->id );
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
R::store( $ilse );
$ilse = R::load( 'person', $ilse->id );
$irene = R::load( 'person', $irene->id );
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
R::storeAll( array( $ilse, $irene) );
$ilse = R::load( 'person', $ilse->id );
$irene = R::load( 'person', $irene->id );
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
asrt( count( $irene->alias('designer')->ownProject), 1 );
asrt( count( $irene->alias('developer')->ownProject), 0 );
R::storeAll( array( $ilse, $irene) );
$ilse = R::load( 'person', $ilse->id );
$irene = R::load( 'person', $irene->id );
asrt( count( $ilse->alias('designer')->ownProject ), 0 );
asrt( count( $ilse->alias('developer')->ownProject ), 2 );
asrt( count( $irene->alias('designer')->ownProject), 1 );
asrt( count( $irene->alias('developer')->ownProject), 0 );
}
/**
* Issue 291. State not cleared.
*
* @return void
*/
public function testFetchTypeConfusionIssue291()
{
list( $teacher, $student ) = R::dispense( 'person', 2 ) ;
$teacher->name = 'jimmy' ;
$student->name = 'jacko' ;
R::store( $teacher ) ;
R::store( $student ) ;
$client = R::dispense( 'client' ) ;
$client->firm = 'bean AG' ;
R::store( $client ) ;
$project = R::dispense( 'project' ) ;
$project->teacher = $teacher ;
$project->student = $student ;
$project->client = $client ;
R::store( $project ) ;
unset( $project->student ) ;
R::store( $project ) ;
$project = R::load( 'project', 1 ) ;
$teacher = $project->fetchAs( 'person' )->teacher ;
$student = $project->fetchAs( 'person' )->student ;
$client = $project->client ; // this will select from "person" instead of "client"
asrt( $client->firm, 'bean AG' );
}
/**
* Test switching alias (also issue #291).
*
* @return void
*/
public function testAliasSwitch()
{
$student = R::dispense( 'person' );
$project = R::dispense( 'project' );
$project->student = $student;
R::store( $project );
$person = R::load( 'person', $student->id );
asrt( count( $person->alias( 'student' )->ownProject ), 1);
asrt( count( $person->alias( 'teacher' )->ownProject ), 0);
}
/**
* Associating two beans, then loading the associated bean
*
* @return void
*/
public function testAssociated()
{
$person = R::dispense( 'person' );
$person->name = 'John';
R::store( $person );
$course = R::dispense( 'course' );
$course->name = 'Math';
R::store( $course );
$course->teacher = $person;
$id = R::store( $course );
$course = R::load( 'course', $id );
$teacher = $course->fetchAs( 'person' )->teacher;
asrt( $teacher->name, 'John' );
//Trying to load a property that has an invalid name
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->wrongProperty = array( $page );
try {
$book->wrongProperty[] = $page;
R::store( $book );
fail();
} catch ( RedException $e ) {
pass();
} catch ( \Exception $e ) {
fail();
}
}
/**
* Test for quick detect change.
*
* @return void
*/
public function basic()
{
$book = R::dispense( 'book' );
asrt( isset( $book->prop ), FALSE ); //not a very good test
asrt( in_array( 'prop', array_keys( $book->export() ) ), FALSE ); //better...
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->paper = $page;
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( FALSE, ( isset( $book->paper ) ) );
asrt( FALSE, ( isset( $book->page ) ) );
/**
* The following tests try to store various things that aren't
* beans (which is expected) with the own* and shared* properties
* which only accept beans as assignments, so they're expected to fail
*/
foreach ( array( 'a string', 1928, TRUE, NULL, array()) as $value ) {
try {
$book->ownPage[] = $value;
R::store( $book );
$book->sharedPage[] = $value;
R::store( $book );
fail();
} catch ( RedException $e ) {
pass();
} catch ( \Exception $e ) {
if (strpos($e->getMessage(),'Array to string conversion')===FALSE) {
fail();
}
}
}
}
/**
* Finding $person beans that have been aliased into various roles
*
* @return void
*/
public function testAliasedFinder()
{
$message = R::dispense( 'message' );
$message->subject = 'Roommate agreement';
list( $sender, $recipient ) = R::dispense( 'person', 2 );
$sender->name = 'Sheldon';
$recipient->name = 'Leonard';
$message->sender = $sender;
$message->recipient = $recipient;
$id = R::store( $message );
$message = R::load( 'message', $id );
asrt( $message->fetchAs( 'person' )->sender->name, 'Sheldon' );
asrt( $message->fetchAs( 'person' )->recipient->name, 'Leonard' );
$otherRecipient = R::dispense( 'person' );
$otherRecipient->name = 'Penny';
$message->recipient = $otherRecipient;
R::store( $message );
$message = R::load( 'message', $id );
asrt( $message->fetchAs( 'person' )->sender->name, 'Sheldon' );
asrt( $message->fetchAs( 'person' )->recipient->name, 'Penny' );
}
/**
* Test Basic Fetch AS functionality.
*/
public function testBasicFetchAs()
{
$project = R::dispense( 'project' );
$project->name = 'Mutant Project';
list( $teacher, $student ) = R::dispense( 'person', 2 );
$teacher->name = 'Charles Xavier';
$project->student = $student;
$project->student->name = 'Wolverine';
$project->teacher = $teacher;
$id = R::store( $project );
$project = R::load( 'project', $id );
asrt( $project->fetchAs( 'person' )->teacher->name, 'Charles Xavier' );
asrt( $project->fetchAs( 'person' )->student->name, 'Wolverine' );
}
/**
* Test Basic list variations.
*
* @return void
*/
public function testBasicListVariations()
{
$farm = R::dispense( 'building' );
$village = R::dispense( 'village' );
$farm->name = 'farm';
$village->name = 'Dusty Mountains';
$farm->village = $village;
$id = R::store( $farm );
$farm = R::load( 'building', $id );
asrt( $farm->name, 'farm' );
asrt( $farm->village->name, 'Dusty Mountains' );
$village = R::dispense( 'village' );
list( $mill, $tavern ) = R::dispense( 'building', 2 );
$mill->name = 'Mill';
$tavern->name = 'Tavern';
$village->ownBuilding = array( $mill, $tavern );
$id = R::store( $village );
$village = R::load( 'village', $id );
asrt( count( $village->ownBuilding ), 2 );
$village2 = R::dispense( 'village' );
$army = R::dispense( 'army' );
$village->sharedArmy[] = $army;
$village2->sharedArmy[] = $army;
$id1 = R::store( $village );
$id2 = R::store( $village2 );
$village1 = R::load( 'village', $id1 );
$village2 = R::load( 'village', $id2 );
asrt( count( $village1->sharedArmy ), 1 );
asrt( count( $village2->sharedArmy ), 1 );
asrt( count( $village1->ownArmy ), 0 );
asrt( count( $village2->ownArmy ), 0 );
}
/**
* Tests whether aliasing plays nice with beautification.
* Ensure that aliased column aren't beautified
*
* @return void
*/
public function testAliasWithBeautify()
{
$points = R::dispense( 'point', 2 );
$line = R::dispense( 'line' );
$line->pointA = $points[0];
$line->pointB = $points[1];
R::store( $line );
$line2 = R::dispense( 'line' );
$line2->pointA = $line->fetchAs('point')->pointA;
$line2->pointB = R::dispense( 'point' );
R::store( $line2 );
//now we have two points per line (1-to-x)
//I want to know which lines cross A:
$a = R::load( 'point', $line->pointA->id ); //reload A
$lines = $a->alias( 'pointA' )->ownLine;
asrt( count( $lines ), 2 );
}
}

View File

@@ -0,0 +1,273 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Arrays
*
* Beans can also be treated like arrays, this test verifies
* whether the bean array interface works correctly in various
* scenarios (combination with lists and so on).
*
* @file RedUNIT/Base/Arrays.php
* @desc Tests the array interface of beans
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Arrays extends Base
{
/**
* Tests basic array access.
*
* @return void
*/
public function testArrayAccess()
{
$bean = R::dispense('bean');
$bean->name = 'bean';
$bean->taste = 'salty';
$properties = array();
foreach($bean as $key => $value) {
$properties[ $key ] = $value;
}
asrt( count( $bean ), 3 );
asrt( count( $properties ), 3 );
asrt( isset( $properties['id'] ), TRUE );
asrt( isset( $properties['name'] ), TRUE );
asrt( isset( $properties['taste'] ), TRUE );
$bean = R::dispense('bean');
$bean['name'] = 'bean';
$bean['taste'] = 'salty';
$properties = array();
foreach($bean as $key => $value) {
$properties[ $key ] = $value;
}
asrt( count( $bean ), 3 );
asrt( count( $properties ), 3 );
asrt( isset( $properties['id'] ), TRUE );
asrt( isset( $properties['name'] ), TRUE );
asrt( isset( $properties['taste'] ), TRUE );
}
/**
* Tests array access with lists.
* Tests whether list properties behave as arrays correctly.
*
* @return void
*/
public function testArrayAccessAndLists()
{
$book = R::dispense('book');
$book['title'] = 'My Book';
//Can we add a bean in list with array access?
$book['ownPage'][] = R::dispense('page');
$book['ownPage'][] = R::dispense('page');
asrt( count( $book ), 3 );
$properties = array();
foreach($book as $key => $value) {
$properties[ $key ] = $value;
}
asrt( count( $properties ), 3 );
//Dont reveal aliased x-own and -List in foreach-loop
asrt( isset( $properties['id'] ), TRUE );
asrt( isset( $properties['title'] ), TRUE );
asrt( isset( $properties['ownPage'] ), TRUE );
asrt( isset( $properties['ownPageList'] ), FALSE );
asrt( isset( $properties['xownPage'] ), FALSE );
asrt( isset( $properties['xownPageList'] ), FALSE );
//But keep them countable
asrt( count( $book['ownPage'] ), 2 );
asrt( count( $book['ownPageList'] ), 2 );
asrt( count( $book['xownPage'] ), 2 );
asrt( count( $book['xownPageList'] ), 2 );
//And reveal state of items with isset()
asrt( isset( $book['id'] ), TRUE );
asrt( isset( $book['title'] ), TRUE );
asrt( isset( $book['ownPage'] ), TRUE );
asrt( isset( $book['ownPageList'] ), TRUE );
asrt( isset( $book['xownPage'] ), TRUE );
asrt( isset( $book['xownPageList'] ), TRUE );
//Can we add using the List alias?
$book['ownPageList'][] = R::dispense('page');
asrt( count( $book['ownPage'] ), 3 );
asrt( count( $book['ownPageList'] ), 3 );
asrt( count( $book['xownPage'] ), 3 );
asrt( count( $book['xownPageList'] ), 3 );
//Can we add using the x-mode alias?
$book['ownPageList'][] = R::dispense('page');
asrt( count( $book['ownPage'] ), 4 );
asrt( count( $book['ownPageList'] ), 4 );
asrt( count( $book['xownPage'] ), 4 );
asrt( count( $book['xownPageList'] ), 4 );
//Can we unset using array access?
unset( $book['ownPage'] );
asrt( isset( $book['ownPage'] ), FALSE );
asrt( isset( $book['ownPageList'] ), FALSE );
asrt( isset( $book['xownPage'] ), FALSE );
asrt( isset( $book['xownPageList'] ), FALSE );
$book['ownPage'] = array( R::dispense('page') );
unset( $book['xownPage'] );
asrt( isset( $book['ownPage'] ), FALSE );
asrt( isset( $book['ownPageList'] ), FALSE );
asrt( isset( $book['xownPage'] ), FALSE );
asrt( isset( $book['xownPageList'] ), FALSE );
$book['ownPage'] = array( R::dispense('page') );
unset( $book['ownPageList'] );
asrt( isset( $book['ownPage'] ), FALSE );
asrt( isset( $book['ownPageList'] ), FALSE );
asrt( isset( $book['xownPage'] ), FALSE );
asrt( isset( $book['xownPageList'] ), FALSE );
$book['ownPage'] = array( R::dispense('page') );
unset( $book['xownPageList'] );
asrt( isset( $book['ownPage'] ), FALSE );
asrt( isset( $book['ownPageList'] ), FALSE );
asrt( isset( $book['xownPage'] ), FALSE );
asrt( isset( $book['xownPageList'] ), FALSE );
//does it work with shared lists as well?
$book['sharedCategory'] = array( R::dispense('category') );
asrt( count( $book ), 3 );
$properties = array();
foreach($book as $key => $value) {
$properties[ $key ] = $value;
}
asrt( isset( $properties['sharedCategory'] ), TRUE );
asrt( isset( $properties['sharedCategoryList'] ), FALSE );
asrt( isset( $book['sharedCategory'] ), TRUE );
asrt( isset( $book['sharedCategoryList'] ), TRUE );
asrt( count( $book['sharedCategory'] ), 1 );
asrt( count( $book['sharedCategoryList'] ), 1 );
$book['sharedCategory'][] = R::dispense( 'category' );
asrt( count( $book['sharedCategory'] ), 2 );
asrt( count( $book['sharedCategoryList'] ), 2 );
$book['sharedCategoryList'][] = R::dispense( 'category' );
asrt( count( $book['sharedCategory'] ), 3 );
asrt( count( $book['sharedCategoryList'] ), 3 );
}
/**
* Tests array access with parent beans.
*
* @return void
*/
public function testArrayAccessWithBeans()
{
$book = R::dispense( 'bean' );
$book['author'] = R::dispense( 'author' );
asrt( isset( $book['author'] ), TRUE );
asrt( count( $book ), 2 );
$book['author']['name'] = 'me';
asrt( $book['author']['name'], 'me' );
$book['author']['address'] = R::dispense( 'address' );
$book['author']['ownTagList'][] = R::dispense( 'tag' );
asrt( isset( $book['author']['address'] ), TRUE );
asrt( isset( $book['author']['ownTag'] ), TRUE );
asrt( count( $book['author']['ownTag'] ), 1 );
asrt( isset( $book['author']['xownTag'] ), TRUE );
asrt( count( $book['author']['xownTag'] ), 1 );
asrt( isset( $book['author']['ownTagList'] ), TRUE );
asrt( count( $book['author']['ownTagList'] ), 1 );
asrt( isset( $book['author']['xownTagList'] ), TRUE );
asrt( count( $book['author']['xownTagList'] ), 1 );
unset( $book['author'] );
asrt( isset( $book['author'] ), FALSE );
asrt( count( $book ), 1 );
}
/**
* Tests array access with CRUD operations.
*
* @return void
*/
public function testArrayAccessWithCRUD()
{
R::nuke();
$book = R::dispense( 'book' );
$book['ownPageList'] = R::dispense( 'page', 3 );
R::store( $book );
$book = $book->fresh();
//note that isset first returns FALSE, so you can check if a list is loaded
asrt( isset( $book['ownPage'] ), FALSE );
asrt( isset( $book['ownPageList'] ), FALSE );
asrt( isset( $book['xownPage'] ), FALSE );
asrt( isset( $book['xownPageList'] ), FALSE );
//count triggers load...
asrt( count( $book['ownPage'] ), 3 );
asrt( isset( $book['ownPage'] ), TRUE );
asrt( isset( $book['ownPageList'] ), TRUE );
asrt( isset( $book['xownPage'] ), TRUE );
asrt( isset( $book['xownPageList'] ), TRUE );
$book = $book->fresh();
asrt( count( $book['xownPage'] ), 3 );
$book = $book->fresh();
asrt( count( $book['ownPageList'] ), 3 );
$book = $book->fresh();
asrt( count( $book['xownPageList'] ), 3 );
$book['ownPage'][] = R::dispense( 'page' );
R::store( $book );
$book = $book->fresh();
asrt( count( $book['ownPage'] ), 4 );
$book = $book->fresh();
asrt( count( $book['xownPage'] ), 4 );
$book = $book->fresh();
asrt( count( $book['ownPageList'] ), 4 );
$book = $book->fresh();
asrt( count( $book['xownPageList'] ), 4 );
//does dependency still work?
$book['xownPageList'] = array();
R::store( $book );
$book = $book->fresh();
asrt( count( $book['ownPage'] ), 0 );
$book = $book->fresh();
asrt( count( $book['xownPage'] ), 0 );
$book = $book->fresh();
asrt( count( $book['ownPageList'] ), 0 );
$book = $book->fresh();
asrt( count( $book['xownPageList'] ), 0 );
//does shared list work as well?
$book['sharedTag'] = R::dispense( 'tag', 2 );
R::store( $book );
$book = $book->fresh();
//note that isset first returns FALSE, so you can check if a list is loaded
asrt( isset( $book['sharedTagList'] ), FALSE );
asrt( isset( $book['sharedTag'] ), FALSE );
//count triggers load...
asrt( count( $book['sharedTagList'] ), 2 );
asrt( count( $book['sharedTag'] ), 2 );
asrt( isset( $book['sharedTagList'] ), TRUE );
asrt( isset( $book['sharedTag'] ), TRUE );
$book['sharedTag'][] = R::dispense( 'tag' );
R::store( $book );
$book = $book->fresh();
asrt( count( $book['sharedTagList'] ), 3 );
asrt( count( $book['sharedTag'] ), 3 );
$book['sharedTagList'][] = R::dispense( 'tag' );
R::store( $book );
$book = $book->fresh();
asrt( count( $book['sharedTagList'] ), 4 );
asrt( count( $book['sharedTag'] ), 4 );
//does it also work with cross-shared
$book['sharedBookList'][] = R::dispense( 'book' );
R::store( $book );
$book = $book->fresh();
asrt( isset( $book['sharedBookList'] ), FALSE );
asrt( count( $book['sharedBookList'] ), 1 );
$first = reset( $book['sharedBookList'] );
$id = $first['id'];
asrt( count( $book['sharedBookList'][$id]['sharedBookList'] ), 1 );
$properties = array();
foreach($book as $key => $value) {
$properties[ $key ] = $value;
}
asrt( count( $properties ), 2 );
$keys = array_keys( $properties );
sort( $keys );
asrt( implode( ',', $keys ), 'id,sharedBook' );
}
}

View File

@@ -0,0 +1,296 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\AssociationManager as AssociationManager;
use RedBeanPHP\RedException\SQL as SQL;
/**
* Association
*
* Originally meant to test R::associate - which is no longer
* used, this class tests all kinds of relations from
* one-to-one to polymorph relations using the poly() method.
*
* @file RedUNIT/Base/Association.php
* @desc Tests Association API (N:N associations)
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Association extends Base
{
/**
* MySQL specific tests.
* Test MySQL specific issues with constraints.
*
* @return void
*/
public function testMySQL()
{
if ( $this->currentlyActiveDriverID !== 'mysql' ) {
return;
}
testpack( 'Throw exception in case of issue with assoc constraint' );
$bunny = R::dispense( 'bunny' );
$carrot = R::dispense( 'carrot' );
$faultyWriter = new \FaultyWriter( R::getToolBox()->getDatabaseAdapter() );
$faultyOODB = new OODB( $faultyWriter );
$faultyOODB->setBeanHelper( R::getRedBean()->getBeanHelper() );
$faultyToolbox = new ToolBox( $faultyOODB, R::getToolBox()->getDatabaseAdapter(), $faultyWriter );
$faultyAssociationManager = new AssociationManager( $faultyToolbox );
$faultyWriter->setSQLState( '23000' );
$faultyAssociationManager->associate( $bunny, $carrot );
pass();
$faultyWriter->setSQLState( '42S22' );
R::nuke();
try {
$faultyAssociationManager->associate( $bunny, $carrot );
fail();
} catch ( SQL $exception ) {
pass();
}
}
/**
* Test fast-track deletion, i.e. bypassing FUSE.
* For link beans.
*
* @return void
*/
public function testFastTrackDeletion()
{
testpack( 'Test fast-track deletion' );
$ghost = R::dispense( 'ghost' );
$house = R::dispense( 'house' );
$house->sharedGhost[] = $ghost;
R::store($house);
\Model_Ghost_House::$deleted = FALSE;
R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost );
// No fast-track, assoc bean got trashed
asrt( \Model_Ghost_House::$deleted, TRUE );
\Model_Ghost_House::$deleted = FALSE;
$house->sharedGhost[] = $ghost;
R::store($house);
R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost, TRUE );
// Fast-track, assoc bean got deleted right away
asrt( \Model_Ghost_House::$deleted, FALSE );
}
/**
* Test self-referential associations.
*
* @return void
*/
public function testCrossAssociation()
{
$ghost = R::dispense( 'ghost' );
$ghost2 = R::dispense( 'ghost' );
R::getRedBean()->getAssociationManager()->associate( $ghost, $ghost2 );
\Model_Ghost_Ghost::$deleted = FALSE;
R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2 );
// No fast-track, assoc bean got trashed
asrt( \Model_Ghost_Ghost::$deleted, TRUE );
\Model_Ghost_Ghost::$deleted = FALSE;
R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2, TRUE );
// Fast-track, assoc bean got deleted right away
asrt( \Model_Ghost_Ghost::$deleted, FALSE );
}
/**
* Test limited support for polymorph associations.
* RedBeanPHP does not really feature polymorph relations since
* they are not really compatible with traditional relational databases.
* However a light-weight, basic implementation has been added for
* those circumstances where you can't live without...
* i.e... possible legacy systems and so on.
*
* @return void
*/
public function testPoly()
{
testpack( 'Testing poly' );
$shoe = R::dispense( 'shoe' );
$lace = R::dispense( 'lace' );
$lace->color = 'white';
$id = R::store( $lace );
$shoe->itemType = 'lace';
$shoe->item = $lace;
$id = R::store( $shoe );
$shoe = R::load( 'shoe', $id );
$x = $shoe->poly( 'itemType' )->item;
asrt( $x->color, 'white' );
}
/**
* Test limited support for 1-to-1 associations.
* The rule is, one-to-ones are supposes to be in the same table,
* this is just for some legacy tables not designed to work
* with RedBeanPHP at all.
*
* @return void
*/
public function testOneToOne()
{
testpack( 'Testing one-to-ones' );
$author = R::dispense( 'author' )->setAttr( 'name', 'a' );;
$bio = R::dispense( 'bio' )->setAttr( 'name', 'a' );
R::storeAll( array( $author, $bio ) );
$id1 = $author->id;
$author = R::dispense( 'author' )->setAttr( 'name', 'b' );;
$bio = R::dispense( 'bio' )->setAttr( 'name', 'b' );
R::storeAll( array( $author, $bio ) );
$x = $author->one( 'bio' );
$y = $bio->one('author');
asrt( $x->name, $bio->name );
asrt( $y->name, $author->name );
asrt( $x->id, $bio->id );
asrt( $y->id, $author->id );
$id2 = $author->id;
list( $a, $b ) = R::loadMulti( 'author,bio', $id1 );
asrt( $a->name, $b->name );
asrt( $a->name, 'a' );
list( $a, $b ) = R::loadMulti( 'author,bio', $id2 );
asrt( $a->name, $b->name );
asrt( $a->name, 'b' );
list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id1 );
asrt( $a->name, $b->name );
asrt( $a->name, 'a' );
list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id2 );
asrt( $a->name, $b->name );
asrt( $a->name, 'b' );
asrt( is_array( R::loadMulti( NULL, 1 ) ), TRUE );
asrt( ( count( R::loadMulti( NULL, 1 ) ) === 0 ), TRUE );
}
/**
* Test single column bases unique constraints.
*
* @return void
*/
public function testSingleColUniqueConstraint()
{
testpack( 'Testing unique constraint on single column' );
$book = R::dispense( 'book' );
$book->title = 'bla';
$book->extra = 2;
$id = R::store( $book );
R::getWriter()->addUniqueIndex( 'book', array( 'title' ) );
$book = R::dispense( 'book' );
$book->title = 'bla';
$expected = NULL;
try {
R::store( $book );
fail();
} catch ( SQL $e ) {
$expected = $e;
}
asrt( ( $expected instanceof SQL ), TRUE );
asrt( R::count( 'book' ), 1 );
$book = R::load( 'book', $id );
// Causes failure, table will be rebuild
$book->extra = 'CHANGE';
$id2 = R::store( $book );
$book2 = R::load( 'book', $id2 );
$book = R::dispense( 'book' );
$book->title = 'bla';
try {
R::store( $book );
fail();
} catch ( SQL $e ) {
$expected = $e;
}
asrt( ( $expected instanceof SQL ), TRUE );
asrt( R::count( 'book' ), 1 );
}
/**
* Test multiple assiociation.
*
* @return void
*/
public function testMultiAssociationDissociation()
{
$wines = R::dispense( 'wine', 3 );
$cheese = R::dispense( 'cheese', 3 );
$olives = R::dispense( 'olive', 3 );
R::getRedBean()->getAssociationManager()->associate( $wines, array_merge( $cheese, $olives ) );
asrt( R::count( 'cheese' ), 3 );
asrt( R::count( 'olive' ), 3 );
asrt( R::count( 'wine' ), 3 );
asrt( count( $wines[0]->sharedCheese ), 3 );
asrt( count( $wines[0]->sharedOlive ), 3 );
asrt( count( $wines[1]->sharedCheese ), 3 );
asrt( count( $wines[1]->sharedOlive ), 3 );
asrt( count( $wines[2]->sharedCheese ), 3 );
asrt( count( $wines[2]->sharedOlive ), 3 );
R::getRedBean()->getAssociationManager()->unassociate( $wines, $olives );
asrt( count( $wines[0]->sharedCheese ), 3 );
asrt( count( $wines[0]->sharedOlive ), 0 );
asrt( count( $wines[1]->sharedCheese ), 3 );
asrt( count( $wines[1]->sharedOlive ), 0 );
asrt( count( $wines[2]->sharedCheese ), 3 );
asrt( count( $wines[2]->sharedOlive ), 0 );
R::getRedBean()->getAssociationManager()->unassociate( array( $wines[1] ), $cheese );
asrt( count( $wines[0]->sharedCheese ), 3 );
asrt( count( $wines[0]->sharedOlive ), 0 );
asrt( count( $wines[1]->sharedCheese ), 0 );
asrt( count( $wines[1]->sharedOlive ), 0 );
asrt( count( $wines[2]->sharedCheese ), 3 );
asrt( count( $wines[2]->sharedOlive ), 0 );
R::getRedBean()->getAssociationManager()->unassociate( array( $wines[2] ), $cheese );
asrt( count( $wines[0]->sharedCheese ), 3 );
asrt( count( $wines[0]->sharedOlive ), 0 );
asrt( count( $wines[1]->sharedCheese ), 0 );
asrt( count( $wines[1]->sharedOlive ), 0 );
asrt( count( $wines[2]->sharedCheese ), 0 );
asrt( count( $wines[2]->sharedOlive ), 0 );
}
/**
* Tests error handling related to association.
* On database systems providing informative SQL STATE error codes
* RedBeanPHP should not mind non-existing tables or columns in
* fluid mode.
*
* @return void
*/
public function testErrorHandling()
{
R::nuke();
list( $book, $page ) = R::dispenseAll( 'book,page' );
$book->sharedPage[] = $page;
R::store( $page );
$redbean = R::getRedBean();
$am = $redbean->getAssociationManager();
//SQLite and CUBRID do not comply with ANSI SQLState codes.
$catchAll = ( $this->currentlyActiveDriverID == 'sqlite' || $this->currentlyActiveDriverID === 'CUBRID' );
try {
$am->related( $book, 'page', 'invalid SQL' );
if ($catchAll) pass(); else fail();
} catch ( SQL $e ) {
if ($catchAll) fail(); else pass();
}
try {
$am->related( $book, 'cover');
pass();
} catch ( SQL $e ) {
fail();
}
try {
$am->related( R::dispense('cover'), 'book' );
pass();
} catch ( SQL $e ) {
fail();
}
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\RedException\SQL as SQL;
/**
* Batch
*
* Test whether we can load a group of beans in one go, i.e.
* the batch loading functionality of RedBeanPHP, also in combination
* with aliasing.
*
* @file RedUNIT/Base/Batch.php
* @desc Tests batch loading of beans, i.e. loading large collections of beans in optimized way.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Batch extends Base
{
/**
* Can we delete beans by find-query?
*
* @return void
*/
public function testHunt()
{
R::nuke();
$books = R::dispenseAll( 'book*3' );
R::storeAll( $books[0] );
pass();
asrt( ( R::count( 'book' ) === 3 ), TRUE );
$ids = R::getCol( 'SELECT id FROM book' );
R::hunt( 'book', ' id IN ( '. R::genSlots( $ids ) .' ) ', $ids );
asrt( ( R::count( 'book' ) === 0 ), TRUE );
}
/**
* Tests batch trashing. Can we trash beans using
* IDs only?
*
* @return void
*/
public function testBatchTrash()
{
R::nuke();
$books = R::dispenseAll( 'book*3' );
R::storeAll( $books[0] );
pass();
asrt( ( R::count( 'book' ) === 3 ), TRUE );
R::trashBatch( 'book', R::getCol( 'SELECT id FROM book' ) );
asrt( ( R::count( 'book' ) === 0 ), TRUE );
}
/**
* Begin testing.
* This method runs the actual test pack.
*
* @return void
*/
public function testBatch()
{
R::freeze( FALSE );
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
$pdo = $adapter->getDatabase();
$page = $redbean->dispense( "page" );
$page->name = "page no. 1";
$page->rating = 1;
$id1 = $redbean->store( $page );
$page = $redbean->dispense( "page" );
$page->name = "page no. 2";
$id2 = $redbean->store( $page );
$batch = $redbean->batch( "page", array( $id1, $id2 ) );
asrt( count( $batch ), 2 );
asrt( $batch[$id1]->getMeta( "type" ), "page" );
asrt( $batch[$id2]->getMeta( "type" ), "page" );
asrt( (int) $batch[$id1]->id, $id1 );
asrt( (int) $batch[$id2]->id, $id2 );
$book = $redbean->dispense( "book" );
$book->name = "book 1";
$redbean->store( $book );
$book = $redbean->dispense( "book" );
$book->name = "book 2";
$redbean->store( $book );
$book = $redbean->dispense( "book" );
$book->name = "book 3";
$redbean->store( $book );
$books = $redbean->batch( "book", $adapter->getCol( "SELECT id FROM book" ) );
asrt( count( $books ), 3 );
$a = $redbean->batch( 'book', 9919 );
asrt( is_array( $a ), TRUE );
asrt( count( $a ), 0 );
$a = $redbean->batch( 'triangle', 1 );
asrt( is_array( $a ), TRUE );
asrt( count( $a ), 0 );
R::freeze( TRUE );
$a = $redbean->batch( 'book', 9919 );
asrt( is_array( $a ), TRUE );
asrt( count( $a ), 0 );
try {
$a = $redbean->batch( 'triangle', 1 );
fail();
} catch(SQL $e) {
pass();
}
R::freeze( FALSE );
asrt( R::wipe( 'spaghettimonster' ), FALSE );
}
/**
* Test missing bean scenarios.
*
* @return void
*/
public function testMissingBeans()
{
testpack( 'deal with missing beans' );
$id = R::store( R::dispense( 'beer' ) );
$bottles = R::batch( 'beer', array( $id, $id + 1, $id + 2 ) );
asrt( count( $bottles ), 3 );
asrt( (int) $bottles[$id]->id, (int) $id );
asrt( (int) $bottles[$id + 1]->id, 0 );
asrt( (int) $bottles[$id + 2]->id, 0 );
}
/**
* Test batch alias loadAll.
*
* @return void
*/
public function testBatchAliasLoadAll()
{
$ids = R::storeAll( R::dispense( 'page', 2 ) );
$pages = R::loadAll( 'page', $ids );
asrt( is_array( $pages ), TRUE );
asrt( count( $pages ), 2 );
asrt( ( $pages[$ids[0]] instanceof OODBBean ), TRUE );
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\SimpleModel as SimpleModel;
/**
* Boxing
*
* Test boxing and unboxing of beans.
*
* @file RedUNIT/Base/Boxing.php
* @desc Tests bean boxing and unboxing functionality.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Boxing extends Base
{
/**
* Test boxing beans.
*
* @return void
*/
public function testBoxing()
{
R::nuke();
$bean = R::dispense( 'boxedbean' )->box();
R::trash( $bean );
pass();
$bean = R::dispense( 'boxedbean' );
$bean->sharedBoxbean = R::dispense( 'boxedbean' )->box();
R::store( $bean );
pass();
$bean = R::dispense( 'boxedbean' );
$bean->ownBoxedbean = R::dispense( 'boxedbean' )->box();
R::store( $bean );
pass();
$bean = R::dispense( 'boxedbean' );
$bean->other = R::dispense( 'boxedbean' )->box();
R::store( $bean );
pass();
$bean = R::dispense( 'boxedbean' );
$bean->title = 'MyBean';
$box = $bean->box();
asrt( ( $box instanceof \Model_Boxedbean ), TRUE );
R::store( $box );
}
/**
* Test fix for issue #512 - thanks for reporting Bernhard H.
* OODBBean::__toString() implementation only works with C_ERR_IGNORE
*
* @return void
*/
public function testToStringIssue512()
{
R::setErrorHandlingFUSE( \RedBeanPHP\OODBBean::C_ERR_FATAL );
$boxedBean = R::dispense( 'boxedbean' );
$str = (string) $boxedBean;
asrt( $str, '{"id":0}' ); //no fatal error
R::setErrorHandlingFUSE( FALSE );
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Chill
*
* Tests 'chill' mode. In this mode some bean types are frozen,
* their schemas cannot be modified while others are fluid and
* can still be adjusted.
*
* @file RedUNIT/Base/Chill.php
* @desc Tests chill list functionality, i.e. freezing a subset of all types.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Chill extends Base
{
/**
* Test Chill mode.
*
* @return void
*/
public function testChill()
{
$bean = R::dispense( 'bean' );
$bean->col1 = '1';
$bean->col2 = '2';
R::store( $bean );
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 3 );
$bean->col3 = '3';
R::store( $bean );
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 4 );
R::freeze( array( 'umbrella' ) );
$bean->col4 = '4';
R::store( $bean );
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 );
R::freeze( array( 'bean' ) );
$bean->col5 = '5';
try {
R::store( $bean );
fail();
} catch (\Exception $e ) {
pass();
}
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 5 );
R::freeze( array() );
$bean->col5 = '5';
R::store( $bean );
asrt( count( R::getWriter()->getColumns( 'bean' ) ), 6 );
}
/**
* Test whether we cannot add unique constraints on chilled tables,
* otherwise you cannot avoid this from happening when adding beans to the
* shared list :) -- this is almost a theoretical issue however we want it
* to work according to specifications!
*
* @return void
*/
public function testDontAddUniqueConstraintForChilledBeanTypes()
{
R::nuke();
$person = R::dispense( 'person' );
$role = R::dispense( 'role' );
$person->sharedRole[] = $role;
R::store( $person );
$person->sharedRole[] = R::dispense( 'role' );
R::store( $person );
$bean = R::getRedBean()->dispense('person_role');
$bean->personId = $person->id;
$bean->roleId = $role->id;
try {
R::store( $bean );
fail();
} catch(\Exception $e) {
pass();
}
asrt(R::count('person_role'), 2);
R::nuke();
$link = R::getRedBean()->dispense('person_role');
$person = R::dispense( 'person' );
$role = R::dispense( 'role' );
$link->person = $person;
$link->role = $role;
R::store( $link );
R::freeze(array('person_role'));
$person->sharedRole[] = R::dispense( 'role' );
R::store( $person );
$bean = R::getRedBean()->dispense('person_role');
$bean->personId = $person->id;
$bean->roleId = $role->id;
try {
R::store( $bean );
pass();
} catch(\Exception $e) {
fail();
}
asrt(R::count('person_role'), 3);
R::freeze( array() ); //set freeze to FALSE and clear CHILL LIST!
}
/**
* Test whether we can set and reset the chill list and check the contents
* of the chill list.
*
* @return void
*/
public function testChillTest()
{
R::freeze( array( 'beer' ) );
$oodb = R::getRedBean();
asrt( $oodb->isChilled( 'beer' ), TRUE );
asrt( $oodb->isChilled( 'wine' ), FALSE );
R::freeze( FALSE );
$oodb = R::getRedBean();
asrt( $oodb->isChilled( 'beer' ), TRUE );
asrt( $oodb->isChilled( 'wine' ), FALSE );
R::freeze( TRUE );
$oodb = R::getRedBean();
asrt( $oodb->isChilled( 'beer' ), TRUE );
asrt( $oodb->isChilled( 'wine' ), FALSE );
R::freeze( array() );
$oodb = R::getRedBean();
asrt( $oodb->isChilled( 'beer' ), FALSE );
asrt( $oodb->isChilled( 'wine' ), FALSE );
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\QueryWriter\SQLiteT as SQLiteT;
/**
* Close
*
* Tests whether we can close the database connection.
*
* @file RedUNIT/Base/Close.php
* @desc Tests database closing functionality.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Close extends Base
{
/**
* Test closing database connection.
*
* @return void
*/
public function testClose()
{
// Test whether we can select a specific feature set
R::useFeatureSet('novice/latest');
pass();
R::useFeatureSet('latest');
pass();
R::useFeatureSet('5.3');
pass();
R::useFeatureSet('novice/5.3');
pass();
try {
R::useFeatureSet('5.2');
fail();
} catch ( \Exception $e ) {
asrt( $e->getMessage(), 'Unknown feature set label.' );
}
try {
R::nuke();
fail();
} catch( \Exception $e ) {
asrt( $e->getMessage(), 'The nuke() command has been disabled using noNuke() or R::feature(novice/...).' );
}
R::useFeatureSet('latest');
//Close
R::getDatabaseAdapter()->setOption( 'setInitQuery', NULL );
asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), TRUE );
R::close();
asrt( R::getDatabaseAdapter()->getDatabase()->isConnected(), FALSE );
}
}

View File

@@ -0,0 +1,314 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\QueryWriter\AQueryWriter;
/**
* Concurrency
*
* Tests whether we can lock beans.
*
* @file RedUNIT/Base/Concurrency.php
* @desc Tests concurrency scenarios
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Concurrency extends Base
{
/**
* Returns the target drivers for this test.
* This test only works with Postgres and MySQL/MariaDB.
*
* @return array
*/
public function getTargetDrivers()
{
return array( 'pgsql','mysql' );
}
/**
* Prepares the database connection.
*
* @return void
*/
public function prepare()
{
try{
R::close();
} catch( \Exception $e ) {}
}
/**
* This test has to be run manually.
*
* @return void
*/
private function testLockException()
{
R::nuke();
$lock = R::dispense('lock');
$lock->value = 123;
R::store($lock);
$c = pcntl_fork();
if ($c == -1) exit(1);
if (!$c) {
R::selectDatabase($this->currentlyActiveDriverID . 'c2');
R::freeze(TRUE);
try { R::exec('SET SESSION innodb_lock_wait_timeout=5');}catch( \Exception $e ){}
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
R::begin();
$lock = R::loadForUpdate('lock', $lock->id);
$lock->value = 4;
sleep(10);
R::store($lock);
exit(0);
} else {
R::selectDatabase($this->currentlyActiveDriverID . 'c2');
sleep(1);
R::freeze(TRUE);
try{ R::exec('SET SESSION innodb_lock_wait_timeout=5');}catch( \Exception $e ){}
try{R::exec("SET lock_timeout = '1s';");}catch( \Exception $e ){}
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
R::begin();
$exception = NULL;
try {
$lock = R::loadForUpdate('lock', $lock->id);
} catch( \Exception $e ) {
$exception = $e;
}
if ( !$exception ) fail();
pass();
$details = $exception->getDriverDetails();
asrt( ($details[1]===1205 || $details[0]==='55P03'), TRUE );
var_dump($lock);
}
try { R::exec('SET autocommit = 1'); }catch( \Exception $e ){}
pcntl_wait($status);
try { R::exec('SET SESSION innodb_lock_wait_timeout=50');}catch( \Exception $e ){}
try{R::exec("SET lock_timeout = '50s';");}catch( \Exception $e ){}
}
/**
* Tests basic locking scenarios using fork().
*
* @return void
*/
public function testConcurrency()
{
$c = pcntl_fork();
if ($c == -1) exit(1);
if (!$c) {
R::selectDatabase($this->currentlyActiveDriverID . 'c');
try{ R::exec('SET SESSION innodb_lock_wait_timeout=51');}catch( \Exception $e ){}
try{R::exec("SET lock_timeout = '51s';");}catch( \Exception $e ){}
R::exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
sleep(2);
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
R::freeze(TRUE);
R::begin();
echo "CHILD: SUBTRACTING 2 START\n";
$i = R::loadForUpdate('inventory', 1);
$i->apples -= 2;
sleep(4);
R::store($i);
R::commit();
echo "CHILD: SUBTRACTING 2 DONE\n";
echo (R::load('inventory', 1));
echo "\n";
exit(0);
} else {
R::selectDatabase($this->currentlyActiveDriverID . 'c');
try{ R::exec('SET SESSION innodb_lock_wait_timeout=51');}catch( \Exception $e ){}
try{R::exec("SET lock_timeout = '51s';");}catch( \Exception $e ){}
echo "PARENT: PREP START\n";
R::nuke();
$i = R::dispense('inventory');
$i->apples = 10;
R::store($i);
R::exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
echo "PARENT: PREP DONE\n";
sleep(3);
echo "PARENT: ADDING 5 START\n";
try { R::exec('SET autocommit = 0'); }catch( \Exception $e ){}
R::freeze( TRUE );
R::begin();
$i = R::findForUpdate('inventory', ' id = ? ', array(1));
$i = reset( $i );
print_r($i);
$i->apples += 5;
R::store($i);
R::commit();
echo "PARENT ADDING 5 DONE\n";
$i = R::getAll('select * from inventory where id = 1');
print_r($i);
asrt((int)$i[0]['apples'], 13);
R::freeze( FALSE );
try { R::exec('SET autocommit = 1'); }catch( \Exception $e ){}
pcntl_wait($status);
}
}
/**
* Test whether we can use setSQLSnippet with find() and batch().
*
* @return void
*/
public function testBatchAndFind()
{
/* baseline */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::batch( 'bean', $ids );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 0 );
$logs->clear();
/* findOneForUpdate */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::findOneForUpdate( 'bean' );
asrt( count( $beans ), 1 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 0 );
$logs->clear();
/* findForUpdate */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::findForUpdate( 'bean' );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 0 );
$logs->clear();
/* batch + snippet */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
R::getWriter()->setSQLSelectSnippet('for update');
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::batch( 'bean', $ids );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 1 );
print_r( $entries );
$logs->clear();
/* baseline */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::batch( 'bean', $ids );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 0 );
$logs->clear();
/* find + snippet */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
R::getWriter()->setSQLSelectSnippet('for update');
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::find( 'bean' );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 1 );
print_r( $entries );
$logs->clear();
/* baseline */
R::nuke();
$beans = R::dispenseAll('bean*10');
R::storeAll($beans[0]);
$ids = array();
foreach($beans[0] as $bean) $ids[] = $bean->id;
R::debug( TRUE, 1 );
$logs = R::getDatabaseAdapter()
->getDatabase()
->getLogger();
$beans = R::batch( 'bean', $ids );
asrt( count( $beans ), 10 );
$entries = $logs->grep('for update');
asrt( count( $entries ), 0 );
$logs->clear();
R::debug( FALSE );
}
/**
* loadForUpdate/findForUpdate should be applied even if caching is on.
* Caching may not interfere with locking beans.
*
* @return void
*/
public function testLockAndCache()
{
R::nuke();
$bean = R::dispense('lock');
$bean->title = 'lock';
$id = R::store( $bean );
R::getWriter()->setUseCache( TRUE );
$lock = R::loadForUpdate( 'lock', $id );
R::debug( TRUE, 1 );
$lock = R::loadForUpdate( 'lock', $id );
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
asrt( count($logs), 1 ); //if no cache clear, then would have been 2
R::debug( FALSE );
$lock = R::findForUpdate( 'lock', 'id = ?', array( $id ) );
R::debug( TRUE, 1 );
$lock = R::findForUpdate( 'lock', 'id = ?', array( $id ) );
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( AQueryWriter::C_SELECT_SNIPPET_FOR_UPDATE );
asrt( count($logs), 1 ); //if no cache clear, then would have been 2
R::getWriter()->setUseCache( FALSE );
R::debug( FALSE );
R::nuke();
}
}

View File

@@ -0,0 +1,224 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Copy
*
* Tests whether we can make a copy or a deep copy of a bean
* and whether recursion is handled well. Also tests
* versioning: copying can be used to implement a versioning feature,
* some test cases will reflect this particular use case.
*
* @file RedUNIT/Base/Copy.php
* @desc Tests whether we can make a deep copy of a bean.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Copy extends Base
{
/**
* Test whether recursion happens
*/
public function testCopyRecursion()
{
$document = R::dispense( 'document' );
$id = R::store( $document );
$document->ownDocument[] = $document;
R::store( $document );
$duplicate = R::dup( $document );
pass(); //if RB cant handle this is will crash (nesting level error from PHP).
$id2 = R::store( $duplicate );
$duplicate = R::load( 'document', $id );
asrt( (int) $document->document_id, $id );
asrt( (int) $duplicate->document_id, $id2 );
// Export variant
$duplicate = R::exportAll( $document );
asrt( (int) $duplicate[0]['document_id'], $id );
}
/**
* Test real world scenario: Versioning
*/
public function testVersioning()
{
$document = R::dispense( 'document' );
$page = R::dispense( 'page' );
$document->title = 'test';
$page->content = 'lorem ipsum';
$user = R::dispense( 'user' );
$user->name = 'Leo';
$document->sharedUser[] = $user;
$document->ownPage[] = $page;
$document->starship_id = 3;
$document->planet = R::dispense( 'planet' );
R::store( $document );
$duplicate = R::dup( $document );
R::store( $duplicate );
$duplicate = R::dup( $document );
R::store( $duplicate );
asrt( R::count( 'planet' ), 1 );
asrt( R::count( 'user' ), 1 );
asrt( R::count( 'document' ), 3 );
asrt( R::count( 'page' ), 3 );
asrt( R::count( 'spaceship' ), 0 );
}
/**
* Same as above but now with intermediate save, counts must be same
*/
public function testVersioningIntermediateSaves()
{
$document = R::dispense( 'document' );
$page = R::dispense( 'page' );
$document->title = 'test';
$page->content = 'lorem ipsum';
$user = R::dispense( 'user' );
$user->name = 'Leo';
$document->sharedUser[] = $user;
$document->ownPage[] = $page;
$document->starship_id = 3;
$document->planet = R::dispense( 'planet' );
R::store( $document );
$duplicate = R::dup( $document );
R::store( $document );
R::store( $duplicate );
R::store( $document );
$duplicate = R::dup( $document );
R::store( $document );
R::store( $duplicate );
asrt( R::count( 'planet' ), 1 );
asrt( R::count( 'user' ), 1 );
asrt( R::count( 'document' ), 3 );
asrt( R::count( 'page' ), 3 );
asrt( R::count( 'spaceship' ), 0 );
// same, but now with intermediate save, counts must be same
R::freeze( TRUE );
$document = R::dispense( 'document' );
$page = R::dispense( 'page' );
$document->title = 'test';
$page->content = 'lorem ipsum';
$user = R::dispense( 'user' );
$user->name = 'Leo';
$document->sharedUser[] = $user;
$document->ownPage[] = $page;
$document->starship_id = 3;
$document->planet = R::dispense( 'planet' );
R::store( $document );
$duplicate = R::dup( $document );
R::store( $document );
R::store( $duplicate );
R::store( $document );
$duplicate = R::dup( $document );
R::store( $document );
R::store( $duplicate );
asrt( R::count( 'planet' ), 2 );
asrt( R::count( 'user' ), 2 );
asrt( R::count( 'document' ), 6 );
asrt( R::count( 'page' ), 6 );
try { asrt( R::count( 'spaceship' ), 0 ); }catch(\Exception $e){pass();}
R::freeze( FALSE );
}
/**
* Test Recursion
*/
public function testRecursion()
{
list( $d1, $d2 ) = R::dispense( 'document', 2 );
$page = R::dispense( 'page' );
list( $p1, $p2 ) = R::dispense( 'paragraph', 2 );
list( $e1, $e2 ) = R::dispense( 'excerpt', 2 );
$id2 = R::store( $d2 );
$p1->name = 'a';
$p2->name = 'b';
$page->title = 'my page';
$page->ownParagraph = array( $p1, $p2 );
$p1->ownExcerpt[] = $e1;
$p2->ownExcerpt[] = $e2;
$e1->ownDocument[] = $d2;
$e2->ownDocument[] = $d1;
$d1->ownPage[] = $page;
$id1 = R::store( $d1 );
$d1 = R::load( 'document', $id1 );
$d = R::dup( $d1 );
$ids = array();
asrt( ( $d instanceof OODBBean ), TRUE );
asrt( count( $d->ownPage ), 1 );
foreach ( end( $d->ownPage )->ownParagraph as $p ) {
foreach ( $p->ownExcerpt as $e ) {
$ids[] = end( $e->ownDocument )->id;
}
}
sort( $ids );
asrt( (int) $ids[0], 0 );
asrt( (int) $ids[1], $id1 );
R::store( $d );
pass();
$phillies = R::dispense( 'diner' );
list( $lonelyman, $man, $woman ) = R::dispense( 'guest', 3 );
$attendant = R::dispense( 'employee' );
$lonelyman->name = 'Bennie Moten';
$man->name = 'Daddy Stovepipe';
$woman->name = 'Mississippi Sarah';
$attendant->name = 'Gus Cannon';
$phillies->sharedGuest = array( $lonelyman, $man, $woman );
$phillies->ownEmployee[] = $attendant;
$props = R::dispense( 'prop', 2 );
$props[0]->kind = 'cigarette';
$props[1]->kind = 'coffee';
$thought = R::dispense( 'thought' );
$thought->content = 'Blues';
$thought2 = R::dispense( 'thought' );
$thought2->content = 'Jazz';
$woman->ownProp[] = $props[0];
$man->sharedProp[] = $props[1];
$attendant->ownThought = array( $thought, $thought2 );
R::store( $phillies );
$diner = R::findOne( 'diner' );
$diner2 = R::dup( $diner );
$id2 = R::store( $diner2 );
$diner2 = R::load( 'diner', $id2 );
asrt( count( $diner->ownEmployee ), 1 );
asrt( count( $diner2->ownEmployee ), 1 );
asrt( count( $diner->sharedGuest ), 3 );
asrt( count( $diner2->sharedGuest ), 3 );
$employee = reset( $diner->ownEmployee );
asrt( count( $employee->ownThought ), 2 );
$employee = reset( $diner2->ownEmployee );
asrt( count( $employee->ownThought ), 2 );
// Can we change something in the duplicate without changing the original?
$employee->name = 'Marvin';
$thought = R::dispense( 'thought' );
$thought->content = 'depression';
$employee->ownThought[] = $thought;
array_pop( $diner2->sharedGuest );
$guest = reset( $diner2->sharedGuest );
$guest->name = 'Arthur Dent';
$id2 = R::store( $diner2 );
$diner2 = R::load( 'diner', $id2 );
asrt( count( $diner->ownEmployee ), 1 );
asrt( count( $diner2->ownEmployee ), 1 );
asrt( count( $diner->sharedGuest ), 3 );
asrt( count( $diner2->sharedGuest ), 2 );
$employeeOld = reset( $diner->ownEmployee );
asrt( count( $employeeOld->ownThought ), 2 );
$employee = reset( $diner2->ownEmployee );
asrt( count( $employee->ownThought ), 3 );
asrt( $employee->name, 'Marvin' );
asrt( $employeeOld->name, 'Gus Cannon' );
// However the shared beans must not be copied
asrt( R::count( 'guest' ), 3 );
asrt( R::count( 'guest_prop' ), 1 );
$arthur = R::findOne( 'guest', ' ' . R::getWriter()->esc( 'name' ) . ' = ? ', array( 'Arthur Dent' ) );
asrt( $arthur->name, 'Arthur Dent' );
}
}

View File

@@ -0,0 +1,226 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\RedException\SQL as SQL;
use RedBeanPHP\OODBBean;
/**
* Count
*
* Tests whether we can count beans with or without
* additional conditions and whether we can count associated
* beans (relationCount).
*
* @file RedUNIT/Base/Count.php
* @desc Tests for simple bean counting.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Count extends Base
{
/**
* Tests type check and conversion in
* OODB for count().
*
* @return void
*/
public function testCountType()
{
R::nuke();
$book = R::dispense( 'book' );
$book->sharedPage = R::dispense( 'page', 10 );
R::store( $book );
asrt( R::count('bookPage'), 10 );
try {
R::count( 'WrongTypeName' );
fail();
} catch ( RedException $ex ) {
pass();
}
try {
R::count( 'wrong_type_name' );
fail();
} catch ( RedException $ex ) {
pass();
}
}
/**
* Test count and wipe.
*
* @return void
*/
public function testCountAndWipe()
{
testpack( "Test count and wipe" );
$page = R::dispense( "page" );
$page->name = "ABC";
R::store( $page );
$n1 = R::count( "page" );
$page = R::dispense( "page" );
$page->name = "DEF";
R::store( $page );
$n2 = R::count( "page" );
asrt( $n1 + 1, $n2 );
R::wipe( "page" );
asrt( R::count( "page" ), 0 );
asrt( R::getRedBean()->count( "page" ), 0 );
asrt( R::getRedBean()->count( "kazoo" ), 0 ); // non existing table
R::freeze( TRUE );
try {
asrt( R::getRedBean()->count( "kazoo" ), 0 ); // non existing table
fail();
} catch( \Exception $e ) {
pass();
}
R::freeze( FALSE );
$page = R::dispense( 'page' );
$page->name = 'foo';
R::store( $page );
$page = R::dispense( 'page' );
$page->name = 'bar';
R::store( $page );
asrt( R::count( 'page', ' name = ? ', array( 'foo' ) ), 1 );
// Now count something that does not exist, this should return 0. (just be polite)
asrt( R::count( 'teapot', ' name = ? ', array( 'flying' ) ), 0 );
asrt( R::count( 'teapot' ), 0 );
$currentDriver = $this->currentlyActiveDriverID;
// Some drivers don't support that many error codes.
if ( $currentDriver === 'mysql' || $currentDriver === 'postgres' ) {
try {
R::count( 'teaport', ' for tea ' );
fail();
} catch ( SQL $e ) {
pass();
}
}
}
/**
* Can we count the number of shared beans?
*
* @return void
*/
public function testCountShared()
{
R::nuke();
$book = R::dispense( 'book' );
$book->sharedPageList = R::dispense( 'page', 5 );
R::store( $book );
asrt( $book->countShared('page'), 5 );
asrt( $book->countShared('leaflet'), 0 );
asrt( R::dispense( 'book' )->countShared('page'), 0 );
$am = R::getRedBean()->getAssociationManager();
asrt( $am->relatedCount( R::dispense( 'book' ), 'page' ), 0);
try {
$am->relatedCount( 'not a bean', 'type' );
fail();
} catch( RedException $e ) {
pass();
}
R::getWriter()->setUseCache(TRUE);
asrt( $book->countShared('page'), 5 );
R::exec('DELETE FROM book_page WHERE book_id > 0 -- keep-cache');
asrt( $book->countShared('page'), 5 );
R::getWriter()->setUseCache(FALSE);
asrt( $book->countShared('page'), 0 );
}
/**
* Test $bean->countOwn($type);
*
* @return void
*/
public function testCountOwn()
{
R::nuke();
$book = R::dispense( 'book' );
$empty = R::dispense( 'book' );
$nothing = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->ownPageList[] = $page;
R::store( $book );
R::store( $empty );
OODBBean::useFluidCount( FALSE );
asrt( $book->countOwn('page'), 1 );
asrt( $empty->countOwn('page'), 0 );
asrt( $nothing->countOwn('page'), 0 );
$old = OODBBean::useFluidCount( TRUE );
asrt( $old, FALSE );
asrt( $book->countOwn('page'), 1 );
asrt( $empty->countOwn('page'), 0 );
asrt( $nothing->countOwn('page'), 0 );
R::freeze( TRUE );
asrt( $book->countOwn('page'), 1 );
asrt( $empty->countOwn('page'), 0 );
asrt( $nothing->countOwn('page'), 0 );
R::freeze( FALSE );
R::nuke();
asrt( $empty->countOwn('page'), 0 );
asrt( $nothing->countOwn('page'), 0 );
R::freeze( TRUE );
asrt( $nothing->countOwn('page'), 0 );
try { asrt( $empty->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
try { asrt( $book->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
R::freeze( FALSE );
OODBBean::useFluidCount( FALSE );
try { asrt( $empty->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
try { asrt( $book->countOwn('page'), 0 ); fail(); } catch(\Exception $e) { pass(); }
OODBBean::useFluidCount( TRUE );
}
/**
* Test $bean->withCondition( ... )->countOwn( $type );
*
* @return void
*/
public function testCountWithCondition()
{
R::nuke();
$book = R::dispense( 'book' );
$book->ownPageList[] = R::dispense( 'page' );
R::store( $book );
OODBBean::useFluidCount( FALSE );
$count = $book
->withCondition(' id > :id ', array( ':id' => 0 ) )
->countOwn('page');
asrt( $count, 1 );
$count = $book
->withCondition(' id > ? ', array( 0 ) )
->countOwn('page');
asrt( $count, 1 );
$count = $book
->withCondition(' id < :id ', array( ':id' => 0 ) )
->countOwn('page');
asrt( $count, 0 );
$count = $book
->withCondition(' id < ? ', array( 0 ) )
->countOwn('page');
asrt( $count, 0 );
OODBBean::useFluidCount( TRUE );
$count = $book
->withCondition(' id > :id ', array( ':id' => 0 ) )
->countOwn('page');
asrt( $count, 1 );
$count = $book
->withCondition(' id > ? ', array( 0 ) )
->countOwn('page');
asrt( $count, 1 );
$count = $book
->withCondition(' id < :id ', array( ':id' => 0 ) )
->countOwn('page');
asrt( $count, 0 );
$count = $book
->withCondition(' id < ? ', array( 0 ) )
->countOwn('page');
asrt( $count, 0 );
}
}

View File

@@ -0,0 +1,474 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\AssociationManager as AssociationManager;
use RedBeanPHP\RedException\SQL as SQL;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Cross
*
* Tests self referential many-to-many relations, including the
* aggr feature.
*
* @file RedUNIT/Base/Cross.php
* @desc Tests associations within the same table (i.e. page_page2 alike)
* Cross tables, self referential many-to-many relations
* and aggregation techniques
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Cross extends Base
{
/**
* Test how well aggr handles fields with no
* values.
*
* @return void
*/
public function testAggrNullHandling()
{
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$page->name = 'Page 3';
$book->xownPageList[] = $page;
R::store( $book );
$book = $book->fresh();
$texts = $book->aggr( 'ownPageList', 'text' );
pass();
asrt( count( $texts ), 0 );
asrt( is_array( $texts ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$page1 = R::dispense( 'page' );
$page1->name = 'Page 1';
$text1 = R::dispense('text');
$text1->content = 'Text 1';
$page1->text = $text1;
$book->xownPageList[] = $page1;
$page2 = R::dispense( 'page' );
$page2->name = 'Page 2';
$text2 = R::dispense( 'text' );
$text2->content = 'Text 2';
$page2->text = $text2;
$book->xownPageList[] = $page2;
$page3 = R::dispense( 'page' );
$page3->name = 'Page 3';
$book->xownPageList[] = $page3;
R::store( $book );
$book = $book->fresh();
$texts = $book->aggr( 'ownPageList', 'text' );
pass();
asrt( count( $texts ), 2 );
}
/**
* Test many different scenarios with self referential
* many-to-many relations.
*
* @return void
*/
public function testSelfReferentialCRUD()
{
R::nuke();
$buddies = R::dispense( 'buddy', 4 );
$buddies[0]->name = 'A';
$buddies[1]->name = 'B';
$buddies[2]->name = 'C';
$buddies[3]->name = 'D';
$buddies[0]->sharedBuddyList = array( $buddies[1], $buddies[2] );
$buddies[3]->sharedBuddyList = array( $buddies[2] );
R::storeAll( $buddies );
$buddies[0] = $buddies[0]->fresh();
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
//does this yield valid combinations - cross relations / self ref n-m
//need to symmetric....
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'B,C' );
unset( $buddies[0]->sharedBuddy );
R::storeAll( $buddies );
$buddies[0] = $buddies[0]->fresh();
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
$buddies[3] = $buddies[3]->fresh();
asrt( count( $buddies[3]->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddies[3]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'C' );
$buddies[2] = $buddies[2]->fresh();
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'A,D' );
$buddies[1] = $buddies[1]->fresh();
asrt( count( $buddies[1]->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'A' );
//Can we add one?
$buddies[1]->sharedBuddyList[] = R::dispense('buddy')->setAttr('name', 'E');
R::store( $buddies[1] );
$buddies[0] = $buddies[0]->fresh();
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'B,C' );
$buddies[1] = $buddies[1]->fresh();
asrt( count( $buddies[1]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'A,E' );
$all = R::find( 'buddy' );
asrt( count( $all ), 5 );
foreach( $buddies[1]->sharedBuddy as $buddy ) {
if ( $buddy->name === 'E' ) {
$buddyE = $buddy;
}
}
asrt( isset( $buddyE ), TRUE );
asrt( $buddyE->name, 'E' );
//can we update?
foreach( $buddies[0]->sharedBuddy as $buddy ) {
if ( $buddy->name === 'C' ) {
$buddy->name = 'C2';
}
}
R::store( $buddies[0] );
$buddies[0] = $buddies[0]->fresh();
asrt( count( $buddies[0]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'B,C2' );
$buddies[2] = $buddies[2]->fresh();
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'A,D' );
//can we delete?
foreach( $buddies[0]->sharedBuddyList as $id => $buddy ) {
if ( $buddy->name === 'B' ) {
unset( $buddies[0]->sharedBuddyList[$id] );
}
}
R::store( $buddies[0] );
$buddies[0] = $buddies[0]->fresh();
asrt( count( $buddies[0]->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddies[0]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'C2' );
$buddies[1] = $buddies[1]->fresh();
asrt( count( $buddies[1]->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddies[1]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'E' );
asrt( R::count( 'buddy' ), 5 );
asrt( R::count( 'buddyBuddy' ), 3 );
$buddies[3] = $buddies[3]->fresh();
asrt( count( $buddies[3]->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddies[3]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'C2' );
$buddies[2] = $buddies[2]->fresh();
asrt( count( $buddies[2]->sharedBuddyList ), 2 );
$names = R::gatherLabels( $buddies[2]->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'A,D' );
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->sharedBuddyList ), 1 );
$names = R::gatherLabels( $buddyE->sharedBuddyList );
sort( $names );
$names = implode( ',', $names );
asrt( $names, 'B' );
if ( $this->currentlyActiveDriverID === 'mysql' ) {
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->with(' HAVING linked_by > 0 ')->sharedBuddyList ), 1 );
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->with(' HAVING linked_by < 0 ')->sharedBuddyList ), 0 );
}
if ( $this->currentlyActiveDriverID === 'sqlite' ) {
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->withCondition(' linked_by > 0 ')->sharedBuddyList ), 1 );
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->withCondition(' linked_by < 0 ')->sharedBuddyList ), 0 );
}
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->withCondition(' buddy_buddy.buddy_id > 0 AND buddy_buddy.buddy2_id > 0 ')->sharedBuddyList ), 1 );
$buddyE = $buddyE->fresh();
asrt( count( $buddyE->withCondition(' buddy_buddy.buddy_id < 0 AND buddy_buddy.buddy2_id < 0 ')->sharedBuddyList ), 0 );
}
/**
* Test self referential N-M relations (page_page).
*
* @return void
*/
public function testSelfReferential()
{
$page = R::dispense( 'page' )->setAttr( 'title', 'a' );
$page->sharedPage[] = R::dispense( 'page' )->setAttr( 'title', 'b' );
R::store( $page );
$page = $page->fresh();
$page = reset( $page->sharedPage );
asrt( $page->title, 'b' );
$tables = array_flip( R::inspect() );
asrt( isset( $tables['page_page'] ), TRUE );
$columns = R::inspect( 'page_page' );
asrt( isset( $columns['page2_id'] ), TRUE );
}
/**
* Test the unique constraint.
* Just want to make sure it is not too limiting and functions
* properly for typical RedBeanPHP usage.
*
* @return void
*/
public function testUnique()
{
R::nuke();
$page1 = R::dispense( 'page' );
$tag1 = R::dispense( 'tag' );
$page2 = R::dispense( 'page' );
$tag2 = R::dispense( 'tag' );
$page3 = R::dispense( 'page' );
$tag3 = R::dispense( 'tag' );
$page1->sharedTag[] = $tag1;
R::store( $page1 );
//can we save all combinations with unique?
asrt( R::count( 'pageTag' ), 1);
$page1->sharedTag[] = $tag2;
R::store( $page1 );
asrt( R::count( 'pageTag' ), 2 );
$page1->sharedTag[] = $tag3;
$page2->sharedTag[] = $tag1;
$page2->sharedTag[] = $tag2;
$page2->sharedTag[] = $tag3;
$page3->sharedTag[] = $tag1;
$page3->sharedTag[] = $tag2;
$page3->sharedTag[] = $tag3;
R::storeAll( array( $page1, $page2, $page3 ) );
asrt( R::count('pageTag'), 9 );
$page1 = $page1->fresh();
$page1->sharedTag[] = $tag3;
R::store( $page1 );
//cant add violates unique
asrt( R::count( 'pageTag' ), 9 );
}
/**
* Shared lists can only be formed using types.
* If you happen to have two beans of the same type you can still
* have a shared list but not with a sense of direction.
* I.e. quest->sharedQuest returns all the quests that follow the first one,
* but also the ones that are followed by the first one.
* If you want to have some sort of direction; i.e. one quest follows another one
* you'll have to use an alias: quest->target, but now you can't use the shared list
* anymore because it will start looking for a type named 'target' (which is just an alias)
* for quest, but it cant find that table and it's not possible to 'keep remembering'
* the alias throughout the system.
*
* The aggr() method solves this inconvenience.
* Aggr iterates through the list identified by its first argument ('target' -> ownQuestTargetList)
* and fetches every ID of the target (quest_target.target_id), loads these beans in batch for
* optimal performance, puts them back in the beans (questTarget->target) and returns the
* references.
*
* @return void
*/
public function testAggregationInSelfRefNM()
{
R::nuke();
$quest1 = R::dispense( 'quest' );
$quest1->name = 'Quest 1';
$quest2 = R::dispense( 'quest' );
$quest2->name = 'Quest 2';
$quest3 = R::dispense( 'quest' );
$quest3->name = 'Quest 3';
$quest4 = R::dispense( 'quest' );
$quest4->name = 'Quest 4';
$quest1->link( 'questTarget' )->target = $quest2;
$quest1->link( 'questTarget' )->target = $quest3;
$quest3->link( 'questTarget' )->target = $quest4;
$quest3->link( 'questTarget' )->target = $quest1;
R::storeAll( array( $quest1, $quest3 ) );
//There should be 4 links
asrt( (int) R::count('questTarget'), 4 );
$quest1 = $quest1->fresh();
$targets = $quest1->aggr( 'ownQuestTargetList', 'target', 'quest' );
//can we aggregate the targets over the link type?
asrt( count( $targets), 2 );
//are the all valid beans?
foreach( $targets as $target ) {
//are they beans?
asrt( ( $target instanceof OODBBean ), TRUE );
//are they fetched as quest?
asrt( ( $target->getMeta( 'type' ) ), 'quest' );
}
//list target should already have been loaded, nuke has no effect
R::nuke();
$links = $quest1->ownQuestTargetList;
//are the links still there, have they been set in the beans as well?
asrt( count( $links ), 2);
//are they references instead of copies, changes in the aggregation set should affect the beans in links!
foreach( $targets as $target ) {
$target->name .= 'b';
asrt( substr( $target->name, -1 ), 'b' );
}
//do the names end with a 'b' here as well ? i.e. have they been changed through references?
foreach( $links as $link ) {
asrt( substr( $target->name, -1 ), 'b' );
}
//now test the effect on existing shadow...
R::nuke();
$quest1 = R::dispense('quest');
$quest1->name = 'Quest 1';
$quest2 = R::dispense('quest');
$quest2->name = 'Quest 2';
$quest3 = R::dispense('quest');
$quest3->name = 'Quest 3';
$quest4 = R::dispense('quest');
$quest4->name = 'Quest 4';
$quest1->link( 'questTarget' )->target = $quest2;
$quest1->link( 'questTarget' )->target = $quest3;
R::store($quest1);
asrt( (int) R::count( 'questTarget' ), 2 );
//now lets first build a shadow
$quest1->link( 'questTarget' )->target = $quest4;
//$quest1 = $quest1->fresh();
$targets = $quest1->aggr( 'ownQuestTargetList', 'target', 'quest' );
//targets should not include the new bean...
asrt( count($targets), 2 );
//this should not overwrite the existing beans
R::store($quest1);
asrt( (int) R::count( 'questTarget' ), 3 );
}
/**
* Test aggr without the aliasing.
*
* @return void
*/
public function testAggrBasic()
{
R::nuke();
$book = R::dispense( 'book' );
$page1 = R::dispense( 'page' );
$page1->name = 'Page 1';
$text1 = R::dispense('text');
$text1->content = 'Text 1';
$page1->text = $text1;
$book->xownPageList[] = $page1;
$page2 = R::dispense( 'page' );
$page2->name = 'Page 2';
$text2 = R::dispense( 'text' );
$text2->content = 'Text 2';
$page2->text = $text2;
$book->xownPageList[] = $page2;
R::store( $book );
$book = $book->fresh();
$texts = $book->aggr( 'ownPageList', 'text' );
R::nuke();
asrt( count( $texts ), 2 );
foreach( $texts as $text ) {
asrt( ( $text instanceof OODBBean ), TRUE );
}
$pages = $book->ownPageList;
asrt( count( $pages ), 2 );
asrt( R::count( 'page' ), 0 );
foreach( $pages as $page ) {
asrt( ( $page instanceof OODBBean ), TRUE );
$text = $page->text;
asrt( ( $text instanceof OODBBean ), TRUE );
$text->content = 'CHANGED';
}
foreach( $texts as $text ) {
asrt( $text->content, 'CHANGED' );
}
}
/**
* Test aggr with basic aliasing.
*
* @return void
*/
public function testAggrWithOnlyAlias()
{
R::nuke();
$book = R::dispense( 'book' );
$page1 = R::dispense( 'page' );
$page1->name = 'Page 1';
$text1 = R::dispense( 'text' );
$text1->content = 'Text 1';
$page1->content = $text1;
$book->xownPageList[] = $page1;
$page2 = R::dispense( 'page' );
$page2->name = 'Page 2';
$text2 = R::dispense( 'text' );
$text2->content = 'Text 2';
$page2->content = $text2;
$book->xownPageList[] = $page2;
R::store( $book );
$book = $book->fresh();
$texts = $book->aggr( 'ownPageList', 'content', 'text' );
R::nuke();
asrt( count( $texts ), 2 );
foreach( $texts as $text ) {
asrt( ( $text instanceof OODBBean), TRUE );
}
$pages = $book->ownPageList;
asrt( count( $pages ), 2 );
asrt( R::count( 'page' ), 0 );
foreach( $pages as $page ) {
asrt( ( $page instanceof OODBBean ), TRUE );
$text = $page->content;
asrt( ( $text instanceof OODBBean ), TRUE );
$text->content = 'CHANGED';
}
foreach( $texts as $text ) {
asrt( $text->content, 'CHANGED' );
}
}
/**
* The aggr method can only be used with own-list.
*
* @return void
*/
public function testErrorHandlingAggr()
{
$wrongLists = array( 'not-an-own-list', 'OWNlist', 'Ownpage', 'ownbook', 'own book', '!', 'sharedBook' );
foreach( $wrongLists as $wrongList ) {
$bean = R::dispense( 'bean' );
try {
$bean->aggr( $wrongList, 'field' );
fail();
} catch ( \Exception $exception ) {
pass();
asrt( ( $exception instanceof RedException ), TRUE );
}
}
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\QueryWriter;
use RedBeanPHP\QueryWriter\AQueryWriter;
/**
* Cursors
*
* Tests whether RedBeanPHP can use cursors (using the
* findCollection method) to iterate over large data sets.
*
* @file RedUNIT/Base/Cursors.php
* @desc Tests whether we can use cursors
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Cursors extends Base
{
/**
* Test whether we can use cursors with raw SQL
* from the facade (#656).
*
* @return void
*/
public function testSQLCursors()
{
R::nuke();
for( $i=0; $i<20; $i++ ) {
$page = R::dispense( 'page' );
$page->number = $i;
$page->content = sha1( $i );
R::store( $page );
}
$cursor = R::getCursor( 'SELECT * FROM page ORDER BY page.number ASC' );
asrt( get_class( $cursor ), 'RedBeanPHP\Cursor\PDOCursor');
$i = 0;
$list = array();
while( $row = $cursor->getNextItem() ) {
asrt( is_array( $row ), TRUE );
asrt( (string) $row['number'], strval( $i ) );
asrt( $row['content'], sha1( $i ) );
$list[] = $row['content'];
$i ++;
}
}
/**
* Test basic cursor functionality.
*
* @return void
*/
public function testBasicCursors()
{
R::nuke();
for( $i=0; $i<20; $i++ ) {
$page = R::dispense( 'page' );
$page->number = $i;
$page->content = sha1( $i );
R::store( $page );
}
$collection = R::findCollection( 'page', 'ORDER BY page.number ASC' );
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
$i = 0;
$list = array();
while( $bean = $collection->next() ) {
asrt( ( $bean instanceof OODBBean ), TRUE );
asrt( (string) $bean->number, strval( $i ) );
asrt( $bean->content, sha1( $i ) );
$list[] = $bean->content;
$i ++;
}
$collection->reset();
$i = 0;
while( $bean = $collection->next() ) {
asrt( ( $bean instanceof OODBBean ), TRUE );
asrt( (string) $bean->number, strval( $i ) );
asrt( $bean->content, sha1( $i ) );
$i ++;
}
$collection = R::findCollection( 'page', ' ORDER BY content ASC ' );
sort( $list );
$i = 0;
while( $bean = $collection->next() ) {
asrt( $bean->content, $list[$i] );
$i ++;
}
$collection = R::findCollection( 'page', ' ORDER BY content ASC LIMIT 5 ' );
sort( $list );
$i = 0;
while( $bean = $collection->next() ) {
asrt( $bean->content, $list[$i] );
$i ++;
if ( $i > 5 ) break;
}
$key = array_rand( $list );
$content = $list[ $key ];
$collection = R::findCollection( 'page', ' content = ? ', array( $content ) );
$bean = $collection->next();
asrt( $bean->content, $content );
$collection->close();
}
/**
* Can we use a filtered cursor?
*
* @return void
*/
public function testCursorWithFilter()
{
R::nuke();
$book = R::dispense( 'book' );
$book->title = 'Title';
R::store( $book );
$filter = array(
QueryWriter::C_SQLFILTER_READ => array(
'book' => array('title' => ' LOWER(book.title) ' )
)
);
AQueryWriter::setSQLFilters( $filter );
$books = R::findCollection( 'book' );
$book = $books->next();
asrt( $book->title, 'title' );
AQueryWriter::setSQLFilters( array() );
$books = R::findCollection( 'book' );
$book = $books->next();
asrt( $book->title, 'Title' );
}
/**
* Test empty collections (NULLCursor).
*
* @return void
*/
public function testEmptyCollection()
{
R::nuke();
$page = R::dispense( 'page' );
$page->content = 'aaa';
R::store( $page );
$collection = R::findCollection( 'page' );
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
$collection = R::findCollection( 'page', ' content = ?', array( 'bbb' ) );
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
asrt( is_null( $collection->next() ), TRUE );
$collection = R::findCollection( 'something' );
asrt( get_class( $collection ), 'RedBeanPHP\BeanCollection');
asrt( is_null( $collection->next() ), TRUE );
asrt( is_null( $collection->reset() ), TRUE );
$collection->close();
}
}

View File

@@ -0,0 +1,461 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\QueryWriter\SQLiteT as SQLiteT;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\AssociationManager as AssociationManager;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\RedException\SQL as SQL;
use RedBeanPHP\QueryWriter\MySQL as MySQL;
use RedBeanPHP\QueryWriter\PostgreSQL as PostgreSQL;
use RedBeanPHP\QueryWriter\CUBRID as CUBRID;
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
/**
* Database
*
* Tests basic RedBeanPHP database functionality.
*
* @file RedUNIT/Base/Database.php
* @desc Tests basic database behaviors
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Database extends Base
{
/**
* What drivers should be loaded for this test pack?
*/
public function getTargetDrivers()
{
return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
}
/**
* Can we use meta mask with find() ?
*
* @return void
*/
public function testSelectFindOne()
{
R::store(R::dispense('book'));
R::store(R::dispense('book'));
if ($this->currentlyActiveDriverID == 'pgsql') {
R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'COUNT(*) OVER()'))), FALSE);
} else {
R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'2'))), FALSE);
}
$books = R::find('book', 'LIMIT 1');
$book = reset($books);
$bundle = $book->getMeta('data.bundle');
asrt(intval($bundle['__meta_total']),2);
R::getWriter()->setSQLFilters(array(), FALSE);
}
/**
* Test whether we cannot just bind function names but
* also function templates, i.e. little SQL snippets.
*
* @return void
*/
public function testBindFuncFunctionTemplates()
{
R::bindFunc('read', 'xbean.lucky', '111 * %s', TRUE);
$bean = R::dispense('xbean');
$bean->lucky = 7;
$id = R::store( $bean );
$bean = R::load( 'xbean', $id );
asrt( intval($bean->lucky), 777 );
R::bindFunc('write', 'xbean.triple', '3 * %s', TRUE);
$bean->triple = 3;
R::store($bean);
$bean = $bean->fresh();
asrt( intval($bean->triple), 9);
R::bindFunc('read', 'xbean.lucky', NULL);
R::bindFunc('write', 'xbean.triple', NULL);
R::getRedBean()->clearAllFuncBindings();
}
/**
* Make ConvertToBean work together with getRow #759.
* When no results are found for getRow it returns []
* Then when you give that to convertToBean it wraps your
* single row into an array of multiple rows, so you get [[]].
* Then this loop has something to
* iterate on foreach ( $rows as $row ) { ...
* And then it crashes on: $id = $row['id'];
*/
public function testHarmonizeConvertToBeanAndGetRow()
{
R::nuke();
$book = R::convertToBean( 'book', R::getRow( 'SELECT * FROM book' ) );
asrt( is_null( $book ), TRUE );
$book = R::convertToBean( 'book', array() );
asrt( is_null( $book ), TRUE );
}
/**
* Test for bugfix:
* adhere to return type specification for R::getRow #728
* public static function getRow is documented as a function
* that returns an array. However, in a situation
* where the resultset is empty, this returns a boolean
* and causes an unexpected failure in
* code like this because it is expecting an array.
*/
public function testReturnTypeGetRow()
{
R::nuke();
$book = R::dispense( 'book' );
R::store( $book );
$row = R::getRow('SELECT * FROM book');
asrt( is_array( $row ), TRUE );
R::trash( $book );
$row = R::getRow('SELECT * FROM book');
asrt( is_array( $row ), TRUE );
R::nuke();
$row = R::getRow('SELECT * FROM book');
asrt( is_array( $row ), TRUE );
}
/**
* Test the (protected) database capability checker method
* of the RedBeanPHP PDO driver (RPDO).
*/
public function testDatabaseCapabilityChecker()
{
$capChecker = new \DatabaseCapabilityChecker( R::getDatabaseAdapter()->getDatabase()->getPDO() );
$result = $capChecker->checkCapability('creativity');
asrt( $result, FALSE ); /* nope, no strong AI yet.. */
}
/**
* Test whether we can obtain the PDO object from the
* database driver for custom database operations.
*
* @return void
*/
public function testGetPDO()
{
$driver = R::getDatabaseAdapter();
asrt( ( $driver instanceof DBAdapter), TRUE );
$pdo = $driver->getDatabase()->getPDO();
asrt( ( $pdo instanceof \PDO ), TRUE );
$pdo2 = R::getPDO();
asrt( ( $pdo2 instanceof \PDO ), TRUE );
asrt( ( $pdo === $pdo2 ), TRUE );
}
/**
* Test setter maximum integer bindings.
*
* @return void
*/
public function testSetMaxBind()
{
$driver = R::getDatabaseAdapter()->getDatabase();
$old = $driver->setMaxIntBind( 10 );
//use SQLite to confirm...
if ( $this->currentlyActiveDriverID === 'sqlite' ) {
$type = R::getCell( 'SELECT typeof( ? ) ', array( 11 ) );
asrt( $type, 'text' );
$type = R::getCell( 'SELECT typeof( ? ) ', array( 10 ) );
asrt( $type, 'integer' );
$type = R::getCell( 'SELECT typeof( ? ) ', array( 9 ) );
asrt( $type, 'integer' );
}
$new = $driver->setMaxIntBind( $old );
asrt( $new, 10 );
try {
$driver->setMaxIntBind( '10' );
fail();
} catch( RedException $e ) {
pass();
}
$new = $driver->setMaxIntBind( $old );
asrt( $new, $old );
$new = $driver->setMaxIntBind( $old );
asrt( $new, $old );
}
/**
* Can we use colons in SQL?
*
* @return void
*/
public function testColonsInSQL()
{
R::nuke();
$book = R::dispense( 'book' );
$book->title = 'About :';
R::store( $book );
pass();
$book = R::findOne( 'book', ' title LIKE :this ', array(
':this' => 'About :'
) );
asrt( ( $book instanceof OODBBean ), TRUE );
//without the colon?
$book = R::findOne( 'book', ' title LIKE :this ', array(
'this' => 'About :'
) );
asrt( ( $book instanceof OODBBean ), TRUE );
$book = R::findOne( 'book', ' title LIKE :this ', array(
':this' => '%:%'
) );
asrt( ( $book instanceof OODBBean ), TRUE );
$book = R::findOne( 'book', ' title LIKE :this OR title LIKE :that', array(
'this' => '%:%', ':that' => 'That'
) );
asrt( ( $book instanceof OODBBean ), TRUE );
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
asrt( count( $records ), 1 );
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
asrt( count( $records ), 1 );
$records = R::getAll('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
asrt( count( $records ), 1 );
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
asrt( count( $records ), 2 );
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
asrt( count( $records ), 2 );
$records = R::getRow('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
asrt( count( $records ), 2 );
}
/**
* Test setting direct PDO.
* Not much to test actually.
*
* @return void
*/
public function testDirectPDO()
{
$pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
R::getDatabaseAdapter()->getDatabase()->setPDO( $pdo );
pass();
}
/**
* Test for testConnection() method.
*
* @return void
*/
public function testConnectionTester()
{
asrt( R::testConnection(), TRUE );
}
/**
* Tests the various ways to fetch (select queries)
* data using adapter methods in the facade.
* Also tests the new R::getAssocRow() method,
* as requested in issue #324.
*/
public function testFetchTypes()
{
R::nuke();
$page = R::dispense( 'page' );
$page->a = 'a';
$page->b = 'b';
R::store( $page );
$page = R::dispense( 'page' );
$page->a = 'c';
$page->b = 'd';
R::store( $page );
$expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
asrt( json_encode( R::getAll( 'SELECT * FROM page' ) ), $expect );
$expect = '{"1":"a","2":"c"}';
asrt( json_encode( R::getAssoc( 'SELECT id, a FROM page' ) ), $expect );
$expect = '{"1":{"a":"a","b":"b"},"2":{"a":"c","b":"d"}}';
asrt( json_encode( R::getAssoc( 'SELECT id, a, b FROM page' ) ), $expect );
$expect = '[{"id":"1","a":"a"},{"id":"2","a":"c"}]';
asrt( json_encode( R::getAssocRow( 'SELECT id, a FROM page' ) ), $expect );
$expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
asrt( json_encode( R::getAssocRow( 'SELECT id, a, b FROM page' ) ), $expect );
$expect = '{"id":"1","a":"a","b":"b"}';
asrt( json_encode( R::getRow( 'SELECT * FROM page WHERE id = 1' ) ), $expect );
$expect = '"a"';
asrt( json_encode( R::getCell( 'SELECT a FROM page WHERE id = 1' ) ), $expect );
$expect = '"b"';
asrt( json_encode( R::getCell( 'SELECT b FROM page WHERE id = 1') ), $expect );
$expect = '"c"';
asrt( json_encode( R::getCell('SELECT a FROM page WHERE id = 2') ), $expect );
$expect = '["a","c"]';
asrt( json_encode( R::getCol( 'SELECT a FROM page' ) ), $expect );
$expect = '["b","d"]';
asrt( json_encode( R::getCol('SELECT b FROM page') ), $expect );
}
/**
* Tests whether we can store an empty bean.
* An empty bean has no properties, only ID. Normally we would
* skip the ID field in an INSERT, this test forces the driver
* to specify a value for the ID field. Different writers have to
* use different values: Mysql uses NULL to insert a new auto-generated ID,
* while Postgres has to use DEFAULT.
*/
public function testEmptyBean()
{
testpack( 'Test Empty Bean Storage.' );
R::nuke();
$bean = R::dispense( 'emptybean' );
$id = R::store( $bean );
asrt( ( $id > 0 ), TRUE );
asrt( R::count( 'emptybean' ), 1 );
$bean = R::dispense( 'emptybean' );
$id = R::store( $bean );
asrt( ( $id > 0 ), TRUE );
asrt( R::count( 'emptybean' ), 2 );
//also test in frozen mode
R::freeze( TRUE );
$bean = R::dispense( 'emptybean' );
$id = R::store( $bean );
asrt( ( $id > 0 ), TRUE );
asrt( R::count( 'emptybean' ), 3 );
R::freeze( FALSE );
}
/**
* Test the database driver and low level functions.
*
* @return void
*/
public function testDriver()
{
$currentDriver = $this->currentlyActiveDriverID;
R::store( R::dispense( 'justabean' ) );
$adapter = new TroubleDapter( R::getToolBox()->getDatabaseAdapter()->getDatabase() );
$adapter->setSQLState( 'HY000' );
$writer = new SQLiteT( $adapter );
$redbean = new OODB( $writer );
$toolbox = new ToolBox( $redbean, $adapter, $writer );
// We can only test this for a known driver...
if ( $currentDriver === 'sqlite' ) {
try {
$redbean->find( 'bean' );
pass();
} catch (\Exception $e ) {
var_dump( $e->getSQLState() );
fail();
}
}
$adapter->setSQLState( -999 );
try {
$redbean->find( 'bean' );
fail();
} catch (\Exception $e ) {
pass();
}
try {
$redbean->wipe( 'justabean' );
fail();
} catch (\Exception $e ) {
pass();
}
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
$pdo = $adapter->getDatabase();
$page = $redbean->dispense( "page" );
try {
$adapter->exec( "an invalid query" );
fail();
} catch ( SQL $e ) {
pass();
}
// Special data type description should result in magic number 99 (specified)
if ( $currentDriver == 'mysql' ) {
asrt( $writer->code( MySQL::C_DATATYPE_SPECIAL_DATE ), 99 );
}
if ( $currentDriver == 'pgsql' ) {
asrt( $writer->code( PostgreSQL::C_DATATYPE_SPECIAL_DATE ), 99 );
}
if ( $currentDriver == 'CUBRID' ) {
asrt( $writer->code( CUBRID::C_DATATYPE_SPECIAL_DATE ), 99 );
}
asrt( (int) $adapter->getCell( "SELECT 123" ), 123 );
$page->aname = "my page";
$id = (int) $redbean->store( $page );
asrt( (int) $page->id, 1 );
asrt( (int) $pdo->GetCell( "SELECT count(*) FROM page" ), 1 );
asrt( $pdo->GetCell( "SELECT aname FROM page LIMIT 1" ), "my page" );
asrt( (int) $id, 1 );
$page = $redbean->load( "page", 1 );
asrt( $page->aname, "my page" );
asrt( ( (bool) $page->getMeta( "type" ) ), TRUE );
asrt( isset( $page->id ), TRUE );
asrt( ( $page->getMeta( "type" ) ), "page" );
asrt( (int) $page->id, $id );
}
/**
* Test selecting.
*
* @return void
*/
public function testSelects()
{
$rooms = R::dispense( 'room', 2 );
$rooms[0]->kind = 'suite';
$rooms[1]->kind = 'classic';
$rooms[0]->number = 6;
$rooms[1]->number = 7;
R::store( $rooms[0] );
R::store( $rooms[1] );
$rooms = R::getAssoc('SELECT * FROM room WHERE id < -999');
asrt(is_array($rooms), TRUE);
asrt(count($rooms), 0);
$rooms = R::getAssoc( 'SELECT ' . R::getWriter()->esc( 'number' ) . ', kind FROM room ORDER BY kind ASC' );
foreach ( $rooms as $key => $room ) {
asrt( ( $key === 6 || $key === 7 ), TRUE );
asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
}
$rooms = R::getDatabaseAdapter()->getAssoc( 'SELECT kind FROM room' );
foreach ( $rooms as $key => $room ) {
asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
asrt( $room, $key );
}
$rooms = R::getAssoc( 'SELECT `number`, kind FROM rooms2 ORDER BY kind ASC' );
asrt( count( $rooms ), 0 );
asrt( is_array( $rooms ), TRUE );
// GetCell should return NULL in case of exception
asrt( NULL, R::getCell( 'SELECT dream FROM fantasy' ) );
}
}
/**
* Malfunctioning database adapter to test exceptions.
*/
class TroubleDapter extends DBAdapter
{
private $sqlState;
public function setSQLState( $sqlState )
{
$this->sqlState = $sqlState;
}
public function get( $sql, $values = array() )
{
$exception = new SQL( 'Just a trouble maker' );
$exception->setSQLState( $this->sqlState );
$exception->setDriverDetails( array(0,1,0) );
throw $exception;
}
public function getRow( $sql, $aValues = array() )
{
$this->get( $sql, $aValues );
}
public function exec( $sql, $aValues = array(), $noEvent = FALSE )
{
$this->get( $sql, $aValues );
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\Facade as Facade;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\Util\DispenseHelper as DispenseHelper;
/**
* Dispense
*
* Tests whether we can dispense beans and tests all
* features of the dispense/dispenseAll functions.
*
* @file RedUNIT/Base/Dispense.php
* @desc Tests bean dispensing functionality.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Dispense extends Base
{
/**
* Test whether findOrDispense and findOneOrDispense
* will trigger same validation Exception for invalid
* bean types as R::dispense(). Github issue #546.
*
* @return void
*/
public function testIssue546()
{
try {
R::findOrDispense( 'invalid_type' );
fail();
} catch ( RedException $exception ) {
pass();
}
try {
R::findOneOrDispense( 'invalid_type' );
fail();
} catch ( RedException $exception ) {
pass();
}
try {
DispenseHelper::checkType( 'invalid_type' );
fail();
} catch ( RedException $exception ) {
pass();
}
}
/**
* Test dispense.
*
* @return void
*/
public function testBasicsDispense()
{
$redbean = R::getRedBean();
// Can we dispense a bean?
$page = $redbean->dispense( "page" );
// Does it have a meta type?
asrt( ( (bool) $page->getMeta( "type" ) ), TRUE );
// Does it have an ID?
asrt( isset( $page->id ), TRUE );
// Type should be 'page'
asrt( ( $page->getMeta( "type" ) ), "page" );
// ID should be 0 because bean does not exist in database yet.
asrt( ( $page->id ), 0 );
// Try some faulty dispense actions.
foreach ( array( "", ".", "-") as $value ) {
try {
$redbean->dispense( $value );
fail();
} catch (RedException $e ) {
pass();
}
}
$bean = $redbean->dispense( "testbean" );
$bean["property"] = 123;
$bean["abc"] = "def";
asrt( $bean["property"], 123 );
asrt( $bean["abc"], "def" );
asrt( $bean->abc, "def" );
asrt( isset( $bean["abd"] ), FALSE );
asrt( isset( $bean["abc"] ), TRUE );
}
/**
* Tests the facade-only dispenseAll method.
*
* @return void
*/
public function testDispenseAll()
{
list( $book, $page ) = Facade::dispenseAll( 'book,page' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( ( $page instanceof OODBBean ), TRUE );
asrt( $book->getMeta( 'type' ), 'book');
asrt( $page->getMeta( 'type' ), 'page');
list( $book, $page, $texts, $mark ) = R::dispenseAll( 'book,page,text*2,mark' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( ( $page instanceof OODBBean ), TRUE );
asrt( is_array( $texts ), TRUE );
asrt( ( $mark instanceof OODBBean ), TRUE );
asrt( $book->getMeta( 'type'), 'book' );
asrt( $page->getMeta( 'type'), 'page' );
asrt( $mark->getMeta( 'type'), 'mark' );
asrt( $texts[0]->getMeta( 'type'), 'text' );
asrt( $texts[1]->getMeta( 'type'), 'text' );
list( $eggs, $milk, $butter ) = R::dispenseAll( 'eggs*3,milk*1,butter*9' );
asrt( count( $eggs ), 3 );
asrt( ( $milk instanceof OODBBean ), TRUE );
asrt( count( $butter ), 9 );
list( $eggs, $milk, $butter ) = R::dispenseAll( 'eggs*3,milk*1,butter*9', TRUE );
asrt( count( $eggs ), 3 );
asrt( count( $milk ), 1 );
asrt( count( $eggs ), 3 );
list( $beer ) = R::dispenseAll( 'beer*0', TRUE );
asrt( is_array( $beer ), TRUE );
asrt( count( $beer ), 0 );
list( $beer ) = R::dispenseAll( 'beer*0', FALSE );
asrt( is_array( $beer ), FALSE );
asrt( is_null( $beer ), TRUE );
}
/**
* Tests different return values of dispense().
*
* @return void
*/
public function testDispenseArray()
{
$oodb = R::getRedBean();
$array = $oodb->dispense( 'book', 0, TRUE );
asrt( is_array( $array ), TRUE );
$array = $oodb->dispense( 'book', 1, TRUE );
asrt( is_array( $array ), TRUE );
$array = $oodb->dispense( 'book', 2, TRUE );
asrt( is_array( $array ), TRUE );
$array = R::dispense( 'book', 0, TRUE );
asrt( is_array( $array ), TRUE );
$array = R::dispense( 'book', 1, TRUE );
asrt( is_array( $array ), TRUE );
$array = R::dispense( 'book', 2, TRUE );
asrt( is_array( $array ), TRUE );
$array = $oodb->dispense( 'book', 0, FALSE );
asrt( is_array( $array ), FALSE );
asrt( is_null( $array ), TRUE );
$array = $oodb->dispense( 'book', 1, FALSE );
asrt( is_array( $array ), FALSE );
asrt( ( $array instanceof OODBBean ), TRUE );
$array = $oodb->dispense( 'book', 2, FALSE );
asrt( is_array( $array ), TRUE );
$array = R::dispense( 'book', 0, FALSE );
asrt( is_array( $array ), FALSE );
$array = R::dispense( 'book', 1, FALSE );
asrt( is_array( $array ), FALSE );
$array = R::dispense( 'book', 2, FALSE );
asrt( is_array( $array ), TRUE );
$array = $oodb->dispense( 'book', 0 );
asrt( is_array( $array ), FALSE );
asrt( is_null( $array ), TRUE );
$array = $oodb->dispense( 'book', 1 );
asrt( is_array( $array ), FALSE );
asrt( ( $array instanceof OODBBean ), TRUE );
$array = $oodb->dispense( 'book', 2 );
asrt( is_array( $array ), TRUE );
$array = R::dispense( 'book', 0 );
asrt( is_array( $array ), FALSE );
$array = R::dispense( 'book', 1 );
asrt( is_array( $array ), FALSE );
$array = R::dispense( 'book', 2 );
asrt( is_array( $array ), TRUE );
}
}

View File

@@ -0,0 +1,647 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\DuplicationManager as DuplicationManager;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\RedException as RedException;
/**
* Dup
*
* Tests duplication. Like the 'copy' test suite but
* focuses on more complex scenarios.
*
* @file RedUNIT/Base/Dup.php
* @desc Intensive test for dup()
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Dup extends Base
{
/**
* Tests basic export functionality
*
* @return void
*/
public function testExport()
{
$bean = R::dispense('bean');
$bean->name = R::dispense('book');
$export = $bean->export( FALSE, FALSE, FALSE, array( 'book' ) );
asrt( isset( $export['name'] ), TRUE );
$export = $bean->export( FALSE, FALSE, FALSE, array( 'book2' ) );
asrt( isset( $export['name'] ), FALSE );
}
/**
* Tests whether the original ID is stored
* in meta data (quite handy for ID mappings).
*
* @return void
*/
public function testKeepOldID()
{
R::nuke();
$book = R::dispense( 'book' );
$book->xownPageList[] = R::dispense( 'page' );
R::store( $book );
$bookID = $book->id;
$page = reset( $book->xownPageList );
$pageID = $page->id;
$book = $book->fresh();
$copy = R::duplicate( $book );
asrt( $copy->getMeta( 'sys.dup-from-id' ), $bookID );
$copyPage = reset( $copy->xownPageList );
asrt( $copyPage->getMeta( 'sys.dup-from-id' ), $pageID );
}
/**
* Test export camelCase.
*
* @return void
*/
public function testExportCamelCase()
{
R::nuke();
$book = R::dispense( 'book' );
$book->isCheap = TRUE;
$book->hasISBNCode = FALSE;
$page = R::dispense('page');
$page->isWrittenWell = TRUE;
$page->containsInterestingText = TRUE;
$book->ownPageList[] = $page;
R::store( $book );
$book = $book->fresh();
$export = R::exportAll( $book );
asrt( isset( $export[0]['id'] ), TRUE );
asrt( isset( $export[0]['is_cheap'] ), TRUE );
asrt( isset( $export[0]['has_isbn_code'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['is_written_well'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['contains_interesting_text'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['book_id'] ), TRUE );
R::useExportCase( 'camel' );
$export = R::exportAll( $book );
asrt( isset( $export[0]['id'] ), TRUE );
asrt( isset( $export[0]['isCheap'] ), TRUE );
asrt( isset( $export[0]['hasIsbnCode'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['isWrittenWell'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['containsInterestingText'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['bookId'] ), TRUE );
R::useExportCase( 'dolphin' );
$export = R::exportAll( $book );
asrt( isset( $export[0]['id'] ), TRUE );
asrt( isset( $export[0]['isCheap'] ), TRUE );
asrt( isset( $export[0]['hasIsbnCode'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['isWrittenWell'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['containsInterestingText'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['bookID'] ), TRUE );
R::useExportCase( 'default' );
$export = R::exportAll( $book );
asrt( isset( $export[0]['id'] ), TRUE );
asrt( isset( $export[0]['is_cheap'] ), TRUE );
asrt( isset( $export[0]['has_isbn_code'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['id'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['is_written_well'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['contains_interesting_text'] ), TRUE );
asrt( isset( $export[0]['ownPage']['0']['book_id'] ), TRUE );
try {
R::useExportCase( 'invalid' );
fail();
} catch ( RedException $exception ) {
pass();
}
}
/**
* Test whether we can duplicate part of a tree
* without infinite loops.
*
* @return void
*/
public function testDupPortionOfATree()
{
R::nuke();
$article = R::dispense( 'article' );
$article->name = 'article 1';
list( $article2, $article3 ) = R::dispense( 'article', 2 );
$article2->name = 'article 2';
$article3->name = 'article 3';
list( $article4, $article5 ) = R::dispense( 'article' , 2);
$article4->name = 'article 4';
$article5->name = 'article 5';
list( $article6, $article7 ) = R::dispense( 'article' , 2);
$article6->name = 'article 6';
$article7->name = 'article 7';
$article3->xownArticleList[] = $article7;
$article4->xownArticleList[] = $article6;
$article2->xownArticleList = array( $article5, $article4 );
$article->xownArticleList = array( $article2, $article3 );
R::store( $article );
asrt( R::count( 'article' ), 7 );
$article2 = $article2->fresh();
$dupArticle2 = R::duplicate( $article2 );
$dupArticle2->name = 'article 2b';
$dupBeans = $dupArticle2->xownArticleList;
foreach( $dupBeans as $dupBean ) {
$list[] = $dupBean->name;
}
sort( $list );
$listStr = implode( ',', $list );
asrt( $listStr, 'article 4,article 5' );
foreach( $dupBeans as $dupBean ) {
if ( $dupBean->name === 'article 4' ) {
$dup4 = $dupBean;
}
}
asrt( isset( $dup4 ), TRUE );
$dupBeans = $dup4->xownArticleList;
foreach( $dupBeans as $dupBean ) {
asrt( $dupBean->name, 'article 6' );
}
//so we have extracted part of the tree, can we store it?
$id = R::store( $dupArticle2 );
asrt( ( $id > 0 ), TRUE );
asrt( R::count( 'article' ), 11 );
$originalArticle = $article->fresh();
asrt( $originalArticle->name, 'article 1' );
$subArticles = $originalArticle->xownArticleList;
$list = array();
foreach( $subArticles as $subArticle ) {
$list[] = $subArticle->name;
}
sort( $list );
$listStr = implode( ',', $list );
asrt( $listStr, 'article 2,article 2b,article 3' );
foreach( $subArticles as $subArticle ) {
if ( $subArticle->name === 'article 2' ) {
$sub2 = $subArticle;
}
if ( $subArticle->name === 'article 3' ) {
$sub3 = $subArticle;
}
}
$subArticles = $sub2->xownArticleList;
$list = array();
foreach( $subArticles as $subArticle ) {
$list[] = $subArticle->name;
}
sort( $list );
$listStr = implode( ',', $list );
asrt( $listStr, 'article 4,article 5' );
$subArticles = $sub3->xownArticleList;
$list = array();
foreach( $subArticles as $subArticle ) {
$list[] = $subArticle->name;
}
sort( $list );
$listStr = implode( ',', $list );
asrt( $listStr, 'article 7' );
$subArticles = $sub2->xownArticleList;
foreach( $subArticles as $subArticle ) {
if ( $subArticle->name === 'article 4' ) {
$sub4 = $subArticle;
}
if ( $subArticle->name === 'article 5' ) {
$sub5 = $subArticle;
}
}
asrt( count( $sub4->xownArticleList ), 1 );
$subBeans = $sub4->xownArticleList;
$subBean = reset( $subBeans );
asrt( $subBean->name, 'article 6');
asrt( count( $sub5->xownArticleList ), 0 );
$dupArticle2 = $dupArticle2->fresh();
$subArticles = $dupArticle2->xownArticleList;
$list = array();
foreach( $subArticles as $subArticle ) {
$list[] = $subArticle->name;
}
sort( $list );
$listStr = implode( ',', $list );
asrt( $listStr, 'article 4,article 5' );
foreach( $subArticles as $subArticle ) {
if ( $subArticle->name === 'article 4' ) {
$sub4 = $subArticle;
}
if ( $subArticle->name === 'article 5' ) {
$sub5 = $subArticle;
}
}
asrt( count( $sub4->xownArticleList ), 1 );
$subBeans = $sub4->xownArticleList;
$subBean = reset( $subBeans );
asrt( $subBean->name, 'article 6');
asrt( count( $sub5->xownArticleList ), 0 );
}
/**
* Test exportAll and caching.
*
* @return void
*/
public function testExportAllAndCache()
{
testpack( 'exportAll() and Cache' );
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
$id = R::store( $can );
R::debug( TRUE );
ob_start();
$can = R::load( 'can', $id );
$cache = $this->getCache();
$data = R::exportAll( array( $can ), TRUE );
$queries = ob_get_contents();
R::debug( FALSE );
ob_end_clean();
$len1 = strlen( $queries );
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
$id = R::store( $can );
R::debug( TRUE );
ob_start();
$can = R::load( 'can', $id );
$cache = $this->getCache();
$data = R::exportAll( array( $can ), TRUE );
$queries = ob_get_contents();
R::debug( FALSE );
ob_end_clean();
$len2 = strlen( $queries );
asrt( ( $len1 ), ( $len2 ) );
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
$id = R::store( $can );
R::debug( TRUE );
ob_start();
$can = R::load( 'can', $id );
$cache = $this->getCache();
R::getDuplicationManager()->setTables( $cache );
$data = R::exportAll( array( $can ), TRUE );
$queries = ob_get_contents();
R::debug( FALSE );
ob_end_clean();
$len3 = strlen( $queries );
asrt( ( ( $len3 ) < ( $len2 ) ), TRUE );
asrt( count( $data ), 1 );
asrt( $data[0]['ownCoffee'][0]['color'], 'black' );
R::getDuplicationManager()->setCacheTables( FALSE );
}
/**
* Test duplication and caching.
*
* @return void
*/
public function DupAndCache()
{
testpack( 'Dup() and Cache' );
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
$can = R::load( 'can', R::store( $can ) );
$d = new DuplicationManager( R::getToolBox() );
$d->setCacheTables( TRUE );
ob_start();
R::debug( 1 );
$x = $d->dup( $can );
$queries = ob_get_contents();
R::debug( 0 );
ob_end_clean();
$len1 = strlen( $queries );
asrt( ( $len1 > 40 ), TRUE );
asrt( isset( $x->ownCoffee ), TRUE );
asrt( count( $x->ownCoffee ), 1 );
asrt( isset( $x->sharedTag ), TRUE );
asrt( count( $x->sharedTag ), 1 );
$cache = $d->getSchema();
R::nuke();
$can = R::dispense( 'can' )->setAttr( 'size', 3 );
$can->ownCoffee[] = R::dispense( 'coffee' )->setAttr( 'color', 'black' );
$can->sharedTag[] = R::dispense( 'tag' )->setAttr( 'name', 'cool' );
$can = R::load( 'can', R::store( $can ) );
$d = new DuplicationManager( R::getToolBox() );
/**
* $cache = '{"book": {
* "id": "INTEGER",
* "title": "TEXT"
* }, "bean": {
* "id": "INTEGER",
* "prop": "INTEGER"
* }, "pessoa": {
* "id": "INTEGER",
* "nome": "TEXT",
* "nome_meio": "TEXT",
* "sobrenome": "TEXT",
* "nascimento": "NUMERIC",
* "reg_owner": "TEXT"
* }, "documento": {
* "id": "INTEGER",
* "nome_documento": "TEXT",
* "numero_documento": "TEXT",
* "reg_owner": "TEXT",
* "ownPessoa_id": "INTEGER"
* }, "can": {
* "id": "INTEGER",
* "size": "INTEGER"
* }, "coffee": {
* "id": "INTEGER",
* "color": "TEXT",
* "can_id": "INTEGER"
* }, "tag": {
* "id": "INTEGER",
* "name": "TEXT"
* }, "can_tag": {
* "id": "INTEGER",
* "tag_id": "INTEGER",
* "can_id": "INTEGER"
* }}'
*/
$d->setTables( $cache );
ob_start();
R::debug( 1 );
$x = $d->dup( $can );
$queries = ob_get_contents();
ob_end_clean();
R::debug( 0 );
$len2 = strlen( $queries );
asrt( isset( $x->ownCoffee ), TRUE );
asrt( count( $x->ownCoffee ), 1 );
asrt( isset( $x->sharedTag ), TRUE );
asrt( count( $x->sharedTag ), 1 );
asrt( json_encode( $cache ), json_encode( $d->getSchema() ) );
asrt( ( $len1 > $len2 ), TRUE );
}
/**
* Test duplication and tainting.
*
* @return void
*/
public function testDupAndExportNonTainting()
{
testpack( 'Dup() and Export() should not taint beans' );
$p = R::dispense( 'page' );
$b = R::dispense( 'book' );
$b->ownPage[] = $p;
$b->title = 'a';
$id = R::store( $b );
$b = R::load( 'book', $id );
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
R::exportAll( $b );
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
R::dup( $b );
asrt( ( !$b->getMeta( 'tainted' ) ), TRUE );
testpack( 'Test issue with ownItems and stealing Ids.' );
R::nuke();
$bill = R::dispense( 'bill' );
$item = R::dispense( 'item' );
$element = R::dispense( 'element' );
$bill->ownItem[] = $item;
$bill->sharedElement[] = $element;
R::store( $bill );
$bill = R::load( 'bill', 1 );
$bill->ownItem;
$bill->sharedElement;
$copy = R::dup( $bill );
R::store( $copy );
$rows = ( R::getAll( 'select * from bill_element' ) );
asrt( count( $rows ), 2 );
$rows = ( R::getAll( 'select * from item' ) );
foreach ( $rows as $row ) {
asrt( ( $row['bill_id'] > 0 ), TRUE );
}
R::nuke();
$this->runOnce();
R::freeze( TRUE );
$this->runOnce( FALSE );
R::freeze( FALSE );
}
/**
* Test exporting with filters.
*
* @return void
*/
public function ExportWithFilters()
{
testpack( 'Export with filters' );
$book = R::dispense( 'book' );
$pages = R::dispense( 'page', 2 );
$texts = R::dispense( 'text', 2 );
$images = R::dispense( 'image', 2 );
$author = R::dispense( 'author' );
$pub = R::dispense( 'publisher' );
$bookmarks = R::dispense( 'bookmark', 2 );
$pages[0]->ownText = array( $texts[0] );
$pages[0]->ownImage = array( $images[0] );
$pages[1]->ownText = array( $texts[1] );
$pages[1]->ownImage = array( $images[1] );
$pages[0]->sharedBookmark[] = $bookmarks[0];
$pages[1]->sharedBookmark[] = $bookmarks[1];
$bookmarks[0]->ownNote[] = R::dispense( 'note' )->setAttr( 'text', 'a note' );
$bookmarks[1]->ownNote[] = R::dispense( 'note' )->setAttr( 'text', 'a note' );
$book->ownPage = $pages;
$book->author = $author;
$author->publisher = $pub;
$bookID = R::store( $book );
R::getDuplicationManager()->setTables( R::getWriter()->getTables() );
$objects = ( R::exportAll( array( $book ), TRUE, array() ) );
asrt( isset( $objects[0]['ownPage'] ), TRUE );
asrt( count( $objects[0]['ownPage'] ), 2 );
asrt( isset( $objects[0]['author'] ), TRUE );
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), TRUE );
asrt( count( $objects[0]['ownPage'][0]['ownImage'] ), 1 );
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page', 'author', 'text', 'image' ) ) );
asrt( isset( $objects[0]['ownPage'] ), TRUE );
asrt( count( $objects[0]['ownPage'] ), 2 );
asrt( isset( $objects[0]['author'] ), TRUE );
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), TRUE );
asrt( count( $objects[0]['ownPage'][0]['ownImage'] ), 1 );
$objects = ( R::exportAll( array( $book ), TRUE, 'author' ) );
asrt( isset( $objects[0]['ownPage'] ), FALSE );
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), FALSE );
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page' ) ) );
asrt( isset( $objects[0]['author'] ), FALSE );
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), FALSE );
$objects = ( R::exportAll( array( $book ), TRUE, array( 'page', 'text' ) ) );
asrt( isset( $objects[0]['author'] ), FALSE );
asrt( isset( $objects[0]['ownPage'] ), TRUE );
asrt( isset( $objects[0]['ownPage'][0]['ownText'] ), TRUE );
asrt( count( $objects[0]['ownPage'][0]['ownText'] ), 1 );
asrt( isset( $objects[0]['ownPage'][0]['ownImage'] ), FALSE );
$objects = ( R::exportAll( array( $book ), TRUE, array( 'none' ) ) );
asrt( isset( $objects[0]['author'] ), FALSE );
asrt( isset( $objects[0]['ownPage'] ), FALSE );
$texts = R::find( 'text' );
R::getDuplicationManager()->setCacheTables( FALSE );
testpack( 'Keyless export' );
$book = R::load( 'book', $bookID );
$book->ownPage;
$export = $book->export();
asrt( isset( $export['ownPage'][0] ), TRUE );
}
/**
* Helper function getCache().
*
* @return array
*/
private function getCache()
{
return array(
'coffee' => array(
'color' => 'color',
'id' => 'id',
'can_id' => 'can_id'
),
'can' => array(
'size' => 'size',
'id' => 'id'
),
'can_tag' => array(
'id' => 'id',
'can_id' => 'can_id',
'tag_id' => 'tag_id'
),
'tag' => array(
'id' => 'id',
'name' => 'name' )
);
}
/**
* Compares object with export
*
* @param type $object
* @param type $array
*
* @return void
*/
private function compare( $object, $array )
{
foreach ( $object as $property => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $index => $nestedObject ) {
if ( $nestedObject->id ) {
$foundMatch = FALSE;
//order might be different
foreach ( $array[$property] as $k => $a ) {
if ( $a['id'] == $nestedObject->id ) {
$foundMatch = TRUE;
$index = $k;
}
}
if ( !$foundMatch ) throw new\Exception( 'failed to find match for object ' . $nestedObject->id );
}
$this->compare( $nestedObject, $array[$property][$index] );
}
} elseif ( !is_object( $value ) ) {
asrt( strval( $array[$property] ), strval( $value ) );
}
}
}
/**
* Run tests
*/
private function runOnce( $n = TRUE )
{
$books = R::dispense( 'book', 10 );
$pages = R::dispense( 'page', 10 );
$readers = R::dispense( 'reader', 10 );
$texts = R::dispense( 'text', 10 );
$i = 0;
foreach ( $books as $book ) $book->name = 'book-' . ( $i++ );
$i = 0;
foreach ( $pages as $page ) $page->name = 'page-' . ( $i++ );
$i = 0;
foreach ( $readers as $reader ) $reader->name = 'reader-' . ( $i++ );
$i = 0;
foreach ( $texts as $text ) $text->content = 'lorem ipsum -' . ( $i++ );
foreach ( $texts as $text ) {
$pages[array_rand( $pages )]->ownText[] = $text;
}
foreach ( $pages as $page ) {
$books[array_rand( $books )]->ownPage[] = $page;
}
foreach ( $readers as $reader ) {
$books[array_rand( $books )]->sharedReader[] = $reader;
}
$i = $noOfReaders = $noOfPages = $noOfTexts = 0;
foreach ( $books as $key => $book ) {
$i++;
$noOfPages += count( $book->ownPage );
$noOfReaders += count( $book->sharedReader );
foreach ( $book->ownPage as $page ) $noOfTexts += count( $page->ownText );
$arr = R::exportAll( $book );
echo "\nIntermediate info: " . json_encode( $arr ) . ": Totals = $i,$noOfPages,$noOfReaders,$noOfTexts ";
$this->compare( $book, $arr[0] );
$copiedBook = R::dup( $book );
$copiedBookArray = R::exportAll( $copiedBook );
$this->compare( $book, $copiedBookArray[0] );
$copiedBookArrayII = $copiedBook->export();
$this->compare( $book, $copiedBookArrayII );
$copyFromCopy = R::dup( $copiedBook );
$copyFromCopyArray = R::exportAll( $copyFromCopy );
$this->compare( $book, $copyFromCopyArray[0] );
$copyFromCopyArrayII = $copyFromCopy->export();
$this->compare( $book, $copyFromCopyArrayII );
$id = R::store( $book );
$copiedBook = R::dup( $book );
R::store( $book ); //should not be damaged
$copiedBookArray = R::exportAll( $copiedBook );
$originalBookArray = R::exportAll( $book );
$this->compare( $copiedBook, $copiedBookArray[0] );
$this->compare( $book, $originalBookArray[0] );
$book = R::load( 'book', $id );
$this->compare( $book, $originalBookArray[0] );
$copiedBook = R::dup( $book );
$this->compare( $copiedBook, $copiedBook->export() );
R::store( $copiedBook );
$this->compare( $copiedBook, $copiedBook->export() );
$copyFromCopy = R::dup( $copiedBook );
$this->compare( $copyFromCopy, $copyFromCopy->export() );
R::store( $copyFromCopy );
$newPage = R::dispense( 'page' );
$newPage->name = 'new';
$copyFromCopy->ownPage[] = $newPage;
$modifiedCopy = R::dup( $copyFromCopy );
$exportMod = R::exportAll( $modifiedCopy );
$this->compare( $modifiedCopy, $exportMod[0] );
asrt( count( $modifiedCopy->ownPage ), count( $copiedBook->ownPage ) + 1 );
R::store( $modifiedCopy );
if ( $n ) {
asrt( (int) R::getCell( 'SELECT count(*) FROM book' ), $i * 4 );
asrt( (int) R::getCell( 'SELECT count(*) FROM page' ), ( $noOfPages * 4 ) + $i );
asrt( (int) R::getCell( 'SELECT count(*) FROM text' ), $noOfTexts * 4 );
asrt( (int) R::getCell( 'SELECT count(*) FROM book_reader' ), $noOfReaders * 4 );
asrt( (int) R::getCell( 'SELECT count(*) FROM reader' ), $noOfReaders );
}
}
if ( $n ) {
asrt( $noOfTexts, 10 );
asrt( $noOfReaders, 10 );
asrt( $noOfPages, 10 );
asrt( $i, 10 );
}
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\RedException\SQL as SQLException;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\Adapter\DBAdapter;
use RedBeanPHP\ToolBox;
use RedBeanPHP\QueryWriter\SQLiteT;
use RedBeanPHP\QueryWriter\AQueryWriter;
use RedBeanPHP\QueryWriter;
use RedBeanPHP\OODB;
use RedBeanPHP\Driver\RPDO;
/**
* Exceptions
*
* Tests exception handling in various scenarios as well
* as exception related functionalities.
*
* @file RedUNIT/Base/Exceptions.php
* @desc Tests exception handling
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Exceptions extends Base {
/**
* Tests load exceptions.
* Load Exceptions (version 5+)
*
* - If a bean does not exist,
* R::load() and R::loadForUpdate() will return an empty bean.
* - If there is an error because of a missing table or column,
* both methods will return an empty bean in fluid mode
* and throw an exception in frozen mode.
* - If something else happens (lock timeout for instance)
* both methods will always throw an exception, even in fluid mode.
*
* @return void
*/
public function testLoadExceptions()
{
/* bean does not exist, and table does not exist */
R::nuke();
$book = R::load( 'book', 1 );
pass();
asrt( $book->id, 0 );
R::freeze( TRUE );
$exception = NULL;
try {
$book = R::load( 'book', 1 );
} catch( RedException $exception ) {}
asrt( ( $exception instanceof RedException ), TRUE );
R::freeze( FALSE );
R::store( $book );
/* bean does not exist - table exists */
$book = R::load( 'book', 2 );
pass();
asrt( $book->id, 0 );
R::freeze( TRUE );
$book = R::load( 'book', 2 );
pass();
asrt( $book->id, 0 );
/* other error */
if ( !( R::getWriter() instanceof SQLiteT ) ) {
R::freeze( FALSE );
$exception = NULL;
try {
$book = R::load( 'book', 1, 'invalid sql' );
} catch( RedException $exception ) {}
//not supported for CUBRID
if ($this->currentlyActiveDriverID !== 'CUBRID') {
asrt( ( $exception instanceof RedException ), TRUE );
}
} else {
/* error handling in SQLite is suboptimal */
R::freeze( FALSE );
$book = R::load( 'book', 1, 'invalid sql' );
pass();
asrt( $book->id, 0 );
}
R::freeze( TRUE );
$exception = NULL;
try {
$book = R::load( 'book', 1, 'invalid sql' );
} catch( RedException $exception ) {}
asrt( ( $exception instanceof RedException ), TRUE );
R::freeze( FALSE );
R::nuke();
}
/**
* Test delete exceptions
*
* - in fluid mode no complaining about missing structures
*
* @return void
*/
public function testDeleteExceptions()
{
R::nuke();
$book = R::dispense( 'book' );
R::store( $book );
R::nuke();
R::trash( $book );
R::freeze( TRUE );
$exception = NULL;
try {
R::trash( $book );
} catch( RedException $exception ) {}
asrt( ( $exception instanceof RedException ), TRUE );
R::freeze( FALSE );
$adapter = R::getDatabaseAdapter();
R::nuke();
$book = R::dispense( 'book' );
R::store( $book );
$broken = new BrokenWriter( $adapter );
$redbean = R::getRedBean();
$oodb = new OODB( $broken, $redbean->isFrozen() );
R::setRedBean( $oodb );
$exception = NULL;
try {
R::trash( $book );
} catch( RedException $exception ) {}
asrt( ( $exception instanceof RedException ), TRUE );
R::freeze( TRUE );
$exception = NULL;
try {
R::trash( $book );
} catch( RedException $exception ) {}
asrt( ( $exception instanceof RedException ), TRUE );
R::setRedBean( $redbean );
}
/**
* Test chaining of exceptions.
*
* @return void
*/
public function testChainingExceptions()
{
R::freeze( TRUE );
$exception = NULL;
try {
$book = R::load( 'book', 1, 'invalid sql' );
} catch( RedException $exception ) {}
pass();
asrt( ( $exception instanceof RedException ), TRUE );
asrt( ( $exception->getPrevious() instanceof \Exception ), TRUE );
}
}
class BrokenWriter extends SQLiteT {
public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() )
{
throw new SQLException('oops');
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\AssociationManager as AssociationManager;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\Adapter as Adapter;
use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Facade
*
* Tests the facade. The facade is a single class (often aliased with R)
* that provides access to all (or most) RedBeanPHP functionality without
* the need to interact with all the objects involved. The facade has
* been designed to be as 'straightfoward' as possible, many methods
* of the facade class should be 'almost identical' to simple calls
* to objects behind the facade. This test focuses on basic usage
* scenarios. Complex scenarios are tested in other suites because
* we often test 'the complete route' as the facade code path always
* includes the non-facade code path as well (as proven by the coverage numbers).
*
* @file RedUNIT/Base/Facade.php
* @desc Tests basic functions through facade.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Facade extends Base
{
/**
* What drivers should be loaded for this test pack?
* This pack contains some SQL incomp. with OCI
*/
public function getTargetDrivers()
{
return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
}
/**
* Tests quick trash method: R::trash( type, id ).
*
* @return void
*/
public function testQuickTrash()
{
R::nuke();
$bean = R::dispense( 'bean' );
$id = R::store( $bean );
asrt( R::count( 'bean' ), 1 );
R::trash( 'bean', $id );
asrt( R::count( 'bean' ), 0 );
}
/**
* Test common Facade usage scenarios.
*
* @return void
*/
public function testCommonUsageFacade()
{
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
$pdo = $adapter->getDatabase();
$a = new AssociationManager( $toolbox );
asrt( R::getRedBean() instanceof OODB, TRUE );
asrt( R::getToolBox() instanceof ToolBox, TRUE );
asrt( R::getDatabaseAdapter() instanceof Adapter, TRUE );
asrt( R::getWriter() instanceof QueryWriter, TRUE );
$book = R::dispense( "book" );
asrt( $book instanceof OODBBean, TRUE );
$book->title = "a nice book";
$id = R::store( $book );
asrt( ( $id > 0 ), TRUE );
$book = R::load( "book", (int) $id );
asrt( $book->title, "a nice book" );
asrt( R::load( 'book', 999 )->title, NULL );
R::freeze( TRUE );
try {
R::load( 'bookies', 999 );
fail();
} catch (\Exception $e ) {
pass();
}
R::freeze( FALSE );
$author = R::dispense( "author" );
$author->name = "me";
R::store( $author );
$book9 = R::dispense( "book" );
$author9 = R::dispense( "author" );
$author9->name = "mr Nine";
$a9 = R::store( $author9 );
$book9->author_id = $a9;
$bk9 = R::store( $book9 );
$book9 = R::load( "book", $bk9 );
$author = R::load( "author", $book9->author_id );
asrt( $author->name, "mr Nine" );
R::trash( $author );
R::trash( $book9 );
pass();
$book2 = R::dispense( "book" );
$book2->title = "second";
R::store( $book2 );
$book3 = R::dispense( "book" );
$book3->title = "third";
R::store( $book3 );
asrt( count( R::find( "book" ) ), 3 );
asrt( count( R::findAll( "book" ) ), 3 );
asrt( count( R::findAll( "book", " LIMIT 2" ) ), 2 );
asrt( count( R::find( "book", " id=id " ) ), 3 );
asrt( count( R::find( "book", " title LIKE ?", array( "third" ) ) ), 1 );
asrt( count( R::find( "book", " title LIKE ?", array( "%d%" ) ) ), 2 );
// Find without where clause
asrt( count( R::findAll( 'book', ' order by id' ) ), 3 );
R::trash( $book3 );
R::trash( $book2 );
asrt( count( R::getAll( "SELECT * FROM book " ) ), 1 );
asrt( count( R::getCol( "SELECT title FROM book " ) ), 1 );
asrt( (int) R::getCell( "SELECT 123 " ), 123 );
$book = R::dispense( "book" );
$book->title = "not so original title";
$author = R::dispense( "author" );
$author->name = "Bobby";
R::store( $book );
$aid = R::store( $author );
$author = R::findOne( "author", " name = ? ", array( "Bobby" ) );
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\Observer as Observer;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\Adapter as Adapter;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
/**
* Foreignkeys
*
* Tests whether foreign keys are correctly generated and whether
* depending beans are correctly removed. Also tests auto resolving
* types inferred by inspecting foreign keys.
*
* @file RedUNIT/Base/Foreignkeys.php
* @desc Tests foreign key handling and dynamic foreign keys with
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Foreignkeys extends Base implements Observer
{
/**
* To log the queries
*
* @var array
*/
private $queries = array();
/**
* Test whether unique constraints are properly created using
* reflection.
*
* @return void
*/
public function testUniqueInspect()
{
$writer = R::getWriter();
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$book->sharedCategory[] = $category;
R::store( $book );
asrt( count( get_uniques_for_type('book_category') ), 1 );
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$book->via( 'library' )->sharedCategory[] = $category;
R::store( $book );
asrt( count( get_uniques_for_type('book_category') ), 0 );
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), FALSE );
asrt( count( get_uniques_for_type('library') ), 1 );
asrt( are_cols_in_unique( 'library', array( 'book_id', 'category_id' ) ), TRUE );
AQueryWriter::clearRenames();
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$book->sharedCategory[] = $category;
R::store( $book );
asrt( count( get_uniques_for_type('book_category') ), 1 );
asrt( are_cols_in_unique( 'book_category', array( 'book_id', 'category_id' ) ), TRUE );
asrt( count( get_uniques_for_type('library') ), 0 );
asrt( are_cols_in_unique( 'library', array( 'book_id', 'category_id' ) ), FALSE );
R::nuke();
$book = R::dispense( 'book' );
$book2 = R::dispense( 'book' );
$book->sharedBook[] = $book2;
R::store( $book );
asrt( count( get_uniques_for_type('book_book') ), 1 );
asrt( are_cols_in_unique( 'book_book', array( 'book_id', 'book2_id' ) ), TRUE );
try {
$result = R::getWriter()->addUniqueConstraint( 'nonexistant', array( 'a', 'b' ) );
} catch( \Exception $e ) {
print_r( $e ); exit;
}
pass(); //dont crash!
asrt( $result, FALSE );
}
/**
* Tests foreign keys but checks using ProxyWriter.
*
* @return void
*/
public function testFKInspect()
{
$faultyWriter = new \FaultyWriter( R::getDatabaseAdapter() );
try {
$null = \ProxyWriter::callMethod( $faultyWriter, 'getForeignKeyForTypeProperty', 'test', 'test' );
pass();
} catch( \Exception $e ) {
fail();
}
asrt( is_null( $null ), TRUE );
$writer = R::getWriter();
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->xownPage[] = $page;
R::store( $book );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
asrt( is_array( $keys ), TRUE );
asrt( $keys['on_delete'], 'CASCADE' );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
asrt( is_null( $keys ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->ownPage[] = $page;
R::store( $book );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
asrt( is_array( $keys ), TRUE );
asrt( $keys['on_delete'], 'SET NULL' );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
asrt( is_null( $keys ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->alias('magazine')->xownPage[] = $page;
R::store( $book );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'magazine_id' );
asrt( is_array( $keys ), TRUE );
asrt( $keys['on_delete'], 'CASCADE' );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'book_id' );
asrt( is_null( $keys ), TRUE );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'page', 'id' );
asrt( is_null( $keys ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->cover= $page;
R::store( $book );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'cover_id' );
asrt( is_array( $keys ), TRUE );
asrt( $keys['on_delete'], 'SET NULL' );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'page_id' );
asrt( is_null( $keys ), TRUE );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book', 'id' );
asrt( is_null( $keys ), TRUE );
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$book->sharedTag[] = $category;
R::store( $book );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'book_id' );
asrt( is_array( $keys ), TRUE );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'category_id' );
asrt( is_array( $keys ), TRUE );
$keys = \ProxyWriter::callMethod( $writer, 'getForeignKeyForTypeProperty', 'book_category', 'id' );
asrt( is_null( $keys ), TRUE );
}
/**
* Test dependencies.
*
* @return void
*/
public function testDependency()
{
$can = $this->createBeanInCan( FALSE );
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
// Bean stays
asrt( R::count( 'bean' ), 1 );
}
/**
* Test dependencies (variation).
*
* @return void
*/
public function testDependency2()
{
$can = $this->createBeanInCan( TRUE );
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
// Bean gone
asrt( R::count( 'bean' ), 0 );
$can = $this->createBeanInCan( FALSE );
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
// Bean stays, constraint removed
asrt( R::count( 'bean' ), 0 );
//need to recreate table to get rid of constraint!
R::nuke();
$can = $this->createBeanInCan( FALSE );
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
// Bean stays, constraint removed
asrt( R::count( 'bean' ), 1 );
}
/**
* Tests dependencies (variation).
*
* @return void
*/
public function testDependency3()
{
R::nuke();
$can = $this->createCanForBean();
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
asrt( R::count( 'bean' ), 1 );
}
/**
* Tests dependencies (variation).
*
* @return void
*/
public function testDependency4()
{
R::nuke();
$can = $this->createBeanInCan( TRUE );
R::store( $can );
R::trash( $can );
$can = $this->createCanForBean();
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
asrt( R::count( 'bean' ), 0 );
$can = $this->createBeanInCan( TRUE );
R::store( $can );
R::trash( $can );
$can = $this->createCanForBean();
asrt( R::count( 'bean' ), 1 );
R::trash( $can );
asrt( R::count( 'bean' ), 0 );
}
/**
* Issue #171
* The index name argument is not unique in processEmbeddedBean etc.
*
* @return void
*/
public function testIssue171()
{
R::getDatabaseAdapter()->addEventListener( 'sql_exec', $this );
$account = R::dispense( 'account' );
$user = R::dispense( 'user' );
$player = R::dispense( 'player' );
$account->ownUser[] = $user;
R::store( $account );
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_user_account' ) !== FALSE, TRUE );
$this->queries = array();
$account->ownPlayer[] = $player;
R::store( $account );
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_player_accou' ) !== FALSE, TRUE );
}
/**
* Tests whether foreign keys are created correctly for certain
* relations.
*
* @return void
*/
public function testCreationOfForeignKeys()
{
$this->queries = array();
$account = R::dispense( 'account' );
$user = R::dispense( 'user' );
$player = R::dispense( 'player' );
$user->account = $account;
R::store( $user );
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_user_account' ) !== FALSE, TRUE );
$this->queries = array();
$player->account = $account;
R::store( $player );
asrt( strpos( implode( ',', $this->queries ), 'index_foreignkey_player_accou' ) !== FALSE, TRUE );
}
/**
* Test helper method.
* Creates a bean in a can. The bean will get a reference
* to the can and can be made dependent.
*
* @return OODBBean $can
*/
private function createBeanInCan( $isExcl )
{
$can = R::dispense( 'can' );
$bean = R::dispense( 'bean' );
$can->name = 'bakedbeans';
$bean->taste = 'salty';
if ($isExcl) {
$can->xownBean[] = $bean;
} else {
$can->ownBean[] = $bean;
}
R::store( $can );
return $can;
}
/**
* Test helper method.
* Creates a bean in a can beginning with the bean. The bean will get a reference
* to the can and can be made dependent.
*
* @return OODBBean $can
*/
private function createCanForBean()
{
$can = R::dispense( 'can' );
$bean = R::dispense( 'bean' );
$bean->can = $can;
R::store( $bean );
return $can;
}
/**
* Log queries
*
* @param string $event
* @param Adapter $info
*/
public function onEvent( $event, $info )
{
$this->queries[] = $info->getSQL();
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Frozen
*
* Frozen mode tests
* When I split the repositories in frozen and fluid I discovered some missed
* code-paths in the tests.
* These tests are here to make sure the following scenarios work properly
* in frozen mode as well.
*
* @file RedUNIT/Base/Frozen.php
* @desc Test some scenarios we haven't covered for frozen mode.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Frozen extends Base
{
/**
* Tests the handling of trashed beans in frozen mode.
* Are the lists unset etc?
*
* @return void
*/
public function testTrash()
{
R::nuke();
$book = R::dispense( 'book' );
$book->xownPageList[] = R::dispense( 'page' );
$book->sharedTagList[] = R::dispense( 'tag' );
R::store( $book );
$book = $book->fresh();
R::freeze( TRUE );
$book->xownPageList = array();
R::store( $book );
$book = $book->fresh();
asrt( R::count('page'), 0 );
$book->xownPageList[] = R::dispense( 'page' );
R::store( $book );
$book = $book->fresh();
asrt( R::count('page'), 1 );
$book->xownPageList;
$book->sharedTagList;
R::trash( $book );
asrt( R::count('book'), 0 );
asrt( R::count('page'), 0 );
asrt( R::count('tag'), 1 );
asrt( R::count('book_tag'), 0 );
R::freeze( FALSE );
}
/**
* Tests whether invalid list checks are
* operational in frozen mode.
*
* @return void
*/
public function testInvalidList()
{
R::nuke();
$book = R::dispense( 'book' );
$book->xownPageList[] = R::dispense( 'page' );
$book->sharedTagList[] = R::dispense( 'tag' );
R::store( $book );
R::freeze( TRUE );
$book = R::dispense( 'book' );
$book->xownPageList[] = 'nonsense';
try {
R::store( $book );
fail();
} catch( \Exception $e ) {
pass();
}
R::freeze( FALSE );
}
/**
* Tests whether loading non-existant beans
* returns the same results in frozen mode.
*
* @return
*/
public function testLoadNonExistant()
{
R::nuke();
R::store( R::dispense( 'bean' ) );
R::freeze( TRUE );
$bean = R::load( 'bean', 123 );
R::freeze( FALSE );
asrt( ( $bean instanceof OODBBean ), TRUE );
asrt( $bean->id, 0 );
}
}

View File

@@ -0,0 +1,420 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\ModelHelper as ModelHelper;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\Adapter as Adapter;
use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
/**
* FUSE
*
* Tests whether we can associate model logic on-the-fly
* by defining models extending from SimpleModel. Tests
* whether the calls to facade trigger the corresponding
* methods on the model.
*
* @file RedUNIT/Base/Fuse.php
* @desc Tests Fuse feature; coupling beans to models.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Fuse extends Base
{
/**
* Hook into the jsonSerialize function #651
* Allow models to provide a jsonSerialize return.
* This test uses the Coffee Helper Model to add
* a description to the JSON representation of the bean.
*
* @return void
*/
public function testFUSEJSonSerialize()
{
if ( phpversion() < 5.4 ) return;
$coffee = R::dispense( 'coffee' );
$coffee->variant = 'Tropical';
$coffee->strength = 4;
$json = json_encode( $coffee );
$array = json_decode( $json, TRUE );
asrt( isset( $array['description'] ), TRUE );
asrt( $array['description'], 'Tropical.4' );
}
/**
* Test whether we can override the getModelForBean() method
* of the BeanHelper and use a custom BeanHelper to attach a model
* based on type.
*
* @return void
*/
public function testCustomBeanHelper()
{
$customBeanHelper = new \SoupBeanHelper( R::getToolbox() );
$oldBeanHelper = R::getRedBean()->getBeanHelper();
asrt( ( $oldBeanHelper instanceof SimpleFacadeBeanHelper ), TRUE );
R::getRedBean()->setBeanHelper( $customBeanHelper );
$meal = R::dispense( 'meal' );
asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
$cake = R::dispense( 'cake' );
asrt( is_null( $cake->box() ), TRUE );
$bean = R::dispense( 'coffee' );
asrt( ( $bean->box() instanceof \Model_Coffee ), TRUE );
$meal->setFlavour( 'tomato' );
asrt( $meal->getFlavour(), 'tomato' );
$meal->rating = 5;
R::store( $meal );
asrt( $meal->getFlavour(), 'tomato' );
$meal = $meal->unbox();
asrt( $meal->getFlavour(), 'tomato' );
$meal = R::findOne( 'meal' );
asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
asrt( $meal->getFlavour(), '' );
$meal->setFlavour( 'tomato' );
asrt( $meal->getFlavour(), 'tomato' );
$meal = $meal->unbox();
asrt( $meal->getFlavour(), 'tomato' );
R::getRedBean()->setBeanHelper( $oldBeanHelper );
}
/**
* Test FUSE hooks (i.e. open, update, update_after etc..)
*
* @return void
*/
public function testHooks()
{
R::nuke();
$probe = R::dispense( 'probe' );
$probe->name = 'test';
asrt( $probe->getLogActionCount(), 1 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 0 );
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
R::store( $probe );
asrt( $probe->getLogActionCount(), 3 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 1 );
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
$probe = R::load( 'probe', $probe->id );
asrt( $probe->getLogActionCount(), 2 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 1 );
asrt( $probe->getLogActionCount( 'update' ), 0 );
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
asrt( ( $probe->getDataFromLog( 1, 'id' ) === $probe->id ), TRUE );
$probe->clearLog();
R::trash( $probe );
asrt( $probe->getLogActionCount(), 2 );
asrt( $probe->getLogActionCount( 'dispense' ), 0 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 0 );
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
asrt( $probe->getLogActionCount( 'delete' ), 1 );
asrt( $probe->getLogActionCount( 'after_delete' ), 1 );
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
asrt( ( $probe->getDataFromLog( 1, 'bean' ) === $probe ), TRUE );
//less 'normal scenarios'
$probe = R::dispense( 'probe' );
$probe->name = 'test';
asrt( $probe->getLogActionCount(), 1 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 0 );
asrt( $probe->getLogActionCount( 'after_update' ), 0 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
R::store( $probe );
asrt( $probe->getLogActionCount(), 3 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 1 );
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
asrt( $probe->getMeta( 'tainted' ), FALSE );
asrt( $probe->getMeta( 'changed' ), FALSE );
R::store( $probe ); //not tainted, no FUSE save!
asrt( $probe->getLogActionCount(), 3 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 1 );
asrt( $probe->getLogActionCount( 'after_update' ), 1 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
$probe->xownProbeList[] = R::dispense( 'probe' );
//tainted, not changed, triggers FUSE
asrt( $probe->getMeta( 'tainted' ), TRUE );
asrt( $probe->getMeta( 'changed' ), FALSE );
R::store( $probe );
asrt( $probe->getMeta( 'tainted' ), FALSE );
asrt( $probe->getMeta( 'changed' ), FALSE );
asrt( $probe->getLogActionCount(), 5 );
asrt( $probe->getLogActionCount( 'dispense' ), 1 );
asrt( $probe->getLogActionCount( 'open' ), 0 );
asrt( $probe->getLogActionCount( 'update' ), 2 );
asrt( $probe->getLogActionCount( 'after_update' ), 2 );
asrt( $probe->getLogActionCount( 'delete' ), 0 );
asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
}
/**
* Tests the SimpleFacadeBeanHelper factory setter.
*
* @return void
*/
public function testFactory()
{
SimpleFacadeBeanHelper::setFactoryFunction( function( $name ) {
$model = new $name();
$model->setNote( 'injected', 'dependency' );
return $model;
} );
$bean = R::dispense( 'band' )->box();
asrt( ( $bean instanceof \Model_Band ), TRUE );
asrt( ( $bean->getNote('injected') ), 'dependency' );
SimpleFacadeBeanHelper::setFactoryFunction( NULL );
}
/**
* Make sure that beans of type book_page can be fused with
* models like BookPage (beautified) as well as Book_Page (non-beautified).
*/
public function testBeutificationOfLinkModel()
{
$page = R::dispense( 'page' );
$widget = R::dispense( 'widget' );
$page->sharedWidgetList[] = $widget;
R::store( $page );
$testReport = \Model_PageWidget::getTestReport();
asrt( $testReport, 'didSave' );
$page = R::dispense( 'page' );
$gadget = R::dispense( 'gadget' );
$page->sharedGadgetList[] = $gadget;
R::store( $page );
$testReport = \Model_Gadget_Page::getTestReport();
asrt( $testReport, 'didSave' );
}
/**
* Only theoretical.
*
* @return void
*/
public function testTheoreticalBeautifications()
{
$bean = R::dispense('bean');
$bean->setMeta('type', 'a_b_c');
R::store($bean);
$testReport = \Model_A_B_C::getTestReport();
asrt( $testReport, 'didSave' );
}
/**
* Test extraction of toolbox.
*
* @return void
*/
public function testGetExtractedToolBox()
{
$helper = new SimpleFacadeBeanHelper;
list( $redbean, $database, $writer, $toolbox ) = $helper->getExtractedToolbox();
asrt( ( $redbean instanceof OODB ), TRUE );
asrt( ( $database instanceof Adapter ), TRUE );
asrt( ( $writer instanceof QueryWriter ), TRUE );
asrt( ( $toolbox instanceof ToolBox ), TRUE );
}
/**
* Test FUSE and model formatting.
*
* @todo move tagging tests to tag tester.
*
* @return void
*/
public function testFUSE()
{
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$blog = R::dispense( 'blog' );
$blog->title = 'testing';
$blog->blog = 'tesing';
R::store( $blog );
$blogpost = R::load( "blog", 1 );
$post = R::dispense( "post" );
$post->message = "hello";
$blog->sharedPost[] = $post;
R::store($blog);
$a = R::getAll( "select * from blog " );
R::tag( $post, "lousy,smart" );
asrt( implode( ',', R::tag( $post ) ), "lousy,smart" );
R::tag( $post, "clever,smart" );
$tagz = implode( ',', R::tag( $post ) );
asrt( ( $tagz == "smart,clever" || $tagz == "clever,smart" ), TRUE );
R::tag( $blog, array( "smart", "interesting" ) );
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting" );
try {
R::tag( $blog, array( "smart", "interesting", "lousy!" ) );
pass();
} catch ( RedException $e ) {
fail();
}
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting,lousy!" );
asrt( implode( ",", R::tag( $blog ) ), "smart,interesting,lousy!" );
R::untag( $blog, array( "smart", "interesting" ) );
asrt( implode( ",", R::tag( $blog ) ), "lousy!" );
asrt( R::hasTag( $blog, array( "lousy!" ) ), TRUE );
asrt( R::hasTag( $blog, array( "lousy!", "smart" ) ), TRUE );
asrt( R::hasTag( $blog, array( "lousy!", "smart" ), TRUE ), FALSE );
R::tag( $blog, FALSE );
asrt( count( R::tag( $blog ) ), 0 );
R::tag( $blog, array( "funny", "comic" ) );
asrt( count( R::tag( $blog ) ), 2 );
R::addTags( $blog, array( "halloween" ) );
asrt( count( R::tag( $blog ) ), 3 );
asrt( R::hasTag( $blog, array( "funny", "commic", "halloween" ), TRUE ), FALSE );
R::unTag( $blog, array( "funny" ) );
R::addTags( $blog, "horror" );
asrt( count( R::tag( $blog ) ), 3 );
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
// No double tags
R::addTags( $blog, "horror" );
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
asrt( count( R::tag( $blog ) ), 3 );
}
/**
* Test error handling options FUSE.
*/
public function testModelErrorHandling()
{
$test = R::dispense( 'feed' );
$test->nonExistantMethod();
pass();
$old = R::setErrorHandlingFUSE( OODBBean::C_ERR_LOG );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], FALSE );
asrt( $old[1], NULL);
$test->nonExistantMethod(); //we cant really test this... :(
pass();
$old = R::setErrorHandlingFUSE( OODBBean::C_ERR_NOTICE );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_LOG );
asrt( $old[1], NULL);
set_error_handler(function($error, $str) {
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
}, E_USER_NOTICE);
$test->nonExistantMethod();
restore_error_handler();
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_WARN );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_NOTICE );
asrt( $old[1], NULL);
set_error_handler(function($error, $str) {
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
}, E_USER_WARNING);
$test->nonExistantMethod();
restore_error_handler();
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FATAL );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_WARN );
asrt( $old[1], NULL);
set_error_handler(function($error, $str) {
asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
}, E_USER_ERROR);
$test->nonExistantMethod();
restore_error_handler();
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_FATAL );
asrt( $old[1], NULL);
try {
$test->nonExistantMethod();
fail();
} catch (\Exception $e) {
pass();
}
global $test_bean;
$test_bean = $test;
global $has_executed_error_func_fuse;
$has_executed_error_func_fuse = FALSE;
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, function( $info ){
global $has_executed_error_func_fuse;
global $test_bean;
$has_executed_error_func_fuse = TRUE;
asrt( is_array( $info ), TRUE );
asrt( $info['method'], 'nonExistantMethod' );
asrt( json_encode( $info['bean']->export() ), json_encode( $test_bean->export() ) );
asrt( $info['message'], 'FUSE: method does not exist in model: nonExistantMethod' );
} );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_EXCEPTION );
asrt( $old[1], NULL);
$test->nonExistantMethod();
asrt( $has_executed_error_func_fuse, TRUE );
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_FUNC );
asrt( is_callable( $old[1] ), TRUE );
$old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
asrt( is_array( $old ), TRUE );
asrt( count( $old ), 2 );
asrt( $old[0], OODBBean::C_ERR_IGNORE );
asrt( $old[1], NULL);
try {
OODBBean::setErrorHandlingFUSE( 900 );
fail();
} catch (\Exception $e) {
pass();
asrt( $e->getMessage(), 'Invalid error mode selected' );
}
try {
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, 'hello' );
fail();
} catch (\Exception $e) {
pass();
asrt( $e->getMessage(), 'Invalid error handler' );
}
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
//make sure ignore FUSE events
$test = R::dispense('feed');
R::store( $test );
$test = $test->fresh();
R::trash( $test );
pass();
OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Hybrid
*
* Test Hybrid mode where the database can be unfozen
* in case of an exception during storing.
*
* @file RedUNIT/Base/Hybrid.php
* @desc Tests hybrid mode
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Hybrid extends Base
{
/**
* Tests hybrid mode.
*
* @return void
*/
public function testHybrid()
{
R::nuke();
$book = R::dispense('book');
$book->pages = 123;
$id = R::store( $book );
R::freeze( TRUE );
R::setAllowHybridMode( FALSE );
$book->title = 'Tales of a misfit';
try {
R::store( $book, TRUE );
fail();
} catch(\Exception $e) {
pass();
}
try {
R::store( $book, FALSE );
fail();
} catch(\Exception $e) {
pass();
}
$book = $book->fresh();
asrt( is_null( $book->title ), TRUE );
R::setAllowHybridMode( TRUE );
$book->title = 'Tales of a misfit';
try {
R::store( $book );
fail();
} catch(\Exception $e) {
pass();
}
try {
R::store( $book, FALSE );
fail();
} catch(\Exception $e) {
pass();
}
try {
R::store( $book, TRUE );
pass();
} catch(\Exception $e) {
fail();
}
$book = $book->fresh();
asrt( $book->title, 'Tales of a misfit' );
R::setAllowHybridMode( FALSE );
R::freeze( FALSE );
}
/**
* Test whether we can use Hybrid mode to alter columns.
* This won't work for SQLite.
*/
public function testHybridDataType()
{
R::nuke();
if ($this->currentlyActiveDriverID == 'mysql') {
R::exec('SET @@SESSION.sql_mode=\'STRICT_TRANS_TABLES\';');
}
if ($this->currentlyActiveDriverID == 'sqlite') return;
$book = R::dispense('book');
$book->pages = 1;
$id = R::store( $book, TRUE );
R::freeze( TRUE );
asrt( R::getRedBean()->isFrozen(), TRUE );
R::setAllowHybridMode( FALSE );
$book->pages = 'too many';
try {
R::store( $book, TRUE );
fail();
} catch(\Exception $e) {
pass();
}
asrt( R::getRedBean()->isFrozen(), TRUE );
R::setAllowHybridMode( TRUE );
asrt( R::getRedBean()->isFrozen(), TRUE );
R::debug(1);
try {
R::store( $book, TRUE );
pass();
} catch(\Exception $e) {
fail();
}
asrt( R::getRedBean()->isFrozen(), TRUE );
$book = $book->fresh();
echo $book;
asrt( $book->pages, 'too many' );
R::setAllowHybridMode( FALSE );
R::freeze( FALSE );
if ($this->currentlyActiveDriverID == 'mysql') {
R::exec('SET @@SESSION.sql_mode=\'\';');
}
}
/**
* Other exceptions must fall through...
*/
public function testHybridNonSQLException()
{
R::nuke();
$toy = R::dispense('brokentoy');
R::freeze( TRUE );
R::setAllowHybridMode( TRUE );
try {
R::store( $toy, TRUE );
fail();
} catch(\Exception $e) {
pass();
}
R::setAllowHybridMode( FALSE );
R::nuke();
$toy = R::dispense('toy');
R::freeze( TRUE );
R::setAllowHybridMode( TRUE );
try {
R::store( $toy, TRUE );
pass();
} catch(\Exception $e) {
fail();
}
R::setAllowHybridMode( FALSE );
}
/**
* Test whether Hybrid mode is only activated
* for latest or 5.4 without novice and ensure
* maintaining backward compatibility by not setting
* Hybrid allowed for 5.3 and earlier.
*/
public function testVersions()
{
R::useFeatureSet('novice/latest');
asrt( R::setAllowHybridMode( FALSE ), FALSE );
R::useFeatureSet('latest');
asrt( R::setAllowHybridMode( FALSE ), TRUE );
R::useFeatureSet('novice/5.4');
asrt( R::setAllowHybridMode( FALSE ), FALSE );
R::useFeatureSet('5.4');
asrt( R::setAllowHybridMode( FALSE ), TRUE );
R::useFeatureSet('novice/5.3');
asrt( R::setAllowHybridMode( FALSE ), FALSE );
R::useFeatureSet('5.3');
asrt( R::setAllowHybridMode( FALSE ), FALSE );
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Indexes
*
* Tests whether indexes are created properly and whether
* index creation errors are supressed (they are not important
* enough to cause exceptions during development phase).
*
* @file RedUNIT/Base/Indexes.php
* @desc Tests whether indexes are applied properly.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Indexes extends Base {
/**
* Tests whether a regular index is created properly.
*
* @return void
*/
public function testIndexCreation()
{
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->ownPageList[] = $page;
R::store( $book );
$indexes = getIndexes( 'page' );
asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE );
}
/**
* Tests indexes on parent beans.
*
* @return void
*/
public function testIndexCreationParentBean()
{
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$page->book = $book;
R::store( $page );
$indexes = getIndexes( 'page' );
asrt( in_array( 'index_foreignkey_page_book', $indexes ), TRUE );
}
/**
* Tests indexes on link tables.
*
* @return void
*/
public function testIndexCreationMany2Many()
{
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$book->sharedCategoryList[] = $category;
R::store( $book );
$indexes = getIndexes( 'book_category' );
asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE );
asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE );
R::nuke();
R::nuke();
$book = R::dispense( 'book' );
$category = R::dispense( 'category' );
$category->sharedBookList[] = $book;
R::store( $category );
$indexes = getIndexes( 'book_category' );
asrt( in_array( 'index_foreignkey_book_category_book', $indexes ), TRUE );
asrt( in_array( 'index_foreignkey_book_category_category', $indexes ), TRUE );
}
/**
* Tests indexes on aliases.
*
* @return void
*/
public function testIndexCreationAlias()
{
R::nuke();
$book = R::dispense( 'book' );
$author = R::dispense( 'author' );
$book->coAuthor = $author;
R::store( $book );
$indexes = getIndexes( 'book' );
asrt( in_array( 'index_foreignkey_book_co_author', $indexes ), TRUE );
R::nuke();
$project = R::dispense( 'project' );
$person = R::dispense( 'person' );
$person->alias( 'teacher' )->ownProject[] = $project;
$person2 = R::dispense( 'person' );
$person2->alias( 'student' )->ownProject[] = $project;
R::store( $person );
$indexes = getIndexes( 'project' );
asrt( in_array( 'index_foreignkey_project_teacher', $indexes ), TRUE );
R::store( $person2 );
$indexes = getIndexes( 'project' );
asrt( in_array( 'index_foreignkey_project_student', $indexes ), TRUE );
}
/**
* Tests index fails.
*
* @return void
*/
public function testIndexCreationFail()
{
R::nuke();
$book = R::dispense( 'book' );
$book->author_id = '999';
R::store( $book );
$indexes = getIndexes( 'book' );
//should just work fine
asrt( in_array( 'index_foreignkey_book_author', $indexes ), TRUE );
//these should just pass, no indexes but no errors as well
R::getWriter()->addIndex( 'book', 'bla', 'nonexist' );
pass();
R::getWriter()->addIndex( 'book', '@#$', 'nonexist' );
pass();
R::getWriter()->addIndex( 'nonexist', 'bla', 'nonexist' );
pass();
$indexesAfter = getIndexes( 'book' );
asrt( count( $indexesAfter ), count( $indexes ) );
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\SimpleModel as SimpleModel;
/**
* Issue259
*
* Tests whether this specific issue on github has been resolved.
* Issue #259 - Stash Cache breaks model delegation in open().
*
* @file RedUNIT/Base/Issue259.php
* @desc Issue #259 - Stash cache breaks model delegation in open().
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Issue259 extends Base
{
/**
* Test to make sure stash cache works with recursively opening models
* with FUSE.
*
* @return void
*/
public function testIssue259()
{
testpack( 'Testing Issue #259 - Stash Cache breaks model delegation in open().' );
$mother = R::dispense( 'mother' );
$mother->desc = 'I am mother';
R::store( $mother );
$child = R::dispense( 'child' );
$child->mother = $mother;
$child->desc = 'I am child';
$id = R::store( $child );
R::findOne( 'child', ' id = ?', array( $id ) );
R::find( 'child', ' id = ? ', array( $id ) );
R::load( 'child', $id );
}
}
/**
* Mock Model.
*/
class Model_Mother extends SimpleModel
{
public function open()
{
$bean = $this->bean;
asrt( $this->bean->desc, 'I am mother' );
}
}
/**
* Mock Model.
*/
class Model_Child extends SimpleModel
{
public function open()
{
$this->bean->mother;
asrt( $this->bean->desc, 'I am child' );
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
/**
* Issue303
*
* Tests whether this specific issue on github has been resolved.
* Test whether we have two different exception messages for
* properties and values.
*
* @file RedUNIT/Base/Issue303.php
* @desc Issue #303 - Split bean property exception.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Issue303 extends Base
{
/**
* Test whether we have two different exception messages for
* properties and values.
*
* @return void
*/
public function testIssue303()
{
testpack( 'Testing Issue #303 - Test splitting bean exception property/value.' );
try {
R::store( R::dispense( 'invalidbean' )->setAttr( 'invalid.property', 'value' ) );
fail();
} catch (RedException $e ) {
asrt( $e->getMessage(), 'Invalid Bean property: property invalid.property' );
}
try {
R::store( R::dispense( 'invalidbean' )->setAttr( 'property', array() ) );
fail();
} catch (RedException $e ) {
asrt( $e->getMessage(), 'Invalid Bean value: property property' );
}
try {
R::store( R::dispense( 'invalidbean' )->setAttr( 'property', new \stdClass ) );
fail();
} catch (RedException $e ) {
asrt( $e->getMessage(), 'Invalid Bean value: property property' );
}
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Issue 408
*
* Tests whether this specific issue on github has been resolved.
* Tests whether we can use export on beans having arrays in properties.
*
* @file RedUNIT/Mysql/Issue408.php
* @desc Test whether we can export beans with arrays in properties
* (deserialized/serialized on open/update).
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Issue408 extends Base
{
/**
* In the past it was not possible to export beans
* like 'feed' (Model_Feed).
*
* @return void
*/
public function testExportIssue()
{
R::nuke();
$feed = R::dispense( 'feed' );
$feed->post = array(
'first',
'second'
);
R::store( $feed );
$rows = R::getAll('SELECT * FROM feed');
asrt( $rows[0]['post'], '["first","second"]' );
$feed = $feed->fresh();
asrt( is_array( $feed->post ), TRUE );
asrt( $feed->post[0], 'first' );
asrt( $feed->post[1], 'second' );
R::store( $feed );
$rows = R::getAll('SELECT * FROM feed');
asrt( $rows[0]['post'], '["first","second"]' );
$feed = R::load( 'feed', $feed->id );
$feed->post[] = 'third';
R::store( $feed );
$rows = R::getAll('SELECT * FROM feed');
asrt( $rows[0]['post'], '["first","second","third"]' );
$feed = $feed->fresh();
asrt( is_array( $feed->post ), TRUE );
asrt( $feed->post[0], 'first' );
asrt( $feed->post[1], 'second' );
asrt( $feed->post[2], 'third' );
//now the catch: can we use export?
//PHP Fatal error: Call to a member function export() on a non-object
$feeds = R::exportAll( R::find( 'feed' ) );
asrt( is_array( $feeds ), TRUE );
$feed = reset( $feeds );
asrt( $feed['post'][0], 'first' );
asrt( $feed['post'][1], 'second' );
asrt( $feed['post'][2], 'third' );
//can we also dup()?
$feedOne = R::findOne( 'feed' );
R::store( R::dup( $feedOne ) );
asrt( R::count( 'feed' ), 2 );
//can we delete?
R::trash( $feedOne );
asrt( R::count( 'feed' ), 1 );
$feedTwo = R::findOne( 'feed' );
$feed = $feedTwo->export();
asrt( $feed['post'][0], 'first' );
asrt( $feed['post'][1], 'second' );
asrt( $feed['post'][2], 'third' );
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Issue 841
*
* After bindFunc() Shared List cannot remove items.
*
* @file RedUNIT/Base/Issue841.php
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Issue841 extends Base
{
/**
* After bindFunc() Shared List cannot remove items.
*
* @return void
*/
public function testIssue841()
{
R::nuke();
R::bindFunc( 'read', 'record.point', 'abs' );
R::bindFunc( 'write', 'record.point', 'abs' );
for($i = 0;$i < 3;$i++){
$tag = R::dispense('tag');
$tag->name = 'TAG_'.$i;
R::store($tag);
}
$record = R::dispense('record');
$record->point = rand(-100,-1);
$record->sharedTagList[] = R::load('tag',2);
R::store($record);
asrt(count($record->sharedTagList),1);
$record = R::load('record',1);
$record->sharedTagList = array();
R::store($record);
$record = R::load('record',1);
asrt(count($record->sharedTagList),0);
R::bindFunc( 'read', 'record.point', null );
R::bindFunc( 'write', 'record.point', null );
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Issue90
*
* Tests whether this specific issue on github has been resolved.
* Checking 'own' relationship, makes it impossible to trash a bean.
*
* @file RedUNIT/Base/Issue90.php
* @desc Issue #90 - cannot trash bean with ownproperty if checked in model.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Issue90 extends Base
{
/**
* Test for issue90.
* Checking 'own' relationship, makes it impossible to trash a bean.
*
* @return void
*/
public function testIssue90()
{
$s = R::dispense( 'box' );
$s->name = 'a';
$f = R::dispense( 'bottle' );
$s->ownBottle[] = $f;
R::store( $s );
$s2 = R::dispense( 'box' );
$s2->name = 'a';
R::store( $s2 );
R::trash( $s2 );
pass();
}
}

View File

@@ -0,0 +1,988 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Joins
*
* Tests the @joined keyword, this keyword in an SQL snippet
* allows you to join another table and use one or more of its columns
* in the query snippet, for instance for sorting or filtering.
*
* @file RedUNIT/Base/Joins.php
* @desc Tests joins in ownLists and trees.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Joins extends Base
{
private function quickTestBeans( $beans, $property ) {
$list = array();
foreach( $beans as $bean ) {
$list[] = $bean->{$property};
}
sort($list);
return implode(',', $list);
}
/**
* Test whether we can use [via] notation in parsed joins.
*
* @return void
*/
public function testParsedJoinsWithVia()
{
$project1 = R::dispense('project');
$project2 = R::dispense('project');
$project1->title = 'project1';
$project2->title = 'project2';
$employee1 = R::dispense('employee');
$employee2 = R::dispense('employee');
$employee1->name = 'a';
$employee2->name = 'b';
$participant1 = R::dispense('participant');
$participant2 = R::dispense('participant');
$participant3 = R::dispense('participant');
$participant1->employee = $employee1;
$participant2->employee = $employee1;
$participant3->employee = $employee2;
$participant1->project = $project1;
$participant2->project = $project2;
$participant3->project = $project2;
R::storeAll(array($participant1,$participant2,$participant3));
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? ', array('a'));
asrt(count($projects),2);
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('a','project1'));
asrt(count($projects),1);
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('a','project2'));
asrt(count($projects),1);
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? ', array('b'));
asrt(count($projects),1);
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('b','project1'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.employee[via:participant].name LIKE ? AND project.title = ? ', array('b','project2'));
asrt(count($projects),1);
/* shouldnt work if no [via:...] and no global aliases */
$projects = R::find('project', ' @shared.participant.name LIKE ? ', array('a'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('a','project1'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('a','project2'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.participant.name LIKE ? ', array('b'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('b','project1'));
asrt(count($projects),0);
$projects = R::find('project', ' @shared.participant.name LIKE ? AND project.title = ? ', array('b','project2'));
asrt(count($projects),0);
R::aliases(array('work'=>'project'));
$company1 = R::dispense('company');
$company2 = R::dispense('company');
$company1->work = $project1;
$company2->work = $project2;
R::storeAll( array( $company1, $company2 ) );
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? ', array('a'));
asrt(count($companies),2);
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('a','project1'));
asrt(count($companies),1);
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('a','project2'));
asrt(count($companies),1);
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? ', array('b'));
asrt(count($companies),1);
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ?', array('b','project1'));
asrt(count($companies),0);
$companies = R::find('company', ' @joined.work.shared.employee[via:participant].name LIKE ? AND @joined.work.title = ? ', array('b','project2'));
asrt(count($companies),1);
}
/**
* Can we use joins with aliases?
* Can we perform bean->alias->ownlist and bean->fetchas->other
* like operation in SQL as well?
*
* In this test:
* Albert: Almanac, Study Guide
* Bob: Poetry Bundle, Study Guide (coauthor)
*
* Texts:
* Example -> in Study Guide (as source)
* Essay -> in Study Guide (as book)
* Poem -> in Poetry Bundle (as book)
* Poem -> also in Almanac (as magazine)
*
* @return void
*/
public function testParsedJoinsWithAliasing()
{
R::nuke();
R::aliases(array());
$albert = R::dispense('person');
$bob = R::dispense('person');
$guide = R::dispense('book');
$almanac= R::dispense('book');
$poetry = R::dispense('book');
$albert->firstname = 'Albert';
$bob->firstname = 'Bob';
$guide->title = 'Study Guide';
$almanac->title = 'Almanac';
$poetry->title = 'Poems';
$guide->author = $albert;
$guide->coauthor = $bob;
$almanac->author = $albert;
$poetry->author = $bob;
$poem = R::dispense('text');
$essay = R::dispense('text');
$example = R::dispense('text');
$poem->content = 'poem';
$essay->content = 'essay';
$example->content = 'example';
$poem->magazine = $almanac;
$poem->book = $poetry;
$essay->book = $guide;
$example->source = $guide;
$fiction = R::dispense('tag');
$nonfiction = R::dispense('tag');
$fiction->description = 'fiction';
$nonfiction->description = 'non-fiction';
$example->sharedTag[] = $nonfiction;
$poem->sharedTag[] = $fiction;
$essay->sharedTag = array( $nonfiction, $fiction );
R::storeAll( array( $poem, $essay, $example ) );
$books = R::find('book', ' @joined.author.firstname = ? ', array('Bob'));
asrt(count($books), 0);
$books = R::find('book', ' @joined.author.firstname = ? ', array('Albert'));
asrt(count($books), 0);
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
asrt(count($books), 1);
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
asrt(count($books), 2);
R::freeze( TRUE );
try { $books = R::find('book', ' @joined.author.firstname = ? ', array('Bob')); fail(); } catch ( \Exception $e ) { pass(); }
try { $books = R::find('book', ' @joined.author.firstname = ? ', array('Albert')); fail(); } catch ( \Exception $e ) {pass();}
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
asrt(count($books), 1);
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
asrt(count($books), 2);
R::aliases(array('author' => 'person','coauthor' => 'person'));
$books = R::find('book', ' @joined.author.firstname = ? ', array('Bob'));
asrt(count($books), 1);
$books = R::find('book', ' @joined.author.firstname = ? ', array('Albert'));
asrt(count($books), 2);
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Bob'));
asrt(count($books), 1);
$books = R::find('book', ' @joined.person[as:author].firstname = ? ', array('Albert'));
asrt(count($books), 2);
R::freeze( FALSE );
R::aliases(array());
//If we want to find all the people who authored books like X
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
//yields Almanac and Poems
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%m%' ) );
asrt( count($authors), 2 );
//yields Almanac and Poems
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%m%' ) );
asrt( count($authors), 2 );
//yields nothing
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%x%' ) );
asrt( count($authors), 0 );
//yields nothing
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%x%' ) );
asrt( count($authors), 0 );
//If we want to find all the people who authored books starting with X
$authors = R::find( 'person', ' @own.book[coauthor].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
R::freeze( TRUE );
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
//yields Almanac and Poems
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%m%' ) );
asrt( count($authors), 2 );
//yields Almanac and Poems
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%m%' ) );
asrt( count($authors), 2 );
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Albert,Bob');
//yields nothing
$authors = R::find( 'person', ' @own.book[author].title LIKE ? ', array( '%x%' ) );
asrt( count($authors), 0 );
//yields nothing
$authors = R::find( 'person', ' @own.book[alias:author].title LIKE ? ', array( '%x%' ) );
asrt( count($authors), 0 );
$authors = R::find( 'person', ' @own.book[coauthor].title LIKE ? ', array( '%Study%' ) );
asrt( count($authors), 1 );
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Bob');
R::freeze(FALSE);
$books = R::find( 'book', ' @joined.person[author/coauthor].firstname = ?', array( 'Bob' ) );
asrt( count($books), 2 );
asrt( $this->quickTestBeans( $books, 'title' ), 'Poems,Study Guide');
//If we want all books where the author or the coauthor is named 'Bob':
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Bob' ) );
asrt( count($books), 2 );
asrt( $this->quickTestBeans( $books, 'title' ), 'Poems,Study Guide');
//If we want all books where the author or the coauthor is named 'Albert':
$books = R::find( 'book', ' @joined.person[as:author/].firstname = ?', array( 'Albert' ) );
asrt( count($books), 2 );
asrt( $this->quickTestBeans( $books, 'title' ), 'Almanac,Study Guide');
$books = R::find( 'book', ' @joined.person[as:coauthor/author].firstname = ?', array( 'Albert' ) );
asrt( count($books), 2 );
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Study%' ) );
asrt( count($authors), 2 );
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Poem%' ) );
asrt( count($authors), 1 );
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Bob');
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Alm%' ) );
asrt( $this->quickTestBeans( $authors, 'firstname' ), 'Albert');
asrt( count($authors), 1 );
R::freeze(TRUE);
$books = R::find( 'book', ' @joined.person[author/coauthor].firstname = ?', array( 'Bob' ) );
asrt( count($books), 2 );
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Bob' ) );
asrt( count($books), 2 );
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Albert' ) );
asrt( count($books), 2 );
$books = R::find( 'book', ' @joined.person[as:author/coauthor].firstname = ?', array( 'Albert' ) );
asrt( count($books), 2 );
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Study%' ) );
asrt( count($authors), 2 );
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Poem%' ) );
asrt( count($authors), 1 );
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].title LIKE ?', array( '%Alm%' ) );
asrt( count($authors), 1 );
R::freeze(FALSE);
//2 people as author/coauthor have written a book (study guide) that contains the essay
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text.content = ?', array( 'essay' ) );
asrt( count($authors), 2 );
//2 people as author/coauthor have written a book as source that contains the example
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source].content = ?', array( 'example' ) );
asrt( count($authors), 2 );
//1 person as author/coauthor has written a book as magazine/source that contains the poem
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source/magazine].content = ?', array( 'poem' ) );
asrt( count($authors), 1 );
//If we include book, we get 2 authors because the poem is also in the poetry bundle (book)
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:book/source/magazine].content = ?', array( 'poem' ) );
asrt( count($authors), 2 );
R::freeze(TRUE);
//2 people as author/coauthor have written a book (study guide) that contains the essay
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text.content = ?', array( 'essay' ) );
asrt( count($authors), 2 );
//2 people as author/coauthor have written a book as source that contains the example
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source].content = ?', array( 'example' ) );
asrt( count($authors), 2 );
//1 person as author/coauthor has written a book as magazine/source that contains the poem
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:source/magazine].content = ?', array( 'poem' ) );
asrt( count($authors), 1 );
//If we include book, we get 2 authors because the poem is also in the poetry bundle (book)
$authors = R::find( 'person', ' @own.book[alias:author/coauthor].own.text[alias:book/source/magazine].content = ?', array( 'poem' ) );
asrt( count($authors), 2 );
R::freeze(FALSE);
//Get all texts in books authored by Bob
$texts = R::find('text', ' @joined.book.joined.person[as:author].firstname = ? ',array('Bob'));
asrt( count($texts), 1 );
//Get all texts in books authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book.joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 2 );
//Get all texts in books as magazines or sources authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 1 );
//Get all texts in books as magazines or sources or books authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 3 );
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Albert'));
asrt( count($texts), 3 );
R::freeze(TRUE);
//Get all texts in books authored by Bob
$texts = R::find('text', ' @joined.book.joined.person[as:author].firstname = ? ',array('Bob'));
asrt( count($texts), 1 );
//Get all texts in books authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book.joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 2 );
//Get all texts in books as magazines or sources authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 1 );
//Get all texts in books as magazines or sources or books authored by Bob as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Bob'));
asrt( count($texts), 3 );
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor
$texts = R::find('text', ' @joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ? ',array('Albert'));
asrt( count($texts), 3 );
R::freeze(FALSE);
//Get all texts in books as magazines or sources or books authored by Albert as author/coauthor that have been tagged
//as non-fiction, i.e. the example text and the essay.
$texts = R::find('text', '
@joined.book[as:magazine/source/book].joined.person[as:author/coauthor].firstname = ?
AND
@shared.tag.description = ?
',array('Albert', 'non-fiction'));
asrt( count($texts), 2 );
//Get all texts in books or sources or books authored by Albert as author/coauthor that have been tagged
//as non-fiction, i.e. nothing, only the essay
$texts = R::find('text', '
@joined.book[as:source/book].joined.person[as:author/coauthor].firstname = ?
AND
@shared.tag.description = ?
',array('Albert', 'fiction'));
asrt( count($texts), 1 );
}
/**
* Non-join keywords starting with @ should be
* left untouched.
*/
public function testNonJoinsLeftUntouched()
{
$writer = R::getWriter();
$types = array( 'book', 'cafe', 'bean' );
$sqls = array(
'@version',
'@oined.satire',
'@oined.satire.laugh',
'@hared.lookalike.title',
'@powned.by.a.hacker',
'nothing here!',
'shared.person.name',
'owned.thing.name',
'joined.thing.name',
'shared.person.shared.tag.name',
'owned.thing.shared.tag.name',
'joined.thing.shared.tag.name',
'shared.person.joined.tag.name',
'owned.thing.joined.tag.name',
'joined.thing.joined.tag.name',
'shared.person.owned.tag.name',
'owned.thing.owned.tag.name',
'joined.thing.owned.tag.name'
);
$ctes = array( TRUE, FALSE );
foreach($types as $type) {
foreach($sqls as $sql) {
foreach($ctes as $cte) {
$same = $writer->parseJoin( $type, $sql, $cte );
asrt( trim($same), trim($sql) );
}
}
}
}
/**
* Can we join multiple tables with the same parent?
*
* @return void
*/
public function testMultipleJoinsSameParent()
{
R::nuke();
$TheHagueNorthEnd = R::dispense(array(
'_type' => 'painting',
'title' => 'Northend, The Hague',
'artist' => array(
'_type' => 'artist',
'name' => 'Isaac Israels',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'Haagse School' )
)
),
'ownDetail' => array(
array(
'_type' => 'detail',
'description' => 'awnings',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'object' )
)
),
array(
'_type' => 'detail',
'description' => 'sunlight',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'daytime' )
)
),
)
));
$Nighthawks = R::dispense(array(
'_type' => 'painting',
'title' => 'Nighthawks',
'artist' => array(
'_type' => 'artist',
'name' => 'Hopper',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'American Realism' )
)
),
'ownDetail' => array(
array(
'_type' => 'detail',
'description' => 'percolator',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'object' )
)
),
array(
'_type' => 'detail',
'description' => 'cigarette',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'object' )
)
),
array(
'_type' => 'detail',
'description' => 'night',
'sharedCategory' => array(
array( '_type' => 'category', 'label' => 'nocturnal' )
)
)
)
));
R::storeAll( array( $Nighthawks, $TheHagueNorthEnd ) );
$paintings = R::find('painting', '
@joined.artist.shared.category.label = ?
OR
@own.detail.shared.category.label= ?
', array('American Realism', 'nocturnal'));
asrt(count($paintings),1);
$painting = reset($paintings);
asrt($painting->title, 'Nighthawks');
$paintings = R::find('painting', '
@joined.artist.shared.category.label = ?
OR
@own.detail.shared.category.label= ?
', array('Haagse School', 'daytime'));
asrt(count($paintings),1);
$painting = reset($paintings);
asrt($painting->title, 'Northend, The Hague');
$paintings = R::find('painting', '
@own.detail.shared.category.label= ?
', array('object'));
asrt(count($paintings),2);
$paintings = R::find('painting', '
@own.detail.shared.category.label= ?
AND @own.detail.description= ?
', array('object', 'percolator'));
asrt(count($paintings),1);
$painting = reset($paintings);
asrt($painting->title, 'Nighthawks');
$paintings = R::find('painting', '
@own.detail.shared.category.label= ?
AND @own.detail.description= ?
', array('object', 'ashtray'));
asrt(count($paintings),0);
}
/**
* Complex tests for the parsed joins featured.
*
* @return void
*/
public function testComplexParsedJoins()
{
R::nuke();
$other = R::dispense('book');
R::store( $other );
$book = R::dispense('book');
$page = R::dispense('page');
$paragraph = R::dispense('paragraph');
$paragraph->title = 'hello';
$book->title = 'book';
$book->ownPage[] = $page;
$page->ownParagraph[] = $paragraph;
$figure = R::dispense('figure');
$chart = R::dispense('chart');
$chart->title = 'results';
$page->ownFigure[] = $figure;
$figure->ownChart[] = $chart;
R::store($book);
$books = R::find('book',' @own.page.own.paragraph.title = ? OR @own.page.own.figure.own.chart.title = ?', array('hello','results'));
asrt(count($books),1);
$book = reset($books);
asrt($book->title, 'book');
R::nuke();
R::aliases(array( 'author' => 'person' ));
$book = R::dispense('book');
$author = R::dispense('person');
$detail = R::dispense('detail');
$shop = R::dispense('shop');
$shop->name = 'Books4you';
$shop2 = R::dispense('shop');
$shop2->name = 'Readers Delight';
$author->name = 'Albert';
$detail->title = 'Book by Albert';
$book->ownDetailList[] = $detail;
$book->author = $author;
$shop->sharedBookList[] = $book;
$book2 = R::dispense('book');
$author2 = R::dispense('person');
$detail2 = R::dispense('detail');
$author2->name = 'Bert';
$detail2->title = 'Book by Bert';
$book2->ownDetailList[] = $detail2;
$book2->author = $author2;
$shop->sharedBookList[] = $book2;
$shop2->sharedBookList[] = $book2;
R::store($shop);
R::store($shop2);
//joined+own
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('Albert', 'Book by Albert'));
asrt(count($books),1);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', '%Book by%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? ', array('%ert%', 'Old Bookshop'));
asrt(count($books),0);
//joined+shared
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Books%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Read%'));
asrt(count($books),1);
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', 'Old Bookshop'));
asrt(count($books),0);
//own+shared
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Read%'));
asrt(count($books),1);
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book%'));
asrt(count($books),2);
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', 'Old Bookshop'));
asrt(count($books),0);
//joined+own+shared
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Books%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Read%'));
asrt(count($books),1);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', 'Old'));
asrt(count($books),0);
$books = R::find('book', ' @joined.author.name LIKE ? AND @own.detail.title LIKE ? AND @shared.shop.name LIKE ? ', array('%ert%', '%Book by%', '%'));
asrt(count($books),2);
//joined+joined
$page = R::dispense('page');
$page->text = 'Lorem Ipsum';
$category = R::dispense('category');
$category->name = 'biography';
$publisher = R::dispense('publisher');
$publisher->name = 'Good Books';
$book->publisher = $publisher;
$book->ownPageList[] = $page;
$category->sharedBookList[] = $book;
$page2 = R::dispense('page');
$page2->text = 'Blah Blah';
$category2 = R::dispense('category');
$category2->name = 'fiction';
$publisher2 = R::dispense('publisher');
$publisher2->name = 'Gutenberg';
$book2->publisher = $publisher2;
$book2->ownPageList = array($page2);
$category2->sharedBookList[] = $book2;
R::store($category);
R::store($category2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%ert%', 'Good Books'));
asrt(count($books),1);
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%ert%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('Unknown', '%'));
asrt(count($books),0);
$books = R::find('book', ' @joined.author.name LIKE ? AND @joined.publisher.name LIKE ?', array('%', '%'));
asrt(count($books),2);
//shared+shared
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Reader%', 'fiction'));
asrt(count($books),1);
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', 'biography'));
asrt(count($books),1);
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Old Bookshop', '%'));
asrt(count($books),0);
$books = R::find('book', ' @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('%', 'horror'));
asrt(count($books),0);
//own+own
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', 'Blah%'));
asrt(count($books),1);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', 'Lorem%'));
asrt(count($books),1);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Book%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('%', '%'));
asrt(count($books),2);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('%', 'Nah'));
asrt(count($books),0);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?', array('Nah', '%'));
asrt(count($books),0);
//joined+joined+shared+shared+own+own
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('Book%', 'Lorem%','Good%','Albert','Books4%','bio%'));
asrt(count($books),1);
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?', array('%', '%','%','%','%','%'));
asrt(count($books),2);
//in order clause
$book = R::findOne('book', ' ORDER BY @shared.category.name ASC LIMIT 1');
asrt($book->author->name, 'Albert');
$book = R::findOne('book', ' ORDER BY @shared.category.name DESC LIMIT 1');
asrt($book->author->name, 'Bert');
$book = R::findOne('book', ' ORDER BY @own.detail.title ASC LIMIT 1');
asrt($book->author->name, 'Albert');
$book = R::findOne('book', ' ORDER BY @own.detail.title DESC LIMIT 1');
asrt($book->author->name, 'Bert');
//order+criteria
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @shared.category.name ASC LIMIT 1', array('%'));
asrt($book->author->name, 'Albert');
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @shared.category.name DESC LIMIT 1', array('%'));
asrt($book->author->name, 'Bert');
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @own.detail.title ASC LIMIT 1', array('%'));
asrt($book->author->name, 'Albert');
$book = R::findOne('book', ' @joined.publisher.name LIKE ? ORDER BY @own.detail.title DESC LIMIT 1', array('%'));
asrt($book->author->name, 'Bert');
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
ORDER BY @own.detail.title ASC
', array('%', '%','%','%','%','%'));
asrt(count($books),2);
$first = reset($books);
$last = end($books);
asrt($first->author->name, 'Albert');
asrt($last->author->name, 'Bert');
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
ORDER BY
@shared.shop.name DESC,
@own.detail.title ASC
', array('%', '%','%','%','%','%'));
asrt(count($books),2);
$first = reset($books);
$last = end($books);
asrt($first->author->name, 'Bert');
asrt($last->author->name, 'Albert');
$books = R::find('book', ' @own.detail.title LIKE ? AND @own.page.text LIKE ?
AND @joined.publisher.name LIKE ? AND @joined.author.name LIKE ?
AND @shared.shop.name LIKE ? AND @shared.category.name LIKE ?
ORDER BY
@joined.publisher.name ASC,
@shared.shop.name DESC,
@own.detail.title ASC
', array('%', '%','%','%','%','%'));
asrt(count($books),2);
$first = reset($books);
$last = end($books);
asrt($first->author->name, 'Albert');
asrt($last->author->name, 'Bert');
}
/**
* Tests new parsed joins.
*
* @return void
*/
public function testParsedJoins()
{
$person = R::dispense('person');
$person->name = 'Albert';
$book = R::dispense('book');
$book->title = 'Book by Albert.';
$book->author = $person;
$person->movement = R::dispense(array('_type'=>'movement','name'=>'romanticism'));
R::store( $book );
$albert = $person;
$person = R::dispense('person');
$person->name = 'Bert';
$book = R::dispense('book');
$book->title = 'Book by Bert.';
$book->author = $person;
$bert = $person;
$person->movement = R::dispense(array('_type'=>'movement','name'=>'gothic'));
R::store( $book );
R::aliases(array( 'author' => 'person' ));
$books = R::find( 'book', ' @joined.author.name LIKE ? ', array('A%'));
asrt(count($books), 1);
$book = reset($books);
asrt($book->title,'Book by Albert.');
asrt($book->fetchAs('person')->author->name,'Albert');
$people = R::find( 'person', ' @joined.movement.name = ? ', array('romanticism')); // This works (aliases not involved)
asrt(count($people), 1);
$people = R::find( 'person', ' @joined.movement.name = ? ', array('gothic')); // This works (aliases not involved)
asrt(count($people), 1);
$people = R::find( 'person', ' @joined.movement.name = ? ', array('popscience')); // This works (aliases not involved)
asrt(count($people), 0);
$movements = R::find( 'movement', ' @own.author.name LIKE ? ', array( 'A%' )); // This works
asrt(count($movements), 1);
$movement = reset($movements);
asrt($movement->name, 'romanticism');
R::freeze(TRUE);
try{
R::find( 'person', ' @own.book.title LIKE ? ', array( 'A%' )); // This doesn't work as RedBean cannot guess which column it should bind the person to in the book table.
fail();
} catch(\Exception $e) {
pass();
}
R::freeze(FALSE);
$group = R::dispense('group');
$group->name = 'a';
$group->sharedAuthorList = array($bert, $albert);
R::store($group);
$group = R::dispense('group');
$group->name = 'b';
$group->sharedAuthorList = array($bert);
R::store($group);
$groups = R::find( 'group', ' @shared.author.name = ? ', array( 'Bert' )); // This works
asrt(count($groups),2);
R::tag($albert, array('male','writer'));
R::tag($bert, array('male'));
$people = R::find( 'person', ' @shared.tag.title = ? ', array( 'writer' )); // This works (aliases not involved)
asrt(count($people),1);
R::tag($albert, array('male','writer'));
R::tag($bert, array('male'));
$people = R::find( 'person', ' @shared.tag.title = ? ', array( 'male' )); // This works (aliases not involved)
asrt(count($people),2);
$user1 = R::dispense('user');
$user1->name = 'user1';
$user2 = R::dispense('user');
$user2->name = 'user2';
$status = R::dispense('status');
$status->whitelist = TRUE;
$user1->status = $status;
$status2 = R::dispense('status');
$status2->whitelist = FALSE;
$user2->status = $status2;
R::storeAll(array($user1,$user2));
$whitelisted = R::find( 'user', ' @joined.status.whitelist = ? ', array( 1 ) );
asrt(count($whitelisted), 1);
$user = reset($whitelisted);
asrt($user->name, 'user1');
$whitelisted = R::find( 'user', ' @joined.status.whitelist = ? ', array( 0 ) );
R::debug(0);
asrt(count($whitelisted), 1);
$user = reset($whitelisted);
asrt($user->name, 'user2');
}
/**
* Tests joins with ownCount().
*
* @return void
*/
public function testJoinsInCount()
{
R::nuke();
$author = R::dispense( 'author' );
$book = R::dispense( 'book' );
$info = R::dispense( 'info' );
$info->title = 'x';
$author->xownBookList[] = $book;
$book->info = $info;
$book = R::dispense( 'book' );
$info = R::dispense( 'info' );
$info->title = 'y';
$author->xownBookList[] = $book;
$book->info = $info;
R::store( $author );
$author = $author->fresh();
$books = $author->withCondition(' @joined.info.title != ? ', array('x'))->countOwn('book');
asrt($books, 1);
$books = $author->withCondition(' @joined.info.title != ? ', array('y'))->countOwn('book');
asrt($books, 1);
$books = $author->withCondition(' @joined.info.title IN (?,?) ', array('x','y'))->countOwn('book');
asrt($books, 2);
}
/**
* Test Joins.
*
* @return void
*/
public function testJoins()
{
R::nuke();
list($a1, $a2, $a3) = R::dispense('area', 3);
list($p1, $p2) = R::dispense('person', 2);
list($v1, $v2, $v3, $v4) = R::dispense('visit', 4);
$a1->name = 'Belgium';
$a2->name = 'Arabia';
$a3->name = 'France';
$v1->person = $p1;
$v2->person = $p1;
$v3->person = $p2;
$v4->person = $p2;
$v1->area = $a3;
$v2->area = $a2;
$v3->area = $a2;
$v4->area = $a1;
$v1->label = 'v1 to France';
$v2->label = 'v2 to Arabia';
$v3->label = 'v3 to Arabia';
$v4->label = 'v4 to Belgium';
R::storeAll( array($v1,$v2,$v3,$v4) );
$visits = $p1->ownVisit;
asrt( is_array( $visits ), TRUE );
asrt( count( $visits ), 2 );
$names = array();
foreach( $visits as $visit ) {
asrt( isset( $visit->label ), TRUE );
asrt( isset( $visit->name ), FALSE );
asrt( isset( $visit->visit_id ), FALSE );
$names[] = $visit->label;
}
$labelList = implode( ',', $names );
asrt( $labelList, 'v1 to France,v2 to Arabia' );
$visits = $p1
->with('ORDER BY @joined.area.name ASC')->ownVisit;
asrt( is_array( $visits ), TRUE );
asrt( count( $visits ), 2 );
$names = array();
foreach( $visits as $visit ) {
asrt( isset( $visit->label ), TRUE );
asrt( isset( $visit->name ), FALSE );
asrt( isset( $visit->visit_id ), FALSE );
$names[] = $visit->label;
}
$labelList = implode( ',', $names );
asrt( $labelList, 'v2 to Arabia,v1 to France' );
}
/**
* Helper for the next test.
*
* @param array $books the books we are going to check
* @param string $numberList the numbers that are expected
*
* @return void
*/
private function checkBookNumbers( $books, $numberList )
{
$numbers = explode( ',', $numberList );
asrt( is_array( $books ), TRUE );
asrt( count( $books ), count( $numbers ) );
$bookNumbers = '';
$bookNumberArray = array();
foreach( $books as $book ) {
asrt( isset( $book->num ), TRUE );
asrt( isset( $book->title), FALSE );
$bookNumberArray[] = $book->num;
}
$bookNumbers = implode( ',', $bookNumberArray);
asrt( $bookNumbers, $numberList );
}
/**
* Tests the more complicated scenarios for
* with-joins.
*
* @return void
*/
private function testComplexCombinationsJoins()
{
$author = R::dispense( 'author' );
$books = R::dispense( 'book', 4 );
$books[0]->num = 0;
$books[1]->num = 1;
$books[2]->num = 2;
$books[3]->num = 3;
$books[0]->info = R::dispense('info')->setAttr('title', 'Learning PHP');
$books[1]->info = R::dispense('info')->setAttr('title', 'Learning PHP and JavaScript');
$books[2]->info = R::dispense('info')->setAttr('title', 'Learning Cobol');
$books[3]->info = R::dispense('info')->setAttr('title','Gardening for Beginners');
$books[0]->category = R::dispense('category')->setAttr('title', 'computers');
$books[1]->category = R::dispense('category')->setAttr('title', 'computers');
$books[2]->category = R::dispense('category')->setAttr('title', 'computers');
$books[3]->category = R::dispense('category')->setAttr('title','gardening');
$author->ownBookList = $books;
R::store($author);
//Base test...
$books = $author->ownBookList;
$this->checkBookNumbers( $books, '0,1,2,3' );
//Just a basic Join...
$books = $author->withCondition(' @joined.info.title LIKE ? ORDER BY book.num ASC ', array( '%PHP%' ) )->ownBookList;
$this->checkBookNumbers( $books, '0,1' );
//Mix Join and criteria
$books = $author->withCondition(' @joined.info.title LIKE ? AND num > 0 ORDER BY book.num ASC ', array( '%PHP%' ) )->ownBookList;
$this->checkBookNumbers( $books, '1' );
//Basic join
$books = $author->withCondition(' @joined.info.title LIKE ? ORDER BY book.num ASC', array( '%ing%' ) )->ownBookList;
$this->checkBookNumbers( $books, '0,1,2,3' );
//Two joins
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.category.title = ? ORDER BY book.num ASC', array( '%ing%', 'computers' ) )->ownBookList;
$this->checkBookNumbers( $books, '0,1,2' );
//Join the same type twice... and order
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.category.title = ? ORDER BY @joined.info.title ASC ', array( '%ing%', 'computers' ) )->ownBookList;
$this->checkBookNumbers( $books, '2,0,1' );
//Join the same type twice
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY book.num ASC', array( '%ing%', '%Learn%' ) )->ownBookList;
$this->checkBookNumbers( $books, '0,1,2' );
//Join the same type 3 times and order
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY @joined.info.title DESC', array( '%ing%', '%Learn%' ) )->ownBookList;
$this->checkBookNumbers( $books, '1,0,2' );
//Join the same type 3 times and order and limit
$books = $author->withCondition(' @joined.info.title LIKE ? AND @joined.info.title LIKE ? ORDER BY @joined.info.title DESC LIMIT 1', array( '%ing%', '%Learn%' ) )->ownBookList;
$this->checkBookNumbers( $books, '1' );
//Other combinations I can think of...
$books = $author->withCondition(' @joined.category.title LIKE ? ORDER BY @joined.info.title DESC', array( '%ing%' ) )->ownBookList;
$this->checkBookNumbers( $books, '3' );
$books = $author->withCondition(' @joined.category.title LIKE ? AND num < 4 ORDER BY @joined.info.title DESC', array( '%ing%' ) )->ownBookList;
$this->checkBookNumbers( $books, '3' );
//multiple ordering
$books = $author->with(' ORDER BY @joined.category.title ASC, @joined.info.title ASC' )->ownBookList;
$this->checkBookNumbers( $books, '2,0,1,3' );
$books = $author->with(' ORDER BY @joined.category.title DESC, @joined.info.title ASC' )->ownBookList;
$this->checkBookNumbers( $books, '3,2,0,1' );
$books = $author->with(' ORDER BY @joined.category.title DESC, @joined.info.title ASC LIMIT 2' )->ownBookList;
$this->checkBookNumbers( $books, '3,2' );
}
/**
* Tests the more complicated scenarios for
* with-joins.
*
* @return void
*/
public function testComplexInFrozenMode()
{
R::freeze( FALSE );
$this->testComplexCombinationsJoins();
R::freeze( TRUE );
$this->testComplexCombinationsJoins();
R::freeze( FALSE );
}
/**
* Tests R::setNarrowFieldMode() and
* OODBBean::ignoreJoinFeature().
*/
public function testSystemWideSettingsForJoins()
{
R::nuke();
$author = R::dispense( 'author' );
$book = R::dispense( 'book' );
$info = R::dispense( 'info' );
$info->title = 'x';
$author->xownBookList[] = $book;
$book->info = $info;
R::store( $author );
$author = $author->fresh();
$books = $author->withCondition(' @joined.info.title != ? ', array('y1') )->xownBookList;
$firstBook = reset( $books );
asrt( isset( $firstBook->title ), FALSE );
R::setNarrowFieldMode( FALSE );
$author = $author->fresh();
$books = $author->withCondition(' @joined.info.title != ? ', array('y2') )->xownBookList;
$firstBook = reset( $books );
asrt( isset( $firstBook->title ), TRUE );
R::setNarrowFieldMode( TRUE );
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Keywords
*
* Tests whether we can use keywords as bean types and
* property names without running into security or stablity issues.
* RedBeanPHP should properly escape all bean types and properties
* so we may use whatever string we want.
*
* @file RedUNIT/Base/Keywords.php
* @desc Tests for possible keyword clashes.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Keywords extends Base
{
/**
* What drivers should be loaded for this test pack?
*
* CUBRID has inescapable keywords :/
*
* @return array
*/
public function getTargetDrivers()
{
return array( 'mysql', 'pgsql', 'sqlite' ); // CUBRID excluded for now.
}
/**
* Test if RedBeanPHP can properly handle keywords.
*
* @return void
*/
public function testKeywords()
{
$keywords = array(
'anokeyword', 'znokeyword', 'group', 'drop',
'inner', 'join', 'select', 'table',
'int', 'cascade', 'float', 'call',
'in', 'status', 'order', 'limit',
'having', 'else', 'if', 'while',
'distinct', 'like'
);
foreach ( $keywords as $k ) {
R::nuke();
$bean = R::dispense( $k );
$bean->$k = $k;
$id = R::store( $bean );
$bean = R::load( $k, $id );
$bean2 = R::dispense( 'other' );
$bean2->name = $k;
$bean->bean = $bean2;
$bean->ownBean[] = $bean2;
$bean->sharedBean[] = $bean2;
$id = R::store( $bean );
R::trash( $bean );
pass();
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Large Number Limit Test (issue #386)
*
* Tests whether we can use large numbers for limit clause.
*
* @file RedUNIT/Base/Largenum.php
* @desc Test whether we can use large numbers in LIMIT clause (PDO bindings).
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Largenum extends Base
{
/**
* Test for issue #386.
* Can we use large numbers in LIMIT ?
*
* @return void
*/
public function testLargeNum()
{
if ( defined( 'HHVM_VERSION' ) ) return; //oops hhvm has incorrect binding for large nums.
$number = R::dispense( 'number' );
$number->name = 'big number';
R::store( $number );
//This should not cause an error... (some people use LIMIT 0, HUGE to simulate OFFSET on MYSQL).
$beans = R::findAll( 'number', ' LIMIT ? ', array( PHP_INT_MAX ) );
asrt( is_array( $beans ), TRUE );
asrt( count( $beans ), 1 );
pass();
}
}

View File

@@ -0,0 +1,256 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\Logger as Logger;
use RedBeanPHP\Logger\RDefault as RDefault;
use RedBeanPHP\Logger\RDefault\Debug as Debug;
/**
* Logging
*
* Tests the Query Logging tools that are part of RedBeanPHP.
*
* @file RedUNIT/Base/Logging.php
* @desc Tests Logging facilities.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Logging extends Base
{
/**
* Test basic logging functionality.
*
* @return void
*/
public function testBasicLogging()
{
R::nuke();
R::startLogging();
R::store( R::dispense( 'book' ) );
$logs = R::getLogs();
$count = count( $logs );
asrt( ( count( $logs ) > 0 ), TRUE );
asrt( ( R::getLogger() instanceof Logger ), TRUE );
R::stopLogging();
R::store( R::dispense( 'book' ) );
$logs = R::getLogs();
asrt( ( count( $logs ) === 0 ), TRUE );
}
/**
* Test for Issue #751 (Update Logger to accept parameter typed bindings):
* While debugging some of our queries, we noticed
* the logger would often display
* 'Array' as value for the bindings,
* even when the SQL query seemed to work correctly.
* Debugging this, it appeared the debug logger did
* not support the new parameter type bindings added in 5.3.
* This merge request adds support for
* the PDO::PARAM_INT and PDO::PARAM_STR
* to the Debug logger, as well as a visible support
* for the RPDO flagUseStringOnlyBinding flag.
*
* @return void
*/
public function testIssue751()
{
R::nuke();
$debugger = new Debug;
$database = R::getDatabaseAdapter()->getDatabase();
$database->setLogger( $debugger );
asrt( $database->getLogger(), $debugger );
$database->setEnableLogging( TRUE );
$debugger->setMode( RDefault::C_LOGGER_ARRAY );
/* debug logger with nostringonlybinding should have unquoted ints */
R::store( R::dispense( 'book' ) );
R::getAll( 'SELECT * FROM book WHERE id < ?', array( array( 999, \PDO::PARAM_INT ) ) );
asrt( count( $debugger->grep('999') ), 1 );
asrt( count( $debugger->grep('\'999\'') ), 0 );
asrt( count( $debugger->grep('rray') ), 0 );
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->clear();
R::getAll( 'SELECT * FROM book WHERE id < ?', array( array( 999, \PDO::PARAM_STR ) ) );
/* ...but quoted strings */
asrt( count( $debugger->grep('\'999\'') ), 1 );
asrt( count( $debugger->grep('rray') ), 0 );
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->clear();
/* even if PARAM INT if stringonlybinding then override */
$debugger->setUseStringOnlyBinding( TRUE );
R::getAll( 'SELECT * FROM book WHERE id < ?', array( array( 999, \PDO::PARAM_INT ) ) );
asrt( count( $debugger->grep('\'999\'') ), 1 );
asrt( count( $debugger->grep('rray') ), 0 );
/* if no type and stringonlybinding always quote */
$debugger->clear();
R::getAll( 'SELECT * FROM book WHERE id < ?', array( 999 ) );
asrt( count( $debugger->grep('\'999\'') ), 1 );
asrt( count( $debugger->grep('rray') ), 0 );
/* a more closer inspection */
/* log implicit INT param without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( 123 ) );
asrt( count( $debugger->grep('123') ), 1 );
asrt( count( $debugger->grep('\'123\'') ), 0 );
/* log implicit STR param without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( 'abc' ) );
asrt( count( $debugger->grep('\'abc\'') ), 1 );
/* log NULL param without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( NULL ) );
asrt( count( $debugger->grep('NULL') ), 1 );
asrt( count( $debugger->grep('\'NULL\'') ), 0 );
/* log explicit INT param without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( 123, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('123') ), 1 );
asrt( count( $debugger->grep('\'123\'') ), 0 );
/* log explicit STR param without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( 'abc', \PDO::PARAM_STR ) );
asrt( count( $debugger->grep('\'abc\'') ), 1 );
/* log NULL with explicit param type without StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( NULL, \PDO::PARAM_STR ) );
asrt( count( $debugger->grep('NULL') ), 1 );
asrt( count( $debugger->grep('\'NULL\'') ), 0 );
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( NULL, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('NULL') ), 1 );
asrt( count( $debugger->grep('\'NULL\'') ), 0 );
/* log implicit INT param with StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( TRUE );
$debugger->log(' Hello ? ', array( 123 ) );
asrt( count( $debugger->grep('\'123\'') ), 1 );
/* log implicit STR param with StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( TRUE );
$debugger->log(' Hello ? ', array( 'abc' ) );
asrt( count( $debugger->grep('\'abc\'') ), 1 );
/* log NULL param with StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( TRUE );
$debugger->log(' Hello ? ', array( NULL ) );
asrt( count( $debugger->grep('\'NULL\'') ), 1 );
/* log explicit INT param with StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( TRUE );
$debugger->log(' Hello ? ', array( 123, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('\'123\'') ), 1 );
/* log explicit STR param with StringOnly */
$debugger->clear();
$debugger->setUseStringOnlyBinding( TRUE );
$debugger->log(' Hello ? ', array( 'abc', \PDO::PARAM_STR ) );
asrt( count( $debugger->grep('\'abc\'') ), 1 );
/* log NULL with explicit param type with StringOnly - remains just NULL */
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( NULL, \PDO::PARAM_STR ) );
asrt( count( $debugger->grep('NULL') ), 1 );
asrt( count( $debugger->grep('\'NULL\'') ), 0 );
$debugger->clear();
$debugger->setUseStringOnlyBinding( FALSE );
$debugger->log(' Hello ? ', array( NULL, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('NULL') ), 1 );
asrt( count( $debugger->grep('\'NULL\'') ), 0 );
$debugger->setUseStringOnlyBinding( FALSE );
/* Does stringonly mode switch along with Database mode ? */
$database->setUseStringOnlyBinding( TRUE );
$debugger->clear();
$debugger->log(' Hello ? ', array( 123, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('\'123\'') ), 1 );
$database->setUseStringOnlyBinding( FALSE );
$debugger->clear();
$debugger->log(' Hello ? ', array( 123, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('\'123\'') ), 0 );
asrt( count( $debugger->grep('123') ), 1 );
$database->setUseStringOnlyBinding( TRUE );
$debugger->clear();
$debugger->log(' Hello ? ', array( 123, \PDO::PARAM_INT ) );
asrt( count( $debugger->grep('\'123\'') ), 1 );
$database->setUseStringOnlyBinding( FALSE );
$debugger->setUseStringOnlyBinding( FALSE );
}
/**
* Can we manually set a logger and enable logging?
*
* @return void
*/
public function testCanSetLogger()
{
R::nuke();
R::store( R::dispense( 'bean' ) );
$logger = new RDefault;
$logger->setMode( RDefault::C_LOGGER_ARRAY );
$database = R::getDatabaseAdapter()->getDatabase();
$database->setLogger( $logger );
asrt( $database->getLogger(), $logger );
$database->setEnableLogging( FALSE );
$logs = $logger->getLogs();
asrt( is_array( $logs ), TRUE );
asrt( count( $logs ), 0 );
$database->setEnableLogging( TRUE );
$logs = $logger->getLogs();
asrt( is_array( $logs ), TRUE );
asrt( count( $logs ), 0 );
R::findOne( 'bean' ); //writes 3 log entries
$logs = $logger->getLogs();
asrt( is_array( $logs ), TRUE );
asrt( count( $logs ), 3 );
}
/**
* Test query counter.
*
* @return void
*/
public function testQueryCount()
{
R::nuke();
R::store( R::dispense( 'bean' ) );
R::resetQueryCount();
asrt( R::getQueryCount(), 0 );
R::findOne( 'bean' );
asrt( R::getQueryCount(), 1 );
R::resetQueryCount();
asrt( R::getQueryCount(), 0 );
R::findOne( 'bean' );
R::findOne( 'bean' );
R::findOne( 'bean' );
asrt( R::getQueryCount(), 0 );
R::store( R::dispense( 'bean2' ) );
R::resetQueryCount();
R::findOne( 'bean' );
R::findOne( 'bean2' );
asrt( R::getQueryCount(), 2 );
R::resetQueryCount();
R::findOne( 'bean', ' id < 100' );
R::findOne( 'bean', ' id < 101' );
R::findOne( 'bean', ' id < 102' );
R::findOne( 'bean', ' id < 103' );
asrt( R::getQueryCount(), 4 );
R::findOne( 'bean', ' id < 100' );
R::findOne( 'bean', ' id < 101' );
R::findOne( 'bean', ' id < 102' );
R::findOne( 'bean', ' id < 103' );
asrt( R::getQueryCount(), 4 );
R::findOne( 'bean', ' id < 104' );
asrt( R::getQueryCount(), 5 );
}
}

View File

@@ -0,0 +1,653 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\Logger\RDefault as RDefault;
use RedBeanPHP\Logger as Logger;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\OODB as OODB;
use RedBeanPHP\Adapter as Adapter;
use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\RedException\SQL as SQL;
use RedBeanPHP\Driver\RPDO as RPDO;
use RedBeanPHP\SimpleModel as SimpleModel;
/**
* Misc
*
* @file RedUNIT/Base/Misc.php
* @desc Various tests.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Misc extends Base
{
/**
* Can we use data definition templates?
*
* @return void
*/
public function testDDLTemplates()
{
R::nuke();
R::debug( TRUE, 1 );
$writer = R::getWriter();
$writer->setDDLTemplate( 'createTable', 'joke', $writer->getDDLTemplate( 'createTable', 'joke' ) . ' /* haha */ ' );
$writer->setDDLTemplate( 'addColumn', 'joke', $writer->getDDLTemplate( 'addColumn', 'joke' ) . ' /* hihi */ ' );
$writer->setDDLTemplate( 'widenColumn', 'joke', $writer->getDDLTemplate( 'widenColumn', 'joke' ) . ' /* hoho */ ' );
$joke = R::dispense('joke');
R::store( $joke );
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( 'haha' );
asrt( count( $logs ), 1 );
$joke->punchline = 1;
R::store( $joke );
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( 'hihi' );
asrt( count( $logs ), 1 );
$joke->punchline = '...';
R::store( $joke );
$logs = R::getDatabaseAdapter()->getDatabase()->getLogger()->grep( 'hoho' );
asrt( count( $logs ), 1 );
R::debug( FALSE );
}
/**
* Github issue:
* Remove $NULL to directly return NULL #625
* @@ -1097,8 +1097,7 @@ public function &__get( $property )
* $this->all = FALSE;
* $this->via = NULL;
*
* - $NULL = NULL;
* - return $NULL;
* + return NULL;
*
* leads to regression:
* PHP Stack trace:
* PHP 1. {main}() testje.php:0
* PHP 2. RedBeanPHP\OODBBean->__get() testje.php:22
* Notice: Only variable references should be returned by reference in rb.php on line 2529
*/
public function testReferencedGetInBeans()
{
$bean = R::dispense( 'bean' );
//this will trigger notice if &__get() returns NULL instead of $NULL.#625
$x = $bean->hello;
pass();
$x = $bean->reference;
pass();
$x = $bean->nullvalue;
pass();
}
public static $setupPartialBeansTestDone = 0;
/**
* Check partial beans at setup()
*/
public function testPartialBeansAtSetup()
{
if (self::$setupPartialBeansTestDone) return; /* only needs to be tested once */
$currentDB = R::$currentDB;
$key = 'partialBeanBase' . time();
$dsn = 'sqlite:/tmp/test.txt';
$user = '';
$pass = '';
$frozen = FALSE;
$partialBeans = TRUE;
R::addDatabase( $key, $dsn, $user, $pass, $frozen, $partialBeans);
$redbean = R::getRedBean();
$wasItSet = $redbean->getCurrentRepository()->usePartialBeans( FALSE );
R::selectDatabase( $key );
$redbean = R::getRedBean();
$wasItSet = $redbean->getCurrentRepository()->usePartialBeans( FALSE );
asrt( $wasItSet, TRUE );
self::$setupPartialBeansTestDone = 1;
R::selectDatabase( $currentDB );
}
/**
* Test whether we can set the 'auto clear'
* option in OODB.
*
* @return void
*/
public function testAutoClearHistory()
{
testpack( 'Auto clear history' );
$book = R::dispense( 'book' );
$book->pages = 100;
$book->title = 'book';
R::store( $book );
$book = R::findOne( 'book' );
asrt( $book->hasChanged( 'title' ), FALSE );
$book->title = 'yes';
R::store( $book );
asrt( $book->hasChanged( 'title' ), TRUE );
OODB::autoClearHistoryAfterStore( TRUE );
$book = R::findOne( 'book' );
asrt( $book->hasChanged( 'title' ), FALSE );
$book->title = 'yes2';
R::store( $book );
asrt( $book->hasChanged( 'title' ), FALSE );
OODB::autoClearHistoryAfterStore( FALSE );
$book = R::findOne( 'book' );
asrt( $book->hasChanged( 'title' ), FALSE );
$book->title = 'yes';
R::store( $book );
asrt( $book->hasChanged( 'title' ), TRUE );
}
/**
* Tests the R::inspect() method on the Facade.
*
* @return void
*/
public function testInspect() {
testpack( 'Test R::inspect() ' );
R::nuke();
R::store( R::dispense( 'book' )->setAttr( 'title', 'book' ) );
$info = R::inspect();
asrt( count( $info ), 1 );
asrt( strtolower( $info[0] ), 'book' );
$info = R::inspect( 'book' );
asrt( count( $info ), 2 );
$keys = array_keys( $info );
sort($keys);
asrt( strtolower( $keys[0] ), 'id' );
asrt( strtolower( $keys[1] ), 'title' );
}
/**
* Test whether we can use the tableExist() method in OODB
* instances directly to help us determine
* the existance of a table.
*
* @return void
*/
public function testTableExist()
{
R::nuke();
R::store( R::dispense( 'book' ) );
R::freeze( FALSE );
asrt( R::getRedBean()->tableExists( 'book' ), TRUE );
asrt( R::getRedBean()->tableExists( 'book2' ), FALSE );
R::freeze( TRUE );
asrt( R::getRedBean()->tableExists( 'book' ), TRUE );
asrt( R::getRedBean()->tableExists( 'book2' ), FALSE );
R::freeze( FALSE );
}
/**
* Normally the check() method is always called indirectly when
* dealing with beans. This test ensures we can call check()
* directly. Even though frozen repositories do not rely on
* bean checking to improve performance the method should still
* offer the same functionality when called directly.
*
* @return void
*/
public function testCheckDirectly()
{
$bean = new OODBBean;
$bean->setProperty('id', 0);
$bean->setMeta( 'type', 'book' );
R::getRedBean()->check( $bean );
$bean->setMeta( 'type', '.' );
try {
R::getRedBean()->check( $bean );
fail();
} catch ( \Exception $e ) {
pass();
}
//check should remain the same even if frozen repo is used, method is public after all!
//we dont want to break the API!
R::freeze( TRUE );
try {
R::getRedBean()->check( $bean );
fail();
} catch ( \Exception $e ) {
pass();
}
R::freeze( FALSE );
}
/**
* Test Backward compatibility writer ESC-method.
*
* @return void
*/
public function testLegacyCode()
{
testpack( 'Test Backward compatibility methods in writer.' );
asrt( R::getWriter()->safeColumn( 'column', TRUE ), R::getWriter()->esc( 'column', TRUE ) );
asrt( R::getWriter()->safeColumn( 'column', FALSE ), R::getWriter()->esc( 'column', FALSE ) );
asrt( R::getWriter()->safeTable( 'table', TRUE ), R::getWriter()->esc( 'table', TRUE ) );
asrt( R::getWriter()->safeTable( 'table', FALSE ), R::getWriter()->esc( 'table', FALSE ) );
}
/**
* Test beautification and array functions.
*
* @return void
*/
public function testBeauficationAndArrayFunctions()
{
$bean = R::dispense( 'bean' );
$bean->isReallyAwesome = TRUE;
asrt( isset( $bean->isReallyAwesome ), TRUE );
asrt( isset( $bean->is_really_awesome ), TRUE );
unset( $bean->is_really_awesome );
asrt( isset( $bean->isReallyAwesome ), FALSE );
asrt( isset( $bean->is_really_awesome ), FALSE );
}
/**
* Test beautification of column names.
*
* @return void
*/
public function testBeautifulColumnNames()
{
testpack( 'Beautiful column names' );
$town = R::dispense( 'town' );
$town->isCapital = FALSE;
$town->hasTrainStation = TRUE;
$town->name = 'BeautyVille';
$houses = R::dispense( 'house', 2 );
$houses[0]->isForSale = TRUE;
$town->ownHouse = $houses;
R::store( $town );
$town = R::load( 'town', $town->id );
asrt( ( $town->isCapital == FALSE ), TRUE );
asrt( ( $town->hasTrainStation == TRUE ), TRUE );
asrt( ( $town->name == 'BeautyVille' ), TRUE );
testpack( 'Accept datetime objects.' );
$cal = R::dispense( 'calendar' );
$cal->when = new\DateTime( '2000-01-01', new\DateTimeZone( 'Pacific/Nauru' ) );
asrt( $cal->when, '2000-01-01 00:00:00' );
testpack( 'Affected rows test' );
$currentDriver = $this->currentlyActiveDriverID;
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
$pdo = $adapter->getDatabase();
$bean = $redbean->dispense( 'bean' );
$bean->prop = 3; //make test run with strict mode as well
$redbean->store( $bean );
$adapter->exec( 'UPDATE bean SET prop = 2' );
asrt( $adapter->getAffectedRows(), 1 );
testpack( 'Testing Logger' );
R::getDatabaseAdapter()->getDatabase()->setLogger( new RDefault );
asrt( ( R::getDatabaseAdapter()->getDatabase()->getLogger() instanceof Logger ), TRUE );
asrt( ( R::getDatabaseAdapter()->getDatabase()->getLogger() instanceof RDefault ), TRUE );
$bean = R::dispense( 'bean' );
$bean->property = 1;
$bean->unsetAll( array( 'property' ) );
asrt( $bean->property, NULL );
asrt( ( $bean->setAttr( 'property', 2 ) instanceof OODBBean ), TRUE );
asrt( $bean->property, 2 );
asrt( preg_match( '/\d\d\d\d\-\d\d\-\d\d/', R::isoDate() ), 1 );
asrt( preg_match( '/\d\d\d\d\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', R::isoDateTime() ), 1 );
$redbean = R::getRedBean();
$adapter = R::getDatabaseAdapter();
$writer = R::getWriter();
asrt( ( $redbean instanceof OODB ), TRUE );
asrt( ( $adapter instanceof Adapter ), TRUE );
asrt( ( $writer instanceof QueryWriter ), TRUE );
R::setRedBean( $redbean );
pass(); //cant really test this
R::setDatabaseAdapter( $adapter );
pass(); //cant really test this
R::setWriter( $writer );
pass(); //cant really test this
$u1 = R::dispense( 'user' );
$u1->name = 'Gabor';
$u1->login = 'g';
$u2 = R::dispense( 'user' );
$u2->name = 'Eric';
$u2->login = 'e';
R::store( $u1 );
R::store( $u2 );
$list = R::getAssoc( 'select login,' . R::getWriter()->esc( 'name' ) . ' from ' . R::getWriter()->esc( 'user' ) . ' ' );
asrt( $list['e'], 'Eric' );
asrt( $list['g'], 'Gabor' );
$painting = R::dispense( 'painting' );
$painting->name = 'Nighthawks';
$id = R::store( $painting );
testpack( 'Testing SQL Error Types' );
foreach ( $writer->typeno_sqltype as $code => $text ) {
asrt( is_integer( $code ), TRUE );
asrt( is_string( $text ), TRUE );
}
foreach ( $writer->sqltype_typeno as $text => $code ) {
asrt( is_integer( $code ), TRUE );
asrt( is_string( $text ), TRUE );
}
testpack( 'Testing Nowhere Pt. 1 (unfrozen)' );
foreach (
array(
'exec', 'getAll', 'getCell', 'getAssoc', 'getRow', 'getCol'
)
as $method ) {
R::$method( 'select * from nowhere' );
pass();
}
testpack( 'Testing Nowhere Pt. 2 (frozen)' );
R::freeze( TRUE );
foreach (
array(
'exec', 'getAll', 'getCell', 'getAssoc', 'getRow', 'getCol'
)
as $method ) {
try {
R::$method( 'select * from nowhere' );
fail();
} catch ( SQL $e ) {
pass();
}
}
R::freeze( FALSE );
}
/**
* Test reflectional functions of database.
*
* @return void
*/
public function testDatabaseProperties()
{
testpack( 'Testing Database Properties' );
$adapter = R::getDatabaseAdapter();
if ( method_exists( R::getDatabaseAdapter()->getDatabase(), 'getPDO' ) ){
asrt( $adapter->getDatabase()->getPDO() instanceof \PDO, TRUE );
}
asrt( strlen( $adapter->getDatabase()->getDatabaseVersion() ) > 0, TRUE );
asrt( strlen( $adapter->getDatabase()->getDatabaseType() ) > 0, TRUE );
}
/**
* Test Transactions.
*
* @return void
*/
public function testTransactions()
{
testpack( 'transactions' );
$false = R::begin();
asrt( $false, FALSE );
$bean = R::dispense( 'bean' );
R::store( $bean );
R::commit();
asrt( R::count( 'bean' ), 1 );
R::trash( $bean );
R::setAllowFluidTransactions( TRUE );
asrt( R::begin(), TRUE );
$bean = R::dispense( 'bean' );
R::store( $bean );
asrt( R::commit(), TRUE );
asrt( R::count( 'bean' ), 1 );
R::trash( $bean );
asrt( R::begin(), TRUE );
$bean = R::dispense( 'bean' );
R::store( $bean );
R::rollback();
asrt( R::count( 'bean' ), 0 );
R::setAllowFluidTransactions( FALSE );
R::wipe('bean');
R::freeze( TRUE );
R::begin();
$bean = R::dispense( 'bean' );
R::store( $bean );
R::rollback();
asrt( R::count( 'bean' ), 0 );
R::freeze( FALSE );
testpack( 'genSlots' );
asrt( R::genSlots( array( 'a', 'b' ) ), '?,?' );
asrt( R::genSlots( array( 'a' ) ), '?' );
asrt( R::genSlots( array() ), '' );
asrt( R::genSlots( array('a', 'b'), ' IN( %s ) ' ), ' IN( ?,? ) ' );
}
/**
* Test nested FUSE scenarios.
*
* @return void
*/
public function testFUSEnested()
{
testpack( 'FUSE models cant touch nested beans in update() - issue 106' );
$spoon = R::dispense( 'spoon' );
$spoon->name = 'spoon for test bean';
$deep = R::dispense( 'deep' );
$deep->name = 'deepbean';
$item = R::dispense( 'item' );
$item->val = 'Test';
$item->deep = $deep;
$test = R::dispense( 'test' );
$test->item = $item;
$test->sharedSpoon[] = $spoon;
$test->isnowtainted = TRUE;
$id = R::store( $test );
$test = R::load( 'test', $id );
asrt( $test->item->val, 'Test2' );
$can = reset( $test->ownCan );
$spoon = reset( $test->sharedSpoon );
asrt( $can->name, 'can for bean' );
asrt( $spoon->name, 'S2' );
asrt( $test->item->deep->name, '123' );
asrt( count( $test->ownCan ), 1 );
asrt( count( $test->sharedSpoon ), 1 );
asrt( count( $test->sharedPeas ), 10 );
asrt( count( $test->ownChip ), 9 );
}
/**
* Tests FUSE and lists, FUSE enforces no more than
* 3 sugar cubes in coffee.
*
* @return void
*/
public function testCoffeeWithSugarAndFUSE()
{
$coffee = R::dispense( 'coffee' );
$coffee->size = 'XL';
$coffee->ownSugar = R::dispense( 'sugar', 5 );
$id = R::store( $coffee );
$coffee = R::load( 'coffee', $id );
asrt( count( $coffee->ownSugar ), 3 );
$coffee->ownSugar = R::dispense( 'sugar', 2 );
$id = R::store( $coffee );
$coffee = R::load( 'coffee', $id );
asrt( count( $coffee->ownSugar ), 2 );
$cocoa = R::dispense( 'cocoa' );
$cocoa->name = 'Fair Cocoa';
list( $taste1, $taste2 ) = R::dispense( 'taste', 2 );
$taste1->name = 'sweet';
$taste2->name = 'bitter';
$cocoa->ownTaste = array( $taste1, $taste2 );
R::store( $cocoa );
$cocoa->name = 'Koko';
R::store( $cocoa );
if ( method_exists( R::getDatabaseAdapter()->getDatabase(), 'getPDO' ) ) {
$pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
$driver = new RPDO( $pdo );
pass();
asrt( $pdo->getAttribute(\PDO::ATTR_ERRMODE ),\PDO::ERRMODE_EXCEPTION );
asrt( $pdo->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE ),\PDO::FETCH_ASSOC );
asrt( strval( $driver->GetCell( 'select 123' ) ), '123' );
}
$a = new SQL;
$a->setSqlState( 'test' );
$b = strval( $a );
asrt( ( strpos( $b, '[test] - ' ) === 0 ), TRUE );
}
/**
* ENUM Basic tests.
*
* @return void
*/
public function testENUMBasics() {
asrt( R::enum( 'gender:male' )->name, 'MALE' );
asrt( R::enum( 'country:South-Africa' )->name, 'SOUTH_AFRICA' );
asrt( R::enum( 'tester:T@E S_t' )->name, 'T_E_S_T' );
}
/**
* Test ENUM in Queries and with short hand notation.
*
* @return void
*/
public function testENUMInQuery()
{
testpack('Test ENUM in Query and test ENUM short notation');
R::nuke();
$coffee = R::dispense( 'coffee' );
$coffee->taste = R::enum( 'flavour:mocca' );
R::store( $coffee );
$coffee = R::dispense( 'coffee' );
$coffee->taste = R::enum( 'flavour:banana' );
R::store( $coffee );
$coffee = R::dispense( 'coffee' );
$coffee->taste = R::enum( 'flavour:banana' );
R::store( $coffee );
//now we have two flavours
asrt( R::count('flavour'), 2 );
//use in query
asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:mocca' )->id ) ), 1);
//use in quer with short notation
asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:mocca' ) ) ), 1);
//use in query
asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:banana' )->id ) ), 2);
//use in quer with short notation
asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:banana' ) ) ), 2);
//use in query
asrt( R::count( 'coffee', ' taste_id = ? ', array( R::enum( 'flavour:strawberry' )->id ) ), 0);
//use in quer with short notation
asrt( R::count( 'coffee', ' taste_id = ? ', array( EID( 'flavour:strawberry' ) ) ), 0);
}
/**
* Test ENUM functionality offered by Label Maker.
*
* @return void
*/
public function testENUM() {
testpack('test ENUM');
$coffee = R::dispense( 'coffee' );
$coffee->taste = R::enum( 'flavour:mocca' );
//did we create an enum?
asrt( implode( '', R::gatherLabels( R::enum( 'flavour' ) ) ), 'MOCCA' );
R::store( $coffee );
$coffee = $coffee->fresh();
//test enum identity check - with alias
asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum('flavour:mocca') ), TRUE );
asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum('flavour:banana') ), FALSE );
//now we have two flavours
asrt( R::count( 'flavour' ), 2 );
asrt( implode( ',', R::gatherLabels( R::enum( 'flavour') ) ), 'BANANA,MOCCA' );
$coffee->flavour = R::enum( 'flavour:mocca' );
R::store($coffee);
//same results, can we have multiple flavours?
asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum( 'flavour:mocca' ) ), TRUE );
asrt( $coffee->fetchAs( 'flavour' )->taste->equals( R::enum( 'flavour:banana' ) ), FALSE );
asrt( $coffee->flavour->equals( R::enum( 'flavour:mocca' ) ), TRUE );
//no additional mocca enum...
asrt( R::count( 'flavour' ), 2 );
$drink = R::dispense( 'drink' );
$drink->flavour = R::enum( 'flavour:choco' );
R::store( $drink );
//now we have three!
asrt( R::count('flavour'), 3 );
$drink = R::load( 'drink', $drink->id );
asrt( $drink->flavour->equals( R::enum('flavour:mint') ), FALSE );
asrt( $drink->flavour->equals( R::enum('flavour:choco') ), TRUE );
asrt( R::count( 'flavour' ), 4 );
//trash should not affect flavour!
R::trash( $drink );
asrt( R::count( 'flavour' ), 4 );
}
/**
* Test trashAll().
*/
public function testMultiDeleteUpdate()
{
testpack( 'test multi delete and multi update' );
$beans = R::dispenseLabels( 'bean', array( 'a', 'b' ) );
$ids = R::storeAll( $beans );
asrt( (int) R::count( 'bean' ), 2 );
R::trashAll( R::batch( 'bean', $ids ) );
asrt( (int) R::count( 'bean' ), 0 );
testpack( 'test assocManager check' );
$rb = new OODB( R::getWriter() );
try {
$rb->getAssociationManager();
fail();
} catch ( RedException $e ) {
pass();
}
}
/**
* Test Bean identity equality.
*/
public function testBeanIdentityEquality() {
$beanA = R::dispense( 'bean' );
$beanB = R::dispense( 'bean' );
$beanA->id = 1;
$beanB->id = 1;
asrt( $beanA->equals( $beanB ), TRUE );
asrt( $beanB->equals( $beanA ), TRUE );
asrt( $beanA->equals( $beanA ), TRUE );
asrt( $beanB->equals( $beanB ), TRUE );
$beanB->id = 2;
asrt( $beanA->equals( $beanB ), FALSE );
asrt( $beanB->equals( $beanA ), FALSE );
$beanA->id = '2';
asrt( $beanA->equals( $beanB ), TRUE );
asrt( $beanB->equals( $beanA ), TRUE );
$beanB = R::dispense( 'carrot' );
$beanB->id = $beanA->id;
asrt( $beanA->equals( $beanB ), FALSE );
asrt( $beanB->equals( $beanA ), FALSE );
}
/**
* Test if adding SimpleModles to a shared list will auto unbox them.
*/
public function testSharedListsAutoUnbox()
{
$boxedBean = R::dispense( 'boxedbean' );
$bean = R::dispense( 'bean' );
$model = new SimpleModel();
$model->loadBean($boxedBean);
$bean->ownBoxedbeanList[] = $model;
try {
R::store( $bean );
pass();
} catch ( \Exception $e ) {
fail();
}
}
/**
* Test if we can obtain a database server version string
* from the Facade.
*/
public function testGetDatabaseServerVersion()
{
$version = R::getDatabaseServerVersion();
asrt(is_string($version), TRUE);
asrt(strlen($version)>0, TRUE);
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Namedparams
*
* Tests whether we can use named parameters in queries and
* SQL snippets.
*
* @file RedUNIT/Base/Namedparams.php
* @desc Test whether you can use named parameters in SQL snippets.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Namedparams extends Base
{
/**
* Test usage of named parameters in SQL snippets.
* Issue #299 on Github.
*
* @return void
*/
public function testNamedParamsInSnippets()
{
testpack( 'Test whether we can use named parameters in SQL snippets.' );
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->title = 'book';
$book->sharedPage[] = $page;
R::store($book);
//should not give error like: Uncaught [HY093] - SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters
$books = $page->withCondition(' title = :title ', array( ':title' => 'book' ) )->sharedBook;
asrt( count( $books ), 1 );
//should not give error...
$books = $page->withCondition( ' title = :title ', array( ':title' => 'book' ) )->sharedBook;
asrt( count( $books ), 1 );
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->title = 'book';
$book->comment = 'comment';
$page->title = 'page';
$book->ownPage[] = $page;
R::store( $book );
//should also not give error..
$count = $book->countOwn( 'page' );
asrt( $count, 1 );
$book = $book->fresh();
//should also not give error..
$count = $book->withCondition( ' title = ? ', array( 'page' ) )->countOwn( 'page' );
asrt( $count, 1 );
$book = $book->fresh();
//should also not give error..
$count = $book->withCondition( ' title = :title ', array( ':title' => 'page' ) )->countOwn( 'page' );
asrt( $count, 1 );
$book = $book->fresh();
$pages = $book->withCondition( ' title = :title ', array( ':title' => 'page' ) )->ownPage;
asrt( count( $pages ), 1 );
//test with duplicate slots...
$page = reset( $pages );
$page2 = R::dispense( 'page' );
$page2->ownPage[] = $page;
R::store( $page2 );
$page2 = $page2->fresh();
$pages = $page2->withCondition( ' title = :title ', array( ':title' => 'page' ) )->ownPage;
asrt( count( $pages ), 1 );
//test with find()
$books = R::getRedBean()->find( 'book',
array(
'title' => array('book')),
' AND title = :title ', array(':title'=>'book'));
asrt( count( $books ), 1 );
$books = R::getRedBean()->find( 'book',
array(
'title' => array('book', 'book2'),
'comment' => array('comment', 'comment2')),
' AND title = :title ', array(':title'=>'book'));
asrt( count( $books ), 1 );
//just check numeric works as well...
$books = R::getRedBean()->find( 'book',
array(
'title' => array('book', 'book2'),
'comment' => array('comment', 'comment2')),
' AND title = ? ', array('book'));
asrt( count( $books ), 1 );
//just extra check to verify glue works
$books = R::getRedBean()->find( 'book',
array(
'title' => array('book', 'book2'),
'comment' => array('comment', 'comment2')),
' ORDER BY id ');
asrt( count( $books ), 1 );
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Nuke
*
* Tests the nuke() command. The nuke command empties an entire database
* and should also be capable of removing all foreign keys, constraints and
* indexes.
*
* @file RedUNIT/Base/Nuke.php
* @desc Test the nuke() function.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Nuke extends Base
{
/**
* Test wipeAll().
*
* @return void
*/
public function testWipe()
{
R::nuke();
$bean = R::dispense( 'bean' );
asrt( count( R::inspect() ), 0 );
R::store( $bean );
asrt( count( R::inspect() ), 1 );
asrt( R::count( 'bean' ), 1 );
R::debug(1);
R::wipeAll();
asrt( count( R::inspect() ), 1 );
asrt( R::count( 'bean' ), 0 );
R::wipeAll( TRUE );
asrt( count( R::inspect() ), 0 );
asrt( R::count( 'bean' ), 0 );
}
/**
* Nuclear test suite.
*
* @return void
*/
public function testNuke()
{
$bean = R::dispense( 'bean' );
R::store( $bean );
asrt( count( R::getWriter()->getTables() ), 1 );
R::nuke();
asrt( count( R::getWriter()->getTables() ), 0 );
$bean = R::dispense( 'bean' );
R::store( $bean );
asrt( count( R::getWriter()->getTables() ), 1 );
R::freeze();
R::nuke();
// No effect
asrt( count( R::getWriter()->getTables() ), 1 );
R::freeze( FALSE );
}
/**
* Test noNuke().
*
* @return void
*/
public function testNoNuke() {
$bean = R::dispense( 'bean' );
R::store( $bean );
asrt( count( R::getWriter()->getTables() ), 1 );
R::noNuke( TRUE );
try {
R::nuke();
fail();
} catch( \Exception $e ) {
pass();
}
asrt( count( R::getWriter()->getTables() ), 1 );
R::noNuke( FALSE );
R::nuke();
asrt( count( R::getWriter()->getTables() ), 0 );
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\OODB as OODB;
/**
* Observers
*
* Tests the basic observer pattern used in RedBeanPHP.
*
* @file RedUNIT/Base/Observers.php
* @desc Tests the observer pattern in RedBeanPHP.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Observers extends Base
{
/**
* Test RedBeanPHP observers.
*
* @return void
*/
public function testObserverMechanism()
{
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
asrt( ( $adapter instanceof DBAdapter ), TRUE );
asrt( ( $writer instanceof QueryWriter ), TRUE );
asrt( ( $redbean instanceof OODB ), TRUE );
$observable = new \ObservableMock();
$observer = new \ObserverMock();
$observable->addEventListener( "event1", $observer );
$observable->addEventListener( "event3", $observer );
$observable->test( "event1", "testsignal1" );
asrt( $observer->event, "event1" );
asrt( $observer->info, "testsignal1" );
$observable->test( "event2", "testsignal2" );
asrt( $observer->event, "event1" );
asrt( $observer->info, "testsignal1" );
$observable->test( "event3", "testsignal3" );
asrt( $observer->event, "event3" );
asrt( $observer->info, "testsignal3" );
}
}

View File

@@ -0,0 +1,262 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Partial Beans
*
* Test whether we can use 'partial bean mode'.
* In 'partial bean mode' only changed properties of a bean
* will get updated in the database. This feature has been designed
* to deal with 'incompatible table fields'. The specific case that
* led to this feature is available as test Postgres/Partial and is
* based on Github issue #547. This test only covers the basic functionality.
*
* @file RedUNIT/Base/Partial.php
* @desc Tests Partial Beans Mode
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Partial extends Base {
/**
* Github Issue #754.
* If I set up the default values to some specific columns,
* these columns can not act the same expectation in partial bean mode. #754.
* "When I upgrade my code to Redbean 5.4 and turn on the partial bean mode,
* I found this issue I mentioned.
* As Redbean recommends, I set up the default values
* to some specific columns before I use them.
* And then I use the partial bean mode to store
* the columns updated rather than the entire bean.
* But I found if I set up the default values,
* it will change the changelist in the bean which is the
* foundation of the partial bean mode."
*
* @return void
*/
public function testChangeListIssue()
{
R::nuke();
R::usePartialBeans( TRUE );
\Model_Coffee::$defaults = array(
'strength' => 'strong',
'beans' => 'Arabica',
'preparation' => 'Kettle'
);
$coffee = R::dispense('coffee');
$changelist = $coffee->getMeta('changelist');
asrt( count( $changelist), 3 );
$coffee->preparation = 'Espresso';
$changelist = $coffee->getMeta('changelist');
asrt( count( $changelist), 4 );
$id = R::store( $coffee );
$coffee = R::load( 'coffee', $id );
$changelist = $coffee->getMeta('changelist');
asrt( count( $changelist), 0 );
}
/**
* Github Issue #754.
* The importRow() function should clear the changeList.
*
* @return void
*/
public function testChangeListImportRow()
{
R::usePartialBeans( TRUE );
$bean = R::dispense( 'bean' );
asrt( count( $bean->getMeta('changelist') ), 0 );
$bean->property = 'abc';
asrt( count( $bean->getMeta('changelist') ), 1 );
$bean->importRow( array( 'property' => 123 ) );
asrt( count( $bean->getMeta('changelist') ), 0 );
}
/**
* Tests the basic scenarios for Partial Beans.
*
* @return void
*/
public function testPartialBeans()
{
R::nuke();
R::usePartialBeans( FALSE );
$book = R::dispense( 'book' );
$book->title = 'A book about half beans';
$book->price = 99;
$book->pages = 60;
$id = R::store( $book );
/* test baseline condition */
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 60 );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 61 );
/* now test partial beans mode */
R::usePartialBeans( TRUE );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->pages, 62 );
/* mask should be cleared... */
R::exec( 'UPDATE book SET pages = ? ', array( 64 ) );
$book->price = 92;
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( (integer) $book->pages, 64 );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->price, 92 );
R::usePartialBeans( FALSE );
}
/**
* Tests whether we can pass a list of specific bean types
* to apply partial saving to.
*
* @return void
*/
public function testPartialBeansTypeList()
{
R::nuke();
R::usePartialBeans( array( 'notbook' ) );
$book = R::dispense( 'book' );
$book->title = 'A book about half beans';
$book->price = 99;
$book->pages = 60;
$id = R::store( $book );
/* test baseline condition */
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 60 );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 61 );
/* now test partial beans mode */
R::usePartialBeans( array( 'book', 'more' ) );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->pages, 62 );
/* mask should be cleared... */
R::exec( 'UPDATE book SET pages = ? ', array( 64 ) );
$book->price = 92;
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( (integer) $book->pages, 64 );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->price, 92 );
R::usePartialBeans( FALSE );
}
/**
* Tests the basic scenarios for Partial Beans.
* Frozen.
*
* @return void
*/
public function testPartialBeansFrozen()
{
R::nuke();
R::usePartialBeans( FALSE );
$book = R::dispense( 'book' );
$book->title = 'A book about half beans';
$book->price = 99;
$book->pages = 60;
$id = R::store( $book );
/* test baseline condition */
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 60 );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 61 );
/* now test partial beans mode */
R::freeze( TRUE );
R::usePartialBeans( TRUE );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->pages, 62 );
/* mask should be cleared... */
R::exec( 'UPDATE book SET pages = ? ', array( 64 ) );
$book->price = 92;
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( (integer) $book->pages, 64 );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->price, 92 );
R::usePartialBeans( FALSE );
R::freeze( FALSE );
}
/**
* Tests whether we can pass a list of specific bean types
* to apply partial saving to.
* Frozen.
*
* @return void
*/
public function testPartialBeansTypeListFrozen()
{
R::nuke();
R::usePartialBeans( array( 'notbook' ) );
$book = R::dispense( 'book' );
$book->title = 'A book about half beans';
$book->price = 99;
$book->pages = 60;
$id = R::store( $book );
/* test baseline condition */
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 60 );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'A book about half beans' );
asrt( (integer) $book->pages, 61 );
/* now test partial beans mode */
R::freeze( TRUE );
R::usePartialBeans( array( 'book', 'more' ) );
$book->pages++;
R::exec( 'UPDATE book SET title = ? ', array( 'Another Title' ) );
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->pages, 62 );
/* mask should be cleared... */
R::exec( 'UPDATE book SET pages = ? ', array( 64 ) );
$book->price = 92;
$id = R::store( $book );
$book = R::load( 'book', $id );
asrt( (integer) $book->pages, 64 );
asrt( $book->title, 'Another Title' );
asrt( (integer) $book->price, 92 );
R::usePartialBeans( FALSE );
R::freeze( FALSE );
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
/**
* Performance
*
* This performance test is used with runperf and xdebug profiler.
*
* @file RedUNIT/Base/Performance.php
* @desc Performance testing.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Performance extends Base
{
/**
* Setup
*
* @return void
*/
public function setup()
{
R::nuke();
//Prepare structure
$book = R::dispense( 'book' );
$book->title = 'book';
$pages = R::dispense( 'page', 10 );
foreach( $pages as $page ) {
$page->content = 'lorem ipsum';
$page->title = 'data';
$page->sequence = 'data';
$page->order = 'data';
$page->columns = 'data';
$page->paragraphs = 'data';
$page->paragraphs1 = 'data';
$page->paragraphs2 = 'data';
$page->paragraphs3 = 'data';
$page->paragraphs4 = 'data';
}
$book->xownPageList = $pages;
$tags = R::dispense( 'tag', 6 );
foreach( $tags as $tag ) {
$tag->label = 'tag';
}
$book->sharedTagList = $tags;
R::store( $book );
}
/**
* CRUD performance.
*
* @return void
*/
public function crud()
{
R::freeze( TRUE );
$book = R::dispense( 'book' );
$book->title = 'Book';
$page = R::dispense('page');
$page->content = 'Content';
$page->title = 'data';
$page->sequence = 'data';
$page->order = 'data';
$page->columns = 'data';
$page->paragraphs = 'data';
$page->paragraphs1 = 'data';
$page->paragraphs2 = 'data';
$page->paragraphs3 = 'data';
$page->paragraphs4 = 'data';
$tag = R::dispense('tag');
$tag->label = 'Tag ';
$book->noLoad()->ownPage[] = $page;
$book->noLoad()->sharedTag[] = $tag;
R::store( $book );
$book = $book->fresh();
$book->ownPage;
$book->sharedTag;
R::trash( $book );
}
/**
* CRUD performance Array Access.
*
* @return void
*/
public function crudaa()
{
R::freeze( TRUE );
$book = R::dispense( 'book' );
$book['title'] = 'Book';
$page = R::dispense('page');
$page['content'] = 'Content';
$page['title'] = 'data';
$page['sequence'] = 'data';
$page['order'] = 'data';
$page['columns'] = 'data';
$page['paragraphs'] = 'data';
$page['paragraphs1'] = 'data';
$page['paragraphs2'] = 'data';
$page['paragraphs3'] = 'data';
$page['paragraphs4'] = 'data';
$tag = R::dispense('tag');
$tag['label'] = 'Tag ';
$book->ownPage[] = $page;
$book->noLoad()->sharedTag[] = $tag;
R::store( $book );
$book = $book->fresh();
$book->ownPage;
$book->sharedTag;
R::trash( $book );
}
}

View File

@@ -0,0 +1,269 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
R::ext('xdispense', function($type){
return R::getRedBean()->dispense($type);
});
define('BOOK', 'tbl_book');
define('AUTHOR', 'tbl_author');
define('COAUTHOR', 'coAuthor');
define('FRIEND', 'tbl_friend');
define('PUBLISHER', 'tbl_publisher');
define('BOOKLIST', 'ownTbl_book');
define('FRIENDLIST', 'sharedTbl_friend');
/**
* Prefixes
*
* Tests whether we can use tables with prefixes.
* Some people seem to like that.
*
* @file RedUNIT/Base/Prefixes.php
* @desc Tests whether you can use RedBeanPHP with table prefixes.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Prefixes extends Base
{
/**
* Test prerequisites.
*/
public function testPrerequisites()
{
R::nuke();
$bean = R::xdispense( 'type_with_underscore' );
asrt( ( $bean instanceof OODBBean ), TRUE );
asrt( constant( 'BOOK' ), 'tbl_book' );
asrt( constant( 'AUTHOR' ), 'tbl_author' );
asrt( constant( 'PUBLISHER' ), 'tbl_publisher' );
asrt( constant( 'FRIEND' ), 'tbl_friend' );
asrt( constant( 'BOOKLIST' ), 'ownTbl_book' );
asrt( constant( 'FRIENDLIST' ), 'sharedTbl_friend' );
asrt( constant( 'COAUTHOR' ), 'coAuthor' );
}
/**
* Test basic CRUD operations.
*/
public function testBasicOperations()
{
//Can we dispense a naughty bean? (with underscore)
$author = R::xdispense( AUTHOR );
asrt( ( $author instanceof OODBBean ), TRUE );
asrt( $author->getMeta('type'), AUTHOR );
$author->name = 'Mr. Quill';
$book = R::xdispense( BOOK );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( $book->getMeta('type'), BOOK );
$book->title = 'Good Stories';
$friend = R::xdispense( FRIEND );
$friend->name = 'Muse';
asrt( ( $friend instanceof OODBBean ), TRUE );
asrt( $friend->getMeta('type'), FRIEND );
$publisher = R::xdispense( PUBLISHER );
$publisher->name = 'Good Books';
asrt( ( $publisher instanceof OODBBean ), TRUE );
asrt( $publisher->getMeta('type'), PUBLISHER );
asrt( is_array( $author->{BOOKLIST} ), TRUE );
//add books to the book list using the constant
$author->{BOOKLIST}[] = $book;
asrt( count( $author->{BOOKLIST} ), 1 );
//can we also add friends? (N-M)
$author->{FRIENDLIST}[] = $friend;
$author->{PUBLISHER} = $publisher;
$id = R::store( $author );
asrt( ( $id > 0 ), TRUE );
$author = $author->fresh();
//Can we add another friend after reload?
$author->{FRIENDLIST}[] = R::xdispense( FRIEND )->setAttr( 'name', 'buddy' );
R::store($author);
$author = $author->fresh();
//Now check the contents of the bean, its lists (books,friends) and parent (publisher)
asrt( $author->name, 'Mr. Quill' );
asrt( count( $author->{BOOKLIST} ), 1 );
$firstBook = reset( $author->{BOOKLIST} );
asrt( $firstBook->title, 'Good Stories' );
asrt( count( $author->{FRIENDLIST} ), 2 );
$firstFriend = reset( $author->{FRIENDLIST} );
$parent = $author->{PUBLISHER};
asrt( ( $parent instanceof OODBBean ), TRUE );
$tables = R::inspect();
//have all tables been prefixed?
foreach( $tables as $table ) asrt( strpos( $table, 'tbl_' ), 0 );
//Can we make an export?
$export = R::exportAll( R::findOne( AUTHOR ), TRUE );
$export = reset( $export );
asrt( isset( $export[ PUBLISHER ] ), TRUE );
asrt( isset( $export[ BOOKLIST ] ), TRUE );
asrt( isset( $export[ FRIENDLIST ] ), TRUE );
asrt( isset( $export[ 'ownBook' ] ), FALSE );
asrt( isset( $export[ 'sharedFriend' ] ), FALSE );
asrt( isset( $export[ 'publisher' ] ), FALSE );
//Can we duplicate?
$copy = R::dup( $author );
$copy->name = 'Mr. Clone';
R::store( $copy );
$copy = $copy->fresh();
asrt( $copy->name, 'Mr. Clone' );
asrt( count( $copy->{BOOKLIST} ), 1 );
$firstBook = reset( $copy->{BOOKLIST} );
asrt( $firstBook->title, 'Good Stories' );
asrt( count( $copy->{FRIENDLIST} ), 2 );
$firstFriend = reset( $copy->{FRIENDLIST} );
$parent = $copy->{PUBLISHER};
asrt( ( $parent instanceof OODBBean ), TRUE );
//Can we count?
asrt( R::count( AUTHOR ), 2 );
$copy = $copy->fresh();
asrt( $copy->countOwn( BOOK ), 1 );
asrt( $copy->countShared( FRIEND ), 2 );
//Can we delete?
R::trash( $author );
asrt( R::count( AUTHOR ), 1 );
//Can we nuke?
R::nuke();
asrt( R::count( AUTHOR ), 0 );
asrt( count( R::inspect() ), 0 );
}
/**
* Test basic operations in frozen mode.
*/
public function testBasicOperationsFrozen()
{
R::nuke();
$author = R::xdispense( AUTHOR );
$author->name = 'Mr. Quill';
$book = R::xdispense( BOOK );
$book->title = 'Good Stories';
$book2 = R::xdispense( BOOK );
$book2->title = 'Good Stories 2';
$friend = R::xdispense( FRIEND );
$friend->name = 'Muse';
$publisher = R::xdispense( PUBLISHER );
$publisher->name = 'Good Books';
$author->{BOOKLIST} = array( $book, $book2 );
$author->{FRIENDLIST}[] = $friend;
$author->{PUBLISHER} = $publisher;
$coAuthor = R::xdispense( AUTHOR );
$coAuthor->name = 'Xavier';
$book2->{COAUTHOR} = $coAuthor;
R::store( $author );
R::freeze( TRUE );
asrt( $author->name, 'Mr. Quill' );
asrt( count( $author->{BOOKLIST} ), 2 );
$firstBook = reset( $author->{BOOKLIST} );
asrt( $firstBook->title, 'Good Stories' );
asrt( count( $author->{FRIENDLIST} ), 1 );
$firstFriend = reset( $author->{FRIENDLIST} );
$parent = $author->{PUBLISHER};
asrt( ( $parent instanceof OODBBean ), TRUE );
$tables = R::inspect();
//have all tables been prefixed?
foreach( $tables as $table ) asrt( strpos( $table, 'tbl_' ), 0 );
//Can we make an export?
$export = R::exportAll( R::findOne( AUTHOR ), TRUE );
$export = reset( $export );
asrt( isset( $export[ PUBLISHER ] ), TRUE );
asrt( isset( $export[ BOOKLIST ] ), TRUE );
asrt( isset( $export[ FRIENDLIST ] ), TRUE );
asrt( isset( $export[ 'ownBook' ] ), FALSE );
asrt( isset( $export[ 'sharedFriend' ] ), FALSE );
asrt( isset( $export[ 'publisher' ] ), FALSE );
R::freeze( FALSE );
}
/**
* Test conditions and aliases.
*/
public function testConditionsAndAliases()
{
R::nuke();
$author = R::xdispense( AUTHOR );
$author->name = 'Mr. Quill';
$book = R::xdispense( BOOK );
$book->title = 'Good Stories';
$book2 = R::xdispense( BOOK );
$book2->title = 'Good Stories 2';
$friend = R::xdispense( FRIEND );
$friend->name = 'Muse';
$publisher = R::xdispense( PUBLISHER );
$publisher->name = 'Good Books';
$author->{BOOKLIST} = array( $book, $book2 );
$author->{FRIENDLIST}[] = $friend;
$author->{PUBLISHER} = $publisher;
$coAuthor = R::xdispense( AUTHOR );
$coAuthor->name = 'Xavier';
$book2->{COAUTHOR} = $coAuthor;
R::store( $author );
$author = $author->fresh();
asrt( R::count( AUTHOR ), 2 );
//Can we use with and withCondition?
asrt( count( $author->{BOOKLIST} ), 2 );
asrt( count( $author->with(' LIMIT 1 ')->{BOOKLIST} ), 1 );
asrt( count( $author->withCondition(' title LIKE ? ', array( '%2%' ) )->{BOOKLIST} ), 1 );
//Can we use an alias?
$book2 = $book2->fresh();
asrt( $book2->fetchAs( AUTHOR )->{COAUTHOR}->name, 'Xavier' );
$coAuthor = $book2->fetchAs( AUTHOR )->{COAUTHOR}->fresh();
asrt( count( $coAuthor->alias( COAUTHOR )->{BOOKLIST} ), 1 );
}
/**
* Test prettier tables using via().
*/
public function testViaPrettification()
{
R::nuke();
R::renameAssociation( 'tbl_author_tbl_friend', 'tbl_author_friend' );
$author = R::xdispense( AUTHOR );
$author->name = 'Mr. Quill';
$friend = R::xdispense( FRIEND );
$friend->name = 'Muse';
$author->{FRIENDLIST}[] = $friend;
$id = R::store( $author );
//print_r(R::inspect()); exit;
$author = R::load( AUTHOR, $id );
$tables = array_flip( R::inspect() );
asrt( isset( $tables[ 'tbl_author_friend' ] ), TRUE );
asrt( isset( $tables[ 'tbl_author_tbl_friend' ] ), FALSE );
asrt( count( $author->{FRIENDLIST} ), 1 );
AQueryWriter::clearRenames();
}
/**
* Test self-referential N-M relations.
*/
public function testSelfRefNM()
{
R::nuke();
$friend1 = R::xdispense( FRIEND );
$friend1->name = 'f1';
$friend2 = R::xdispense( FRIEND );
$friend2->name = 'f2';
$friend3 = R::xdispense( FRIEND );
$friend3->name = 'f3';
$friend1->{FRIENDLIST} = array( $friend2, $friend3 );
$friend3->{FRIENDLIST} = array( $friend1 );
R::storeAll( array( $friend1, $friend2, $friend3 ) );
$friend1 = $friend1->fresh();
$friend2 = $friend2->fresh();
$friend3 = $friend3->fresh();
asrt( count( $friend1->{FRIENDLIST} ), 2 );
asrt( count( $friend2->{FRIENDLIST} ), 1 );
asrt( count( $friend3->{FRIENDLIST} ), 1 );
$friend = reset( $friend3->{FRIENDLIST} );
asrt( $friend->name, 'f1' );
}
}

View File

@@ -0,0 +1,233 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
use RedBeanPHP\Util\MatchUp;
use RedBeanPHP\Util\Look;
/**
* MatchUp
*
* Tests the MatchUp functionality.
* Tired of creating login systems and password-forget systems?
* MatchUp is an ORM-translation of these kind of problems.
* A matchUp is a match-and-update combination in terms of beans.
* Typically login related problems are all about a match and
* a conditional update.
*
* @file RedUNIT/Base/Matchup.php
* @desc Tests MatchUp
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Productivity extends Base
{
/**
* Test matchup
*
* @return void
*/
public function testPasswordForget()
{
R::nuke();
$account = R::dispense( 'account' );
$account->uname = 'Shawn';
$account->pass = sha1( 'sheep' );
$account->archived = 0;
$account->attempts = 1;
R::store( $account );
$matchUp = new MatchUp( R::getToolbox() );
/* simulate a token generation script */
$account = NULL;
$didGenToken = $matchUp->matchUp( 'account', ' uname = ? AND archived = ?', array('Shawn',0), array(
'token' => sha1(rand(0,9000) . time()),
'tokentime' => time()
), NULL, $account );
asrt( $didGenToken, TRUE );
asrt( !is_null( $account->token ) , TRUE );
asrt( !is_null( $account->tokentime ) , TRUE );
/* simulate a password reset script */
$newpass = '1234';
$didResetPass = $matchUp->matchUp( 'account', ' token = ? AND tokentime > ? ', array( $account->token, time()-100 ), array(
'pass' => $newpass,
'token' => ''
), NULL, $account );
asrt( $account->pass, '1234' );
asrt( $account->token, '' );
/* simulate a login */
$didFindUsr = $matchUp->matchUp( 'account', ' uname = ? ', array( 'Shawn' ), array(
'attempts' => function( $acc ) {
return ( $acc->pass !== '1234' ) ? ( $acc->attempts + 1 ) : 0;
}
), NULL, $account);
asrt( $didFindUsr, TRUE );
asrt( $account->attempts, 0 );
/* Login failure */
$didFindUsr = $matchUp->matchUp( 'account', ' uname = ? ', array( 'Shawn' ), array(
'attempts' => function( $acc ) {
return ( $acc->pass !== '1236' ) ? ( $acc->attempts + 1 ) : 0;
}
), NULL, $account);
/* Create user if not exists */
$didFindUsr = R::matchUp( 'account', ' uname = ? ', array( 'Anonymous' ), array(
), array(
'uname' => 'newuser'
), $account);
asrt( $didFindUsr, FALSE );
asrt( $account->uname, 'newuser' );
}
/**
* Tests the look function.
*/
public function testLook()
{
R::nuke();
$beans = R::dispenseAll( 'color*3' );
list( $red, $green, $blue ) = $beans[0];
$red->name = 'red';
$green->name = 'green';
$blue->name = 'blue';
$red->thevalue = 'r';
$green->thevalue = 'g';
$blue->thevalue = 'b';
R::storeAll( array( $red, $green, $blue ) );
$look = R::getLook();
asrt( ( $look instanceof Look ), TRUE );
$str = R::getLook()->look( 'SELECT * FROM color WHERE thevalue != ? ORDER BY thevalue ASC', array( 'g' ), array( 'thevalue', 'name' ),
'<option value="%s">%s</option>', 'strtoupper', "\n"
);
asrt( $str,
"<option value=\"B\">BLUE</option>\n<option value=\"R\">RED</option>"
);
$str = R::look( 'SELECT * FROM color WHERE thevalue != ? ORDER BY thevalue ASC', array( 'g' ), array( 'thevalue', 'name' ),
'<option value="%s">%s</option>', 'strtoupper', "\n"
);
asrt( $str,
"<option value=\"B\">BLUE</option>\n<option value=\"R\">RED</option>"
);
}
/**
* Test Bean differ.
*/
public function testDiff()
{
R::nuke();
$ad = R::dispense( 'ad' );
$ad->title = 'dog looking for new home';
$ad->created = time();
$ad->modified = time();
$ad->ownDog[] = R::dispense( 'dog' );
$ad->ownDog[0]->name = 'Dweep';
$ad->ownDog[0]->color = 'green';
$ad->author = R::dispense('user');
$ad->author->name = 'John';
R::store( $ad );
$ad->title = 'green dog';
$diff = R::diff( $ad->fresh(), $ad );
/* simple case, property changed */
var_dump( $diff );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 1 );
asrt( $diff['ad.1.title'][0], 'dog looking for new home' );
asrt( $diff['ad.1.title'][1], 'green dog' );
/* test use specific format */
$diff = R::diff( $ad->fresh(), $ad, array(), '%1$s.%3$s' );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 1 );
asrt( $diff['ad.title'][0], 'dog looking for new home' );
asrt( $diff['ad.title'][1], 'green dog' );
/* skip created modified */
$ad = $ad->fresh();
$ad->modified = 111;
$ad->created = 111;
$diff = R::diff( $ad->fresh(), $ad );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 0 );
/* unless we set anothe filter */
$ad = $ad->fresh();
$ad->modified = 111;
$ad->created = 111;
$ad->name = 'x';
$diff = R::diff( $ad->fresh(), $ad, array( 'name' ) );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 2 );
asrt( $diff['ad.1.modified'][1], 111 );
asrt( $diff['ad.1.created'][1], 111 );
$ad = $ad->fresh();
/* also diff changes in related beans */
$ad->fetchAs('user')->author->name = 'Fred';
$dog = reset( $ad->ownDog );
$dog->color = 999;
$old = $ad->fresh();
$old->ownDog;
$old->fetchAs('user')->author;
$diff = R::diff( $ad, $old );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 2 );
asrt( $diff['ad.1.ownDog.1.color'][1], 'green' );
asrt( $diff['ad.1.ownDog.1.color'][0], 999 );
asrt( $diff['ad.1.author.1.name'][1], 'John' );
asrt( $diff['ad.1.author.1.name'][0], 'Fred' );
$diff = R::diff( $ad, null );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 0 );
$diff = R::diff( null, $ad );
asrt( is_array( $diff ), TRUE );
asrt( count( $diff ), 0 );
/* demo case */
list($book,$pages) = R::dispenseAll('book,page*2');
$book->title = 'Old Book';
$book->price = 999;
$book->ownPageList = $pages;
$pages[0]->text = 'abc';
$pages[1]->text = 'def';
R::store($book);
$book->title = 'new Book';
$page = end($book->ownPageList);
$page->text = 'new';
$oldBook = $book->fresh();
$oldBook->ownPageList;
$diff = R::diff($oldBook, $book);
}
/**
* Test misc. matchUp scenarios.
*
* @return void
*/
public function testMatchUpMisc()
{
R::nuke();
asrt( R::count( 'bean' ), 0 );
$found = R::matchUp( 'bean', ' id = ? ', array(1), array(), array(
'notfound' => function( $bean ) {
$bean->status = 'not found';
}
) );
asrt( $found, FALSE );
asrt( R::count( 'bean' ), 1 );
$bean = R::findOne( 'bean' );
asrt( $bean->status, 'not found' );
$null = R::matchUp( 'bean', ' id = ? ', array( $bean->id ) );
asrt( is_null( $null ), TRUE );
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* PullRequest530
*
* Tests whether this specific issue on github has been resolved.
* Pull Request #530 - OODBBean __set() checks if $property is a field link
*
* @file RedUNIT/Base/PullRequest530.php
* @desc Pull Request #530 - OODBBean __set() checks if $property is a field link
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class PullRequest530 extends Base
{
/**
* testPullRequest530
*
* Test to check if OODBBean correctly stores a bean if a field link is set directly.
* (We have to unset the linked bean (if loaded), so that the Repository::processEmbeddedBean
* function call does not update the field link property and overwrites the change
* in the following statement: <code>if ($bean->$linkField != $id) $bean->$linkField = $id;</code>
*
* @return void
*/
public function testPullRequest530()
{
testpack( 'Testing Pull Request #530 - OODBBean __set() checks if $property is a field link' );
R::freeze( FALSE );
$linkedObjects = R::dispense('linked', 2);
R::storeAll($linkedObjects);
$tester = R::dispense('parent');
$tester->linked = $linkedObjects[0];
R::store($tester);
$tester = R::findOne('parent');
asrt($tester->linked->id, $linkedObjects[0]->id);
$tester->linked_id = $linkedObjects[1]->id;
R::store($tester);
asrt($tester->linked->id, $linkedObjects[1]->id);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* QuickExport
*
* Tests the Quick Export functionality.
* The Quick Export Utility Class provides functionality to easily
* expose the result of SQL queries as well-known formats like CSV.
*
* @file RedUNIT/Base/Quickexport.php
* @desc Tests Quick Export
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Quickexport extends Base
{
/**
* Test whether we can generate a CSV file from a query.
*
* @return void
*/
public function testCSV()
{
if ( phpversion() < 5.5 || strpos( strtolower( phpversion() ), 'hhvm' ) !== FALSE ) return;
R::store( R::dispense( array( '_type'=>'bean', 'a' => 1, 'b' => 2, 'c' => 3 ) ) );
$path = '/tmp/redbeantest.txt';
R::csv( 'SELECT a,b,c FROM bean', array(), array( 'A', 'B', 'C' ), $path, FALSE );
$csv = file_get_contents( $path );
$expected = "A,B,C\n1,2,3";
asrt( strpos($csv, $expected) !== FALSE, TRUE );
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,221 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
/**
* Tags
*
* Tests RedBeanPHP tagging functionality, should be easy
* to tag beans, collect tags and integrate tags in SQL
* snippets. Tags automatically result in N-M relations, i.e.
* shared lists.
*
* @file RedUNIT/Base/Tags.php
* @desc Tests the tagging of beans.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Tags extends Base
{
public function testTagsCache()
{
R::nuke();
R::getWriter()->setUseCache( TRUE );
list( $beer1, $beer2, $beer3 ) = R::dispense( 'beer', 3 );
$beer1->title = 'b1';
$beer2->title = 'b2';
$beer3->title = 'b3';
R::tag( $beer1, 'stout' );
R::tag( $beer2, 'porter' );
R::tag( $beer3, 'lager,popular' );
$beers = R::tagged( 'beer', 'lager,popular' );
asrt(count($beers),1);
R::exec( 'DELETE FROM beer_tag WHERE beer_id = ? -- keep-cache', array( $beer3->id ) );
$beers = R::tagged( 'beer', 'lager,popular' );
asrt(count($beers),1);
R::getWriter()->setUseCache( FALSE );
$beers = R::tagged( 'beer', 'lager,popular' );
asrt(count($beers),0);
}
/**
* Tests tags with SQL.
*
* @return void
*/
public function testTagsWithSQL()
{
R::nuke();
list( $m1, $m2, $m3 ) = R::dispense( 'movie', 3 );
$m1->title = 'Frankenstein';
$m2->title = 'Fall of the House Usher';
$m3->title = 'Sleepy Hollow';
R::tag($m1, 'horror,gothic');
R::tag($m2, 'horror,gothic,short');
R::tag($m3, 'horror,legend');
asrt( count( R::tagged( 'movie', 'horror' ) ), 3);
asrt( count( R::tagged( 'movie', 'horror', ' LIMIT 2' ) ), 2);
asrt( count( R::tagged( 'movie', 'horror', ' LIMIT ?', array( 2 ) ) ), 2);
asrt( count( R::tagged( 'movie', 'horror', ' ORDER BY movie.title DESC LIMIT ?', array( 2 ) ) ), 2);
asrt( count( R::tagged( 'movie', 'horror,gothic', ' ORDER BY movie.title DESC LIMIT ?', array( 1 ) ) ), 1);
asrt( count( R::tagged( 'movie', 'horror,gothic') ), 3 );
asrt( count( R::taggedAll( 'movie', 'horror,gothic') ), 2 );
asrt( R::countTaggedAll( 'movie', 'horror,gothic'), 2 );
asrt( count( R::tagged( 'movie', 'horror,gothic', ' LIMIT ? ', array( 2 ) ) ), 2 );
asrt( ( R::countTagged( 'movie', 'horror,gothic', ' LIMIT ? ', array( 2 ) ) ), 2 );
asrt( count( R::taggedAll( 'movie', 'horror,gothic', ' LIMIT ? ', array( 2 ) ) ), 2 );
asrt( R::countTaggedAll( 'movie', 'horror,gothic', ' LIMIT ? ', array( 2 ) ), 2 );
asrt( count( R::tagged( 'movie', 'horror,gothic', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( ( R::countTagged( 'movie', 'horror,gothic', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( count( R::taggedAll( 'movie', 'horror,gothic', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'horror,gothic', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( count( R::tagged( 'movie', 'horror,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( ( R::countTagged( 'movie', 'horror,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( count( R::taggedAll( 'movie', 'horror,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'horror,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( count( R::tagged( 'movie', 'gothic,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( ( R::countTagged( 'movie', 'gothic,legend', ' LIMIT ? ', array( 1 ) ) ), 1 );
asrt( count( R::taggedAll( 'movie', 'gothic,legend', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', 'gothic,legend', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( count( R::tagged( 'movie', 'romance', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( ( R::countTagged( 'movie', 'romance', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( count( R::taggedAll( 'movie', 'romance', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', 'romance', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( count( R::tagged( 'movie', 'romance,xmas', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( ( R::countTagged( 'movie', 'romance,xmas', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( count( R::taggedAll( 'movie', 'romance,xmas', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', 'romance,xmas', ' LIMIT ? ', array( 1 ) ) ), 0 );
asrt( count( R::tagged( 'movie', 'gothic,short', ' LIMIT ? ', array( 4 ) ) ), 2 );
asrt( ( R::countTagged( 'movie', 'gothic,short', ' LIMIT ? ', array( 4 ) ) ), 2 );
asrt( count( R::taggedAll( 'movie', 'gothic,short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'gothic,short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( count( R::tagged( 'movie', 'gothic,short', ' LIMIT 4 ' ) ), 2 );
asrt( ( R::countTagged( 'movie', 'gothic,short', ' LIMIT 4 ' ) ), 2 );
asrt( count( R::taggedAll( 'movie', 'gothic,short', ' LIMIT 4 ' ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'gothic,short', ' LIMIT 4 ' ) ), 1 );
asrt( count( R::tagged( 'movie', 'gothic,short', ' ORDER BY movie.id DESC LIMIT 4 ' ) ), 2 );
asrt( ( R::countTagged( 'movie', 'gothic,short', ' ORDER BY movie.id DESC LIMIT 4 ' ) ), 2 );
asrt( count( R::taggedAll( 'movie', 'gothic,short', ' ORDER BY movie.id DESC LIMIT 4 ' ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'gothic,short', ' ORDER BY movie.id DESC LIMIT 4 ' ) ), 1 );
asrt( count( R::tagged( 'movie', 'short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( ( R::countTagged( 'movie', 'short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( count( R::taggedAll( 'movie', 'short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( ( R::countTaggedAll( 'movie', 'short', ' LIMIT ? ', array( 4 ) ) ), 1 );
asrt( count( R::tagged( 'movie', '', ' LIMIT ? ', array( 4 ) ) ), 0 );
asrt( ( R::countTagged( 'movie', '', ' LIMIT ? ', array( 4 ) ) ), 0 );
asrt( count( R::taggedAll( 'movie', '', ' LIMIT ? ', array( 4 ) ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', '', ' LIMIT ? ', array( 4 ) ) ), 0 );
asrt( count( R::tagged( 'movie', '', ' LIMIT 4 ' ) ), 0 );
asrt( ( R::countTagged( 'movie', '', ' LIMIT 4 ' ) ), 0 );
asrt( count( R::taggedAll( 'movie', '', ' LIMIT 4 ' ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', '', ' LIMIT 4 ' ) ), 0 );
asrt( count( R::tagged( 'movie', '', '' ) ), 0 );
asrt( ( R::countTagged( 'movie', '', '' ) ), 0 );
asrt( count( R::taggedAll( 'movie', '', '' ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', '', '' ) ), 0 );
asrt( count( R::tagged( 'movie', '' ) ), 0 );
asrt( ( R::countTagged( 'movie', '' ) ), 0 );
asrt( count( R::taggedAll( 'movie', '' ) ), 0 );
asrt( ( R::countTaggedAll( 'movie', '' ) ), 0 );
}
/**
* Some basic tests.
*
* @return void
*/
public function testTags()
{
list( $c, $d, $e, $f ) = R::dispense( 'coffee', 4 );
R::tag( $c, 'strong,black' );
R::tag( $d, 'black' );
R::tag( $e, 'strong,sweet' );
R::tag( $f, 'black,strong' );
asrt( count( R::taggedAll( 'coffee', 'strong,sweet' ) ), 1 );
asrt( count( R::taggedAll( 'coffee', 'strong' ) ), 3 );
asrt( count( R::taggedAll( 'coffee', '' ) ), 0 );
asrt( count( R::taggedAll( 'coffee', 'sweet' ) ), 1 );
asrt( count( R::taggedAll( 'coffee', 'sweet,strong' ) ), 1 );
asrt( count( R::taggedAll( 'coffee', 'black,strong' ) ), 2 );
asrt( count( R::taggedAll( 'coffee', array( 'black', 'strong' ) ) ), 2 );
asrt( count( R::taggedAll( 'coffee', 'salty' ) ), 0 );
$blog = R::dispense( 'blog' );
$blog->title = 'testing';
$blog->blog = 'tesing';
R::store( $blog );
$blogpost = ( R::load( "blog", 1 ) );
$post = R::dispense( "post" );
$post->message = "hello";
R::tag( $post, "lousy,smart" );
asrt( implode( ',', R::tag( $post ) ), "lousy,smart" );
R::tag( $post, "clever,smart" );
$tagz = implode( ',', R::tag( $post ) );
asrt( ( $tagz == "smart,clever" || $tagz == "clever,smart" ), TRUE );
R::tag( $blog, array( "smart", "interesting" ) );
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting" );
try {
R::tag( $blog, array( "smart", "interesting", "lousy!" ) );
pass();
} catch ( RedException $e ) {
fail();
}
asrt( implode( ',', R::tag( $blog ) ), "smart,interesting,lousy!" );
R::untag( $blog, array( "smart", "interesting" ) );
asrt( implode( ",", R::tag( $blog ) ), "lousy!" );
asrt( R::hasTag( $blog, array( "lousy!" ) ), TRUE );
asrt( R::hasTag( $blog, array( "lousy!", "smart" ) ), TRUE );
asrt( R::hasTag( $blog, array( "lousy!", "smart" ), TRUE ), FALSE );
R::tag( $blog, FALSE );
asrt( count( R::tag( $blog ) ), 0 );
R::tag( $blog, array( "funny", "comic" ) );
asrt( count( R::tag( $blog ) ), 2 );
R::addTags( $blog, array( "halloween" ) );
asrt( count( R::tag( $blog ) ), 3 );
asrt( R::hasTag( $blog, array( "funny", "commic", "halloween" ), TRUE ), FALSE );
R::unTag( $blog, "funny" );
R::addTags( $blog, "horror" );
asrt( count( R::tag( $blog ) ), 3 );
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
//no double tags
R::addTags( $blog, "horror" );
asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
asrt( R::hasTag( $blog, "horror,commic,halloween", TRUE ), FALSE );
asrt( count( R::tag( $blog ) ), 3 );
testpack( "fetch tagged items" );
}
/**
* Fetching tagged items.
*
* @return void
*/
public function fetchTaggedItems()
{
$b = R::dispense( "book" );
$b->title = 'horror';
R::store( $b );
$c = R::dispense( "book" );
$c->title = 'creepy';
R::store( $c );
$d = R::dispense( "book" );
$d->title = "chicklit";
R::store( $d );
R::tag( $b, "horror,classic" );
R::tag( $d, "women,classic" );
R::tag( $c, "horror" );
$x = R::tagged( "book", "classic" );
asrt( count( $x ), 2 );
$x = R::tagged( "book", "classic,horror" );
asrt( count( $x ), 3 );
$x = R::tagged( "book", array( "classic", "horror" ) );
asrt( count( $x ), 3 );
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
/**
* Threeway
*
* Tests link/via relations, i.e. N-M tables with additional
* columns.
*
* @file RedUNIT/Base/Threeway.php
* @desc Various tests for 3-way tables or X-way tables.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Threeway extends Base
{
/**
* Test whether we can use threeway tables without being
* bothered by unique constraints.
*
* @return void
*/
public function testUniqueConstraintOnThreeways()
{
AQueryWriter::clearRenames();
R::nuke();
$person = R::dispense( 'person' );
$role = R::dispense( 'role' );
$person->sharedRole[] = $role;
R::store( $person );
$person->link( 'person_role', array(
'unit' => R::dispense('unit')
))->role = $role;
//Can we add a duplicate role now? - No because we started with a simple N-M table
//and unique constraint has been applied accordingly, manually change database.
asrt( R::count( 'person_role' ), 1 );
R::nuke();
$person = R::dispense( 'person' );
$role = R::dispense( 'role' );
$person->via('participant')->sharedRole[] = $role;
R::store( $person );
$person->link( 'participant', array(
'unit' => R::dispense('unit')
))->role = $role;
//Can we add a duplicate role now? - No because we started with a simple N-M table
//and unique constraint has been applied accordingly, manually change database.
asrt( R::count( 'participant' ), 1 );
R::nuke();
$participant = R::dispense( 'participant' );
$person = R::dispense( 'person' );
$role = R::dispense( 'role' );
$unit = R::dispense( 'unit' );
$participant->person = $person;
$participant->role = $role;
$participant->unit = $unit;
R::store( $participant );
$person->link( 'participant', array(
'unit' => R::dispense('unit')
))->role = $role;
R::store( $person );
//Can we add a duplicate role now?
asrt( R::count( 'participant' ), 2 );
AQueryWriter::clearRenames();
}
/**
* Test whether a duplicate bean in the list isnt saved.
* This was an issue with Postgres while testing the threeway tables.
* Postgres returned the ID as a string while other drivers returned
* a numeric value causing different outcome in array_diff when
* calculating the shared additions.
*
* @return void
*/
public function testIssueWithDriverReturnID()
{
AQueryWriter::clearRenames();
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->sharedPageList[] = $page;
R::store( $book );
asrt( R::count( 'page' ), 1 );
$book = $book->fresh();
$book->sharedPageList[] = $page;
R::store( $book );
//don't save the duplicate bean!
asrt( R::count( 'page' ), 1 );
$book = $book->fresh();
$page->item = 2; //even if we change a property ?
$book->sharedPageList[] = $page;
R::store( $book );
foreach( $book->sharedPageList as $listItem) {
asrt( is_string( $listItem->id ), TRUE );
}
//same test but for own-list
R::nuke();
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->ownPageList[] = $page;
R::store( $book );
asrt( R::count( 'page' ), 1 );
$book = $book->fresh();
$book->ownPageList[] = $page;
R::store( $book );
//don't save the duplicate bean!
asrt( R::count( 'page' ), 1 );
$book = $book->fresh();
$book->ownPageList[] = $page;
$page->item = 3;
R::store( $book );
//don't save the duplicate bean!
asrt( R::count( 'page' ), 1 );
foreach( $book->ownPageList as $listItem) {
asrt( is_string( $listItem->id ), TRUE );
}
AQueryWriter::clearRenames();
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Trash
*
* Test trashing of beans.
*
* @file RedUNIT/Base/Trash.php
* @desc Tests R::trash()
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Trash extends Base
{
/**
* Test whether R::trash/trashAll() returns the correct
* number of deleted beans.
*
* @return void
*/
public function testTrash()
{
R::nuke();
$id = R::store(R::dispense(array(
'_type'=>'book',
'pages'=>3
)
));
asrt( R::count('book'), 1 );
$n = R::trash(R::findOne('book'));
asrt( $n, 1 );
asrt( R::count('book'), 0 );
list($books) = R::dispenseAll('book*10');
R::storeAll( $books );
asrt( R::count('book'), 10 );
$n = R::trashAll( $books );
asrt( R::count('book'), 0 );
}
}

View File

@@ -0,0 +1,249 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Traverse
*
* Tests whether RedBeanPHP can easily deal with hierarchies
* of beans.
*
* @file RedUNIT/Base/Traverse.php
* @desc Tests traversal functionality
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Traverse extends Base
{
/**
* Very simple traverse case (one-level).
*
* @return void
*/
public function testSimplestTraversal()
{
R::nuke();
$books = R::dispense( 'book', 10 );
$i = 1;
foreach( $books as $book ) {
$book->title = 'Book ' . ( $i++ );
}
$books[5]->marked = TRUE;
$shelf = R::dispense( 'shelf' );
$shelf->ownBook = $books;
$found = NULL;
$shelf->traverse('ownBookList', function( $book ) use ( &$found ) {
if ( $book->marked ) $found = $book;
});
asrt( ( $found->marked == TRUE ), TRUE );
asrt( $found->title, 'Book 6' );
}
/**
* Tests basic traversal.
*
* @return void
*/
public function testBasicTraversal()
{
R::nuke();
$pageA = R::dispense( 'page' )->setAttr( 'title', 'a' );
$pageB = R::dispense( 'page' )->setAttr( 'title', 'b' );
$pageC = R::dispense( 'page' )->setAttr( 'title', 'c' );
$pageD = R::dispense( 'page' )->setAttr( 'title', 'd' );
$pageE = R::dispense( 'page' )->setAttr( 'title', 'e' );
$pageF = R::dispense( 'page' )->setAttr( 'title', 'f' );
$pageG = R::dispense( 'page' )->setAttr( 'title', 'g' );
$pageH = R::dispense( 'page' )->setAttr( 'title', 'h' );
$pageA->ownPage = array( $pageB, $pageC );
$pageB->ownPage = array( $pageD );
$pageC->ownPage = array( $pageE, $pageF );
$pageD->ownPage = array( $pageG );
$pageF->ownPage = array( $pageH );
R::store( $pageA );
$pageA = $pageA->fresh();
//also tests non-existant column handling by count().
asrt( R::count( 'page', ' price = ? ', array( '5' ) ), 0);
asrt( R::count( 'tag', ' title = ? ', array( 'new' ) ), 0);
$pageA->traverse( 'ownPageList', function( $bean ) {
$bean->price = 5;
});
R::store( $pageA );
asrt( R::count( 'page', ' price = ? ', array( '5' ) ), 7);
}
/**
* Test traversing paths, ancestry.
*
* @return void
*/
public function testTraversePaths()
{
R::nuke();
$pageA = R::dispense( 'page' )->setAttr( 'title', 'a' );
$pageB = R::dispense( 'page' )->setAttr( 'title', 'b' );
$pageC = R::dispense( 'page' )->setAttr( 'title', 'c' );
$pageD = R::dispense( 'page' )->setAttr( 'title', 'd' );
$pageE = R::dispense( 'page' )->setAttr( 'title', 'e' );
$pageF = R::dispense( 'page' )->setAttr( 'title', 'f' );
$pageG = R::dispense( 'page' )->setAttr( 'title', 'g' );
$pageH = R::dispense( 'page' )->setAttr( 'title', 'h' );
$pageA->ownPage = array( $pageB, $pageC );
$pageB->ownPage = array( $pageD );
$pageC->ownPage = array( $pageE, $pageF );
$pageD->ownPage = array( $pageG );
$pageF->ownPage = array( $pageH );
R::store( $pageA );
$parents = array();
$pageF->traverse( 'page', function( $page ) use ( &$parents ) {
$parents[] = $page->title;
} );
asrt( implode( ',', $parents ), 'c,a' );
$parents = array();
$pageH->traverse( 'page', function( $page ) use ( &$parents ) {
$parents[] = $page->title;
} );
asrt( implode( ',', $parents ), 'f,c,a' );
$parents = array();
$pageG->traverse( 'page', function( $page ) use ( &$parents ) {
$parents[] = $page->title;
} );
asrt( implode( ',', $parents ), 'd,b,a' );
$path = array();
$pageA->traverse( 'ownPageList', function( $page ) use ( &$path ) {
$path[] = $page->title;
} );
asrt( implode( ',', $path ), 'b,d,g,c,e,f,h' );
$path = array();
$pageC->traverse( 'ownPageList', function( $page ) use ( &$path ) {
$path[] = $page->title;
} );
asrt( implode( ',', $path ), 'e,f,h' );
$path = array();
$pageA->traverse( 'ownPageList', function( $page ) use ( &$path ) {
$path[] = $page->title;
}, 2 );
asrt( implode( ',', $path ), 'b,d,c,e,f' );
}
/**
* Test traversal with embedded SQL snippets.
*
* @return void
*/
public function testTraversalWithSQL()
{
$tasks = R::dispense('task', 10);
foreach( $tasks as $key => $task ) {
$task->descr = 't'.$key;
}
$tasks[0]->ownTask = array( $tasks[1], $tasks[9], $tasks[7] );
$tasks[1]->ownTask = array( $tasks[5] );
$tasks[9]->ownTask = array( $tasks[3], $tasks[8] );
$tasks[2]->ownTask = array( $tasks[4] );
$tasks[7]->ownTask = array( $tasks[6] );
R::storeAll( $tasks );
$task = R::load('task', $tasks[0]->id);
$todo = array();
$task->with(' ORDER BY descr ASC ')->traverse('ownTaskList', function( $t ) use ( &$todo ) {
$todo[] = $t->descr;
} );
asrt( implode( ',', $todo ), 't1,t5,t7,t6,t9,t3,t8' );
$task = R::load( 'task', $tasks[0]->id );
$todo = array();
$task->withCondition( ' ( descr = ? OR descr = ? ) ', array( 't7','t6' ) )
->traverse( 'ownTaskList', function( $task ) use( &$todo ){
$todo[] = $task->descr;
} );
asrt( implode( ',', $todo ), 't7,t6' );
}
/**
* Test traversal with aliases.
*
* @return void
*/
public function testTraversalWithAlias()
{
R::nuke();
$book = R::dispense( 'book' );
$cats = R::dispense( 'category', 3 );
$cats[0]->gname = 'SF';
$cats[1]->gname = 'Fantasy';
$cats[2]->gname = 'Horror';
$book->genre = $cats[0];
$book->name = 'Space Story';
$cats[0]->genre = $cats[1];
$cats[2]->genre = $cats[1];
R::store( $book );
$book2 = R::dispense( 'book' );
$book2->genre = $cats[2];
$book2->name = 'Ghost Story';
R::store( $book2 );
$fantasy = R::load( 'category', $cats[1]->id );
$cats = array();
$book = $book->fresh();
$book->fetchAs( 'category' )->traverse( 'genre', function( $cat ) use ( &$cats ) {
$cats[] = $cat->gname;
} );
asrt( implode( ',', $cats ), 'SF,Fantasy' );
$catList = array();
$fantasy->alias( 'genre' )
->with( ' ORDER BY gname ASC ' )
->traverse( 'ownCategory', function( $cat ) use ( &$catList ) {
$catList[] = $cat->gname;
} );
asrt( implode( ',', $catList ), 'Horror,SF' );
}
/**
* Traverse can only work with own-lists, otherwise infinite loops.
*
* @return void
*/
public function testSharedTraversal()
{
$friend = R::dispense( 'friend' );
try {
$friend->traverse( 'sharedFriend', function( $friend ){ } );
fail();
} catch( RedException $e ) {
pass();
}
}
/**
* Test whether traverse() passes the depth of the
* current item in the tree along with the bean.
*/
public function testDepthCount()
{
R::nuke();
$page = R::dispense('page');
$page->num = 1;
$root = $page;
for($i = 2; $i < 10; $i++) {
$child = R::dispense('page');
$page->ownPageList[] = $child;
$child->num = $i;
$page = $child;
}
R::store($root);
$total = 0;
$page = $root->fresh();
$page->traverse('ownPageList', function( $child, $count ) use(&$total) {
asrt( $count+1, intval($child->num) );
$total += $count;
});
asrt( $total, 36 );
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Typechecking
*
* Tests whether RedBeanPHP handles type casting correctly.
*
* @file RedUNIT/Base/Typechecking.php
* @desc Tests basic bean validation rules; invalid bean handling.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Typechecking extends Base
{
/**
* Test types.
* Test how RedBeanPHP OODB and OODBBean handle type and type casts.
*
* Rules:
*
* 1. before storing a bean all types are preserved except booleans (they are converted to STRINGS '0' or '1')
* 2. after store-reload all bean property values are STRINGS or NULL
* (or ARRAYS but that's only from a user perspective because these are lazy loaded)
* 3. the ID returned by store() is an INTEGER (if possible; on 32 bit systems overflowing values will be cast to STRINGS!)
*
* After loading:
* ALL VALUES EXCEPT NULL -> STRING
* NULL -> NULL
*
* @note Why not simply return bean->id in store()? Because not every driver returns the same type:
* databases without insert_id support require a separate query or a suffix returning STRINGS, not INTEGERS.
*
* @note Why not preserve types? I.e. I store integer, why do I get back a string?
* Answer: types are handled different across database platforms, would cause overhead to inspect every value for type,
* also PHP is a dynamically typed language so types should not matter that much. Another reason: due to the nature
* of RB columns in the database might change (INT -> STRING) this would cause return types to change as well which would
* cause 'cascading errors', i.e. a column gets widened and suddenly your code would break.
*
* @note Unfortunately the 32/64-bit issue cannot be tested fully. Return-strategy store() is probably the safest
* solution.
*
* @return void
*/
public function testTypes()
{
testpack( 'Beans can only contain STRING and NULL after reload' );
R::nuke();
$bean = R::dispense( 'bean' );
$bean->number = 123;
$bean->float = 12.3;
$bean->bool = FALSE;
$bean->bool2 = TRUE;
$bean->text = 'abc';
$bean->null = null;
$bean->datetime = new\DateTime( 'NOW', new\DateTimeZone( 'Europe/Amsterdam' ) );
$id = R::store( $bean );
asrt( is_int( $id ), TRUE );
asrt( is_float( $bean->float ), TRUE );
asrt( is_integer( $bean->number ), TRUE );
asrt( is_string( $bean->bool ), TRUE );
asrt( is_string( $bean->bool2 ), TRUE );
asrt( is_string( $bean->datetime ), TRUE );
asrt( is_string( $bean->text ), TRUE );
asrt( is_null( $bean->null ), TRUE );
$bean = R::load('bean', $id );
asrt( is_string( $bean->id ), TRUE );
asrt( is_string( $bean->float ), TRUE );
asrt( is_string( $bean->number ), TRUE );
asrt( is_string( $bean->bool ), TRUE );
asrt( is_string( $bean->bool2 ), TRUE );
asrt( is_string( $bean->datetime ), TRUE );
asrt( is_string( $bean->text ), TRUE );
asrt( is_null( $bean->null ), TRUE );
asrt( $bean->bool, '0' );
asrt( $bean->bool2, '1' );
}
/**
* Test bean type checking.
*
* @return void
*/
public function testBeanTypeChecking()
{
$redbean = R::getRedBean();
$bean = $redbean->dispense( "page" );
// Set some illegal values in the bean; this should trigger Security exceptions.
// Arrays are not allowed.
$bean->name = array( "1" );
try {
$redbean->store( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
try {
$redbean->check( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
$bean->name = new OODBBean;
try {
$redbean->check( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
// Property names should be alphanumeric
$prop = ".";
$bean->$prop = 1;
try {
$redbean->store( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
try {
$redbean->check( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
// Really...
$prop = "-";
$bean->$prop = 1;
try {
$redbean->store( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
try {
$redbean->check( $bean );
fail();
} catch ( RedException $e ) {
pass();
}
}
}

View File

@@ -0,0 +1,448 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\RedException as RedException;
use RedBeanPHP\QueryWriter as QueryWriter;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
use RedBeanPHP\Logger\RDefault as Logger;
/**
* Update
*
* Tests basic update functionality - however this test suite
* has grown to cover various other scenarios involving updates as
* well, including setting of property filters (necessary for
* spatial tools in MySQL), storiging INF value and more...
*
* @file RedUNIT/Base/Update.php
* @desc Tests basic storage features through OODB class.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Update extends Base
{
/**
* Tests whether no unncessary DESCRIBE-queries are executed,
* (Commit 3b8ce88e5b796bfde6485ab0a51a4fcfb1bcf0fa by davidsickmiller).
* Even thtough we add 2 properties, only 1 DESCRIBE query is necessary
* to load the column cache.
*/
public function testModifySchemaColCache()
{
R::nuke();
$toolbox = R::getToolbox();
$repository = $toolbox->getRedBean()->getCurrentRepository();
$database = $toolbox->getDatabaseAdapter()->getDatabase();
$logger = new Logger;
$database->setLogger( $logger );
$bean = R::dispense('bean');
$bean->property1 = 'test';
$bean->property2 = 'test'; //should not cause 2nd DESCRIBE.
R::startLogging();
R::store( $bean );
$logger = R::getLogger();
asrt(
count( $logger->grep('DESCRIBE') ) +
count( $logger->grep('SELECT column_name') ) +
count( $logger->grep('SHOW COLUMNS') ) + //CUBRID
count( $logger->grep('PRAGMA table_info') )
, 1);
R::stopLogging();
//new round, same results, no cache between beans
R::nuke();
$bean = R::dispense('bean');
$bean->property1 = 'test';
$bean->property2 = 'test'; //should not cause 2nd DESCRIBE.
R::startLogging();
R::store( $bean );
$logger = R::getLogger();
asrt(
count( $logger->grep('DESCRIBE') ) +
count( $logger->grep('SELECT column_name') ) +
count( $logger->grep('SHOW COLUMNS') ) + //CUBRID
count( $logger->grep('PRAGMA table_info') )
, 1);
R::stopLogging();
}
/**
* Test whether we can use SQL filters and
* whether they are being applied properly for
* different types of SELECT queries in the QueryWriter.
*/
public function testSQLFilters()
{
R::nuke();
AQueryWriter::setSQLFilters(array(
QueryWriter::C_SQLFILTER_READ => array(
'book' => array( 'title' => ' LOWER(book.title) '),
),
QueryWriter::C_SQLFILTER_WRITE => array(
'book' => array( 'title' => ' UPPER(?) '),
),
));
$book = R::dispense( 'book' );
$book->title = 'story';
R::store( $book );
asrt( R::getCell( 'SELECT title FROM book WHERE id = ?', array( $book->id ) ), 'STORY' );
$book = $book->fresh();
asrt( $book->title, 'story' );
$library = R::dispense( 'library' );
$library->sharedBookList[] = $book;
R::store( $library );
$library = $library->fresh();
$books = $library->sharedBookList;
$book = reset( $books );
asrt( $book->title, 'story' );
$otherBook = R::dispense('book');
$otherBook->sharedBook[] = $book;
R::store( $otherBook );
$otherBook = $otherBook->fresh();
$books = $otherBook->sharedBookList;
$book = reset( $books );
asrt( $book->title, 'story' );
$links = $book->ownBookBookList;
$link = reset( $links );
$link->shelf = 'x13';
AQueryWriter::setSQLFilters(array(
QueryWriter::C_SQLFILTER_READ => array(
'book' => array( 'title' => ' LOWER(book.title) '),
'book_book' => array( 'shelf' => ' LOWER(book_book.shelf) '),
),
QueryWriter::C_SQLFILTER_WRITE => array(
'book' => array( 'title' => ' UPPER(?) '),
'book_book' => array( 'shelf' => ' UPPER(?) ')
),
));
R::store( $link );
asrt( R::getCell( 'SELECT shelf FROM book_book WHERE id = ?', array( $link->id ) ), 'X13' );
$otherBook = $otherBook->fresh();
unset($book->sharedBookList[$otherBook->id]);
R::store( $book );
AQueryWriter::setSQLFilters(array());
}
/**
* Test unsetting properties.
*
* @return void
*/
public function testUnsetUpdate()
{
R::nuke();
$book = R::dispense( 'book' );
$book->name = 'x';
$book->price = 40;
R::store( $book );
$book = $book->fresh();
$book->name = 'y';
unset( $book->name );
R::store( $book );
$book = $book->fresh();
asrt( $book->name, 'x' );
asrt( (int) $book->price, 40 );
$book->price = 30;
R::store( $book );
$book = $book->fresh();
asrt( $book->name, 'x' );
asrt( (int) $book->price, 30 );
$book->price = 20;
unset( $book->price );
$book->name = 'y';
R::store( $book );
$book = $book->fresh();
asrt( $book->name, 'y' );
asrt( (int) $book->price, 30 );
}
/**
* Tests whether we can update or unset a parent bean
* with an alias without having to use fetchAs and
* without loading the aliased bean causing table-not-found
* errors.
*/
public function testUpdatingParentBeansWithAliases()
{
testpack( 'Test updating parent beans with aliases' );
R::nuke();
$trans = R::dispense( 'transaction' );
$seller = R::dispense( 'user' );
$trans->seller = $seller;
$id = R::store( $trans );
R::freeze( TRUE );
$trans = R::load( 'transaction', $id );
//should not try to load seller, should not require fetchAs().
try {
$trans->seller = R::dispense( 'user' );
pass();
} catch( Exception $e ) {
fail();
}
$trans = R::load( 'transaction', $id );
//same for unset...
try {
unset( $trans->seller );
pass();
} catch ( Exception $e ) {
fail();
}
R::freeze( FALSE );
$account = R::dispense( 'user' );
asrt( count( $account->alias( 'seller' )->ownTransaction ), 0 );
$account->alias( 'seller' )->ownTransaction = R::dispense( 'transaction', 10 );
$account->alias( 'boo' ); //try to trick me...
$id = R::store( $account );
R::freeze( TRUE );
$account = R::load( 'user', $id );
asrt( count( $account->alias( 'seller' )->ownTransaction ), 10 );
//you cannot unset a list
unset( $account->alias( 'seller' )->ownTransaction );
$id = R::store( $account );
$account = R::load( 'user', $id );
asrt( count( $account->alias( 'seller' )->ownTransaction ), 10 );
$account->alias( 'seller' )->ownTransaction = array();
$id = R::store( $account );
$account = R::load( 'user', $id );
asrt(count($account->alias( 'seller' )->ownTransaction), 0 );
asrt(count($account->ownTransaction), 0 );
R::freeze( FALSE );
//but also make sure we don't cause extra column issue #335
R::nuke();
$building = R::dispense('building');
$village = R::dispense('village');
$building->village = $village;
R::store($building);
$building = $building->fresh();
$building->village = NULL;
R::store($building);
$building = $building->fresh();
$columns = R::inspect('building');
asrt( isset( $columns['village'] ), FALSE );
asrt( isset( $building->village ), FALSE );
R::nuke();
$building = R::dispense('building');
$village = R::dispense('village');
$building->village = $village;
R::store($building);
$building = $building->fresh();
unset($building->village);
R::store($building);
$building = $building->fresh();
$columns = R::inspect('building');
asrt( isset( $columns['village'] ), FALSE );
asrt( isset( $building->village ), FALSE );
$building = R::dispense('building');
$village = R::dispense('village');
$building->village = $village;
R::store($building);
$building = $building->fresh();
$building->village = FALSE;
R::store($building);
$building = $building->fresh();
$columns = R::inspect('building');
asrt( isset( $columns['village'] ), FALSE );
asrt( isset( $building->village ), FALSE );
}
/**
* All kinds of tests for basic CRUD.
*
* Does the data survive?
*
* @return void
*/
public function testUpdatingBeans()
{
testpack( 'Test basic support UUID/override ID default value' );
$bean = R::dispense( 'bean' );
R::store( $bean );
if ($this->currentlyActiveDriverID === 'mysql') {
//otherwise UTF8 causes index overflow in mysql: SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
R::exec('alter table bean modify column id char(3);');
} else {
R::getWriter()->widenColumn( 'bean', 'id', R::getWriter()->scanType( 'abc' ) );
}
$bean->id = 'abc';
R::store( $bean );
asrt( $bean->id, 'abc' );
testpack( 'Test Update' );
try {
R::store( array() );
fail();
} catch ( RedException $e ) {
pass();
}
$toolbox = R::getToolBox();
$adapter = $toolbox->getDatabaseAdapter();
$writer = $toolbox->getWriter();
$redbean = $toolbox->getRedBean();
$pdo = $adapter->getDatabase();
$page = $redbean->dispense( "page" );
$page->name = "old name";
$id = $redbean->store( $page );
asrt( $page->getMeta( 'tainted' ), FALSE );
$page->setAttr( 'name', "new name" );
asrt( $page->getMeta( 'tainted' ), TRUE );
$id = $redbean->store( $page );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
// Null should == NULL after saving
$page->rating = NULL;
$newid = $redbean->store( $page );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( ( $page->rating === NULL ), TRUE );
asrt( !$page->rating, TRUE );
$page->rating = FALSE;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( (bool) $page->rating, FALSE );
asrt( ( $page->rating == FALSE ), TRUE );
asrt( !$page->rating, TRUE );
$page->rating = TRUE;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( (bool) $page->rating, TRUE );
asrt( ( $page->rating == TRUE ), TRUE );
asrt( ( $page->rating == TRUE ), TRUE );
$page->rating = NULL;
R::store( $page );
$page = R::load( 'page', $page->id );
asrt( $page->rating, NULL );
$page->rating = '1';
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( $page->rating, "1" );
$page->rating = "0";
$newid = $redbean->store( $page );
asrt( $page->rating, "0" );
$page->rating = 0;
$newid = $redbean->store( $page );
asrt( $page->rating, 0 );
$page->rating = "0";
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( !$page->rating, TRUE );
asrt( ( $page->rating == 0 ), TRUE );
asrt( ( $page->rating == FALSE ), TRUE );
$page->rating = 5;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( strval( $page->rating ), "5" );
$page->rating = 300;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( strval( $page->rating ), "300" );
$page->rating = -2;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( strval( $page->rating ), "-2" );
$page->rating = 2.5;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( ( $page->rating == 2.5 ), TRUE );
$page->rating = -3.3;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( ( $page->rating == -3.3 ), TRUE );
$page->rating = "good";
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( $page->rating, "good" );
$longtext = str_repeat( 'great! because..', 100 );
$page->rating = $longtext;
$newid = $redbean->store( $page );
asrt( $newid, $id );
$page = $redbean->load( "page", $id );
asrt( $page->name, "new name" );
asrt( $page->rating, $longtext );
// Test leading zeros
$numAsString = "0001";
$page->numasstring = $numAsString;
$redbean->store( $page );
$page = $redbean->load( "page", $id );
asrt( $page->numasstring, "0001" );
$page->numnotstring = "0.123";
$redbean->store( $page );
$page = $redbean->load( "page", $id );
asrt( $page->numnotstring == 0.123, TRUE );
$page->numasstring2 = "00.123";
$redbean->store( $page );
$page = $redbean->load( "page", $id );
asrt( $page->numasstring2, "00.123" );
try {
$redbean->trash( array() );
fail();
} catch ( RedException $e ) {
pass();
}
$redbean->trash( $page );
asrt( (int) $pdo->GetCell( "SELECT count(*) FROM page" ), 0 );
}
/**
* Tests whether empty strings are preserved as such.
*
* @return void
*/
public function testEmptyStringShouldNotBeStoredAsInteger()
{
R::nuke();
$bean = R::dispense('bean');
$bean->str = '';
R::store($bean);
$bean = $bean->fresh();
asrt( ( $bean->str === '' ), TRUE);
}
/**
* Test handling of infinity values.
*
* @return void
*/
public function testStoringInf()
{
R::nuke();
$bean = R::dispense( 'bean' );
$bean->inf = INF;
R::store( $bean );
$bean = $bean->fresh();
asrt( ( $bean->inf === 'INF' ), TRUE );
asrt( ( $bean->inf == 'INF' ), TRUE );
$bean->modifyme = 'yes';
R::store( $bean );
$bean = $bean->fresh();
asrt( ( $bean->inf === 'INF' ), TRUE );
asrt( ( $bean->inf == 'INF' ), TRUE );
$bean->modifyme = 'yes';
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Utf8
*
* Tests whether we can store and retrive unicode characters.
*
* @file RedUNIT/Base/UTF8.php
* @desc Tests handling of NULL values.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Utf8 extends Base
{
/**
* Tests whether we can process malformed strings in beans.
*
* @return void
*/
public function testMalformed()
{
$byte = pack( 'I', 129 );
$bean = R::dispense( 'bean' );
$bean->byte = $byte;
OODBBean::setEnforceUTF8encoding( TRUE );
$str = strval( $bean );
OODBBean::setEnforceUTF8encoding( FALSE );
pass();
}
/**
* Test UTF8 handling.
*
* @return void
*/
public function testUTF8()
{
//skip if < 5.3
if (version_compare(PHP_VERSION, '5.4', '<')) return pass();
$str = '𠜎ὃ𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜';
$bean = R::dispense( 'bean' );
$bean->bla = $str;
R::store( $bean );
$bean = R::load( 'bean', $bean->id );
asrt( $bean->bla, $str );
pass();
}
}

View File

@@ -0,0 +1,330 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\ToolBox as ToolBox;
use RedBeanPHP\AssociationManager as AssociationManager;
use RedBeanPHP\RedException\SQL as SQL;
use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
/**
* Via
*
* Tests the via() function to hop over an N-M table to
* obtain a indirectly related bean, i.e. given person, participant
* and project you should be able to obtain a the project associated
* with a certain person via() the participant entity.
*
* @file RedUNIT/Base/Via.php
* @desc Via tests
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Via extends Base
{
/**
* Tests fix for issue #378.
* Via property does not get cleared properly.
*
* @return void
*/
public function testIssue378()
{
R::nuke();
$mediaBean = R::dispense('media');
$fooPerson = R::dispense('person');
$mediaBean->sharedPersonList[] = $fooPerson;
R::store($mediaBean);
asrt( count( $mediaBean->sharedPersonList ), 1 );
$person = R::findOne('person');
$person->via('relation')->sharedMarriageList[] = R::dispense('marriage');
//this second one caused the via property to not get cleared
$person->via('relation')->sharedMarriageList;
asrt( count( $person->sharedMediaList ), 1 );
//also found this scenario, non-existing property
$book = R::dispense('book');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->nothing;
asrt( count( $book->sharedPageList ), 1 );
//yet another
$book = R::dispense('magazine');
$book->ownAdList[] = R::dispense('ad');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->ownAdList;
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('folder');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->sharedItemList[] = R::dispense('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('folder2');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->sharedItemList[] = R::dispense('item');
$book->via('garbage')->sharedItemList[] = R::dispense('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('folder3');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->item = R::dispense('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('folder3');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->item = 'test';
asrt( count( $book->sharedPageList ), 1 );
//yet another
$book = R::dispense('leaflet');
$book->title = 'leaflet';
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->title;
asrt( count( $book->sharedPageList ), 1 );
//yet another
$book = R::dispense('paper');
$book->author = R::dispense('author');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->author;
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('paper2');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('garbage')->author;
asrt( count( $book->sharedPageList ), 1 );
//yet another one
$book = R::dispense('archive');
$book->sharedItem[] = R::dispense('item');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
unset( $book->via('garbage')->sharedItem );
asrt( count( $book->sharedPageList ), 1 );
//theoretic cases
$book = R::dispense('guide');
$book->sharedItem[] = R::dispense('item');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('relation')->countShared('item');
$book->via('relation')->countShared('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('catalogue');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('relation')->countShared('item');
$book->via('relation')->countShared('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('tabloid');
$book->ownItemList[] = R::dispense('item');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('relation')->countOwn('item');
$book->via('relation')->countOwn('item');
asrt( count( $book->sharedPageList ), 1 );
$book = R::dispense('booklet');
$book->ownItemList[] = R::dispense('item');
$book->sharedPage[] = R::dispense('page');
R::store($book);
$book = $book->fresh();
$book->via('relation')->countOwn('item');
$book->via('relation')->countOwn('item');
asrt( count( $book->sharedPageList ), 1 );
AQueryWriter::clearRenames();
}
/**
* Via specific tests.
*
* @return void
*/
public function testViaAndSQL()
{
R::nuke();
list($p1, $p2) = R::dispense('participant', 2);
list($e1, $e2) = R::dispense('employee', 2);
list($x1, $x2) = R::dispense('project', 2);
$e1->name = 'Anna';
$e2->name = 'John';
$p1->project = $x1;
$p1->employee = $e1;
$p1->arole = 'designer';
$p2->project = $x1;
$p2->employee = $e2;
$p2->arole = 'coder';
R::storeAll(array( $p1, $p2 ));
$project = R::load('project', $x1->id);
$designers = $project
->withCondition(' participant.arole = ? ', array( 'designer' ) )
->via( 'participant' )
->sharedEmployeeList;
$anna = reset( $designers );
asrt(count($designers), 1);
asrt($anna->name, 'Anna');
$coders = $project
->withCondition(' participant.arole = ? ', array( 'coder' ) )
->via( 'participant' )
->sharedEmployeeList;
$john = reset( $coders );
asrt(count($coders), 1);
asrt($john->name, 'John');
}
/**
* Test Via and Link together.
*
* @return void
*/
public function testViaAndLink()
{
R::nuke();
list( $John, $Anna, $Celine ) = R::dispense( 'employee', 3 );
$John->badge = 'John';
$Anna->badge = 'Anna';
$Celine->badge = 'Celine';
$project = R::dispense( 'project' );
$project->name = 'x';
$project2 = R::dispense( 'project' );
$project2->name = 'y';
$John->link( 'participant', array(
'arole' => 'designer'
) )->project = $project;
$Anna->link( 'participant', array(
'arole' => 'developer'
) )->project = $project;
$Celine->link( 'participant', array(
'arole' => 'sales'
) )->project = $project2;
$Anna->link('participant', array(
'arole' => 'lead'
) )->project = $project2;
R::storeAll( array( $project, $project2, $John, $Anna, $Celine ) );
$employees = $project
->with(' ORDER BY badge ASC ')
->via( 'participant' )
->sharedEmployee;
asrt( is_array( $employees ), TRUE );
asrt( count( $employees ), 2 );
$badges = array();
foreach( $employees as $employee ) {
$badges[] = $employee->badge;
}
asrt( implode( ',', $badges ), 'Anna,John' );
$employees = $project2
->with(' ORDER BY badge ASC ')
->via( 'participant' )
->sharedEmployee;
asrt( is_array( $employees ), TRUE );
asrt( count( $employees ), 2 );
$badges = array();
foreach( $employees as $employee ) {
$badges[] = $employee->badge;
}
asrt( implode( ',', $badges ), 'Anna,Celine' );
$projects = $John->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 1 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'x' );
$projects = $Anna->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 2 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'x,y' );
$projects = $Anna->via( 'participant' )->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 2 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'x,y' );
$projects = $Celine->via( 'participant' )->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 1 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'y' );
$roles = $Anna->ownParticipant;
asrt( is_array( $roles ), TRUE );
asrt( count( $roles ), 2 );
$roleList = array();
foreach( $roles as $role ) {
$roleList[] = $role->arole;
}
sort( $roleList );
asrt( implode( ',', $roleList ), 'developer,lead' );
$project2->sharedEmployee[] = $John;
R::store( $project2 );
$projects = $John->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 2 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'x,y' );
$projects = $John->via( 'participant' )->sharedProject;
asrt( is_array( $projects ), TRUE );
asrt( count( $projects ), 2 );
$projectList = array();
foreach( $projects as $project ) {
$projectList[] = $project->name;
}
sort( $projectList );
asrt( implode( ',', $projectList ), 'x,y' );
}
/**
* Test effect of via on shared list removal of beans.
*
* @return void
*/
public function testViaAndRemove()
{
R::nuke();
$project = R::dispense( 'project' );
$employees = R::dispense( 'employee', 2);
$project->via( 'partcipant' )->sharedEmployeeList = $employees;
R::store( $project );
asrt( R::count('employee'), 2 );
asrt( R::count('participant'), 2 );
$project = $project->fresh();
$project->sharedEmployee = array();
R::store( $project );
asrt( R::count( 'employee' ), 2 );
asrt( R::count( 'participant' ), 0 );
}
}

View File

@@ -0,0 +1,581 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* With
*
* With/WithCondition can be used to fetch bean lists with additional
* requirements, filters or ordering specification using SQL snippets.
* This suite tests the handling of with() and withCondition() snippets.
* Additionally this test suite also tests noLoad() and all() modifiers.
*
* @file RedUNIT/Base/With.php
* @desc Tests query modification of own-lists with prefix-with
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class With extends Base
{
/**
* This test suite uses specific SQL, only suited for MySQL.
*
* @return array
*/
public function getTargetDrivers()
{
return array( 'mysql' );
}
/**
* Tests no-load modifier for lists.
*
* @return void
*/
public function testNoLoad()
{
$book = R::dispense( array(
'_type' => 'book',
'title' => 'Book of Lorem Ipsum',
'ownPage' => array(
array(
'_type' => 'page',
'content' => 'Lorem Ipsum',
)
),
'sharedTag' => array(
array(
'_type' => 'tag',
'label' => 'testing'
)
)
) );
R::store( $book );
$book = $book->fresh();
asrt( R::count( 'book' ), 1 );
asrt( count( $book->ownPage ), 1 );
//now try with no-load
$book = $book->fresh();
asrt( count( $book->noLoad()->ownPage ), 0 );
asrt( count( $book->noLoad()->sharedTag ), 0 );
//now try to add with no-load
$book = $book->fresh();
$book->noLoad()->xownPageList[] = R::dispense( 'page' );
$book->noLoad()->sharedTagList[] = R::dispense( 'tag' );
R::store( $book );
$book = $book->fresh();
asrt( count( $book->ownPage ), 2 );
asrt( count( $book->sharedTagList ), 2 );
//no-load overrides with and withCondition
$book = $book->fresh();
asrt( count( $book->with(' invalid sql ')->noLoad()->ownPage ), 0 );
asrt( count( $book->withCondition(' invalid sql ')->noLoad()->sharedTag ), 0 );
//no-load overrides all and alias
$book = $book->fresh();
asrt( count( $book->all()->noLoad()->ownPage ), 0 );
asrt( count( $book->alias('nothing')->noLoad()->sharedTag ), 0 );
//no-load gets cleared
$book = $book->fresh();
asrt( count( $book->ownPage ), 2 );
asrt( count( $book->sharedTagList ), 2 );
//We cant clear with no-load accidentally?
$book = $book->fresh();
$book->noLoad()->ownPage = array();
$book->noLoad()->sharedTagList = array();
R::store( $book );
asrt( count( $book->ownPage ), 2 );
asrt( count( $book->sharedTagList ), 2 );
//No-load does not have effect if list is already cached
$book = $book->fresh();
$book->ownPage;
$book->sharedTag;
asrt( count( $book->ownPage ), 2 );
asrt( count( $book->sharedTagList ), 2 );
}
/**
* Test all().
*
* @return void
*/
public function testAll()
{
$book = R::dispense( 'book' );
$book->ownPage = R::dispense( 'page', 10 );
R::store( $book );
asrt( count( $book->with( ' LIMIT 3 ' )->ownPage ), 3 );
asrt( count( $book->ownPage ), 3 );
asrt( count( $book->all()->ownPage ), 10 );
asrt( count( $book->ownPage ), 10 );
R::nuke();
asrt( count( $book->ownPage ), 10 );
asrt( count( $book->all()->ownPage ), 0 );
}
/**
* Test embedded SQL snippets using with and withCondition.
*
* @return void
*/
public function testEmbeddedSQL()
{
list( $page1, $page2, $page3 ) = R::dispense( 'page', 3 );
list( $ad1, $ad2, $ad3 ) = R::dispense( 'ad', 3 );
$ad2->name = 'shampoo';
$page3->name = 'homepage';
$page1->sharedAd = array( $ad1, $ad3 );
$page2->sharedAd = array( $ad2, $ad3 );
$page3->sharedAd = array( $ad3, $ad2, $ad1 );
R::storeAll( array( $page1, $page2, $page3 ) );
$page1 = R::load( 'page', $page1->id );
asrt( 1, count( $page1->with( ' LIMIT 1 ' )->sharedAd ) );
$page2 = R::load( 'page', $page2->id );
$adsOnPage2 = $page2->withCondition( ' `name` = ? ', array( 'shampoo' ) )->sharedAd;
asrt( 1, count( $adsOnPage2 ) );
$ad = reset( $adsOnPage2 );
asrt( $ad->name, 'shampoo' );
$ad = R::load( 'ad', $ad->id );
asrt( count( $ad->sharedPage ), 2 );
$ad = R::load( 'ad', $ad->id );
$homepage = reset( $ad->withCondition( ' `name` LIKE ? AND page.id > 0 ORDER BY id DESC ', array( '%ome%' ) )->sharedPage );
asrt( $homepage->name, 'homepage' );
}
/**
* More variations...
*
* @return void
*/
public function testEmbeddedSQLPart2()
{
list( $book1, $book2, $book3 ) = R::dispense( 'book', 3 );
$book1->position = 1;
$book2->position = 2;
$book3->position = 3;
$shelf = R::dispense( 'shelf' );
$shelf->ownBook = array( $book1, $book2, $book3 );
$id = R::store( $shelf );
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' ORDER BY position ASC ' )->ownBook;
$book1 = array_shift( $books );
asrt( (int) $book1->position, 1 );
$book2 = array_shift( $books );
asrt( (int) $book2->position, 2 );
$book3 = array_shift( $books );
asrt( (int) $book3->position, 3 );
$books = $shelf->with( ' ORDER BY position DESC ' )->ownBook;
$book1 = array_shift( $books );
asrt( (int) $book1->position, 3 );
$book2 = array_shift( $books );
asrt( (int) $book2->position, 2 );
$book3 = array_shift( $books );
asrt( (int) $book3->position, 1 );
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position > 2 ' )->ownBook;
asrt( count( $books ), 1 );
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position < ? ', array( 3 ) )->ownBook;
asrt( count( $books ), 2 );
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position = 1 ' )->ownBook;
asrt( count( $books ), 1 );
$shelf = R::load( 'shelf', $id );
$books = $shelf->withCondition( ' position > -1 ' )->ownBook;
asrt( count( $books ), 3 );
// With-condition should not affect storing
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position = 1 ' )->ownBook;
asrt( count( $books ), 1 );
asrt( count( $shelf->ownBook ), 1 );
$book = reset( $shelf->ownBook );
$book->title = 'Trees and other Poems';
R::store( $shelf );
$books = $shelf->withCondition( ' position > -1 ' )->ownBook;
asrt( count( $books ), 3 );
asrt( count( $shelf->ownBook ), 3 );
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position = 1 ' )->ownBook;
// Also with trashing -- just trash one!
$shelf->ownBook = array();
R::store( $shelf );
$books = $shelf->withCondition( ' position > -1 ' )->ownBook;
asrt( count( $books ), 2 );
// With should cause a reload of a list
$shelf = R::load( 'shelf', $id );
$books = $shelf->with( ' AND position = 2 ' )->ownBook;
asrt( count( $books ), 1 );
$books = $shelf->withCondition( ' position > -1 ' )->ownBook;
asrt( count( $books ), 2 );
$book = reset( $books );
$book->title = 'Venetian Music';
// Should not affect storage (fact that we used with twice, unsetting prop)
R::store( $shelf );
$shelf = R::load( 'shelf', $id );
asrt( count( $shelf->ownBook ), 2 );
// Alias
list( $game1, $game2, $game3 ) = R::dispense( 'game', 3 );
list( $t1, $t2, $t3 ) = R::dispense( 'team', 3 );
$t1->name = 'Bats';
$t2->name = 'Tigers';
$t3->name = 'Eagles';
$game1->name = 'a';
$game1->team1 = $t1;
$game1->team2 = $t2;
$game2->name = 'b';
$game2->team1 = $t1;
$game2->team2 = $t3;
$game3->name = 'c';
$game3->team1 = $t2;
$game3->team2 = $t3;
R::storeAll( array( $game1, $game2, $game3 ) );
$team1 = R::load( 'team', $t1->id );
$team2 = R::load( 'team', $t2->id );
$team3 = R::load( 'team', $t3->id );
asrt( count( $team1->alias( 'team1' )->ownGame ), 2 );
asrt( count( $team2->alias( 'team1' )->ownGame ), 1 );
$team1 = R::load( 'team', $t1->id );
$team2 = R::load( 'team', $t2->id );
asrt( count( $team1->alias( 'team2' )->ownGame ), 0 );
asrt( count( $team2->alias( 'team2' )->ownGame ), 1 );
asrt( count( $team3->alias( 'team1' )->ownGame ), 0 );
$team3 = R::load( 'team', $t3->id );
asrt( count( $team3->alias( 'team2' )->ownGame ), 2 );
$team1 = R::load( 'team', $t1->id );
$games = $team1->alias( 'team1' )->ownGame;
$game4 = R::dispense( 'game' );
$game4->name = 'd';
$game4->team2 = $t3;
$team1->alias( 'team1' )->ownGame[] = $game4;
R::store( $team1 );
$team1 = R::load( 'team', $t1->id );
asrt( count( $team1->alias( 'team1' )->ownGame ), 3 );
foreach ( $team1->ownGame as $g ) {
if ( $g->name == 'a' ) $game = $g;
}
$game->name = 'match';
R::store( $team1 );
$team1 = R::load( 'team', $t1->id );
asrt( count( $team1->alias( 'team1' )->ownGame ), 3 );
$found = 0;
foreach ( $team1->ownGame as $g ) {
if ( $g->name == 'match' ) $found = 1;
}
if ( $found ) pass();
$team1->ownGame = array();
R::store( $team1 );
$team1 = R::load( 'team', $t1->id );
asrt( count( $team1->alias( 'team1' )->ownGame ), 0 );
$team1->ownBook[] = $book1;
R::store( $team1 );
$team1 = R::load( 'team', $t1->id );
asrt( count( $team1->alias( 'team1' )->ownGame ), 0 );
asrt( count( $team1->ownBook ), 1 );
}
/**
* Test when to reload and when to NOT reload beans.
* Use UNSET to reload a parent bean. Use UNSET or
* a modifier (with, withCondition, all) to reload a list.
* Use noLoad() to obtain an empty list - does not reload
* but sets an empty array.
*
* @return void
*/
public function testWhenToReload()
{
$book = R::dispense( 'book' );
$book->ownPage = R::dispense( 'page', 3 );
$book->author = R::dispense( 'author' );
$book->coauthor = R::dispense( 'author' );
R::store( $book );
$book = $book->fresh();
$firstPage = reset( $book->ownPage );
$id = $firstPage->id;
$book->ownPage[ $id ]->title = 'a';
//Do not reload an own list after manipulations
asrt( $book->ownPage[ $id ]->title, 'a' ); //dont reload!
$book->ownPage[] = R::dispense( 'page' ); //dont reload!
asrt( $book->ownPage[ $id ]->title, 'a' ); //dont reload!
asrt( $book->ownPageList[ $id ]->title, 'a' ); //dont reload!
asrt( $book->xownPageList[ $id ]->title, 'a' ); //dont reload!
asrt( $book->xownPage[ $id ]->title, 'a' ); //dont reload!
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
unset( $book->ownPageList );
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
unset( $book->xownPageList );
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
unset( $book->xownPage );
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
unset( $book->ownPage );
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
$book->all()->ownPage;
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
$book->all()->xownPage;
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
$book->all()->ownPageList;
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//now trigger reload
$book->all()->xownPageList;
asrt( count( $book->ownPageList ), 3 );
$book->ownPage[] = R::dispense( 'page' );
asrt( count( $book->ownPageList ), 4 );
//Do not reload an own list if told to not reload using noLoad()
$book->noLoad()->with(' LIMIT 1 ')->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->noLoad()->all()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->noLoad()->alias('magazine')->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->noLoad()->withCondition('')->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
//even if modifiers proceed noLoad()
$book->with(' LIMIT 1 ')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->all()->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->alias('magazine')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->withCondition('')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
//even in combinations
$book->all()->with(' LIMIT 1 ')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->alias('magazine')->all()->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->alias('magazine')->with('LIMIT 1')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
$book->alias('magazine')->withCondition('')->noLoad()->ownPage; //dont reload!
asrt( count( $book->xownPage ), 0); //dont reload!
//now test shared list
$book->sharedTag = R::dispense( 'tag', 16 );
asrt( count( $book->sharedTag ), 16 );
$book->sharedTag[] = R::dispense( 'tag' );
asrt( count( $book->sharedTag ), 17 ); //dont reload after adding
$last = end( $book->sharedTagList );
$id = $last->id;
$book->sharedTag[ $id ]->title = 'b';
asrt( count( $book->sharedTag ), 17 ); //dont reload after manipulation
unset( $book->sharedTagList[ $id ] );
asrt( count( $book->sharedTag ), 16 ); //dont reload after manipulation
//now trigger reload
unset( $book->sharedTagList );
asrt( count( $book->sharedTag ), 0 );
$book->sharedTag = R::dispense( 'tag', 16 );
asrt( count( $book->sharedTag ), 16 );
//now trigger reload
unset( $book->sharedTag );
asrt( count( $book->sharedTag ), 0 );
$book->sharedTag = R::dispense( 'tag', 16 );
asrt( count( $book->sharedTag ), 16 );
//now trigger reload
$book->all()->sharedTag;
asrt( count( $book->sharedTag ), 0 );
$book->sharedTag = R::dispense( 'tag', 16 );
asrt( count( $book->sharedTag ), 16 );
//now trigger reload
$book->all()->sharedTagList;
asrt( count( $book->sharedTag ), 0 );
$book->sharedTag = R::dispense( 'tag', 16 );
asrt( count( $book->sharedTag ), 16 );
//Do not reload a sharedTag list if told to not reload using noLoad()
$book->noLoad()->with(' LIMIT 1 ')->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->noLoad()->all()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->noLoad()->alias('magazine')->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->noLoad()->withCondition('')->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
//even if modifiers proceed noLoad()
$book->with(' LIMIT 1 ')->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->all()->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->alias('magazine')->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->withCondition('')->noLoad()->ownPage; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
//even in combinations
$book->all()->with(' LIMIT 1 ')->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->alias('magazine')->all()->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->alias('magazine')->with('LIMIT 1')->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
$book->alias('magazine')->withCondition('')->noLoad()->sharedTag; //dont reload!
asrt( count( $book->sharedTag ), 0); //dont reload!
//test do not reload parent bean
$book->author->name = 'me';
asrt( $book->author->name, 'me' );
$book->fetchAs('author')->coauthor;
asrt( $book->author->name, 'me' );
$book->fetchAs('author')->author;
asrt( $book->author->name, 'me' );
$book->with(' LIMIT 1 ')->author;
asrt( $book->author->name, 'me' );
$book->withCondition('')->author;
asrt( $book->author->name, 'me' );
$book->all()->author;
asrt( $book->author->name, 'me' );
$book->noLoad()->author;
asrt( $book->author->name, 'me' );
$book->noLoad()->all()->author;
asrt( $book->author->name, 'me' );
$book->with('LIMIT 1')->noLoad()->all()->author;
asrt( $book->author->name, 'me' );
//now trigger reload
unset( $book->author );
asrt( $book->author->name, NULL );
$book->author->name = 'me';
asrt( $book->author->name, 'me' );
}
/**
* Tests whether modifiers are cleared after reading or
* writing a bean property.
*
* @return void
*/
public function testClearanceOfModFlags()
{
//test base condition, retrieving list or parent should not set flags
$book = R::dispense( 'book' );
asrt( $book->getModFlags(), '' );
$book->ownPage = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->xownPage = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->ownPageList = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->xownPageList = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->ownPage[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->xownPage[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->ownPageList[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->xownPageList[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->sharedPage = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->sharedPageList = R::dispense( 'page', 2 );
asrt( $book->getModFlags(), '' );
$book->sharedPage[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->sharedPageList[] = R::dispense( 'page', 1 );
asrt( $book->getModFlags(), '' );
$book->author = R::dispense( 'author' );
asrt( $book->getModFlags(), '' );
$book->title = 'title';
//Test whether appropriate flags are set and whether they are cleared after
//accessing a property.
$modifiers = array('with'=>'w', 'withCondition'=>'w', 'alias'=>'a', 'fetchAs'=>'f', 'all'=>'r', 'noLoad'=>'n');
$properties = array('ownPage', 'ownPageList', 'xownPage', 'xownPageList', 'sharedPage', 'sharedPageList', 'author', 'title');
foreach( $modifiers as $modifier => $flag ) {
foreach( $properties as $property ) {
$book = R::dispense( 'book' );
$book->$modifier('something');
$flags = $book->getModFlags();
$expect = $flag;
asrt( $flags, $expect );
$book->$property;
$flags = $book->getModFlags();
asrt( $flags, '' );
}
}
//now test combinations and also test whether we can
//clear modifiers manually using the clearModifiers() method.
foreach( $modifiers as $modifier => $flag ) {
foreach( $modifiers as $modifier2 => $flag2 ) {
foreach( $properties as $property ) {
$book = R::dispense( 'book' );
$book->$modifier( 'something' )->$modifier2( 'something' );
$flags = $book->getModFlags();
$expect = array($flag, $flag2);
$expect = array_unique( $expect );
sort( $expect );
$expect = implode( '', $expect );
asrt( $flags, $expect );
$book->$modifier( 'something' )->$modifier2( 'something' )->clearModifiers();
$flags = $book->getModFlags();
asrt( $flags, '' );
$book->$modifier( 'something' )->$modifier2( 'something' )->clearModifiers();
$book->$property;
$flags = $book->getModFlags();
asrt( $flags, '' );
}
}
}
$book = R::dispense( 'book' );
$book->ownPage = R::dispense( 'page', 2 );
$book->sharedPage = R::dispense( 'page', 2 );
R::store( $book );
$book = R::dispense( 'book' );
$book->alias('magazine')->ownPage = R::dispense( 'page', 2 );
R::store( $book );
//test modifier with countOwn and countShared methods
foreach( $modifiers as $modifier => $flag ) {
$book = R::dispense( 'book' );
if ($modifier === 'withCondition') $book->$modifier( ' 1 ' );
elseif ($modifier === 'with') $book->$modifier( ' LIMIT 1 ' );
elseif ($modifier === 'alias') $book->$modifier('magazine');
else $book->$modifier('something');
$flags = $book->getModFlags();
$expect = $flag;
asrt( $flags, $expect );
$book->countOwn('page');
$flags = $book->getModFlags();
asrt( $flags, '' );
if ($modifier === 'withCondition') $book->$modifier( ' 1 ' );
elseif ($modifier === 'with') $book->$modifier( ' LIMIT 1 ' );
elseif ($modifier === 'alias') $book->$modifier('magazine');
else $book->$modifier('something');
$flags = $book->getModFlags();
$expect = $flag;
asrt( $flags, $expect );
$book->countShared('page');
$flags = $book->getModFlags();
asrt( $flags, '' );
if ($modifier === 'withCondition') $book->$modifier( ' 1 ' );
elseif ($modifier === 'with') $book->$modifier( ' LIMIT 1 ' );
elseif ($modifier === 'alias') $book->$modifier('magazine');
else $book->$modifier('something');
$flags = $book->getModFlags();
$expect = $flag;
asrt( $flags, $expect );
unset( $book->author );
$flags = $book->getModFlags();
asrt( $flags, '' );
}
}
}

View File

@@ -0,0 +1,427 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
/**
* Writecache
*
* The Query Writer Cache tries to avoid unnecessary queries
* by using cache markers. This means repeatingly fetching the
* same parent bean for instance (in loop) won't hurt performance.
* The Query Writer Cache gets emptied if the chain of markers
* gets broken. All 'non destructive' queries are marked, as long
* as no other queries have been executed we can safely assume
* nothing in the database has changed for the current request.
* You might want to turn this form of caching off in long running
* PHP processes.
*
* @file RedUNIT/Base/Writecache.php
* @desc Tests the Query Writer cache implemented in the
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Writecache extends Base
{
/**
* What drivers should be loaded for this test pack?
*/
public function getTargetDrivers()
{
return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
}
/**
* Lynesth:
* Using flushCache() obviously fixes it.
* The example might be a bit stupid but it gets to the point.
* Someone might be using a specific query more than once with
* getCell (or any other) and thinking the
* cache works for those as well, try to make use of it.
*
* @return void
*/
public function testCacheFail()
{
R::nuke();
R::freeze( FALSE );
$author = R::dispense('author');
$author->name = "John";
$id = R::store($author);
$author = R::load('author', 1); // This gets cached
$author = $author->fresh(); // This gets cached
$author = R::findOne('author', ' id = ? ', array( 1 )); // This gets cached
asrt($author->name, 'John');
R::exec('UPDATE author SET name = "Bob" WHERE id = 1');
// It's broken because there's no cache check between those two calls
//more realistic query: 'SELECT MAX(id) FROM author -- keep-cache');
R::getCell('SELECT 123 -- keep-cache');
$author = R::load('author', $id);
asrt($author->name, 'John');
$author = $author->fresh();
asrt($author->name, 'John');
$author = R::findOne('author', ' id = ? ', array( 1 ));
asrt($author->name, 'John');
}
/**
* Test whether cache size remains constant (per type).
* Avoiding potential memory leaks. (Issue #424).
*
* @return void
*/
public function testCacheSize()
{
R::nuke();
R::useWriterCache( TRUE );
$writer = R::getWriter();
$bean = R::dispense( 'bean' );
$bean->prop = 1;
R::store( $bean );
$writer->flushCache( 20 );
$count = $writer->flushCache();
asrt( $count, 0 );
R::find( 'bean', ' prop < ? ', array( 1 ) );
$count = $writer->flushCache();
asrt( $count, 2 );
R::find( 'bean', ' prop < ? ', array( 2 ) );
$count = $writer->flushCache();
asrt( $count, 5 );
R::find( 'bean', ' prop < ? ', array( 2 ) );
$count = $writer->flushCache();
asrt( $count, 5 );
for( $i = 0; $i < 40; $i ++ ) {
R::find( 'bean', ' prop < ? ', array( $i ) );
}
$count = $writer->flushCache();
asrt( $count, 85 );
for( $i = 0; $i < 120; $i ++ ) {
R::find( 'bean', ' prop < ? ', array( $i ) );
}
$count = $writer->flushCache( 1 );
asrt( $count, 85 );
for( $i = 0; $i < 20; $i ++ ) {
R::find( 'bean', ' prop < ? ', array( $i ) );
}
$count = $writer->flushCache( 20 );
asrt( $count, 9 );
}
/**
* When using fetchAs(), Query Cache does not recognize objects
* that have been previously fetched, see issue #400.
*/
public function testCachingAndFetchAs()
{
testpack( 'Testing whether you can cache multiple records of the same type' );
R::debug( TRUE, 1 );
$logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
R::nuke();
$coauthor1 = R::dispense( 'author' );
$coauthor1->name = 'John';
$book = R::dispense( 'book' );
$book->title = 'a Funny Tale';
$book->coauthor = $coauthor1;
$id = R::store( $book );
$coauthor = R::dispense( 'author' );
$coauthor->name = 'Pete';
$book = R::dispense( 'book' );
$book->title = 'a Funny Tale 2';
$book->coauthor = $coauthor;
$id = R::store( $book );
$book = R::dispense( 'book' );
$book->title = 'a Funny Tale 3';
$book->coauthor = $coauthor1;
$id = R::store( $book );
$books = R::find( 'book' );
$logger->clear();
$authors = array();
$authorsByName = array();
foreach($books as $book) {
$coAuthor = $book->with( ' ORDER BY title ASC ' )
->fetchAs( 'author' )->coauthor;
$authors[] = $coAuthor->name;
$authorsByName[ $coAuthor->name ] = $coAuthor;
}
asrt( count( $logger->grep( 'SELECT' ) ), 2 ); //must be 2! 3 if cache does not work!
asrt( count( $authors ), 3 );
asrt( isset( $authorsByName[ 'John' ] ), TRUE );
asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
$logger->clear();
$authors = array();
$authorsByName = array();
foreach($books as $book) {
$coAuthor = $book->with( ' ORDER BY title DESC ' )
->fetchAs( 'author' )->coauthor;
$authors[] = $coAuthor->name;
$authorsByName[ $coAuthor->name ] = $coAuthor;
}
asrt( count( $logger->grep( 'SELECT' ) ), 0 ); //must be 0!
asrt( count( $authors ), 3 );
asrt( isset( $authorsByName[ 'John' ] ), TRUE );
asrt( isset( $authorsByName[ 'Pete' ] ), TRUE );
}
/**
* Test effects of cache.
*
* @return void
*/
public function testCachingEffects()
{
testpack( 'Testing WriteCache Query Writer Cache' );
R::setNarrowFieldMode( FALSE );
R::useWriterCache( FALSE );
R::debug( TRUE, 1 );
$logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
$book = R::dispense( 'book' )->setAttr( 'title', 'ABC' );
$book->ownPage[] = R::dispense( 'page' );
$id = R::store( $book );
// Test load cache -- without
$logger->clear();
$book = R::load( 'book', $id );
$book = R::load( 'book', $id );
asrt( count( $logger->grep( 'SELECT' ) ), 2 );
// With cache
R::useWriterCache( TRUE );
$logger->clear();
$book = R::load( 'book', $id );
$book = R::load( 'book', $id );
asrt( count( $logger->grep( 'SELECT' ) ), 1 );
R::useWriterCache( FALSE );
// Test find cache
$logger->clear();
$book = R::find( 'book' );
$book = R::find( 'book' );
asrt( count( $logger->grep( 'SELECT' ) ), 2 );
// With cache
R::getWriter()->setUseCache( TRUE );
$logger->clear();
$book = R::find( 'book' );
$book = R::find( 'book' );
asrt( count( $logger->grep( 'SELECT' ) ), 1 );
R::getWriter()->setUseCache( FALSE );
// Test combinations
$logger->clear();
$book = R::findOne( 'book', ' id = ? ', array( $id ) );
$book->ownPage;
R::batch( 'book', array( $id ) );
$book = R::findOne( 'book', ' id = ? ', array( $id ) );
$book->ownPage;
R::batch( 'book', array( $id ) );
asrt( count( $logger->grep( 'SELECT' ) ), 6 );
// With cache
R::getWriter()->setUseCache( TRUE );
$logger->clear();
R::batch( 'book', array( $id ) );
$book = R::findOne( 'book', ' id = ? ', array( $id ) );
$book->ownPage;
$book = R::findOne( 'book', ' id = ? ', array( $id ) );
$book->ownPage;
asrt( count( $logger->grep( 'SELECT' ) ), 3 );
R::getWriter()->setUseCache( FALSE );
// Test auto flush
$logger->clear();
$book = R::findOne( 'book' );
$book->name = 'X';
R::store( $book );
$book = R::findOne( 'book' );
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
// With cache
R::getWriter()->setUseCache( TRUE );
$logger->clear();
$book = R::findOne( 'book' );
$book->name = 'Y';
// Will flush
R::store( $book );
$book = R::findOne( 'book' );
// Now the same, auto flushed
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
R::getWriter()->setUseCache( FALSE );
// Test whether delete flushes as well (because uses selectRecord - might be a gotcha!)
R::store( R::dispense( 'garbage' ) );
$garbage = R::findOne( 'garbage' );
$logger->clear();
$book = R::findOne( 'book' );
R::trash( $garbage );
$book = R::findOne( 'book' );
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
R::store( R::dispense( 'garbage' ) );
$garbage = R::findOne( 'garbage' );
// With cache
R::getWriter()->setUseCache( TRUE );
$logger->clear();
$book = R::findOne( 'book' );
R::trash( $garbage );
$book = R::findOne( 'book' );
// Now the same, auto flushed
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
R::getWriter()->setUseCache( FALSE );
R::store( R::dispense( 'garbage' ) );
$garbage = R::findOne( 'garbage' );
// With cache
R::getWriter()->setUseCache( TRUE );
$logger->clear();
$book = R::findOne( 'book' );
R::getWriter()->queryRecord( 'garbage', array( 'id' => array( $garbage->id ) ) );
$book = R::findOne( 'book' );
// Now the same, auto flushed
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
$page = R::dispense('page');
$book->sharedPage[] = $page;
R::store( $book );
$logger->clear();
$link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
asrt( count( $logger->grep( 'SELECT' ) ), 1 );
$link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
asrt( count( $logger->grep( 'SELECT' ) ), 1 );
R::getWriter()->setUseCache( FALSE );
$link = R::getWriter()->queryRecordLink( 'book', 'page', $book->id, $page->id );
asrt( count( $logger->grep( 'SELECT' ) ), 2 );
R::getWriter()->setUseCache( TRUE );
R::setNarrowFieldMode( TRUE );
}
/**
* Try to fool the cache :)
*
* @return void
*/
public function testRegressions()
{
testpack( 'Testing possible regressions: Try to fool the cache' );
$str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' WHERE ( ' . R::getWriter()->esc( 'id', TRUE ) . ' IN ( 1) ) ';
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id = R::store( $bean );
$bean = R::load( 'bean', $id );
$bean->title = 'xxx';
R::store( $bean );
// Fire exact same query so cache may think no other query has been fired
R::exec( $str );
$bean = R::load( 'bean', $id );
asrt( $bean->title, 'xxx' );
}
/**
* Test keep-cache comment.
*
* @return void
*/
public function testKeepCacheCommentInSQL()
{
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id = R::store( $bean );
$bean = R::load( 'bean', $id );
$bean->title = 'xxx';
R::store( $bean );
// Causes flush even though it contains -- keep-cache (not at the end, not intended)
R::findOne( 'bean', ' title = ? ', array( '-- keep-cache' ) );
$bean = R::load( 'bean', $id );
asrt( $bean->title, 'xxx' );
}
/**
*
* Same as above.. test keep cache.
*
* @return void
*/
public function testInstructNoDrop()
{
$str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id = R::store( $bean );
$bean = R::load( 'bean', $id );
$bean->title = 'xxx';
R::store( $bean );
R::exec( $str );
$bean = R::load( 'bean', $id );
asrt( $bean->title, 'abc' );
R::nuke();
// Now INSTRUCT the cache to not drop the cache CASE 2
$str = 'SELECT * FROM ' . R::getWriter()->esc( 'bean', TRUE ) . ' -- keep-cache';
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id = R::store( $bean );
$bean = R::load( 'bean', $id );
$bean->title = 'xxx';
R::store( $bean );
R::findOne( 'bean', ' title = ? ', array( 'cache' ) );
$bean = R::load( 'bean', $id );
asrt( $bean->title, 'xxx' );
}
/**
* Can we confuse the cache?
*
* @return void
*/
public function testConfusionRegression()
{
testpack( 'Testing possible confusion regression' );
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id1 = R::store( $bean );
$bean = R::dispense( 'bean' );
$bean->title = 'abc2';
$id2 = R::store( $bean );
$bean = R::load( 'bean', $id1 );
asrt( $bean->title, 'abc' );
$bean = R::load( 'bean', $id2 );
asrt( $bean->title, 'abc2' );
}
/**
* Test Ghost beans....
*
* @return void
*/
public function testGhostBeans()
{
testpack( 'Testing ghost beans' );
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id1 = R::store( $bean );
R::trash( $bean );
$bean = R::load( 'bean', $id1 );
asrt( (int) $bean->id, 0 );
}
/**
* Test explicit flush.
*
* @return void
*/
public function testExplicitCacheFlush()
{
testpack( 'Test cache flush (explicit)' );
R::setNarrowFieldMode( FALSE );
R::debug( TRUE, 1 );
$logger = R::getDatabaseAdapter()->getDatabase()->getLogger();
$bean = R::dispense( 'bean' );
$bean->title = 'abc';
$id1 = R::store( $bean );
$logger->clear();
$bean = R::load( 'bean', $id1 );
asrt( $bean->title, 'abc' );
asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
$bean = R::load( 'bean', $id1 );
asrt( count( $logger->grep( 'SELECT *' ) ), 1 );
R::getWriter()->flushCache();
$bean = R::load( 'bean', $id1 );
asrt( count( $logger->grep( 'SELECT *' ) ), 2 );
R::getWriter()->flushCache();
R::getWriter()->setUseCache( FALSE );
R::setNarrowFieldMode( TRUE );
}
}

View File

@@ -0,0 +1,280 @@
<?php
namespace RedUNIT\Base;
use RedUNIT\Base as Base;
use RedBeanPHP\Facade as R;
use RedBeanPHP\OODBBean as OODBBean;
/**
* Null
*
* Tests NULL handling.
*
* @file RedUNIT/Base/Xnull.php
* @desc Tests handling of NULL values.
* @author Gabor de Mooij and the RedBeanPHP Community
* @license New BSD/GPLv2
*
* (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
* This source file is subject to the New BSD/GPLv2 License that is bundled
* with this source code in the file license.txt.
*/
class Xnull extends Base
{
/**
* Tests whether we can create queries containing IS-NULL with
* the IS-NULL-Condition flag.
*/
public function testISNULLConditions()
{
R::nuke();
R::useISNULLConditions( FALSE );
$book = R::dispense('book');
$book->title = 'Much ado about Null';
R::store( $book );
$book = R::dispense('book');
$book->title = NULL;
R::store( $book );
$books = R::findLike('book', array( 'title' => NULL ) );
asrt(count($books), 2);
$wasFalse = R::useISNULLConditions( TRUE );
asrt( $wasFalse, FALSE );
$books = R::findLike('book', array( 'title' => NULL ) );
asrt(count($books), 1);
$books = R::find('book', ' title = :title ', array( 'title' => NULL ) );
asrt(count($books), 0);
}
/**
* Test Null bindings.
*/
public function testBindings()
{
R::nuke();
$book = R::dispense( 'book' );
$book->content = NULL;
//can we store a NULL?
asrt( is_null( $book->content ), TRUE );
R::store( $book );
//did we really store the NULL value ?
$book = R::findOne( 'book', ' content IS NULL ' );
asrt( ( $book instanceof OODBBean ), TRUE );
//still NULL, not empty STRING ?
asrt( is_null( $book->content ), TRUE );
$book->pages = 100;
R::store( $book );
//did we save it once again as NULL?
$book = R::findOne( 'book', ' content IS NULL ' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( is_null( $book->content ), TRUE );
asrt( gettype( $book->pages ), 'string' );
$otherBook = R::dispense( 'book' );
$otherBook->pages = 99;
//also if the column is VARCHAR-like?
$otherBook->content = 'blah blah';
R::store( $otherBook );
$book = R::findOne( 'book', ' content IS NULL ' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( is_null( $book->content ), TRUE );
asrt( intval( $book->pages ), 100 );
//can we query not NULL as well?
$book = R::findOne( 'book', ' content IS NOT NULL ' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( is_null( $book->content ), FALSE );
asrt( intval( $book->pages ), 99 );
asrt( $book->content, 'blah blah' );
//Can we bind NULL directly?
$book->isGood = FALSE;
//Is NULL the default? And... no confusion with boolean FALSE?
R::store( $book );
$book = R::findOne( 'book', ' is_good IS NULL' );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( is_null( $book->content ), TRUE );
asrt( intval( $book->pages ), 100 );
$book = R::findOne( 'book', ' is_good = ?', array( 0 ) );
asrt( ( $book instanceof OODBBean ), TRUE );
asrt( is_null( $book->content ), FALSE );
asrt( intval( $book->pages ), 99 );
}
/**
* Tests whether we can NULLify a parent bean
* page->book if the parent (book) is already
* NULL. (isset vs array_key_exists bug).
*
* @return void
*/
public function testUnsetParent()
{
R::nuke();
$book = R::dispense( 'book' );
$book->title = 'My Book';
$page = R::dispense( 'page' );
$page->text = 'Lorem Ipsum';
$book->ownPage[] = $page;
R::store( $book );
$page = $page->fresh();
R::freeze( TRUE );
asrt( (int) $page->book->id, (int) $book->id );
unset( $page->book );
R::store( $page );
$page = $page->fresh();
asrt( (int) $page->book->id, (int) $book->id );
$page->book = NULL;
R::store( $page );
$page = $page->fresh();
asrt( $page->book, NULL );
asrt( $page->book_id, NULL );
asrt( $page->bookID, NULL );
asrt( $page->bookId, NULL );
$page = R::dispense( 'page' );
$page->text = 'Another Page';
$page->book = NULL;
try {
R::store( $page );
fail();
} catch( \Exception $exception ) {
pass();
}
unset($page->book);
R::store($page);
$page = $page->fresh();
$page->book = NULL; //this must set field id to NULL not ADD column!
try {
R::store($page);
pass();
} catch( \Exception $exception ) {
fail();
}
$page = $page->fresh();
$page->book = NULL;
R::store( $page );
$page = $page->fresh();
asrt( is_null( $page->book_id ), TRUE );
$page->book = $book;
R::store( $page );
$page = $page->fresh();
asrt( (int) $page->book->id, (int) $book->id );
$page->book = NULL;
R::store( $page );
asrt( is_null( $page->book_id ), TRUE );
asrt( is_null( $page->book ), TRUE );
R::freeze( FALSE );
}
/**
* Test nullifying aliased parent.
*
* @return void
*/
public function testUnsetAliasedParent()
{
R::nuke();
$book = R::dispense( 'book' );
$author = R::dispense( 'author' );
$book->coauthor = $author;
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), FALSE );
unset( $book->coauthor );
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), FALSE );
$book->coauthor = NULL;
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), TRUE );
R::trash( $book );
R::trash( $author );
R::freeze( TRUE );
$book = R::dispense( 'book' );
$author = R::dispense( 'author' );
$book->coauthor = $author;
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), FALSE );
unset( $book->coauthor );
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), FALSE );
$book->coauthor = NULL;
R::store( $book );
$book = $book->fresh();
asrt( is_null( $book->fetchAs('author')->coauthor ), TRUE );
R::trash( $book );
R::trash( $author );
R::freeze( FALSE );
}
/**
* Test NULL handling, setting a property to NULL must
* cause a change.
*
* @return void
*/
public function testBasicNullHandling()
{
// NULL can change bean
$bean = R::dispense( 'bean' );
$bean->bla = 'a';
R::store( $bean );
$bean = $bean->fresh();
asrt( $bean->hasChanged( 'bla' ), FALSE );
$bean->bla = NULL;
asrt( $bean->hasChanged( 'bla' ), TRUE );
// NULL test
$page = R::dispense( 'page' );
$book = R::dispense( 'book' );
$page->title = 'a NULL page';
$page->book = $book;
$book->title = 'Why NUll is painful..';
R::store( $page );
$bookid = $page->book->id;
unset( $page->book );
$id = R::store( $page );
$page = R::load( 'page', $id );
$page->title = 'another title';
R::store( $page );
pass();
$page = R::load( 'page', $id );
$page->title = 'another title';
$page->book_id = NULL;
R::store( $page );
pass();
}
/**
* Here we test whether the column type is set correctly.
* Normally if you store NULL, the smallest type (bool/set) will
* be selected. However in case of a foreign key type INT should
* be selected because fks columns require matching types.
*
* @return void
*/
public function ColumnType()
{
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$book->ownPage[] = $page;
R::store( $book );
pass();
asrt( $page->getMeta( 'cast.book_id' ), 'id' );
}
/**
* Test meta column type.
*
* @return void
*/
public function TypeColumn()
{
$book = R::dispense( 'book' );
$page = R::dispense( 'page' );
$page->book = $book;
R::store( $page );
pass();
asrt( $page->getMeta( 'cast.book_id' ), 'id' );
}
}