/**
  * Tests the basic attribute API.
  */
 public function testAttributeAPI()
 {
     // Create an attribute.
     $attribute = $this->createAttribute();
     // Test retrieval.
     $loaded_attribute = uc_attribute_load($attribute->aid);
     // Check the attribute integrity.
     foreach ($this->attributeFieldsToTest() as $field) {
         if ($loaded_attribute->{$field} != $attribute->{$field}) {
             $this->fail('Attribute integrity check failed.');
             break;
         }
     }
     // Add a product.
     $product = $this->createProduct();
     // Attach the attribute to a product.
     uc_attribute_subject_save($attribute, 'product', $product->id());
     // Confirm the database is correct.
     $this->assertEqual($attribute->aid, db_query('SELECT aid FROM {uc_product_attributes} WHERE nid = :nid', [':nid' => $product->id()])->fetchField(), 'Attribute was attached to a product properly.');
     $this->assertTrue(uc_attribute_subject_exists($attribute->aid, 'product', $product->id()));
     // Test retrieval.
     $loaded_attribute = uc_attribute_load($attribute->aid, $product->id(), 'product');
     // Check the attribute integrity.
     foreach ($this->attributeFieldsToTest('product') as $field) {
         if ($loaded_attribute->{$field} != $attribute->{$field}) {
             $this->fail('Attribute integrity check failed.');
             break;
         }
     }
     // Delete it.
     uc_attribute_subject_delete($attribute->aid, 'product', $product->id());
     // Confirm again.
     $this->assertFalse(db_query('SELECT aid FROM {uc_product_attributes} WHERE nid = :nid', [':nid' => $product->id()])->fetchField(), 'Attribute was detached from a product properly.');
     $this->assertFalse(uc_attribute_subject_exists($attribute->aid, 'product', $product->id()));
     // Add a product class.
     $product_class = $this->createProductClass();
     // Attach the attribute to a product class.
     uc_attribute_subject_save($attribute, 'class', $product_class->id());
     // Confirm the database is correct.
     $this->assertEqual($attribute->aid, db_query('SELECT aid FROM {uc_class_attributes} WHERE pcid = :pcid', [':pcid' => $product_class->id()])->fetchField(), 'Attribute was attached to a product class properly.');
     $this->assertTrue(uc_attribute_subject_exists($attribute->aid, 'class', $product_class->id()));
     // Test retrieval.
     $loaded_attribute = uc_attribute_load($attribute->aid, $product_class->id(), 'class');
     // Check the attribute integrity.
     foreach ($this->attributeFieldsToTest('class') as $field) {
         if ($loaded_attribute->{$field} != $attribute->{$field}) {
             $this->fail('Attribute integrity check failed.');
             break;
         }
     }
     // Delete it.
     uc_attribute_subject_delete($attribute->aid, 'class', $product_class->id());
     // Confirm again.
     $this->assertFalse(db_query('SELECT aid FROM {uc_class_attributes} WHERE pcid = :pcid', [':pcid' => $product_class->id()])->fetchField(), 'Attribute was detached from a product class properly.');
     $this->assertFalse(uc_attribute_subject_exists($attribute->aid, 'class', $product_class->id()));
     // Create a few more.
     for ($i = 0; $i < 5; $i++) {
         $a = $this->createAttribute();
         $attributes[$a->aid] = $a;
     }
     // Add some options, organizing them by aid and oid.
     $attribute_aids = array_keys($attributes);
     $all_options = array();
     foreach ($attribute_aids as $aid) {
         for ($i = 0; $i < 3; $i++) {
             $option = $this->createAttributeOption(array('aid' => $aid));
             $all_options[$option->aid][$option->oid] = $option;
         }
     }
     for ($i = 0; $i < 3; $i++) {
         $option = $this->createAttributeOption(array('aid' => $aid));
         $all_options[$option->aid][$option->oid] = $option;
     }
     // Get the options.
     $attribute = uc_attribute_load($attribute->aid);
     // Load every attribute we got.
     $attributes_with_options = uc_attribute_load_multiple();
     // Make sure all the new options are on attributes correctly.
     foreach ($all_options as $aid => $options) {
         foreach ($options as $oid => $option) {
             foreach ($this->attributeOptionFieldsToTest() as $field) {
                 if ($option->{$field} != $attributes_with_options[$aid]->options[$oid]->{$field}) {
                     $this->fail('Option integrity check failed.');
                     break;
                 }
             }
         }
     }
     // Pick 5 keys to check at random.
     $aids = array_rand($attributes, 3);
     $aids = array_combine($aids, $aids);
     // Load the attributes back.
     $loaded_attributes = uc_attribute_load_multiple($aids);
     // Make sure we only got the attributes we asked for. No more, no less.
     $this->assertEqual(count($aids), count($loaded_attributes), 'Verifying attribute result.');
     $this->assertEqual(count($aids), count(array_intersect_key($aids, $loaded_attributes)), 'Verifying attribute result.');
     // Check the attributes' integrity.
     foreach ($loaded_attributes as $aid => $loaded_attribute) {
         foreach ($this->attributeFieldsToTest() as $field) {
             if ($attributes[$aid]->{$field} != $loaded_attributes[$aid]->{$field}) {
                 $this->fail('Attribute integrity check failed.');
                 break;
             }
         }
     }
     // Add the selected attributes to the product.
     foreach ($loaded_attributes as $loaded_attribute) {
         uc_attribute_subject_save($loaded_attribute, 'product', $product->id(), TRUE);
     }
     // Test loading all product attributes. (This covers uc_attribute_load_product_attributes(),
     // as the semantics are the same -cha0s)
     $loaded_product_attributes = uc_attribute_load_multiple(array(), 'product', $product->id());
     // We'll get all in $loaded_attributes above, plus the original.
     $product_attributes = $loaded_attributes;
     // Make sure we only got the attributes we asked for. No more, no less.
     $this->assertEqual(count($loaded_product_attributes), count($product_attributes), 'Verifying attribute result.');
     $this->assertEqual(count($loaded_product_attributes), count(array_intersect_key($loaded_product_attributes, $product_attributes)), 'Verifying attribute result.');
     // Check the attributes' integrity.
     foreach ($loaded_product_attributes as $aid => $loaded_product_attribute) {
         foreach ($this->attributeFieldsToTest('product') as $field) {
             if ($loaded_product_attributes[$aid]->{$field} != $product_attributes[$aid]->{$field}) {
                 $this->fail('Attribute integrity check failed.');
                 break;
             }
         }
     }
     // Make sure all the options are on attributes correctly.
     foreach ($all_options as $aid => $options) {
         foreach ($options as $oid => $option) {
             if (empty($loaded_product_attributes[$aid]) || empty($loaded_product_attributes[$aid]->options[$oid])) {
                 continue;
             }
             foreach ($this->attributeOptionFieldsToTest() as $field) {
                 if ($option->{$field} != $loaded_product_attributes[$aid]->options[$oid]->{$field}) {
                     $this->fail('Option integrity check failed.');
                     break;
                 }
             }
         }
     }
     // Add the selected attributes to the product.
     foreach ($loaded_attributes as $loaded_attribute) {
         uc_attribute_subject_save($loaded_attribute, 'class', $product_class->id(), TRUE);
     }
     // Test loading all product attributes. (This covers uc_attribute_load_product_attributes(),
     // as the semantics are the same -cha0s)
     $loaded_class_attributes = uc_attribute_load_multiple(array(), 'class', $product_class->id());
     // We'll get all in $loaded_attributes above, plus the original.
     $class_attributes = $loaded_attributes;
     // Make sure we only got the attributes we asked for. No more, no less.
     $this->assertEqual(count($loaded_class_attributes), count($class_attributes), 'Verifying attribute result.');
     $this->assertEqual(count($loaded_class_attributes), count(array_intersect_key($loaded_class_attributes, $class_attributes)), 'Verifying attribute result.');
     // Check the attributes' integrity.
     foreach ($loaded_class_attributes as $aid => $loaded_class_attribute) {
         foreach ($this->attributeFieldsToTest('class') as $field) {
             if ($loaded_class_attributes[$aid]->{$field} != $class_attributes[$aid]->{$field}) {
                 $this->fail('Attribute integrity check failed.');
                 break;
             }
         }
     }
     // Make sure all the options are on attributes correctly.
     foreach ($all_options as $aid => $options) {
         foreach ($options as $oid => $option) {
             if (empty($loaded_class_attributes[$aid]) || empty($loaded_class_attributes[$aid]->options[$oid])) {
                 continue;
             }
             foreach ($this->attributeOptionFieldsToTest() as $field) {
                 if ($option->{$field} != $loaded_class_attributes[$aid]->options[$oid]->{$field}) {
                     $this->fail('Option integrity check failed.');
                     break;
                 }
             }
         }
     }
     // Test deletion of base attribute.
     $options = $attribute->options;
     uc_attribute_delete($attribute->aid);
     $this->assertFalse(uc_attribute_load($attribute->aid), 'Attribute was deleted properly.');
     // Sanity check!
     $this->assertFalse(db_query('SELECT aid FROM {uc_attributes} WHERE aid = :aid', [':aid' => $attribute->aid])->fetchField(), 'Attribute was seriously deleted properly!');
     // Test that options were deleted properly.
     foreach ($options as $option) {
         $this->assertFalse(db_query('SELECT oid FROM {uc_attribute_options} WHERE oid = :oid', [':oid' => $option->oid])->fetchField(), 'Make sure options are deleted properly.');
     }
     // Test the deletion applied to products too.
     $loaded_product_attributes = uc_attribute_load_multiple(array(), 'product', $product->id());
     // We'll get all in $loaded_attributes above, without the original. (Which
     // has been deleted.)
     $product_attributes = $loaded_attributes;
     // Make sure we only got the attributes we asked for. No more, no less.
     $this->assertEqual(count($loaded_product_attributes), count($product_attributes), 'Verifying attribute result.');
     $this->assertEqual(count($loaded_product_attributes), count(array_intersect_key($loaded_product_attributes, $product_attributes)), 'Verifying attribute result.');
     // Test the deletion applied to classes too.
     $loaded_class_attributes = uc_attribute_load_multiple(array(), 'class', $product_class->id());
     // We'll get all in $loaded_attributes above, without the original. (Which
     // has been deleted.)
     $class_attributes = $loaded_attributes;
     // Make sure we only got the attributes we asked for. No more, no less.
     $this->assertEqual(count($loaded_class_attributes), count($class_attributes), 'Verifying attribute result.');
     $this->assertEqual(count($loaded_class_attributes), count(array_intersect_key($loaded_class_attributes, $class_attributes)), 'Verifying attribute result.');
     // Add some adjustments.
     $this->createProductAdjustment(array('combination' => 'a:1:{i:1;s:1:"1";}', 'nid' => 1));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:1;s:1:"2";}', 'nid' => 1));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:1;s:1:"3";}', 'nid' => 1));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:2;s:1:"1";}', 'nid' => 2));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:3;s:1:"1";}', 'nid' => 2));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:1;s:1:"2";}', 'nid' => 3));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:1;s:1:"3";}', 'nid' => 3));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:3;s:1:"2";}', 'nid' => 3));
     $this->createProductAdjustment(array('combination' => 'a:1:{i:3;s:1:"3";}', 'nid' => 4));
     // Test deletion by nid.
     uc_attribute_adjustments_delete(array('nid' => 1));
     $this->assertEqual(6, db_query('SELECT COUNT(*) FROM {uc_product_adjustments}')->fetchField());
     // Test deletion by aid.
     uc_attribute_adjustments_delete(array('aid' => 2));
     $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {uc_product_adjustments}')->fetchField());
     // Test deletion by oid.
     uc_attribute_adjustments_delete(array('oid' => 2));
     $this->assertEqual(3, db_query('SELECT COUNT(*) FROM {uc_product_adjustments}')->fetchField());
     // Test deletion by aid and oid.
     uc_attribute_adjustments_delete(array('aid' => 1, 'oid' => 3));
     $this->assertEqual(2, db_query('SELECT COUNT(*) FROM {uc_product_adjustments}')->fetchField());
 }
 /**
  * Creates a product with all attribute types and options.
  *
  * @param $product_class
  *   Defaults to FALSE to create a normal product, set to TRUE to
  *   create a product class instead.
  */
 protected function createCartLinksProduct($product_class = FALSE)
 {
     // Create a product
     if ($product_class) {
         $product = $this->createProductClass(array('promote' => 0));
     } else {
         $product = $this->createProduct(array('promote' => 0));
     }
     // Create some attributes
     for ($i = 0; $i < 5; $i++) {
         $attribute = $this->createAttribute();
         $attributes[$attribute->aid] = $attribute;
     }
     // Add some options, organizing them by aid and oid.
     $attribute_aids = array_keys($attributes);
     $all_options = array();
     foreach ($attribute_aids as $aid) {
         for ($i = 0; $i < 3; $i++) {
             $option = $this->createAttributeOption(array('aid' => $aid));
             $all_options[$option->aid][$option->oid] = $option;
         }
     }
     // array('required' => TRUE)
     // Get the options.
     $attribute = uc_attribute_load($attribute->aid);
     // Load every attribute we got.
     $attributes_with_options = uc_attribute_load_multiple();
     // Pick 5 keys to check at random.
     $aids = array_rand($attributes, 3);
     // Load the attributes back.
     $loaded_attributes = uc_attribute_load_multiple($aids);
     // TODO: add attributes of all 4 types
     // TODO: create both required and not required attributes
     // Add the selected attributes to the product.
     foreach ($loaded_attributes as $loaded_attribute) {
         uc_attribute_subject_save($loaded_attribute, 'product', $product->id(), TRUE);
     }
     return $product;
 }