diff --git a/.gitignore b/.gitignore index 6367e5f..841c65c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,25 @@ +tmp/* +[Cc]onfig/core.php +[Cc]onfig/database.php +app/tmp/* +app/[Cc]onfig/core.php +app/[Cc]onfig/database.php +!empty /vendor/* -/config/app.php -/tmp/* -/logs/* + +# OS +.DS_Store +.AppleDouble +.LSOverride +Icon? +._* +.Spotlight-V100 +.Trashes +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk # Ignore the composer lock file-- it should never be included in a distributed package. /composer.lock diff --git a/src/Model/Behavior/CreatorModifierBehavior.php b/src/Model/Behavior/CreatorModifierBehavior.php index 755a875..0755bb6 100644 --- a/src/Model/Behavior/CreatorModifierBehavior.php +++ b/src/Model/Behavior/CreatorModifierBehavior.php @@ -1,16 +1,29 @@ $when) { if (!in_array($when, ['always', 'new', 'existing'])) { - throw new \UnexpectedValueException( + throw new UnexpectedValueException( sprintf('When should be one of "always", "new" or "existing". The passed value "%s" is invalid', $when) ); } @@ -147,15 +160,6 @@ protected function updateField(Entity $entity, $field) { $entity->set($field, $this->getUserId()); } - /** - * Factory method for the Request object. - * - * @return \Cake\Network\Request New instance of the Request object. - */ - protected function newRequest() { - return new \Cake\Network\Request(); - } - /** * Return the User.id grabbed from the Session information. * @@ -167,8 +171,25 @@ protected function sessionUserId() { if ($request->session()->started()) { $userId = $request->session()->read($this->_config['sessionUserIdKey']); + } else { + $this->log('The Session is not started. This typically means a User is not logged in. In this case there is no Session value for the currently active User and therefore we will set the `creator_id` and `modifier_id` to a null value. As a fallback, we are manually starting the session and reading the `$this->_config[sessionUserIdKey]` value, which is probably not correct.', 'debug'); + try { + $request->session()->start(); + $userId = $request->session()->read($this->_config['sessionUserIdKey']); + } catch (RuntimeException $e) { + } } return $userId; } + + /** + * Factory method for the Request object. + * + * @return \Cake\Network\Request New instance of the Request object. + * @codeCoverageIgnore Don't test PHP's ability to use new. + */ + protected function newRequest() { + return new Request(); + } } diff --git a/tests/TestCase/Model/Behavior/CreatorModifierBehaviorTest.php b/tests/TestCase/Model/Behavior/CreatorModifierBehaviorTest.php index 258cfe6..9e94976 100644 --- a/tests/TestCase/Model/Behavior/CreatorModifierBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/CreatorModifierBehaviorTest.php @@ -1,4 +1,7 @@ getMock('Cake\ORM\Table'); + $config = [ + 'events' => [ + 'Model.beforeSave' => [], + ], + ]; + + $this->Behavior = $this->getMock( + '\CreatorModifier\Model\Behavior\CreatorModifierBehavior', + ['sessionUserId'], + [$table, $config] + ); + $this->Behavior->expects($this->any()) + ->method('sessionUserId') + ->will($this->returnValue($this->mockedUserUUID)); + + $entity = new Entity(['username' => 'timestamp test']); + $return = $this->Behavior->createdOrModifed($entity); + $this->assertFalse($return, 'createdOrModifed is expected to do nothing and return false'); + $this->assertNull($entity->modifier_id, 'modifier_id field is NOT expected to change'); + $this->assertNull($entity->creator_id, 'creator_id field is NOT expected to change'); + } + + /** + * Test the CreatedModifiedBehavior in the event that nothing is expected to + * happen. * * @return void */ @@ -280,7 +334,7 @@ public function testCreatedOrModifiedNoop() { } /** - * testTouchCustomEvent + * Test handling a custom fired event. * * @return void */ @@ -314,4 +368,224 @@ public function testCreatedOrModifiedCustomEvent() { 'Creator_id field is NOT expected to change' ); } + + /** + * Test the ::updateField method when the field is marked as dirty. + * + * @return void + */ + public function testUpdateFieldIsDirty() { + $existingValue = '54108b70-a178-4590-9df4-1a900a00020f'; + $field = 'modifier_id'; + $entity = $this->getMock( + '\Cake\ORM\Entity', + ['dirty'], + [['name' => 'Foo', $field => $existingValue]] + ); + $entity->expects($this->once()) + ->method('dirty') + ->with($field) + ->will($this->returnValue(true)); + + $table = $this->getMock('Cake\ORM\Table'); + $behavior = $this->getMock( + '\CreatorModifier\Test\TestCase\Model\Behavior\TestCreatorModifierBehavior', + ['getUserId'], + [$table] + ); + $behavior->expects($this->never()) + ->method('getUserId') + ->will($this->returnValue($this->mockedUserUUID)); + + $return = $behavior->updateField($entity, 'modifier_id'); + $this->assertNull( + $return, + 'When attempting to update a field marked already dirty, ::updateField returns null and does nothing.' + ); + } + + /** + * Test the ::updateField method when the field is marked as dirty. + * + * @return void + */ + public function testUpdateFieldIsClean() { + $existingValue = '54108b70-a178-4590-9df4-1a900a00020f'; + $field = 'modifier_id'; + $entity = $this->getMock( + '\Cake\ORM\Entity', + ['dirty', 'set'], + [['name' => 'Foo', $field => $existingValue]] + ); + $entity->expects($this->once()) + ->method('dirty') + ->with($field) + ->will($this->returnValue(false)); + $entity->expects($this->once()) + ->method('set') + ->with($field, $this->mockedUserUUID) + ->will($this->returnValue(true)); + + $table = $this->getMock('\Cake\ORM\Table'); + $behavior = $this->getMock( + '\CreatorModifier\Test\TestCase\Model\Behavior\TestCreatorModifierBehavior', + ['getUserId'], + [$table] + ); + $behavior->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue($this->mockedUserUUID)); + + $return = $behavior->updateField($entity, 'modifier_id'); + $this->assertNull( + $return, + 'When attempting to update a field marked clean, ::updateField returns null and sets the return from ::getUserId to the field for the Entity.' + ); + } + + /** + * Test ::sessionUserId when the session exists, is started and returns a value. + * + * @return void + */ + public function testSessionUserIdSessionExists() { + $request = $this->getMock( + '\Cake\Network\Request', + ['session', 'started', 'read'] + ); + $request->expects($this->any()) + ->method('session') + ->with() + ->will($this->returnSelf()); + $request->expects($this->once()) + ->method('started') + ->with() + ->will($this->returnValue(true)); + $request->expects($this->once()) + ->method('read') + ->with('Auth.User.id') + ->will($this->returnValue($this->mockedUserUUID)); + + $table = $this->getMock('\Cake\ORM\Table'); + + $behavior = $this->getMock( + '\CreatorModifier\Test\TestCase\Model\Behavior\TestCreatorModifierBehavior', + ['newRequest'], + [$table] + ); + $behavior->expects($this->once()) + ->method('newRequest') + ->with() + ->will($this->returnValue($request)); + + $output = $behavior->sessionUserId(); + $this->assertEquals( + $this->mockedUserUUID, + $output, + 'On the session being started and returns a mocked value, that mocked value should be returned.' + ); + } + + /** + * Test ::sessionUserId when the session is not started. + * + * @return void + */ + public function testSessionUserIdSessionNotStarted() { + $request = $this->getMock( + '\Cake\Network\Request', + ['session', 'started', 'read', 'start'] + ); + $request->expects($this->any()) + ->method('session') + ->with() + ->will($this->returnSelf()); + $request->expects($this->once()) + ->method('started') + ->with() + ->will($this->returnValue(false)); + $request->expects($this->once()) + ->method('start') + ->with() + ->will($this->returnValue(true)); + $request->expects($this->once()) + ->method('read') + ->with('Auth.User.id') + ->will($this->returnValue($this->mockedUserUUID)); + + $table = $this->getMock('\Cake\ORM\Table'); + + $behavior = $this->getMock( + '\CreatorModifier\Test\TestCase\Model\Behavior\TestCreatorModifierBehavior', + ['newRequest', 'log'], + [$table] + ); + $behavior->expects($this->once()) + ->method('newRequest') + ->with() + ->will($this->returnValue($request)); + $behavior->expects($this->once()) + ->method('log') + ->with($this->anything(), 'debug') + ->will($this->returnValue(true)); + + $output = $behavior->sessionUserId(); + $this->assertEquals( + $this->mockedUserUUID, + $output, + 'On the session not being already started and then manually started and returns a mocked value, that mocked value should be returned.' + ); + } + + /** + * Test ::sessionUserId when the session is not started. + * + * @return void + */ + public function testSessionUserIdSessionNotStartedStartingThrowsException() { + $exception = new RuntimeException(); + + $request = $this->getMock( + '\Cake\Network\Request', + ['session', 'started', 'read', 'start'] + ); + $request->expects($this->any()) + ->method('session') + ->with() + ->will($this->returnSelf()); + $request->expects($this->once()) + ->method('started') + ->with() + ->will($this->returnValue(false)); + $request->expects($this->once()) + ->method('start') + ->with() + ->will($this->throwException($exception)); + $request->expects($this->never()) + ->method('read') + ->with('Auth.User.id') + ->will($this->returnValue($this->mockedUserUUID)); + + $table = $this->getMock('\Cake\ORM\Table'); + + $behavior = $this->getMock( + '\CreatorModifier\Test\TestCase\Model\Behavior\TestCreatorModifierBehavior', + ['newRequest', 'log'], + [$table] + ); + $behavior->expects($this->once()) + ->method('newRequest') + ->with() + ->will($this->returnValue($request)); + $behavior->expects($this->once()) + ->method('log') + ->with($this->anything(), 'debug') + ->will($this->returnValue(true)); + + $output = $behavior->sessionUserId(); + $this->assertNull( + $output, + 'On the session not being started, a `null` value should be returned.' + ); + } }