3 namespace RedUNIT\Base;
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7 use RedBeanPHP\ModelHelper as ModelHelper;
8 use RedBeanPHP\RedException as RedException;
9 use RedBeanPHP\OODBBean as OODBBean;
10 use RedBeanPHP\OODB as OODB;
11 use RedBeanPHP\ToolBox as ToolBox;
12 use RedBeanPHP\QueryWriter as QueryWriter;
13 use RedBeanPHP\Adapter as Adapter;
14 use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper;
19 * Tests whether we can associate model logic on-the-fly
20 * by defining models extending from SimpleModel. Tests
21 * whether the calls to facade trigger the corresponding
22 * methods on the model.
24 * @file RedUNIT/Base/Fuse.php
25 * @desc Tests Fuse feature; coupling beans to models.
26 * @author Gabor de Mooij and the RedBeanPHP Community
27 * @license New BSD/GPLv2
29 * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
30 * This source file is subject to the New BSD/GPLv2 License that is bundled
31 * with this source code in the file license.txt.
33 class Fuse extends Base
36 * Test whether we can override the getModelForBean() method
37 * of the BeanHelper and use a custom BeanHelper to attach a model
42 public function testCustomBeanHelper()
44 $customBeanHelper = new \SoupBeanHelper( R::getToolbox() );
45 $oldBeanHelper = R::getRedBean()->getBeanHelper();
46 asrt( ( $oldBeanHelper instanceof SimpleFacadeBeanHelper ), TRUE );
47 R::getRedBean()->setBeanHelper( $customBeanHelper );
48 $meal = R::dispense( 'meal' );
49 asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
50 $cake = R::dispense( 'cake' );
51 asrt( is_null( $cake->box() ), TRUE );
52 $bean = R::dispense( 'coffee' );
53 asrt( ( $bean->box() instanceof \Model_Coffee ), TRUE );
54 $meal->setFlavour( 'tomato' );
55 asrt( $meal->getFlavour(), 'tomato' );
58 asrt( $meal->getFlavour(), 'tomato' );
59 $meal = $meal->unbox();
60 asrt( $meal->getFlavour(), 'tomato' );
61 $meal = R::findOne( 'meal' );
62 asrt( ( $meal->box() instanceof \Model_Soup ), TRUE );
63 asrt( $meal->getFlavour(), '' );
64 $meal->setFlavour( 'tomato' );
65 asrt( $meal->getFlavour(), 'tomato' );
66 $meal = $meal->unbox();
67 asrt( $meal->getFlavour(), 'tomato' );
68 R::getRedBean()->setBeanHelper( $oldBeanHelper );
72 * Test FUSE hooks (i.e. open, update, update_after etc..)
76 public function testHooks()
79 $probe = R::dispense( 'probe' );
80 $probe->name = 'test';
81 asrt( $probe->getLogActionCount(), 1 );
82 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
83 asrt( $probe->getLogActionCount( 'open' ), 0 );
84 asrt( $probe->getLogActionCount( 'update' ), 0 );
85 asrt( $probe->getLogActionCount( 'after_update' ), 0 );
86 asrt( $probe->getLogActionCount( 'delete' ), 0 );
87 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
88 asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
90 asrt( $probe->getLogActionCount(), 3 );
91 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
92 asrt( $probe->getLogActionCount( 'open' ), 0 );
93 asrt( $probe->getLogActionCount( 'update' ), 1 );
94 asrt( $probe->getLogActionCount( 'after_update' ), 1 );
95 asrt( $probe->getLogActionCount( 'delete' ), 0 );
96 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
97 asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
98 $probe = R::load( 'probe', $probe->id );
99 asrt( $probe->getLogActionCount(), 2 );
100 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
101 asrt( $probe->getLogActionCount( 'open' ), 1 );
102 asrt( $probe->getLogActionCount( 'update' ), 0 );
103 asrt( $probe->getLogActionCount( 'after_update' ), 0 );
104 asrt( $probe->getLogActionCount( 'delete' ), 0 );
105 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
106 asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
107 asrt( ( $probe->getDataFromLog( 1, 'id' ) === $probe->id ), TRUE );
110 asrt( $probe->getLogActionCount(), 2 );
111 asrt( $probe->getLogActionCount( 'dispense' ), 0 );
112 asrt( $probe->getLogActionCount( 'open' ), 0 );
113 asrt( $probe->getLogActionCount( 'update' ), 0 );
114 asrt( $probe->getLogActionCount( 'after_update' ), 0 );
115 asrt( $probe->getLogActionCount( 'delete' ), 1 );
116 asrt( $probe->getLogActionCount( 'after_delete' ), 1 );
117 asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
118 asrt( ( $probe->getDataFromLog( 1, 'bean' ) === $probe ), TRUE );
119 //less 'normal scenarios'
120 $probe = R::dispense( 'probe' );
121 $probe->name = 'test';
122 asrt( $probe->getLogActionCount(), 1 );
123 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
124 asrt( $probe->getLogActionCount( 'open' ), 0 );
125 asrt( $probe->getLogActionCount( 'update' ), 0 );
126 asrt( $probe->getLogActionCount( 'after_update' ), 0 );
127 asrt( $probe->getLogActionCount( 'delete' ), 0 );
128 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
129 asrt( ( $probe->getDataFromLog( 0, 'bean' ) === $probe ), TRUE );
131 asrt( $probe->getLogActionCount(), 3 );
132 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
133 asrt( $probe->getLogActionCount( 'open' ), 0 );
134 asrt( $probe->getLogActionCount( 'update' ), 1 );
135 asrt( $probe->getLogActionCount( 'after_update' ), 1 );
136 asrt( $probe->getLogActionCount( 'delete' ), 0 );
137 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
138 asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
139 asrt( $probe->getMeta( 'tainted' ), FALSE );
140 asrt( $probe->getMeta( 'changed' ), FALSE );
141 R::store( $probe ); //not tainted, no FUSE save!
142 asrt( $probe->getLogActionCount(), 3 );
143 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
144 asrt( $probe->getLogActionCount( 'open' ), 0 );
145 asrt( $probe->getLogActionCount( 'update' ), 1 );
146 asrt( $probe->getLogActionCount( 'after_update' ), 1 );
147 asrt( $probe->getLogActionCount( 'delete' ), 0 );
148 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
149 asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
150 $probe->xownProbeList[] = R::dispense( 'probe' );
151 //tainted, not changed, triggers FUSE
152 asrt( $probe->getMeta( 'tainted' ), TRUE );
153 asrt( $probe->getMeta( 'changed' ), FALSE );
155 asrt( $probe->getMeta( 'tainted' ), FALSE );
156 asrt( $probe->getMeta( 'changed' ), FALSE );
157 asrt( $probe->getLogActionCount(), 5 );
158 asrt( $probe->getLogActionCount( 'dispense' ), 1 );
159 asrt( $probe->getLogActionCount( 'open' ), 0 );
160 asrt( $probe->getLogActionCount( 'update' ), 2 );
161 asrt( $probe->getLogActionCount( 'after_update' ), 2 );
162 asrt( $probe->getLogActionCount( 'delete' ), 0 );
163 asrt( $probe->getLogActionCount( 'after_delete' ), 0 );
164 asrt( ( $probe->getDataFromLog( 2, 'bean' ) === $probe ), TRUE );
168 * Tests the SimpleFacadeBeanHelper factory setter.
172 public function testFactory()
174 SimpleFacadeBeanHelper::setFactoryFunction( function( $name ) {
175 $model = new $name();
176 $model->setNote( 'injected', 'dependency' );
180 $bean = R::dispense( 'band' )->box();
182 asrt( ( $bean instanceof \Model_Band ), TRUE );
183 asrt( ( $bean->getNote('injected') ), 'dependency' );
185 SimpleFacadeBeanHelper::setFactoryFunction( NULL );
189 * Make sure that beans of type book_page can be fused with
190 * models like BookPage (beautified) as well as Book_Page (non-beautified).
192 public function testBeutificationOfLinkModel()
194 $page = R::dispense( 'page' );
195 $widget = R::dispense( 'widget' );
196 $page->sharedWidgetList[] = $widget;
198 $testReport = \Model_PageWidget::getTestReport();
199 asrt( $testReport, 'didSave' );
201 $page = R::dispense( 'page' );
202 $gadget = R::dispense( 'gadget' );
203 $page->sharedGadgetList[] = $gadget;
205 $testReport = \Model_Gadget_Page::getTestReport();
206 asrt( $testReport, 'didSave' );
214 public function testTheoreticalBeautifications()
216 $bean = R::dispense('bean');
217 $bean->setMeta('type', 'a_b_c');
219 $testReport = \Model_A_B_C::getTestReport();
220 asrt( $testReport, 'didSave' );
224 * Test extraction of toolbox.
228 public function testGetExtractedToolBox()
230 $helper = new SimpleFacadeBeanHelper;
232 list( $redbean, $database, $writer, $toolbox ) = $helper->getExtractedToolbox();
234 asrt( ( $redbean instanceof OODB ), TRUE );
235 asrt( ( $database instanceof Adapter ), TRUE );
236 asrt( ( $writer instanceof QueryWriter ), TRUE );
237 asrt( ( $toolbox instanceof ToolBox ), TRUE );
241 * Test FUSE and model formatting.
243 * @todo move tagging tests to tag tester.
247 public function testFUSE()
249 $toolbox = R::getToolBox();
250 $adapter = $toolbox->getDatabaseAdapter();
251 $blog = R::dispense( 'blog' );
252 $blog->title = 'testing';
253 $blog->blog = 'tesing';
255 $blogpost = R::load( "blog", 1 );
256 $post = R::dispense( "post" );
257 $post->message = "hello";
258 $blog->sharedPost[] = $post;
260 $a = R::getAll( "select * from blog " );
261 R::tag( $post, "lousy,smart" );
262 asrt( implode( ',', R::tag( $post ) ), "lousy,smart" );
263 R::tag( $post, "clever,smart" );
264 $tagz = implode( ',', R::tag( $post ) );
265 asrt( ( $tagz == "smart,clever" || $tagz == "clever,smart" ), TRUE );
266 R::tag( $blog, array( "smart", "interesting" ) );
267 asrt( implode( ',', R::tag( $blog ) ), "smart,interesting" );
269 R::tag( $blog, array( "smart", "interesting", "lousy!" ) );
271 } catch ( RedException $e ) {
274 asrt( implode( ',', R::tag( $blog ) ), "smart,interesting,lousy!" );
275 asrt( implode( ",", R::tag( $blog ) ), "smart,interesting,lousy!" );
276 R::untag( $blog, array( "smart", "interesting" ) );
277 asrt( implode( ",", R::tag( $blog ) ), "lousy!" );
278 asrt( R::hasTag( $blog, array( "lousy!" ) ), TRUE );
279 asrt( R::hasTag( $blog, array( "lousy!", "smart" ) ), TRUE );
280 asrt( R::hasTag( $blog, array( "lousy!", "smart" ), TRUE ), FALSE );
281 R::tag( $blog, FALSE );
282 asrt( count( R::tag( $blog ) ), 0 );
283 R::tag( $blog, array( "funny", "comic" ) );
284 asrt( count( R::tag( $blog ) ), 2 );
285 R::addTags( $blog, array( "halloween" ) );
286 asrt( count( R::tag( $blog ) ), 3 );
287 asrt( R::hasTag( $blog, array( "funny", "commic", "halloween" ), TRUE ), FALSE );
288 R::unTag( $blog, array( "funny" ) );
289 R::addTags( $blog, "horror" );
290 asrt( count( R::tag( $blog ) ), 3 );
291 asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
293 R::addTags( $blog, "horror" );
294 asrt( R::hasTag( $blog, array( "horror", "commic", "halloween" ), TRUE ), FALSE );
295 asrt( count( R::tag( $blog ) ), 3 );
299 * Test error handling options FUSE.
301 public function testModelErrorHandling()
303 $test = R::dispense( 'feed' );
304 $test->nonExistantMethod();
306 $old = R::setErrorHandlingFUSE( OODBBean::C_ERR_LOG );
307 asrt( is_array( $old ), TRUE );
308 asrt( count( $old ), 2 );
309 asrt( $old[0], FALSE );
310 asrt( $old[1], NULL);
311 $test->nonExistantMethod(); //we cant really test this... :(
313 $old = R::setErrorHandlingFUSE( OODBBean::C_ERR_NOTICE );
314 asrt( is_array( $old ), TRUE );
315 asrt( count( $old ), 2 );
316 asrt( $old[0], OODBBean::C_ERR_LOG );
317 asrt( $old[1], NULL);
318 set_error_handler(function($error, $str) {
319 asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
321 $test->nonExistantMethod();
322 restore_error_handler();
323 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_WARN );
324 asrt( is_array( $old ), TRUE );
325 asrt( count( $old ), 2 );
326 asrt( $old[0], OODBBean::C_ERR_NOTICE );
327 asrt( $old[1], NULL);
328 set_error_handler(function($error, $str) {
329 asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
331 $test->nonExistantMethod();
332 restore_error_handler();
333 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FATAL );
334 asrt( is_array( $old ), TRUE );
335 asrt( count( $old ), 2 );
336 asrt( $old[0], OODBBean::C_ERR_WARN );
337 asrt( $old[1], NULL);
338 set_error_handler(function($error, $str) {
339 asrt( $str, 'FUSE: method does not exist in model: nonExistantMethod' );
341 $test->nonExistantMethod();
342 restore_error_handler();
343 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
344 asrt( is_array( $old ), TRUE );
345 asrt( count( $old ), 2 );
346 asrt( $old[0], OODBBean::C_ERR_FATAL );
347 asrt( $old[1], NULL);
349 $test->nonExistantMethod();
351 } catch (\Exception $e) {
356 global $has_executed_error_func_fuse;
357 $has_executed_error_func_fuse = FALSE;
358 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, function( $info ){
359 global $has_executed_error_func_fuse;
361 $has_executed_error_func_fuse = TRUE;
362 asrt( is_array( $info ), TRUE );
363 asrt( $info['method'], 'nonExistantMethod' );
364 asrt( json_encode( $info['bean']->export() ), json_encode( $test_bean->export() ) );
365 asrt( $info['message'], 'FUSE: method does not exist in model: nonExistantMethod' );
367 asrt( is_array( $old ), TRUE );
368 asrt( count( $old ), 2 );
369 asrt( $old[0], OODBBean::C_ERR_EXCEPTION );
370 asrt( $old[1], NULL);
371 $test->nonExistantMethod();
372 asrt( $has_executed_error_func_fuse, TRUE );
373 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
374 asrt( is_array( $old ), TRUE );
375 asrt( count( $old ), 2 );
376 asrt( $old[0], OODBBean::C_ERR_FUNC );
377 asrt( is_callable( $old[1] ), TRUE );
378 $old = OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );
379 asrt( is_array( $old ), TRUE );
380 asrt( count( $old ), 2 );
381 asrt( $old[0], OODBBean::C_ERR_IGNORE );
382 asrt( $old[1], NULL);
384 OODBBean::setErrorHandlingFUSE( 900 );
386 } catch (\Exception $e) {
388 asrt( $e->getMessage(), 'Invalid error mode selected' );
391 OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_FUNC, 'hello' );
393 } catch (\Exception $e) {
395 asrt( $e->getMessage(), 'Invalid error handler' );
397 OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_EXCEPTION );
398 //make sure ignore FUSE events
399 $test = R::dispense('feed');
401 $test = $test->fresh();
404 OODBBean::setErrorHandlingFUSE( OODBBean::C_ERR_IGNORE );