(NFC) Style fixups
[civicrm-core.git] / Civi / Test / Api3DocTrait.php
CommitLineData
195557d5
TO
1<?php
2
3namespace 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 */
22trait 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) {
744c6b13 71 if ($params['version'] != 3 || (defined('DONT_DOCUMENT_TEST_CONFIG') && DONT_DOCUMENT_TEST_CONFIG)) {
195557d5
TO
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.
76d184ee 80 $exampleName = \CRM_Utils_String::convertStringToCamel($action);
c64f69d9 81 $knownPrefixes = [
195557d5
TO
82 'Get',
83 'Set',
84 'Create',
85 'Update',
86 'Send',
c64f69d9 87 ];
195557d5
TO
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
c64f69d9 100 $desc = [];
195557d5
TO
101 if (is_string($description) && strlen($description)) {
102 foreach (explode("\n", $description) as $line) {
103 $desc[] = trim($line);
104 }
105 }
76d184ee 106 $smarty = \CRM_Core_Smarty::singleton();
195557d5
TO
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 }
59f95e35 126 $f = fopen($civicrm_root . "/api/v3/examples/$entity/$exampleName.ex.php", "w+b");
8390b11b
SL
127 $contents = $smarty->fetch($civicrm_root . '/tests/templates/documentFunction.tpl');
128 $contents = \CRM_Core_CodeGen_Util_ArraySyntaxConverter::convert($contents);
129 fwrite($f, $contents);
195557d5
TO
130 fclose($f);
131 }
132 }
133
134 /**
135 * Tidy up examples array so that fields that change often ..don't
136 * and debug related fields are unset
137 *
138 * @param array $result
139 */
140 public function tidyExampleResult(&$result) {
141 if (!is_array($result)) {
142 return;
143 }
c64f69d9 144 $fieldsToChange = [
195557d5
TO
145 'hash' => '67eac7789eaee00',
146 'modified_date' => '2012-11-14 16:02:35',
147 'created_date' => '2013-07-28 08:49:19',
148 'create_date' => '20120130621222105',
149 'application_received_date' => '20130728084957',
150 'in_date' => '2013-07-28 08:50:19',
151 'scheduled_date' => '20130728085413',
152 'approval_date' => '20130728085413',
153 'pledge_start_date_high' => '20130726090416',
154 'start_date' => '2013-07-29 00:00:00',
155 'event_start_date' => '2013-07-29 00:00:00',
156 'end_date' => '2013-08-04 00:00:00',
157 'event_end_date' => '2013-08-04 00:00:00',
158 'decision_date' => '20130805000000',
c64f69d9 159 ];
195557d5 160
c64f69d9 161 $keysToUnset = ['xdebug', 'undefined_fields'];
195557d5
TO
162 foreach ($keysToUnset as $unwantedKey) {
163 if (isset($result[$unwantedKey])) {
164 unset($result[$unwantedKey]);
165 }
166 }
167 if (isset($result['values'])) {
168 if (!is_array($result['values'])) {
169 return;
170 }
171 $resultArray = &$result['values'];
172 }
173 elseif (is_array($result)) {
174 $resultArray = &$result;
175 }
176 else {
177 return;
178 }
179
180 foreach ($resultArray as $index => &$values) {
181 if (!is_array($values)) {
182 continue;
183 }
184 foreach ($values as $key => &$value) {
185 if (substr($key, 0, 3) == 'api' && is_array($value)) {
186 if (isset($value['is_error'])) {
187 // we have a std nested result format
188 $this->tidyExampleResult($value);
189 }
190 else {
191 foreach ($value as &$nestedResult) {
192 // this is an alternative syntax for nested results a keyed array of results
193 $this->tidyExampleResult($nestedResult);
194 }
195 }
196 }
197 if (in_array($key, $keysToUnset)) {
198 unset($values[$key]);
199 break;
200 }
201 if (array_key_exists($key, $fieldsToChange) && !empty($value)) {
202 $value = $fieldsToChange[$key];
203 }
204 if (is_string($value)) {
205 $value = addslashes($value);
206 }
207 }
208 }
209 }
210
211}