cms/drupal/modules/simpletest/tests/schema.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Tests for the Database Schema API.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Unit tests for the Schema API.
       
    10  */
       
    11 class SchemaTestCase extends DrupalWebTestCase {
       
    12   /**
       
    13    * A global counter for table and field creation.
       
    14    */
       
    15   var $counter;
       
    16 
       
    17   public static function getInfo() {
       
    18     return array(
       
    19       'name' => 'Schema API',
       
    20       'description' => 'Tests table creation and modification via the schema API.',
       
    21       'group' => 'Database',
       
    22     );
       
    23   }
       
    24 
       
    25   /**
       
    26    *
       
    27    */
       
    28   function testSchema() {
       
    29     // Try creating a table.
       
    30     $table_specification = array(
       
    31       'description' => 'Schema table description.',
       
    32       'fields' => array(
       
    33         'id'  => array(
       
    34           'type' => 'int',
       
    35           'default' => NULL,
       
    36         ),
       
    37         'test_field'  => array(
       
    38           'type' => 'int',
       
    39           'not null' => TRUE,
       
    40           'description' => 'Schema column description.',
       
    41         ),
       
    42       ),
       
    43     );
       
    44     db_create_table('test_table', $table_specification);
       
    45 
       
    46     // Assert that the table exists.
       
    47     $this->assertTrue(db_table_exists('test_table'), 'The table exists.');
       
    48 
       
    49     // Assert that the table comment has been set.
       
    50     $this->checkSchemaComment($table_specification['description'], 'test_table');
       
    51 
       
    52     // Assert that the column comment has been set.
       
    53     $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
       
    54 
       
    55     // An insert without a value for the column 'test_table' should fail.
       
    56     $this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
       
    57 
       
    58     // Add a default value to the column.
       
    59     db_field_set_default('test_table', 'test_field', 0);
       
    60     // The insert should now succeed.
       
    61     $this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.');
       
    62 
       
    63     // Remove the default.
       
    64     db_field_set_no_default('test_table', 'test_field');
       
    65     // The insert should fail again.
       
    66     $this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
       
    67 
       
    68     // Test for fake index and test for the boolean result of indexExists().
       
    69     $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
       
    70     $this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
       
    71     // Add index.
       
    72     db_add_index('test_table', 'test_field', array('test_field'));
       
    73     // Test for created index and test for the boolean result of indexExists().
       
    74     $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
       
    75     $this->assertIdentical($index_exists, TRUE, 'Index created.');
       
    76 
       
    77     // Rename the table.
       
    78     db_rename_table('test_table', 'test_table2');
       
    79 
       
    80     // Index should be renamed.
       
    81     $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
       
    82     $this->assertTrue($index_exists, 'Index was renamed.');
       
    83 
       
    84     // We need the default so that we can insert after the rename.
       
    85     db_field_set_default('test_table2', 'test_field', 0);
       
    86     $this->assertFalse($this->tryInsert(), 'Insert into the old table failed.');
       
    87     $this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.');
       
    88 
       
    89     // We should have successfully inserted exactly two rows.
       
    90     $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
       
    91     $this->assertEqual($count, 2, 'Two fields were successfully inserted.');
       
    92 
       
    93     // Try to drop the table.
       
    94     db_drop_table('test_table2');
       
    95     $this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.');
       
    96 
       
    97     // Recreate the table.
       
    98     db_create_table('test_table', $table_specification);
       
    99     db_field_set_default('test_table', 'test_field', 0);
       
   100     db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.'));
       
   101 
       
   102     // Assert that the column comment has been set.
       
   103     $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
       
   104 
       
   105     // Change the new field to a serial column.
       
   106     db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial')));
       
   107 
       
   108     // Assert that the column comment has been set.
       
   109     $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
       
   110 
       
   111     $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
       
   112     $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
       
   113     $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
       
   114     $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
       
   115     $this->assertTrue($max2 > $max1, 'The serial is monotone.');
       
   116 
       
   117     $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
       
   118     $this->assertEqual($count, 2, 'There were two rows.');
       
   119 
       
   120     // Use database specific data type and ensure that table is created.
       
   121     $table_specification = array(
       
   122       'description' => 'Schema table description.',
       
   123       'fields' => array(
       
   124         'timestamp'  => array(
       
   125           'mysql_type' => 'timestamp',
       
   126           'pgsql_type' => 'timestamp',
       
   127           'sqlite_type' => 'datetime',
       
   128           'not null' => FALSE,
       
   129           'default' => NULL,
       
   130         ),
       
   131       ),
       
   132     );
       
   133     try {
       
   134       db_create_table('test_timestamp', $table_specification);
       
   135     }
       
   136     catch (Exception $e) {}
       
   137     $this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.');
       
   138   }
       
   139 
       
   140   function tryInsert($table = 'test_table') {
       
   141     try {
       
   142        db_insert($table)
       
   143          ->fields(array('id' => mt_rand(10, 20)))
       
   144          ->execute();
       
   145       return TRUE;
       
   146     }
       
   147     catch (Exception $e) {
       
   148       return FALSE;
       
   149     }
       
   150   }
       
   151 
       
   152   /**
       
   153    * Checks that a table or column comment matches a given description.
       
   154    *
       
   155    * @param $description
       
   156    *   The asserted description.
       
   157    * @param $table
       
   158    *   The table to test.
       
   159    * @param $column
       
   160    *   Optional column to test.
       
   161    */
       
   162   function checkSchemaComment($description, $table, $column = NULL) {
       
   163     if (method_exists(Database::getConnection()->schema(), 'getComment')) {
       
   164       $comment = Database::getConnection()->schema()->getComment($table, $column);
       
   165       $this->assertEqual($comment, $description, 'The comment matches the schema description.');
       
   166     }
       
   167   }
       
   168 
       
   169   /**
       
   170    * Tests creating unsigned columns and data integrity thereof.
       
   171    */
       
   172   function testUnsignedColumns() {
       
   173     // First create the table with just a serial column.
       
   174     $table_name = 'unsigned_table';
       
   175     $table_spec = array(
       
   176       'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)),
       
   177       'primary key' => array('serial_column'),
       
   178     );
       
   179     $ret = array();
       
   180     db_create_table($table_name, $table_spec);
       
   181 
       
   182     // Now set up columns for the other types.
       
   183     $types = array('int', 'float', 'numeric');
       
   184     foreach ($types as $type) {
       
   185       $column_spec = array('type' => $type, 'unsigned'=> TRUE);
       
   186       if ($type == 'numeric') {
       
   187         $column_spec += array('precision' => 10, 'scale' => 0);
       
   188       }
       
   189       $column_name = $type . '_column';
       
   190       $table_spec['fields'][$column_name] = $column_spec;
       
   191       db_add_field($table_name, $column_name, $column_spec);
       
   192     }
       
   193 
       
   194     // Finally, check each column and try to insert invalid values into them.
       
   195     foreach ($table_spec['fields'] as $column_name => $column_spec) {
       
   196       $this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
       
   197       $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type'])));
       
   198     }
       
   199   }
       
   200 
       
   201   /**
       
   202    * Tries to insert a negative value into columns defined as unsigned.
       
   203    *
       
   204    * @param $table_name
       
   205    *   The table to insert
       
   206    * @param $column_name
       
   207    *   The column to insert
       
   208    * @return
       
   209    *   TRUE if the insert succeeded, FALSE otherwise
       
   210    */
       
   211   function tryUnsignedInsert($table_name, $column_name) {
       
   212     try {
       
   213       db_insert($table_name)
       
   214          ->fields(array($column_name => -1))
       
   215          ->execute();
       
   216       return TRUE;
       
   217     }
       
   218     catch (Exception $e) {
       
   219       return FALSE;
       
   220     }
       
   221   }
       
   222 
       
   223   /**
       
   224    * Test adding columns to an existing table.
       
   225    */
       
   226   function testSchemaAddField() {
       
   227     // Test varchar types.
       
   228     foreach (array(1, 32, 128, 256, 512) as $length) {
       
   229       $base_field_spec = array(
       
   230         'type' => 'varchar',
       
   231         'length' => $length,
       
   232       );
       
   233       $variations = array(
       
   234         array('not null' => FALSE),
       
   235         array('not null' => FALSE, 'default' => '7'),
       
   236         array('not null' => TRUE, 'initial' => 'd'),
       
   237         array('not null' => TRUE, 'initial' => 'd', 'default' => '7'),
       
   238       );
       
   239 
       
   240       foreach ($variations as $variation) {
       
   241         $field_spec = $variation + $base_field_spec;
       
   242         $this->assertFieldAdditionRemoval($field_spec);
       
   243       }
       
   244     }
       
   245 
       
   246     // Test int and float types.
       
   247     foreach (array('int', 'float') as $type) {
       
   248       foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) {
       
   249         $base_field_spec = array(
       
   250           'type' => $type,
       
   251           'size' => $size,
       
   252         );
       
   253         $variations = array(
       
   254           array('not null' => FALSE),
       
   255           array('not null' => FALSE, 'default' => 7),
       
   256           array('not null' => TRUE, 'initial' => 1),
       
   257           array('not null' => TRUE, 'initial' => 1, 'default' => 7),
       
   258         );
       
   259 
       
   260         foreach ($variations as $variation) {
       
   261           $field_spec = $variation + $base_field_spec;
       
   262           $this->assertFieldAdditionRemoval($field_spec);
       
   263         }
       
   264       }
       
   265     }
       
   266 
       
   267     // Test numeric types.
       
   268     foreach (array(1, 5, 10, 40, 65) as $precision) {
       
   269       foreach (array(0, 2, 10, 30) as $scale) {
       
   270         if ($precision <= $scale) {
       
   271           // Precision must be smaller then scale.
       
   272           continue;
       
   273         }
       
   274 
       
   275         $base_field_spec = array(
       
   276           'type' => 'numeric',
       
   277           'scale' => $scale,
       
   278           'precision' => $precision,
       
   279         );
       
   280         $variations = array(
       
   281           array('not null' => FALSE),
       
   282           array('not null' => FALSE, 'default' => 7),
       
   283           array('not null' => TRUE, 'initial' => 1),
       
   284           array('not null' => TRUE, 'initial' => 1, 'default' => 7),
       
   285         );
       
   286 
       
   287         foreach ($variations as $variation) {
       
   288           $field_spec = $variation + $base_field_spec;
       
   289           $this->assertFieldAdditionRemoval($field_spec);
       
   290         }
       
   291       }
       
   292     }
       
   293   }
       
   294 
       
   295   /**
       
   296    * Assert that a given field can be added and removed from a table.
       
   297    *
       
   298    * The addition test covers both defining a field of a given specification
       
   299    * when initially creating at table and extending an existing table.
       
   300    *
       
   301    * @param $field_spec
       
   302    *   The schema specification of the field.
       
   303    */
       
   304   protected function assertFieldAdditionRemoval($field_spec) {
       
   305     // Try creating the field on a new table.
       
   306     $table_name = 'test_table_' . ($this->counter++);
       
   307     $table_spec = array(
       
   308       'fields' => array(
       
   309         'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
       
   310         'test_field' => $field_spec,
       
   311       ),
       
   312       'primary key' => array('serial_column'),
       
   313     );
       
   314     db_create_table($table_name, $table_spec);
       
   315     $this->pass(format_string('Table %table created.', array('%table' => $table_name)));
       
   316 
       
   317     // Check the characteristics of the field.
       
   318     $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
       
   319 
       
   320     // Clean-up.
       
   321     db_drop_table($table_name);
       
   322 
       
   323     // Try adding a field to an existing table.
       
   324     $table_name = 'test_table_' . ($this->counter++);
       
   325     $table_spec = array(
       
   326       'fields' => array(
       
   327         'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
       
   328       ),
       
   329       'primary key' => array('serial_column'),
       
   330     );
       
   331     db_create_table($table_name, $table_spec);
       
   332     $this->pass(format_string('Table %table created.', array('%table' => $table_name)));
       
   333 
       
   334     // Insert some rows to the table to test the handling of initial values.
       
   335     for ($i = 0; $i < 3; $i++) {
       
   336       db_insert($table_name)
       
   337         ->useDefaults(array('serial_column'))
       
   338         ->execute();
       
   339     }
       
   340 
       
   341     db_add_field($table_name, 'test_field', $field_spec);
       
   342     $this->pass(format_string('Column %column created.', array('%column' => 'test_field')));
       
   343 
       
   344     // Check the characteristics of the field.
       
   345     $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
       
   346 
       
   347     // Clean-up.
       
   348     db_drop_field($table_name, 'test_field');
       
   349     db_drop_table($table_name);
       
   350   }
       
   351 
       
   352   /**
       
   353    * Assert that a newly added field has the correct characteristics.
       
   354    */
       
   355   protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) {
       
   356     // Check that the initial value has been registered.
       
   357     if (isset($field_spec['initial'])) {
       
   358       // There should be no row with a value different then $field_spec['initial'].
       
   359       $count = db_select($table_name)
       
   360         ->fields($table_name, array('serial_column'))
       
   361         ->condition($field_name, $field_spec['initial'], '<>')
       
   362         ->countQuery()
       
   363         ->execute()
       
   364         ->fetchField();
       
   365       $this->assertEqual($count, 0, 'Initial values filled out.');
       
   366     }
       
   367 
       
   368     // Check that the default value has been registered.
       
   369     if (isset($field_spec['default'])) {
       
   370       // Try inserting a row, and check the resulting value of the new column.
       
   371       $id = db_insert($table_name)
       
   372         ->useDefaults(array('serial_column'))
       
   373         ->execute();
       
   374       $field_value = db_select($table_name)
       
   375         ->fields($table_name, array($field_name))
       
   376         ->condition('serial_column', $id)
       
   377         ->execute()
       
   378         ->fetchField();
       
   379       $this->assertEqual($field_value, $field_spec['default'], 'Default value registered.');
       
   380     }
       
   381 
       
   382     db_drop_field($table_name, $field_name);
       
   383   }
       
   384 }