Merge in 5.11
[civicrm-core.git] / Civi / Test / Api3DocTrait.php
1 <?php
2
3 namespace Civi\Test;
4
5 /**
6 * Class Api3DocTrait
7 * @package Civi\Test
8 *
9 * This trait defines helper functions for testing and documenting APIv3. In
10 * particular, it supports a workflow that links a unit-test file to an
11 * example data file:
12 *
13 * - When defining a new API, write a unit test for it.
14 * - As part of the unit test, use `callAPIAndDocument($entity, $action, ...)`.
15 * - When the test executes, the inputs and outputs are logged to an example file.
16 * - You can commit this file to git.
17 * - Whenever the inputs/output change, they'll be visible in SCM/git because
18 * the example file also changes.
19 *
20 * This trait is intended for use with PHPUnit-based test cases.
21 */
22 trait Api3DocTrait {
23 use Api3TestTrait;
24
25 /**
26 * This function exists to wrap api functions.
27 * so we can ensure they succeed, generate and example & throw exceptions without litterering the test with checks
28 *
29 * @param string $entity
30 * @param string $action
31 * @param array $params
32 * @param string $function
33 * Pass this in to create a generated example.
34 * @param string $file
35 * Pass this in to create a generated example.
36 * @param string $description
37 * @param string|null $exampleName
38 *
39 * @return array|int
40 */
41 public function callAPIAndDocument($entity, $action, $params, $function, $file, $description = "", $exampleName = NULL) {
42 $params['version'] = $this->_apiversion;
43 $result = $this->callAPISuccess($entity, $action, $params);
44 $this->documentMe($entity, $action, $params, $result, $function, $file, $description, $exampleName);
45 return $result;
46 }
47
48 /**
49 * Create test generated example in api/v3/examples.
50 *
51 * To turn this off (e.g. on the server) set
52 * define(DONT_DOCUMENT_TEST_CONFIG ,1);
53 * in your settings file
54 *
55 * @param string $entity
56 * @param string $action
57 * @param array $params
58 * Array as passed to civicrm_api function.
59 * @param array $result
60 * Array as received from the civicrm_api function.
61 * @param string $testFunction
62 * Calling function - generally __FUNCTION__.
63 * @param string $testFile
64 * Called from file - generally __FILE__.
65 * @param string $description
66 * Descriptive text for the example file.
67 * @param string $exampleName
68 * Name for this example file (CamelCase) - if omitted the action name will be substituted.
69 */
70 private function documentMe($entity, $action, $params, $result, $testFunction, $testFile, $description = "", $exampleName = NULL) {
71 if (defined('DONT_DOCUMENT_TEST_CONFIG') && DONT_DOCUMENT_TEST_CONFIG) {
72 return;
73 }
74 $entity = _civicrm_api_get_camel_name($entity);
75 $action = strtolower($action);
76
77 if (empty($exampleName)) {
78 // Attempt to convert lowercase action name to CamelCase.
79 // This is clunky/imperfect due to the convention of all lowercase actions.
80 $exampleName = \CRM_Utils_String::convertStringToCamel($action);
81 $knownPrefixes = array(
82 'Get',
83 'Set',
84 'Create',
85 'Update',
86 'Send',
87 );
88 foreach ($knownPrefixes as $prefix) {
89 if (strpos($exampleName, $prefix) === 0 && $prefix != $exampleName) {
90 $exampleName[strlen($prefix)] = strtoupper($exampleName[strlen($prefix)]);
91 }
92 }
93 }
94
95 $this->tidyExampleResult($result);
96 if (isset($params['version'])) {
97 unset($params['version']);
98 }
99 // Format multiline description as array
100 $desc = array();
101 if (is_string($description) && strlen($description)) {
102 foreach (explode("\n", $description) as $line) {
103 $desc[] = trim($line);
104 }
105 }
106 $smarty = \CRM_Core_Smarty::singleton();
107 $smarty->assign('testFunction', $testFunction);
108 $smarty->assign('function', _civicrm_api_get_entity_name_from_camel($entity) . "_$action");
109 foreach ($params as $index => $param) {
110 if (is_string($param)) {
111 $params[$index] = addslashes($param);
112 }
113 }
114 $smarty->assign('params', $params);
115 $smarty->assign('entity', $entity);
116 $smarty->assign('testFile', basename($testFile));
117 $smarty->assign('description', $desc);
118 $smarty->assign('result', $result);
119 $smarty->assign('action', $action);
120
121 global $civicrm_root;
122 if (file_exists($civicrm_root . '/tests/templates/documentFunction.tpl')) {
123 if (!is_dir($civicrm_root . "/api/v3/examples/$entity")) {
124 mkdir($civicrm_root . "/api/v3/examples/$entity");
125 }
126 $f = fopen($civicrm_root . "/api/v3/examples/$entity/$exampleName.php", "w+b");
127 fwrite($f, $smarty->fetch($civicrm_root . '/tests/templates/documentFunction.tpl'));
128 fclose($f);
129 }
130 }
131
132 /**
133 * Tidy up examples array so that fields that change often ..don't
134 * and debug related fields are unset
135 *
136 * @param array $result
137 */
138 public function tidyExampleResult(&$result) {
139 if (!is_array($result)) {
140 return;
141 }
142 $fieldsToChange = array(
143 'hash' => '67eac7789eaee00',
144 'modified_date' => '2012-11-14 16:02:35',
145 'created_date' => '2013-07-28 08:49:19',
146 'create_date' => '20120130621222105',
147 'application_received_date' => '20130728084957',
148 'in_date' => '2013-07-28 08:50:19',
149 'scheduled_date' => '20130728085413',
150 'approval_date' => '20130728085413',
151 'pledge_start_date_high' => '20130726090416',
152 'start_date' => '2013-07-29 00:00:00',
153 'event_start_date' => '2013-07-29 00:00:00',
154 'end_date' => '2013-08-04 00:00:00',
155 'event_end_date' => '2013-08-04 00:00:00',
156 'decision_date' => '20130805000000',
157 );
158
159 $keysToUnset = array('xdebug', 'undefined_fields');
160 foreach ($keysToUnset as $unwantedKey) {
161 if (isset($result[$unwantedKey])) {
162 unset($result[$unwantedKey]);
163 }
164 }
165 if (isset($result['values'])) {
166 if (!is_array($result['values'])) {
167 return;
168 }
169 $resultArray = &$result['values'];
170 }
171 elseif (is_array($result)) {
172 $resultArray = &$result;
173 }
174 else {
175 return;
176 }
177
178 foreach ($resultArray as $index => &$values) {
179 if (!is_array($values)) {
180 continue;
181 }
182 foreach ($values as $key => &$value) {
183 if (substr($key, 0, 3) == 'api' && is_array($value)) {
184 if (isset($value['is_error'])) {
185 // we have a std nested result format
186 $this->tidyExampleResult($value);
187 }
188 else {
189 foreach ($value as &$nestedResult) {
190 // this is an alternative syntax for nested results a keyed array of results
191 $this->tidyExampleResult($nestedResult);
192 }
193 }
194 }
195 if (in_array($key, $keysToUnset)) {
196 unset($values[$key]);
197 break;
198 }
199 if (array_key_exists($key, $fieldsToChange) && !empty($value)) {
200 $value = $fieldsToChange[$key];
201 }
202 if (is_string($value)) {
203 $value = addslashes($value);
204 }
205 }
206 }
207 }
208
209 }