* @throws \Exception
*/
public static function create(&$params) {
+ // This is the database default
+ $params += ['extends' => 'Contact'];
// create custom group dao, populate fields and then save.
$group = new CRM_Core_DAO_CustomGroup();
if (isset($params['title'])) {
$action = $spec->getAction();
$spec->getFieldByName('extends')
- ->setRequired($action === 'create')
->setSuffixes(['name', 'label', 'grouping']);
}
--- /dev/null
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Service\Spec\Provider;
+
+use Civi\Api4\Service\Spec\RequestSpec;
+
+class MembershipTypeCreationSpecProvider implements Generic\SpecProviderInterface {
+
+ /**
+ * @param \Civi\Api4\Service\Spec\RequestSpec $spec
+ */
+ public function modifySpec(RequestSpec $spec): void {
+ $spec->getFieldByName('duration_interval')->setDefaultValue(1);
+ }
+
+ /**
+ * When does this apply.
+ *
+ * @param string $entity
+ * @param string $action
+ *
+ * @return bool
+ */
+ public function applies($entity, $action): bool {
+ return $entity === 'MembershipType' && $action === 'create';
+ }
+
+}
'period_type' => 'rolling',
'member_of_contact_id' => 1,
'financial_type_id:name' => 'Member Dues',
- 'duration_unit' => 1,
+ 'duration_unit' => 'month',
]
)->execute()->first()['id'];
}
+++ /dev/null
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved. |
- | |
- | This work is published under the GNU AGPLv3 license with some |
- | permitted exceptions and without any warranty. For full license |
- | and copyright information, see https://civicrm.org/licensing |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- */
-
-
-namespace api\v4\Action;
-
-use api\v4\Api4TestBase;
-use Civi\Api4\Activity;
-use Civi\Api4\Contact;
-use Civi\Test\CiviEnvBuilder;
-use Civi\Test\TransactionalInterface;
-
-/**
- * @group headless
- *
- * This class tests a series of complex query situations described in the
- * initial APIv4 specification
- */
-class ComplexQueryTest extends Api4TestBase implements TransactionalInterface {
-
- public function setUpHeadless(): CiviEnvBuilder {
- $this->loadDataSet('DefaultDataSet');
- return parent::setUpHeadless();
- }
-
- public function tearDown(): void {
- $relatedTables = [
- 'civicrm_activity',
- 'civicrm_activity_contact',
- ];
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- parent::tearDown();
- }
-
- /**
- * Fetch all phone call activities
- * Expects at least one activity loaded from the data set.
- *
- * @throws \API_Exception
- */
- public function testGetAllHousingSupportActivities(): void {
- $results = Activity::get(FALSE)
- ->addWhere('activity_type_id:name', '=', 'Phone Call')
- ->execute();
-
- $this->assertGreaterThan(0, count($results));
- }
-
- /**
- *
- */
- public function testGetWithCount() {
- $myName = uniqid('count');
- for ($i = 1; $i <= 20; ++$i) {
- Contact::create()
- ->addValue('first_name', "Contact $i")
- ->addValue('last_name', $myName)
- ->setCheckPermissions(FALSE)->execute();
- }
-
- $get1 = Contact::get()
- ->addWhere('last_name', '=', $myName)
- ->selectRowCount()
- ->addSelect('first_name')
- ->setLimit(10)
- ->setDebug(TRUE)
- ->setCheckPermissions(FALSE)->execute();
-
- $this->assertEquals(20, $get1->count());
- $this->assertCount(10, (array) $get1);
-
- }
-
- /**
- * Fetch contacts named 'Bob' and all of their blue activities
- */
- public function testGetAllBlueActivitiesForBobs() {
-
- }
-
- /**
- * Get all contacts in a zipcode and return their Home or Work email addresses
- */
- public function testGetHomeOrWorkEmailsForContactsWithZipcode() {
-
- }
-
- /**
- * Fetch all activities where Bob is the assignee or source
- */
- public function testGetActivitiesWithBobAsAssigneeOrSource() {
-
- }
-
- /**
- * Get all contacts which
- * (a) have address in zipcode 94117 or 94118 or in city "San Francisco","LA"
- * and
- * (b) are not deceased and
- * (c) have a custom-field "most_important_issue=Environment".
- */
- public function testAWholeLotOfConditions() {
-
- }
-
- /**
- * Get participants who attended CiviCon 2012 but not CiviCon 2013.
- * Return their name and email.
- */
- public function testGettingNameAndEmailOfAttendeesOfCiviCon2012Only() {
-
- }
-
-}
['first_name' => 'abc', 'last_name' => $lastName, 'birth_date' => 'now - 1 year - 1 month'],
['first_name' => 'def', 'last_name' => $lastName, 'birth_date' => 'now - 21 year - 6 month'],
];
- Contact::save(FALSE)
- ->setRecords($sampleData)
- ->execute();
+ $this->saveTestRecords('Contact', ['records' => $sampleData]);
$result = Contact::get(FALSE)
->addWhere('last_name', '=', $lastName)
->execute()->single();
}
+ /**
+ *
+ */
+ public function testGetWithCount() {
+ $myName = uniqid('count');
+ for ($i = 1; $i <= 20; ++$i) {
+ $this->createTestRecord('Contact', [
+ 'first_name' => "Contact $i",
+ 'last_name' => $myName,
+ ]);
+ }
+
+ $get1 = Contact::get(FALSE)
+ ->addWhere('last_name', '=', $myName)
+ ->selectRowCount()
+ ->addSelect('first_name')
+ ->setLimit(10)
+ ->execute();
+
+ $this->assertEquals(20, $get1->count());
+ $this->assertCount(10, (array) $get1);
+
+ }
+
}
use api\v4\Api4TestBase;
use Civi\Api4\Activity;
-use Civi\Api4\CiviCase;
use Civi\Api4\Contact;
use Civi\Api4\Email;
use Civi\Api4\EntityTag;
-use Civi\Api4\Phone;
use Civi\Api4\Relationship;
use Civi\Api4\Tag;
use Civi\Test\TransactionalInterface;
*/
class FkJoinTest extends Api4TestBase implements TransactionalInterface {
- public function setUpHeadless() {
- $this->loadDataSet('DefaultDataSet');
-
- return parent::setUpHeadless();
- }
-
public function tearDown(): void {
$relatedTables = [
'civicrm_activity',
* loaded from the data set.
*/
public function testThreeLevelJoin() {
+ $this->createTestRecord('Activity', [
+ 'activity_type_id:name' => 'Phone Call',
+ ]);
+
$results = Activity::get(FALSE)
->addWhere('activity_type_id:name', '=', 'Phone Call')
->execute();
}
public function testOptionalJoin() {
- // DefaultDataSet includes 2 phones for contact 1, 0 for contact 2.
+ $contact1 = $this->createTestRecord('Contact');
+ $contact2 = $this->createTestRecord('Contact');
+
+ $this->createTestRecord('Phone', [
+ 'location_type_id:name' => 'Home',
+ 'contact_id' => $contact1['id'],
+ ]);
+ $this->createTestRecord('Phone', [
+ 'location_type_id:name' => 'Work',
+ 'contact_id' => $contact1['id'],
+ ]);
+
// We'll add one for contact 2 as a red herring to make sure we only get back the correct ones.
- Phone::create(FALSE)
- ->setValues(['contact_id' => $this->getReference('test_contact_2')['id'], 'phone' => '123456'])
- ->execute();
+ $this->createTestRecord('Phone', [
+ 'contact_id' => $contact2['id'],
+ ]);
$contacts = Contact::get(FALSE)
->addJoin('Phone', FALSE)
->addSelect('id', 'phone.phone')
- ->addWhere('id', 'IN', [$this->getReference('test_contact_1')['id']])
+ ->addWhere('id', 'IN', [$contact1['id']])
->addOrderBy('phone.id')
->execute();
$this->assertCount(2, $contacts);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[0]['id']);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[1]['id']);
+ $this->assertEquals($contact1['id'], $contacts[0]['id']);
+ $this->assertEquals($contact1['id'], $contacts[1]['id']);
}
public function testRequiredJoin() {
+ $contact1 = $this->createTestRecord('Contact');
+ $contact2 = $this->createTestRecord('Contact');
+
+ $this->createTestRecord('Phone', [
+ 'location_type_id:name' => 'Home',
+ 'contact_id' => $contact1['id'],
+ 'phone' => '+35355439483',
+ ]);
+ $this->createTestRecord('Phone', [
+ 'location_type_id:name' => 'Work',
+ 'contact_id' => $contact1['id'],
+ ]);
+
// Joining with no condition
$contacts = Contact::get(FALSE)
->addSelect('id', 'phone.phone')
->addJoin('Phone', TRUE)
- ->addWhere('id', 'IN', [$this->getReference('test_contact_1')['id'], $this->getReference('test_contact_2')['id']])
+ ->addWhere('id', 'IN', [$contact1['id'], $contact2['id']])
->addOrderBy('phone.id')
->execute();
$this->assertCount(2, $contacts);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[0]['id']);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[1]['id']);
+ $this->assertEquals($contact1['id'], $contacts[0]['id']);
+ $this->assertEquals($contact1['id'], $contacts[1]['id']);
// Add is_primary condition, should result in only one record
$contacts = Contact::get(FALSE)
->addSelect('id', 'phone.phone', 'phone.location_type_id')
->addJoin('Phone', TRUE, ['phone.is_primary', '=', TRUE])
- ->addWhere('id', 'IN', [$this->getReference('test_contact_1')['id'], $this->getReference('test_contact_2')['id']])
+ ->addWhere('id', 'IN', [$contact1['id'], $contact2['id']])
->addOrderBy('phone.id')
->execute();
$this->assertCount(1, $contacts);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[0]['id']);
+ $this->assertEquals($contact1['id'], $contacts[0]['id']);
$this->assertEquals('+35355439483', $contacts[0]['phone.phone']);
$this->assertEquals('1', $contacts[0]['phone.location_type_id']);
}
public function testImplicitJoinOnExplicitJoin() {
+ $contact1 = $this->createTestRecord('Contact');
+ $this->createTestRecord('Address', [
+ 'contact_id' => $contact1['id'],
+ 'country_id' => 1228,
+ ]);
+
$contacts = Contact::get(FALSE)
- ->addWhere('id', '=', $this->getReference('test_contact_1')['id'])
+ ->addWhere('id', '=', $contact1['id'])
->addJoin('Address AS address', TRUE, ['id', '=', 'address.contact_id'], ['address.location_type_id', '=', 1])
->addSelect('id', 'address.country_id.iso_code')
->execute();
}
public function testExcludeJoin() {
+ $contact1 = $this->createTestRecord('Contact');
+ $this->createTestRecord('Address', [
+ 'contact_id' => $contact1['id'],
+ 'country_id' => 1228,
+ ]);
$contacts = Contact::get(FALSE)
->addJoin('Address AS address', 'EXCLUDE', ['id', '=', 'address.contact_id'], ['address.location_type_id', '=', 1])
->addSelect('id')
->execute()->column('id');
- $this->assertNotContains($this->getReference('test_contact_1')['id'], $contacts);
+ $this->assertNotContains($contact1['id'], $contacts);
}
public function testInvalidJoinAlias() {
}
public function testJoinWithExpression() {
- Phone::create(FALSE)
- ->setValues(['contact_id' => $this->getReference('test_contact_1')['id'], 'phone' => '654321'])
- ->execute();
+
+ $contact1 = $this->createTestRecord('Contact');
+ $contact2 = $this->createTestRecord('Contact');
+ $this->createTestRecord('Phone', [
+ 'contact_id' => $contact1['id'],
+ 'phone' => '654321',
+ ]);
+
$contacts = Contact::get(FALSE)
->addSelect('id', 'phone.phone')
->addJoin('Phone', 'INNER', ['LOWER(phone.phone)', '=', "CONCAT('6', '5', '4', '3', '2', '1')"])
- ->addWhere('id', 'IN', [$this->getReference('test_contact_1')['id'], $this->getReference('test_contact_2')['id']])
+ ->addWhere('id', 'IN', [$contact1['id'], $contact2['id']])
->addOrderBy('phone.id')
->execute();
$this->assertCount(1, $contacts);
- $this->assertEquals($this->getReference('test_contact_1')['id'], $contacts[0]['id']);
+ $this->assertEquals($contact1['id'], $contacts[0]['id']);
$this->assertEquals('654321', $contacts[0]['phone.phone']);
}
public function testJoinCaseRoles() {
\CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
- $this->loadDataSet('CaseType');
- $contactID = $this->createEntity(['type' => 'Individual'])['id'];
- $managerID = $this->createEntity(['type' => 'Individual'])['id'];
+ $contactID = $this->createTestRecord('Contact')['id'];
+ $managerID = $this->createTestRecord('Contact')['id'];
- $case = CiviCase::create(FALSE)
- ->addValue('case_type_id', $this->getReference('test_case_type_1')['id'])
- ->addValue('status_id', 1)
- ->addValue('creator_id', $managerID)
- ->addValue('contact_id', $contactID)
- ->execute()
- ->first();
+ $caseType = $this->createTestRecord('CaseType');
+ $case = $this->createTestRecord('Case', [
+ 'creator_id' => $managerID,
+ 'contact_id' => $contactID,
+ 'case_type_id' => $caseType['id'],
+ ]);
- $contacts = \Civi\Api4\Contact::get()
+ $contacts = Contact::get(FALSE)
->addSelect('*', 'case.*')
->addJoin('Case AS case', 'INNER', 'RelationshipCache', ['id', '=', 'case.far_contact_id'], ['case.far_relation', '=', '"Parent of"'])
->addWhere('case.id', '=', $case['id'])
* Test that creating a location entity or deleting one re-assigns is_primary correctly.
*/
public function testPrimaryHandling() {
- $contactID = self::createEntity(['type' => 'Individual'])['id'];
+ $contactID = $this->createTestRecord('Contact')['id'];
// Create an entity of each type.
Email::create()->setValues(['email' => 'b@example.com', 'contact_id' => $contactID])->execute();
Phone::create()->setValues(['phone' => '123', 'contact_id' => $contactID])->execute();
namespace api\v4;
-use api\v4\Traits\TestDataLoaderTrait;
use Civi\Api4\UFMatch;
+use Civi\Api4\Utils\CoreUtil;
use Civi\Test\HeadlessInterface;
require_once 'api/Exception.php';
*/
class Api4TestBase extends \PHPUnit\Framework\TestCase implements HeadlessInterface {
- use TestDataLoaderTrait;
+ /**
+ * Records created which will be deleted during tearDown
+ *
+ * @var array
+ */
+ public $testRecords = [];
/**
* @see CiviUnitTestCase
return \Civi\Test::headless()->apply();
}
+ /**
+ */
+ public function tearDown(): void {
+ // Delete all test records in reverse order to prevent fk constraints
+ foreach (array_reverse($this->testRecords) as $record) {
+ civicrm_api4($record[0], 'delete', ['checkPermissions' => FALSE, 'where' => $record[1]]);
+ }
+ }
+
/**
* Quick clean by emptying tables created for the test.
*
\CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
}
- /**
- * Quick record counter
- *
- * @param string $table_name
- * @returns int record count
- */
- public function getRowCount($table_name) {
- $sql = "SELECT count(id) FROM $table_name";
- return (int) \CRM_Core_DAO::singleValueQuery($sql);
- }
-
/**
* Emulate a logged in user since certain functions use that.
* value to store a record in the DB (like activity)
* Contact ID of the created user.
*/
public function createLoggedInUser() {
- $contactID = $this->createEntity(['type' => 'Individual'])['id'];
+ $contactID = $this->createTestRecord('Contact')['id'];
UFMatch::delete(FALSE)->addWhere('uf_id', '=', 6)->execute();
- UFMatch::create(FALSE)->setValues([
+ $this->createTestRecord('UFMatch', [
'contact_id' => $contactID,
'uf_name' => 'superman',
'uf_id' => 6,
- ])->execute();
+ ]);
$session = \CRM_Core_Session::singleton();
$session->set('userID', $contactID);
}
/**
- * Create sample entities (using V3 for now).
+ * Inserts a test record, supplying all required values if not provided.
*
- * @param array $params
- * (type, seq, overrides, count)
- * @return array
- * (either single, or array of array if count >1)
- * @throws \CiviCRM_API3_Exception
- * @throws \Exception
+ * Test records will be automatically deleted during tearDown.
+ *
+ * @param string $entityName
+ * @param array $values
+ * @return array|null
+ * @throws \API_Exception
+ * @throws \Civi\API\Exception\NotImplementedException
*/
- public static function createEntity($params) {
- $params += [
- 'count' => 1,
- 'seq' => 0,
+ public function createTestRecord(string $entityName, array $values = []) {
+ return $this->saveTestRecords($entityName, ['records' => [$values]])->first();
+ }
+
+ /**
+ * Saves one or more test records, supplying default values.
+ *
+ * Test records will be automatically deleted during tearDown.
+ *
+ * @param string $entityName
+ * @param array $saveParams
+ * @return \Civi\Api4\Generic\Result
+ * @throws \API_Exception
+ * @throws \Civi\API\Exception\NotImplementedException
+ */
+ public function saveTestRecords(string $entityName, array $saveParams) {
+ $saveParams += [
+ 'checkPermissions' => FALSE,
+ 'defaults' => [],
];
- $entities = [];
- $entity = NULL;
- for ($i = 0; $i < $params['count']; $i++) {
- $params['seq']++;
- $data = self::sample($params);
- $api_params = ['sequential' => 1] + $data['sample_params'];
- $result = civicrm_api3($data['entity'], 'create', $api_params);
- if ($result['is_error']) {
- throw new \Exception("creating $data[entity] failed");
- }
- $entity = $result['values'][0];
- if (!($entity['id'] > 0)) {
- throw new \Exception("created entity is malformed");
+ $idField = CoreUtil::getIdFieldName($entityName);
+ foreach ($saveParams['records'] as &$record) {
+ $record += $saveParams['defaults'];
+ if (empty($record[$idField])) {
+ $this->getRequiredValuesToCreate($entityName, $record);
}
- $entities[] = $entity;
}
- return $params['count'] == 1 ? $entity : $entities;
+ $saved = civicrm_api4($entityName, 'save', $saveParams);
+ foreach ($saved as $item) {
+ $this->testRecords[] = [$entityName, [[$idField, '=', $item[$idField]]]];
+ }
+ return $saved;
}
/**
- * Helper function for creating sample entities.
- *
- * Depending on the supplied sequence integer, plucks values from the dummy data.
- * Constructs a foreign entity when an ID is required but isn't supplied in the overrides.
+ * Get the required fields for the api entity + action.
*
- * Inspired by CiviUnitTestCase::
- * @todo - extract this function to own class and share with CiviUnitTestCase?
- * @param array $params
- * - type: string roughly matching entity type
- * - seq: (optional) int sequence number for the values of this type
- * - overrides: (optional) array of fill in parameters
+ * @param string $entity
+ * @param array $values
*
* @return array
- * - entity: string API entity type (usually the type supplied except for contact subtypes)
- * - sample_params: array API sample_params properties of sample entity
+ * @throws \API_Exception
*/
- public static function sample($params) {
- $params += [
- 'seq' => 0,
- 'overrides' => [],
- ];
- $type = $params['type'];
- // sample data - if field is array then chosed based on `seq`
- $sample_params = [];
- if (in_array($type, ['Individual', 'Organization', 'Household'])) {
- $sample_params['contact_type'] = $type;
- $entity = 'Contact';
+ public function getRequiredValuesToCreate(string $entity, &$values = []) {
+ $requiredFields = civicrm_api4($entity, 'getfields', [
+ 'action' => 'create',
+ 'loadOptions' => TRUE,
+ 'where' => [
+ ['type', 'IN', ['Field', 'Extra']],
+ ['OR',
+ [
+ ['required', '=', TRUE],
+ // Include contitionally-required fields only if they don't create a circular FK reference
+ ['AND', [['required_if', 'IS NOT EMPTY'], ['fk_entity', '!=', $entity]]],
+ ],
+ ],
+ ['default_value', 'IS EMPTY'],
+ ['readonly', 'IS EMPTY'],
+ ],
+ ], 'name');
+
+ $extraValues = [];
+ foreach ($requiredFields as $fieldName => $requiredField) {
+ if (!isset($values[$fieldName])) {
+ $extraValues[$fieldName] = $this->getRequiredValue($requiredField);
+ }
}
- else {
- $entity = $type;
+
+ // Hack in some extra per-entity values that couldn't be determined by metadata.
+ // Try to keep this to a minimum and improve metadata as a first-resort.
+
+ switch ($entity) {
+ case 'UFField':
+ $extraValues['field_name'] = 'activity_campaign_id';
+ break;
+
+ case 'Translation':
+ $extraValues['entity_table'] = 'civicrm_event';
+ $extraValues['entity_field'] = 'description';
+ $extraValues['entity_id'] = $this->getFkID('Event');
+ break;
+
+ case 'Case':
+ $extraValues['creator_id'] = $this->getFkID('Contact');
+ break;
+
+ case 'CaseContact':
+ // Prevent "already exists" error from using an existing contact id
+ $extraValues['contact_id'] = $this->createTestRecord('Contact')['id'];
+ break;
+
+ case 'CaseType':
+ $extraValues['definition'] = [
+ "activityTypes" => [
+ [
+ "name" => "Open Case",
+ "max_instances" => "1",
+ ],
+ [
+ "name" => "Follow up",
+ ],
+ ],
+ "activitySets" => [
+ [
+ "name" => "standard_timeline",
+ "label" => "Standard Timeline",
+ "timeline" => 1,
+ "activityTypes" => [
+ [
+ "name" => "Open Case",
+ "status" => "Completed",
+ ],
+ [
+ "name" => "Follow up",
+ "reference_activity" => "Open Case",
+ "reference_offset" => "3",
+ "reference_select" => "newest",
+ ],
+ ],
+ ],
+ ],
+ "timelineActivityTypes" => [
+ [
+ "name" => "Open Case",
+ "status" => "Completed",
+ ],
+ [
+ "name" => "Follow up",
+ "reference_activity" => "Open Case",
+ "reference_offset" => "3",
+ "reference_select" => "newest",
+ ],
+ ],
+ "caseRoles" => [
+ [
+ "name" => "Parent of",
+ "creator" => "1",
+ "manager" => "1",
+ ],
+ ],
+ ];
+ break;
}
- // use the seq to pluck a set of params out
- foreach (self::sampleData($type) as $key => $value) {
- if (is_array($value)) {
- $sample_params[$key] = $value[$params['seq'] % count($value)];
- }
- else {
- $sample_params[$key] = $value;
- }
+
+ $values += $extraValues;
+ return $values;
+ }
+
+ /**
+ * Attempt to get a value using field option, defaults, FKEntity, or a random
+ * value based on the data type.
+ *
+ * @param array $field
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ private function getRequiredValue(array $field) {
+ if (!empty($field['options'])) {
+ return key($field['options']);
+ }
+ if (!empty($field['fk_entity'])) {
+ return $this->getFkID($field['fk_entity']);
}
- if ($type == 'Individual') {
- $sample_params['email'] = strtolower(
- $sample_params['first_name'] . '_' . $sample_params['last_name'] . '@civicrm.org'
- );
- $sample_params['prefix_id'] = 3;
- $sample_params['suffix_id'] = 3;
+ if (isset($field['default_value'])) {
+ return $field['default_value'];
}
- if (!count($sample_params)) {
- throw new \Exception("unknown sample type: $type");
+ if ($field['name'] === 'contact_id') {
+ return $this->getFkID('Contact');
}
- $sample_params = $params['overrides'] + $sample_params;
- // make foreign enitiies if they haven't been supplied
- foreach ($sample_params as $key => $value) {
- if (substr($value, 0, 6) === 'dummy.') {
- $foreign_entity = self::createEntity([
- 'type' => substr($value, 6),
- 'seq' => $params['seq'],
- ]);
- $sample_params[$key] = $foreign_entity['id'];
+ if ($field['name'] === 'entity_id') {
+ // What could possibly go wrong with this?
+ switch ($field['table_name'] ?? NULL) {
+ case 'civicrm_financial_item':
+ return $this->getFkID(\Civi\Api4\Service\Spec\Provider\FinancialItemCreationSpecProvider::DEFAULT_ENTITY);
+
+ default:
+ return $this->getFkID('Contact');
}
}
- return compact("entity", "sample_params");
+
+ $randomValue = $this->getRandomValue($field['data_type']);
+
+ if ($randomValue) {
+ return $randomValue;
+ }
+
+ throw new \API_Exception('Could not provide default value');
}
/**
- * Provider of sample data.
+ * Get an ID for the appropriate entity.
*
- * @return array
- * Array values represent a set of allowable items.
- * Strings in the form "dummy.Entity" require creating a foreign entity first.
+ * @param string $fkEntity
+ *
+ * @return int
+ *
+ * @throws \API_Exception
*/
- public static function sampleData($type) {
- $data = [
- 'Individual' => [
- // The number of values in each list need to be coprime numbers to not have duplicates
- 'first_name' => ['Anthony', 'Joe', 'Terrence', 'Lucie', 'Albert', 'Bill', 'Kim'],
- 'middle_name' => ['J.', 'M.', 'P', 'L.', 'K.', 'A.', 'B.', 'C.', 'D', 'E.', 'Z.'],
- 'last_name' => ['Anderson', 'Miller', 'Smith', 'Collins', 'Peterson'],
- 'contact_type' => 'Individual',
- ],
- 'Organization' => [
- 'organization_name' => [
- 'Unit Test Organization',
- 'Acme',
- 'Roberts and Sons',
- 'Cryo Space Labs',
- 'Sharper Pens',
- ],
- ],
- 'Household' => [
- 'household_name' => ['Unit Test household'],
- ],
- 'Event' => [
- 'title' => 'Annual CiviCRM meet',
- 'summary' => 'If you have any CiviCRM related issues or want to track where CiviCRM is heading, Sign up now',
- 'description' => 'This event is intended to give brief idea about progess of CiviCRM and giving solutions to common user issues',
- 'event_type_id' => 1,
- 'is_public' => 1,
- 'start_date' => 20081021,
- 'end_date' => 20081023,
- 'is_online_registration' => 1,
- 'registration_start_date' => 20080601,
- 'registration_end_date' => 20081015,
- 'max_participants' => 100,
- 'event_full_text' => 'Sorry! We are already full',
- 'is_monetary' => 0,
- 'is_active' => 1,
- 'is_show_location' => 0,
- ],
- 'Participant' => [
- 'event_id' => 'dummy.Event',
- 'contact_id' => 'dummy.Individual',
- 'status_id' => 2,
- 'role_id' => 1,
- 'register_date' => 20070219,
- 'source' => 'Wimbeldon',
- 'event_level' => 'Payment',
- ],
- 'Contribution' => [
- 'contact_id' => 'dummy.Individual',
- // donation, 2 = member, 3 = campaign contribution, 4=event
- 'financial_type_id' => 1,
- 'total_amount' => 7.3,
- ],
- 'Activity' => [
- //'activity_type_id' => 1,
- 'subject' => 'unit testing',
- 'source_contact_id' => 'dummy.Individual',
- ],
- 'Group' => [
- 'title' => 'unit testing',
- ],
- ];
- if ($type == 'Contact') {
- $type = 'Individual';
+ private function getFkID(string $fkEntity) {
+ $params = ['checkPermissions' => FALSE];
+ // Be predictable about what type of contact we select
+ if ($fkEntity === 'Contact') {
+ $params['where'] = [['contact_type', '=', 'Individual']];
}
- return $data[$type];
+ $entityList = civicrm_api4($fkEntity, 'get', $params);
+ // If no existing entities, create one
+ if ($entityList->count() < 1) {
+ return $this->createTestRecord($fkEntity)['id'];
+ }
+
+ return $entityList->last()['id'];
+ }
+
+ /**
+ * @param $dataType
+ *
+ * @return int|null|string
+ */
+ private function getRandomValue($dataType) {
+ switch ($dataType) {
+ case 'Boolean':
+ return TRUE;
+
+ case 'Integer':
+ return random_int(1, 2000);
+
+ case 'String':
+ return \CRM_Utils_String::createRandom(10, implode('', range('a', 'z')));
+
+ case 'Text':
+ return \CRM_Utils_String::createRandom(100, implode('', range('a', 'z')));
+
+ case 'Money':
+ return sprintf('%d.%2d', rand(0, 2000), rand(10, 99));
+
+ case 'Date':
+ return '20100102';
+
+ case 'Timestamp':
+ return 'now';
+ }
+
+ return NULL;
}
}
->addValue('time_format', 2)
->execute();
- $contactID = $this->createEntity(['type' => 'Individual'])['id'];
+ $contactID = $this->createTestRecord('Contact')['id'];
CustomValue::create('MyContactDateFields', FALSE)
->addValue('date_field', '2022-02-02')
class CustomFieldAlterTest extends CustomTestBase {
public function testChangeSerialize() {
- $contact = $this->createEntity(['type' => 'Individual']);
+ $contact = $this->createTestRecord('Contact');
$customGroup = CustomGroup::create(FALSE)
->addValue('title', 'MyFieldsToAlter')
}
public function testParticipantRole() {
- $event = $this->createEntity(['type' => 'Event']);
- $contact = $this->createEntity(['type' => 'Individual']);
- $participant = Participant::create()
- ->addValue('contact_id', $contact['id'])
- ->addValue('event_id', $event['id'])
- ->addValue('role_id:label', ['Attendee', 'Volunteer'])
- ->execute()->first();
+ $event = $this->createTestRecord('Event');
+ $contact = $this->createTestRecord('Contact');
+ $participant = $this->createTestRecord('Participant', [
+ 'contact_id' => $contact['id'],
+ 'event_id' => $event['id'],
+ 'role_id:label' => ['Attendee', 'Volunteer'],
+ ]);
$search1 = Participant::get()
->addSelect('role_id', 'role_id:label')
\CRM_Core_BAO_ConfigSetting::enableComponent('CiviContribute');
\CRM_Core_BAO_ConfigSetting::enableComponent('CiviCampaign');
- $contact = $this->createEntity(['type' => 'Individual']);
+ $contact = $this->createTestRecord('Contact');
$campaignTitle = uniqid('Test ');
+++ /dev/null
-{
- "CaseType": [
- {
- "name": "test_case_type",
- "title": "Test Case Type",
- "definition": {
- "activityTypes": [
- {
- "name": "Open Case",
- "max_instances": "1"
- },
- {
- "name": "Follow up"
- }
- ],
- "activitySets": [
- {
- "name": "standard_timeline",
- "label": "Standard Timeline",
- "timeline": 1,
- "activityTypes": [
- {
- "name": "Open Case",
- "status": "Completed"
- },
- {
- "name": "Follow up",
- "reference_activity": "Open Case",
- "reference_offset": "3",
- "reference_select": "newest"
- }
- ]
- }
- ],
- "timelineActivityTypes": [
- {
- "name": "Open Case",
- "status": "Completed"
- },
- {
- "name": "Follow up",
- "reference_activity": "Open Case",
- "reference_offset": "3",
- "reference_select": "newest"
- }
- ],
- "caseRoles": [
- {
- "name": "Parent of",
- "creator": "1",
- "manager": "1"
- }
- ]
- },
- "@ref": "test_case_type_1"
- }
- ]
-}
+++ /dev/null
-{
- "Contact": [
- {
- "first_name": "Janice",
- "last_name": "Voss",
- "contact_type": "Individual",
- "@ref": "test_contact_1"
- },
- {
- "first_name": "Jim",
- "last_name": "Henson",
- "contact_type": "Individual",
- "@ref": "test_contact_2"
- }
- ],
- "Case": [
- {
- "case_type_id": "@ref test_case_type_1.id",
- "status_id": 1,
- "contact_id": "@ref test_contact_1.id",
- "creator_id": "@ref test_contact_1.id"
- }
- ],
- "CustomGroup": [
- {
- "name": "MyFavoriteThings",
- "title": "MyFavoriteThings",
- "extends": "Contact"
- }
- ],
- "Event": [
- {
- "start_date": "20401010000000",
- "title": "The Singularity",
- "event_type_id": "major_historical_event"
- }
- ],
- "Group": [
- {
- "name": "the_group",
- "title": "The Group"
- }
- ],
- "Mapping": [
- {
- "name": "the_mapping",
- "mapping_type_id": "1"
- }
- ],
- "Activity": [
- {
- "subject": "Test A Phone Activity",
- "activity_type_id:name": "Phone Call",
- "source_contact_id": "@ref test_contact_1.id"
- }
- ],
- "Contribution": [
- {
- "total_amount": 999.89,
- "financial_type_id:name": "Donation",
- "contact_id": "@ref test_contact_1.id"
- }
- ],
- "Pledge" : [
- {
- "contact_id": "@ref test_contact_1.id",
- "original_installment_amount" : 10,
- "amount" : 10,
- "start_date" : "now",
- "create_date" : "now",
- "status_id:name" : "Pending"
- }
- ],
- "PaymentProcessor" : [
- {
- "payment_processor_type_id:name": "Dummy"
- }
- ],
- "Batch": [
- {
- "title": "Batch 433397",
- "name": "Batch_433397",
- "status_id": "1"
- }
- ],
- "Product": [
- {
- "name": "Test Product",
- "price": "10.00",
- "sku": "Test SKU",
- "financial_type_id:name": "Donation",
- "min_contribution": "10.00"
- }
- ],
- "MembershipType": [
- {
- "name": "General",
- "period_type" : "fixed",
- "financial_type_id:name": "Donation",
- "duration_unit" : "month",
- "member_of_contact_id" : "@ref test_contact_1.id"
- }
- ],
- "ContributionPage": [
- {
- "name": "General"
- }
- ],
- "MembershipType": [
- {
- "domain_id": "1",
- "name": "General",
- "description": "Regular annual membership.",
- "member_of_contact_id": "1",
- "financial_type_id": "2",
- "minimum_fee": "100.000000000",
- "duration_unit": "year",
- "duration_interval": "2",
- "period_type": "rolling",
- "relationship_type_id": [
- "7"
- ],
- "relationship_direction": [
- "b_a"
- ],
- "visibility": "Public",
- "weight": "1",
- "auto_renew": "0",
- "is_active": "1"
- }
- ],
- "Membership": [
- {
- "membership_type_id:name": "General",
- "contact_id" : "@ref test_contact_1.id",
- "status_id:name" : "Pending"
- }
- ]
-}
+++ /dev/null
-{
- "Contact": [
- {
- "first_name": "Phoney",
- "last_name": "Contact",
- "contact_type": "Individual",
- "@ref": "test_contact_1"
- },
- {
- "first_name": "Second",
- "last_name": "Test",
- "contact_type": "Individual",
- "@ref": "test_contact_2"
- }
- ],
- "Activity": [
- {
- "subject": "Test Phone Activity",
- "activity_type_id:name": "Phone Call",
- "source_contact_id": "@ref test_contact_1.id"
- },
- {
- "subject": "Another Activity",
- "activity_type_id:name": "Meeting",
- "source_contact_id": "@ref test_contact_1.id",
- "assignee_contact_id": [
- "@ref test_contact_1.id",
- "@ref test_contact_2.id"
- ]
- }
- ],
- "Phone": [
- {
- "contact_id": "@ref test_contact_1.id",
- "phone": "+35355439483",
- "location_type_id": "1",
- "@ref": "test_phone_1"
- },
- {
- "contact_id": "@ref test_contact_1.id",
- "phone": "+3538733439483",
- "location_type_id": "2"
- }
- ],
- "Address": [
- {
- "contact_id": "@ref test_contact_1.id",
- "street_address": "123 Sesame St.",
- "country_id": "1228",
- "location_type_id": "1",
- "@ref": "test_address_1"
- }
- ]
-}
+++ /dev/null
-{
- "Contact": [
- {
- "first_name": "First",
- "last_name": "Contact",
- "contact_type": "Individual",
- "@ref": "test_contact_1"
- },
- {
- "first_name": "Second",
- "last_name": "Contact",
- "contact_type": "Individual",
- "@ref": "test_contact_2"
- }
- ],
- "Email": [
- {
- "contact_id": "@ref test_contact_1.id",
- "email": "test_contact_one_home@fakedomain.com",
- "location_type_id": 1,
- "@ref": "test_email_1"
- },
- {
- "contact_id": "@ref test_contact_1.id",
- "email": "test_contact_one_work@fakedomain.com",
- "location_type_id": 2,
- "@ref": "test_email_2"
- },
- {
- "contact_id": "@ref test_contact_2.id",
- "email": "test_contact_two_home@fakedomain.com",
- "location_type_id": 1,
- "@ref": "test_email_3"
- },
- {
- "contact_id": "@ref test_contact_2.id",
- "email": "test_contact_two_work@fakedomain.com",
- "location_type_id": 2,
- "@ref": "test_email_4"
- }
- ]
-}
+++ /dev/null
-{
- "Contact": [
- {
- "first_name": "Single",
- "last_name": "Contact",
- "contact_type": "Individual",
- "preferred_communication_method": "1",
- "@ref": "test_contact_1"
- }
- ],
- "Activity": [
- {
- "subject": "Won A Nobel Prize",
- "activity_type_id:name": "Meeting",
- "source_contact_id": "@ref test_contact_1.id",
- "@ref": "test_activity_1"
- },
- {
- "subject": "Cleaned The House",
- "activity_type_id:name": "Meeting",
- "source_contact_id": "@ref test_contact_1.id",
- "assignee_contact_id": [
- "@ref test_contact_1.id"
- ],
- "@ref": "test_activity_2"
- }
- ],
- "Phone": [
- {
- "contact_id": "@ref test_contact_1.id",
- "phone": "+1111111111111",
- "location_type_id": 1
- },
- {
- "contact_id": "@ref test_contact_1.id",
- "phone": "+2222222222222",
- "location_type_id": 2
- }
- ],
- "Email": [
- {
- "contact_id": "@ref test_contact_1.id",
- "email": "test_contact_home@fakedomain.com",
- "location_type_id": 1
- },
- {
- "contact_id": "@ref test_contact_1.id",
- "email": "test_contact_work@fakedomain.com",
- "location_type_id": 2
- }
- ],
- "Address": [
- {
- "contact_id": "@ref test_contact_1.id",
- "street_address": "123 Sesame St.",
- "location_type_id": 1
- }
- ],
- "Website": [
- {
- "contact_id": "@ref test_contact_1.id",
- "url": "http://test.com",
- "website_id": 1
- }
- ],
- "OpenID": [
- {
- "contact_id": "@ref test_contact_1.id",
- "openid": "123",
- "allowed_to_login": 1,
- "location_type_id": 1
- }
- ],
- "IM": [
- {
- "contact_id": "@ref test_contact_1.id",
- "name": "123",
- "location_type_id": 1
- }
- ]
-}
namespace api\v4\Entity;
-use Civi\Api4\CiviCase;
use api\v4\Api4TestBase;
use Civi\Api4\Relationship;
-use Civi\Test\TransactionalInterface;
/**
* @group headless
*/
-class CaseTest extends Api4TestBase implements TransactionalInterface {
+class CaseTest extends Api4TestBase {
public function setUp(): void {
parent::setUp();
\CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
- $this->loadDataSet('CaseType');
- }
-
- public function tearDown(): void {
- $relatedTables = [
- 'civicrm_activity',
- 'civicrm_activity_contact',
- 'civicrm_relationship',
- 'civicrm_case_contact',
- 'civicrm_case_type',
- 'civicrm_case',
- ];
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- parent::tearDown();
}
public function testCreateUsingLoggedInUser() {
$uid = $this->createLoggedInUser();
- $contactID = $this->createEntity(['type' => 'Individual'])['id'];
+ $contactID = $this->createTestRecord('Contact')['id'];
- $case = CiviCase::create(FALSE)
- ->addValue('case_type_id', $this->getReference('test_case_type_1')['id'])
- ->addValue('creator_id', 'user_contact_id')
- ->addValue('status_id', 1)
- ->addValue('contact_id', $contactID)
- ->execute()
- ->first();
+ $case = $this->createTestRecord('Case', [
+ 'creator_id' => 'user_contact_id',
+ 'contact_id' => $contactID,
+ ]);
$relationships = Relationship::get(FALSE)
->addWhere('case_id', '=', $case['id'])
}
public function testCgExtendsObjects() {
+ $this->createTestRecord('CaseType', [
+ 'title' => 'Test Case Type',
+ 'name' => 'test_case_type1',
+ ]);
+
$field = \Civi\Api4\CustomGroup::getFields(FALSE)
->setLoadOptions(TRUE)
->addValue('extends', 'Case')
namespace api\v4\Entity;
-use api\v4\Service\TestCreationParameterProvider;
use api\v4\Traits\CheckAccessTrait;
use api\v4\Traits\TableDropperTrait;
use Civi\API\Exception\UnauthorizedException;
use Civi\Api4\Event\ValidateValuesEvent;
use Civi\Api4\Service\Spec\CustomFieldSpec;
use Civi\Api4\Service\Spec\FieldSpec;
-use Civi\Api4\Service\Spec\SpecGatherer;
use Civi\Api4\Utils\CoreUtil;
use Civi\Core\Event\PostEvent;
use Civi\Core\Event\PreEvent;
use CheckAccessTrait;
use TableDropperTrait;
- /**
- * @var \api\v4\Service\TestCreationParameterProvider
- */
- protected $creationParamProvider;
-
/**
* Set up baseline for testing
*
public function setUp(): void {
// Enable all components
\CRM_Core_BAO_ConfigSetting::enableAllComponents();
- $this->loadDataSet('CaseType');
- $this->loadDataSet('ConformanceTest');
- $gatherer = new SpecGatherer();
- $this->creationParamProvider = new TestCreationParameterProvider($gatherer);
parent::setUp();
$this->resetCheckAccess();
}
$this->setCheckAccessGrants(["{$entity}::create" => TRUE]);
$this->assertEquals(0, $this->checkAccessCounts["{$entity}::create"]);
- $requiredParams = $this->creationParamProvider->getRequired($entity);
+ $requiredParams = $this->getRequiredValuesToCreate($entity);
$createResult = $entityClass::create()
->setValues($requiredParams)
->setCheckPermissions(!$isReadOnly)
$this->setCheckAccessGrants(["{$entity}::create" => FALSE]);
$this->assertEquals(0, $this->checkAccessCounts["{$entity}::create"]);
- $requiredParams = $this->creationParamProvider->getRequired($entity);
+ $requiredParams = $this->getRequiredValuesToCreate($entity);
try {
$entityClass::create()
use Civi\Api4\Contact;
use Civi\Api4\OptionValue;
use api\v4\Api4TestBase;
-use Civi\Test\TransactionalInterface;
/**
* @group headless
*/
-class ContactJoinTest extends Api4TestBase implements TransactionalInterface {
-
- public function setUpHeadless() {
- $relatedTables = [
- 'civicrm_address',
- 'civicrm_email',
- 'civicrm_phone',
- 'civicrm_openid',
- 'civicrm_im',
- 'civicrm_website',
- 'civicrm_activity',
- 'civicrm_activity_contact',
- ];
-
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- $this->loadDataSet('SingleContact');
-
- return parent::setUpHeadless();
- }
+class ContactJoinTest extends Api4TestBase {
public function testContactJoin() {
- $contact = $this->getReference('test_contact_1');
+ $contact = $this->createTestRecord('Contact', [
+ 'first_name' => uniqid(),
+ 'last_name' => uniqid(),
+ ]);
$entitiesToTest = ['Address', 'OpenID', 'IM', 'Website', 'Email', 'Phone'];
foreach ($entitiesToTest as $entity) {
+ $this->createTestRecord($entity, [
+ 'contact_id' => $contact['id'],
+ ]);
$results = civicrm_api4($entity, 'get', [
'where' => [['contact_id', '=', $contact['id']]],
'select' => ['contact_id.*_name', 'contact_id.id'],
}
public function testJoinToPCMWillReturnArray() {
- $contact = Contact::create()->setValues([
+ $contact = $this->createTestRecord('Contact', [
'preferred_communication_method' => [1, 2, 3],
'contact_type' => 'Individual',
'first_name' => 'Test',
'last_name' => 'PCM',
- ])->execute()->first();
+ ]);
$fetchedContact = Contact::get()
->addWhere('id', '=', $contact['id'])
$optionValues = array_column($options, 'value');
$labels = array_column($options, 'label');
- $contact = Contact::create()->setValues([
+ $contact = $this->createTestRecord('Contact', [
'preferred_communication_method' => $optionValues,
'contact_type' => 'Individual',
'first_name' => 'Test',
'last_name' => 'PCM',
- ])->execute()->first();
+ ]);
- $contact2 = Contact::create()->setValues([
+ $contact2 = $this->createTestRecord('Contact', [
'preferred_communication_method' => $optionValues,
'contact_type' => 'Individual',
'first_name' => 'Test',
'last_name' => 'PCM2',
- ])->execute()->first();
+ ]);
$contactIds = array_column([$contact, $contact2], 'id');
namespace api\v4\Entity;
use api\v4\Api4TestBase;
-use Civi\Api4\GroupContact;
-use Civi\Test\TransactionalInterface;
/**
* @group headless
*/
-class GroupContactTest extends Api4TestBase implements TransactionalInterface {
+class GroupContactTest extends Api4TestBase {
public function testCreate() {
- $contact = $this->createEntity(['type' => 'Individual']);
- $group = $this->createEntity(['type' => 'Group']);
- $result = GroupContact::create(FALSE)
- ->addValue('group_id', $group['id'])
- ->addValue('contact_id', $contact['id'])
- ->execute()
- ->first();
+ $contact = $this->createTestRecord('Contact');
+ $group = $this->createTestRecord('Group');
+ $result = $this->createTestRecord('GroupContact', [
+ 'group_id' => $group['id'],
+ 'contact_id' => $contact['id'],
+ ]);
$this->assertEquals('Added', $result['status']);
}
class NoteTest extends Api4TestBase implements TransactionalInterface {
public function testDeleteWithChildren() {
- $c1 = $this->createEntity(['type' => 'Individual']);
+ $c1 = $this->createTestRecord('Contact');
$text = uniqid(__FUNCTION__, TRUE);
public function testGet() {
$rows = $this->getRowCount('civicrm_participant');
if ($rows > 0) {
- $this->markTestSkipped('Participant table must be empty');
+ $this->fail('Participant table must be empty');
}
// With no records:
$expectedFirstEventCount = ceil($participantCount / $eventCount);
$dummy = [
- 'contacts' => $this->createEntity([
- 'type' => 'Individual',
- 'count' => $contactCount,
- 'seq' => 1,
+ 'contacts' => $this->saveTestRecords('Contact', [
+ 'records' => array_fill(0, $contactCount, []),
]),
- 'events' => $this->createEntity([
- 'type' => 'Event',
- 'count' => $eventCount,
- 'seq' => 1,
+ 'events' => $this->saveTestRecords('Event', [
+ 'records' => array_fill(0, $eventCount, []),
]),
'sources' => ['Paddington', 'Springfield', 'Central'],
];
// - create dummy participants record
+ $records = [];
for ($i = 0; $i < $participantCount; $i++) {
- $dummy['participants'][$i] = $this->sample([
- 'type' => 'Participant',
- 'overrides' => [
- 'event_id' => $dummy['events'][$i % $eventCount]['id'],
- 'contact_id' => $dummy['contacts'][$i % $contactCount]['id'],
- // 3 = number of sources
- 'source' => $dummy['sources'][$i % 3],
- ],
- ])['sample_params'];
-
- Participant::create()
- ->setValues($dummy['participants'][$i])
- ->setCheckPermissions(FALSE)
- ->execute();
+ $records[] = [
+ 'event_id' => $dummy['events'][$i % $eventCount]['id'],
+ 'contact_id' => $dummy['contacts'][$i % $contactCount]['id'],
+ // 3 = number of sources
+ 'source' => $dummy['sources'][$i % 3],
+ ];
}
+ $this->saveTestRecords('Participant', [
+ 'records' => $records,
+ 'defaults' => [
+ 'status_id' => 2,
+ 'role_id' => 1,
+ 'register_date' => 20070219,
+ 'event_level' => 'Payment',
+ ],
+ ]);
$sqlCount = $this->getRowCount('civicrm_participant');
$this->assertEquals($participantCount, $sqlCount, "Unexpected count");
$this->assertCount(1, Participant::get()->selectRowCount()->addWhere('id', '=', $testParticipants->first()['id'])->execute());
}
+ /**
+ * Quick record counter
+ *
+ * @param string $table_name
+ * @returns int record count
+ */
+ private function getRowCount($table_name) {
+ $sql = "SELECT count(id) FROM $table_name";
+ return (int) \CRM_Core_DAO::singleValueQuery($sql);
+ }
+
}
private $lastValidator;
- protected function setUp(): void {
+ public function setUp(): void {
$this->lastValidator = NULL;
parent::setUp();
}
- protected function tearDown(): void {
+ public function tearDown(): void {
$this->setValidator(NULL);
parent::tearDown();
}
use Civi\API\Request;
use Civi\Api4\Query\Api4SelectQuery;
use api\v4\Api4TestBase;
-use Civi\Test\TransactionalInterface;
/**
* @group headless
*/
-class Api4SelectQueryTest extends Api4TestBase implements TransactionalInterface {
-
- public function setUpHeadless() {
- $relatedTables = [
- 'civicrm_address',
- 'civicrm_email',
- 'civicrm_phone',
- 'civicrm_openid',
- 'civicrm_im',
- 'civicrm_website',
- 'civicrm_activity',
- 'civicrm_activity_contact',
- ];
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- $this->loadDataSet('DefaultDataSet');
- $displayNameFormat = '{contact.first_name}{ }{contact.last_name}';
- \Civi::settings()->set('display_name_format', $displayNameFormat);
-
- return parent::setUpHeadless();
- }
+class Api4SelectQueryTest extends Api4TestBase {
public function testManyToOneJoin() {
- $phoneNum = $this->getReference('test_phone_1')['phone'];
- $contact = $this->getReference('test_contact_1');
+ $contact = $this->createTestRecord('Contact', [
+ 'first_name' => uniqid(),
+ 'last_name' => uniqid(),
+ ]);
+ $phone = $this->createTestRecord('Phone', [
+ 'contact_id' => $contact['id'],
+ 'phone' => uniqid(),
+ ]);
+
+ $phoneNum = $phone['phone'];
$api = Request::create('Phone', 'get', [
'version' => 4,
use Civi\Api4\Query\Api4SelectQuery;
use api\v4\Api4TestBase;
-use Civi\Test\TransactionalInterface;
/**
* @group headless
*/
-class OptionValueJoinTest extends Api4TestBase implements TransactionalInterface {
-
- public function setUpHeadless() {
- $relatedTables = [
- 'civicrm_address',
- 'civicrm_email',
- 'civicrm_phone',
- 'civicrm_openid',
- 'civicrm_im',
- 'civicrm_website',
- 'civicrm_activity',
- 'civicrm_activity_contact',
- ];
-
- $this->cleanup(['tablesToTruncate' => $relatedTables]);
- $this->loadDataSet('SingleContact');
-
- return parent::setUpHeadless();
- }
+class OptionValueJoinTest extends Api4TestBase {
public function testCommunicationMethodJoin() {
+ $this->createTestRecord('Contact', [
+ 'preferred_communication_method' => 1,
+ ]);
+
$api = \Civi\API\Request::create('Contact', 'get', [
'version' => 4,
'checkPermissions' => FALSE,
use Civi\Api4\Email;
use api\v4\Api4TestBase;
-use Civi\Test\TransactionalInterface;
/**
* Class SelectQueryMultiJoinTest
* @package api\v4\Query
* @group headless
*/
-class SelectQueryMultiJoinTest extends Api4TestBase implements TransactionalInterface {
-
- public function setUpHeadless() {
- $this->cleanup(['tablesToTruncate' => ['civicrm_contact', 'civicrm_email']]);
- $this->loadDataSet('MultiContactMultiEmail');
- return parent::setUpHeadless();
- }
+class SelectQueryMultiJoinTest extends Api4TestBase {
public function testManyToOneSelect() {
+
+ $contact1 = $this->createTestRecord('Contact', [
+ 'first_name' => 'First',
+ 'last_name' => 'Contact',
+ ]);
+ $firstEmail = $this->createTestRecord('Email', [
+ 'contact_id' => $contact1['id'],
+ 'location_type_id:name' => 'Home',
+ ]);
+ $secondEmail = $this->createTestRecord('Email', [
+ 'contact_id' => $contact1['id'],
+ 'location_type_id:name' => 'Work',
+ ]);
+ $contact2 = $this->createTestRecord('Contact', [
+ 'first_name' => 'Second',
+ 'last_name' => 'Contact',
+ ]);
+ $thirdEmail = $this->createTestRecord('Email', [
+ 'contact_id' => $contact2['id'],
+ 'location_type_id:name' => 'Home',
+ ]);
+ $fourthEmail = $this->createTestRecord('Email', [
+ 'contact_id' => $contact2['id'],
+ 'location_type_id:name' => 'Work',
+ ]);
+
$results = Email::get()
->addSelect('contact_id.display_name')
->execute()
->indexBy('id');
- $firstEmail = $this->getReference('test_email_1');
- $secondEmail = $this->getReference('test_email_2');
- $thirdEmail = $this->getReference('test_email_3');
- $fourthEmail = $this->getReference('test_email_4');
$firstContactEmailIds = [$firstEmail['id'], $secondEmail['id']];
$secondContactEmailIds = [$thirdEmail['id'], $fourthEmail['id']];
+++ /dev/null
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved. |
- | |
- | This work is published under the GNU AGPLv3 license with some |
- | permitted exceptions and without any warranty. For full license |
- | and copyright information, see https://civicrm.org/licensing |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- */
-
-
-namespace api\v4\Service;
-
-use Civi\Api4\Service\Spec\FieldSpec;
-use Civi\Api4\Service\Spec\Provider\FinancialItemCreationSpecProvider;
-use Civi\Api4\Service\Spec\SpecGatherer;
-
-class TestCreationParameterProvider {
-
- /**
- * @var \Civi\Api4\Service\Spec\SpecGatherer
- */
- protected $gatherer;
-
- /**
- * @param \Civi\Api4\Service\Spec\SpecGatherer $gatherer
- */
- public function __construct(SpecGatherer $gatherer) {
- $this->gatherer = $gatherer;
- }
-
- /**
- * Get the required fields for the api entity + action.
- *
- * @param string $entity
- *
- * @return array
- * @throws \API_Exception
- */
- public function getRequired(string $entity) {
- $requiredFields = civicrm_api4($entity, 'getfields', [
- 'action' => 'create',
- 'loadOptions' => TRUE,
- 'where' => [
- ['OR', [['required', '=', TRUE], ['required_if', 'IS NOT EMPTY']]],
- ['readonly', 'IS EMPTY'],
- ],
- ], 'name');
-
- $requiredParams = [];
- foreach ($requiredFields as $fieldName => $requiredField) {
- $requiredParams[$fieldName] = $this->getRequiredValue($requiredField);
- }
-
- // This is a ruthless hack to avoid peculiar constraints - but
- // it's also a test class & hard to care enough to do something
- // better
- $overrides = [];
- $overrides['UFField'] = [
- 'field_name' => 'activity_campaign_id',
- ];
- $overrides['Translation'] = [
- 'entity_table' => 'civicrm_event',
- 'entity_field' => 'description',
- 'entity_id' => \CRM_Core_DAO::singleValueQuery('SELECT min(id) FROM civicrm_event'),
- ];
-
- if (isset($overrides[$entity])) {
- $requiredParams = array_merge($requiredParams, $overrides[$entity]);
- }
-
- return $requiredParams;
- }
-
- /**
- * Attempt to get a value using field option, defaults, FKEntity, or a random
- * value based on the data type.
- *
- * @param array $field
- *
- * @return mixed
- * @throws \Exception
- */
- private function getRequiredValue(array $field) {
-
- if (!empty($field['options'])) {
- return key($field['options']);
- }
- if (!empty($field['fk_entity'])) {
- return $this->getFkID($field['fk_entity']);
- }
- if (isset($field['default_value'])) {
- return $field['default_value'];
- }
- if ($field['name'] === 'contact_id') {
- return $this->getFkID('Contact');
- }
- if ($field['name'] === 'entity_id') {
- // What could possibly go wrong with this?
- switch ($field['table_name'] ?? NULL) {
- case 'civicrm_financial_item':
- return $this->getFkID(FinancialItemCreationSpecProvider::DEFAULT_ENTITY);
-
- default:
- return $this->getFkID('Contact');
- }
- }
-
- $randomValue = $this->getRandomValue($field['data_type']);
-
- if ($randomValue) {
- return $randomValue;
- }
-
- throw new \API_Exception('Could not provide default value');
- }
-
- /**
- * @param \Civi\Api4\Service\Spec\FieldSpec $field
- *
- * @return mixed
- */
- private function getOption(FieldSpec $field) {
- $options = array_column($field->getOptions(), 'label', 'id');
- return key($options);
- }
-
- /**
- * Get an ID for the appropriate entity.
- *
- * @param string $fkEntity
- *
- * @return mixed
- *
- * @throws \API_Exception
- */
- private function getFkID(string $fkEntity) {
- $params = ['checkPermissions' => FALSE];
- // Be predictable about what type of contact we select
- if ($fkEntity === 'Contact') {
- $params['where'] = [['contact_type', '=', 'Individual']];
- }
- $entityList = civicrm_api4($fkEntity, 'get', $params);
- // If no existing entities, create one
- if ($entityList->count() < 1) {
- $entityList = civicrm_api4($fkEntity, 'create', [
- 'checkPermissions' => FALSE,
- 'values' => $this->getRequired($fkEntity),
- ]);
- }
-
- return $entityList->last()['id'];
- }
-
- /**
- * @param $dataType
- *
- * @return int|null|string
- */
- private function getRandomValue($dataType) {
- switch ($dataType) {
- case 'Boolean':
- return TRUE;
-
- case 'Integer':
- return random_int(1, 2000);
-
- case 'String':
- return \CRM_Utils_String::createRandom(10, implode('', range('a', 'z')));
-
- case 'Text':
- return \CRM_Utils_String::createRandom(100, implode('', range('a', 'z')));
-
- case 'Money':
- return sprintf('%d.%2d', rand(0, 2000), rand(10, 99));
-
- case 'Date':
- return '20100102';
-
- case 'Timestamp':
- return 'now';
- }
-
- return NULL;
- }
-
-}
+++ /dev/null
-<?php
-
-/*
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC. All rights reserved. |
- | |
- | This work is published under the GNU AGPLv3 license with some |
- | permitted exceptions and without any warranty. For full license |
- | and copyright information, see https://civicrm.org/licensing |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC https://civicrm.org/licensing
- */
-
-
-namespace api\v4\Traits;
-
-/**
- * This probably should be a separate class
- */
-trait TestDataLoaderTrait {
-
- /**
- * @var array
- * References to entities used for loading test data
- */
- protected $references;
-
- /**
- * Creates entities from a JSON data set
- *
- * @param $path
- */
- protected function loadDataSet($path) {
- if (!file_exists($path)) {
- $path = __DIR__ . '/../DataSets/' . $path . '.json';
- }
-
- $dataSet = json_decode(file_get_contents($path), TRUE);
- foreach ($dataSet as $entityName => $entities) {
- foreach ($entities as $entityValues) {
-
- $entityValues = $this->replaceReferences($entityValues);
-
- $params = ['values' => $entityValues, 'checkPermissions' => FALSE];
- $result = civicrm_api4($entityName, 'create', $params);
- if (isset($entityValues['@ref'])) {
- $this->references[$entityValues['@ref']] = $result->first();
- }
- }
- }
- }
-
- /**
- * @param $name
- *
- * @return null|mixed
- */
- protected function getReference($name) {
- return $this->references[$name] ?? NULL;
- }
-
- /**
- * @param array $entityValues
- *
- * @return array
- */
- private function replaceReferences($entityValues) {
- foreach ($entityValues as $name => $value) {
- if (is_array($value)) {
- $entityValues[$name] = $this->replaceReferences($value);
- }
- elseif (substr($value, 0, 4) === '@ref') {
- $referenceName = substr($value, 5);
- list ($reference, $property) = explode('.', $referenceName);
- $entityValues[$name] = $this->references[$reference][$property];
- }
- }
- return $entityValues;
- }
-
-}