From 5b6950b421903deac02f83f2c012f718ea07fa9a Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Mon, 17 Dec 2007 00:12:15 -0500 Subject: [PATCH] The API is now all unit tested * Api.php * UnitTest/ApiTest.php --- Api.php | 98 +++++++------------ UnitTest/ApiTest.php | 220 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 250 insertions(+), 68 deletions(-) diff --git a/Api.php b/Api.php index 4ca7c89..4049f52 100644 --- a/Api.php +++ b/Api.php @@ -56,11 +56,6 @@ if (!defined('REQ_AUTO')) * Index for requirement type */ define('F_REQ', 1); - - /** - * Index for verification type - */ - define('F_VERIFY', 2); } /** @@ -69,6 +64,12 @@ if (!defined('REQ_AUTO')) * Abstract class that is used as an API base for most common database interaction * schemes. Creates a simple structure that holds data and can update, remove, and * insert. +* +* Life-cycle of a new object: +* 1. $o = new SubApi(); +* 2. $o->set('foo', 'abc'); +* 3. $o->set('test', 45); +* 4. try { $o->insert(); } catch (ApiException $e) {} * * @author Blue Static * @copyright Copyright (c)2002 - 2007, Blue Static @@ -79,7 +80,7 @@ abstract class BSApi { /** * Fields: used for verification and sanitization - * NAME => array(TYPE, REQUIRED, VERIFY METHOD (:self for self-named method), RELATION => array(FILE, CLASS IN FILE, ALTERNATE FIELD NAME)) + * NAME => array(TYPE, REQUIRED) * @var array */ protected $fields = array(); @@ -97,7 +98,7 @@ abstract class BSApi protected $prefix = ''; /** - * Values array: sanitized and verified field values + * Values array: sanitized and validated field values * @var array */ public $values = array(); @@ -112,7 +113,7 @@ abstract class BSApi * WHERE condition * @var string */ - private $condition = ''; + protected $condition = ''; /** * The object table row; a fetched row that represents this instance @@ -171,30 +172,14 @@ abstract class BSApi // ################################################################### /** - * Returns the list of exceptions contained in the ApiException - * - * @return array Array of errors - */ - public function isValid() - { - if ($this->exception == null) - { - return array(); - } - - return $this->exception->getExceptions(); - } - - // ################################################################### - /** - * Sets a value, sanitizes it, and verifies it + * Sets a value, sanitizes it, and validates it * * @param string Field name * @param mixed Value * @param bool Do clean? - * @param bool Do verify? + * @param bool Do validation? */ - public function set($field, $value, $doclean = true, $doverify = true) + public function set($field, $value, $doclean = true, $dovalidate = true) { if (!isset($this->fields["$field"])) { @@ -205,28 +190,9 @@ abstract class BSApi $this->setfields["$field"] = $field; - if (isset($this->fields["$field"][F_VERIFY]) AND $doverify) + if ($dovalidate AND method_exists($this, "validate_$field")) { - if ($this->fields["$field"][F_VERIFY] == ':self') - { - $verify = $this->{"verify_$field"}($field); - } - else - { - $verify = $this->{$this->fields["$field"][F_VERIFY]}($field); - } - - if ($verify !== true) - { - if ($verify === false) - { - $this->_error(new FieldException(sprintf(_('Validation of "%1$s" failed'), $field))); - } - else - { - $this->_error($verify); - } - } + $this->{"validate_$field"}($field); } } @@ -237,7 +203,7 @@ abstract class BSApi * * @param mixed String with WHERE condition; array of fields to use for WHERE builder */ - public function setCondition($condition = '') + public function setCondition($condition = null) { if (is_array($condition) AND sizeof($condition) > 0) { @@ -254,7 +220,7 @@ abstract class BSApi } $this->condition = implode(' AND ', $condbits); } - else if ($condition != '') + else if ($condition) { $this->condition = $condition; } @@ -273,7 +239,7 @@ abstract class BSApi } } - if ($this->condition == '') + if (!$this->condition) { throw new Exception('No REQ_AUTO fields are present and therefore the condition cannot be created'); } @@ -291,7 +257,7 @@ abstract class BSApi */ public function fetch($doPre = true, $doPost = true) { - if ($this->condition == '') + if (!$this->condition) { $this->setCondition(); } @@ -323,11 +289,12 @@ abstract class BSApi */ public function insert($doPre = true, $doPost = true) { - $this->verify(); + $this->_verifyRequired(); $this->_processErrorQueue(); $this->_runActionMethod('pre_insert', $doPre); + $fields = $values = array(); foreach ($this->setfields AS $field) { $fields[] = $field; @@ -366,7 +333,7 @@ abstract class BSApi */ public function update($doPre = true, $doPost = true) { - if ($this->condition == '') + if (!$this->condition) { $this->setCondition(); } @@ -395,7 +362,7 @@ abstract class BSApi */ public function remove($doPre = true, $doPost = true) { - if ($this->condition == '') + if (!$this->condition) { $this->setCondition(); } @@ -413,7 +380,7 @@ abstract class BSApi /** * Verifies that all required fields are set */ - private function verify() + private function _verifyRequired() { foreach ($this->fields AS $name => $options) { @@ -483,11 +450,12 @@ abstract class BSApi /** * Verify field: not a zero value */ - protected function verify_nozero($field) + protected function _verifyIsNotZero($field) { - if ($this->values["$field"] == 0) + if ($this->values[$field] == 0) { - return new FieldException(sprintf(_('The field "%1$s" cannot be zero'), $field), $field); + $this->_error(new FieldException(sprintf(_('The field "%1$s" cannot be zero'), $field), $field)); + return false; } return true; @@ -497,11 +465,12 @@ abstract class BSApi /** * Verify field: not empty */ - protected function verify_noempty($field) + protected function _verifyIsNotEmpty($field) { - if (empty($this->values["$field"])) + if (empty($this->values[$field])) { - return new FieldException(sprintf(_('The field "%1$s" cannot be empty'), $field), $field); + $this->_error(new FieldException(sprintf(_('The field "%1$s" cannot be empty'), $field), $field)); + return false; } return true; @@ -511,12 +480,13 @@ abstract class BSApi /** * Verify field: unique */ - protected function verify_unique($field) + protected function _verifyIsNotUnique($field) { $res = BSApp::Registry()->getType('Db')->queryFirst("SELECT $field FROM {$this->prefix}{$this->table} WHERE $field = " . $this->_prepareFieldForSql($field) . (empty($this->condition) ? "" : " AND !({$this->condition})")); if ($res) { - return new FieldException(sprintf(_('The "%1$s" field must contain a unique value'), $field), $field); + $this->_error(new FieldException(sprintf(_('The "%1$s" field must contain a unique value'), $field), $field)); + return false; } return true; diff --git a/UnitTest/ApiTest.php b/UnitTest/ApiTest.php index 08db303..22191a0 100644 --- a/UnitTest/ApiTest.php +++ b/UnitTest/ApiTest.php @@ -32,6 +32,7 @@ class ApiTest extends PHPUnit_Framework_TestCase abool boolean not null, aint integer not null, afloat float not null, + autoset varchar(200) not null, PRIMARY KEY (id) ) "); @@ -116,6 +117,196 @@ class ApiTest extends PHPUnit_Framework_TestCase catch (Exception $e) {} } + + public function testActionMethods() + { + $mock = $this->getMock('TestApiFixture', array('pre_insert', 'post_insert')); + $mock->expects($this->once())->method('pre_insert'); + $mock->expects($this->once())->method('post_insert'); + + $this->fixture = $mock; + + $this->testInsert(); + } + + public function testZeroValidation() + { + $this->fixture->set('aint', 0); + try + { + $this->fixture->insert(); + $this->fail('exception expected'); + } + catch (Exception $e) + {} + + $this->fixture = new TestApiFixture(); + $this->fixture->set('aint', 4); + $this->fixture->set('avarchar', 'foo'); + try + { + $this->fixture->insert(); + } + catch (Exception $e) + { + $this->fail('unexpected exception'); + } + } + + public function testEmptyValidation() + { + $this->fixture->set('avarchar', ''); + try + { + $this->fixture->insert(); + $this->fail('exception expected'); + } + catch (Exception $e) + {} + + $this->fixture = new TestApiFixture(); + $this->fixture->set('aint', 3); + $this->fixture->set('avarchar', 'foo'); + try + { + $this->fixture->insert(); + } + catch (Exception $e) + { + $this->fail('unexpected exception'); + } + } + + public function testUniqueValidation() + { + $this->fixture = new TestApiFixture(); + $this->fixture->set('afloat', 5); + $this->fixture->set('id', 1); + $this->fixture->set('avarchar', 'foo'); + $this->fixture->insert(); + + $this->fixture = new TestApiFixture(); + $this->fixture->set('afloat', 5); + $this->fixture->set('id', 1); + try + { + $this->fixture->insert(); + $this->fail('exception expected'); + } + catch (Exception $e) + {} + + $this->fixture = new TestApiFixture(); + $this->fixture->set('afloat', 6); + $this->fixture->set('id', 16); + $this->fixture->set('avarchar', 'foo'); + try + { + $this->fixture->insert(); + } + catch (Exception $e) + { + $this->fail('unexpected exception'); + } + } + + public function testFetch() + { + $this->fixture->set('id', 1); + $this->assertFalse($this->fixture->fetch()); + + $this->testInsert(); + + $this->fixture->set('id', 1); + $this->assertTrue($this->fixture->fetch()); + + $this->assertEquals($this->fixture->record['atext'], 'moocow'); + $this->assertEquals($this->fixture->record['avarchar'], 'hello'); + $this->assertEquals($this->fixture->record['abin'], 'åß∂œ∑†å∂ƒåß∂ƒå∂ƒ'); + $this->assertEquals((bool)$this->fixture->record['abool'], true); + $this->assertEquals($this->fixture->record['aint'], 3); + $this->assertEquals($this->fixture->record['afloat'], 2.53); + } + + public function testRemove() + { + $this->fixture->set('id', 1); + $this->fixture->set('aint', 4); + $this->fixture->set('avarchar', 'foo'); + $this->fixture->insert(); + + $this->fixture = new TestApiFixture(); + $this->fixture->set('id', 1); + + try + { + $this->fixture->remove(); + } + catch (Exception $e) + { + $this->fail('unexpected exception'); + } + + $this->assertEquals(1, $this->fixture->record['id']); + $this->assertEquals(4, $this->fixture->record['aint']); + + $this->fixture = new TestApiFixture(); + $this->fixture->set('id', 1); + $this->assertFalse($this->fixture->fetch()); + } + + public function testUpdate() + { + $this->fixture->set('id', 1); + $this->fixture->set('aint', 4); + $this->fixture->set('avarchar', 'foo'); + $this->fixture->insert(); + + $this->fixture = new TestApiFixture(); + $this->fixture->set('id', 1); + $this->fixture->set('aint', 6); + try + { + $this->fixture->update(); + } + catch (Exception $e) + { + $es = $e->getExceptions(); + $this->fail('unexpected exception: ' . print_r($es[0]->getMessage())); + } + + $this->fixture = new TestApiFixture(); + $this->fixture->set('id', 1); + $this->fixture->fetch(); + + $this->assertEquals(6, $this->fixture->record['aint']); + } + + public function testAutoSet() + { + $this->fixture->set('id', 1); + $this->fixture->set('avarchar', 'foo'); + $this->fixture->insert(); + + $this->fixture = new TestApiFixture(); + $this->fixture->set('id', 1); + $this->fixture->fetch(); + + $this->assertEquals('this is auto', $this->fixture->record['autoset']); + } + + public function testFieldException() + { + try + { + throw new FieldException('this is an error', 'field'); + } + catch (Exception $e) + { + $this->assertEquals('this is an error', $e->getMessage()); + $this->assertEquals('field', $e->getField()); + } + } } class TestApiFixture extends BSApi @@ -123,11 +314,12 @@ class TestApiFixture extends BSApi protected $fields = array( 'id' => array(TYPE_UINT, REQ_AUTO), 'atext' => array(TYPE_STR, REQ_NO), - 'avarchar' => array(TYPE_STR, REQ_NO), + 'avarchar' => array(TYPE_STR, REQ_YES), 'abin' => array(TYPE_BIN, REQ_NO), - 'abool' => array(TYPE_BOOL, REQ_YES), - 'aint' => array(TYPE_INT, REQ_YES), - 'afloat' => array(TYPE_FLOAT, REQ_NO) + 'abool' => array(TYPE_BOOL, REQ_NO), + 'aint' => array(TYPE_INT, REQ_NO), + 'afloat' => array(TYPE_FLOAT, REQ_NO), + 'autoset' => array(TYPE_STR, REQ_SET) ); protected $table = 'apitest'; @@ -138,6 +330,26 @@ class TestApiFixture extends BSApi { return $this->condition; } + + protected function validate_aint() + { + $this->_verifyIsNotZero('aint'); + } + + protected function validate_avarchar() + { + $this->_verifyIsNotEmpty('avarchar'); + } + + protected function validate_afloat() + { + $this->_verifyIsNotUnique('afloat'); + } + + protected function set_autoset() + { + $this->set('autoset', 'this is auto'); + } } ?> \ No newline at end of file -- 2.22.5