api de gestion de ticket, basé sur php-crud-api. Le but est de décorrélé les outils de gestion des données, afin
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

tests.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. <?php
  2. if (!file_exists(__DIR__.'/config.php')) {
  3. copy(__DIR__.'/config.php.dist',__DIR__.'/config.php');
  4. }
  5. require __DIR__.'/config.php';
  6. require __DIR__.'/../api.php';
  7. class API
  8. {
  9. protected $test;
  10. protected $api;
  11. public function __construct($test)
  12. {
  13. $this->test = $test;
  14. }
  15. private function action($method,$url,$data='')
  16. {
  17. $url = parse_url($url);
  18. $query = isset($url['query'])?$url['query']:'';
  19. parse_str($query,$get);
  20. $this->api = new PHP_CRUD_API(array(
  21. 'dbengine'=>PHP_CRUD_API_Config::$dbengine,
  22. 'hostname'=>PHP_CRUD_API_Config::$hostname,
  23. 'username'=>PHP_CRUD_API_Config::$username,
  24. 'password'=>PHP_CRUD_API_Config::$password,
  25. 'database'=>PHP_CRUD_API_Config::$database,
  26. // callbacks
  27. 'table_authorizer'=>function($action,$database,$table) { return true; },
  28. 'column_authorizer'=>function($action,$database,$table,$column) { return !($column=='password'&&$action=='list'); },
  29. 'record_filter'=>function($action,$database,$table) { return ($table=='posts')?array('id,neq,13'):false; },
  30. 'tenancy_function'=>function($action,$database,$table,$column) { return ($table=='users'&&$column=='id')?1:null; },
  31. 'input_sanitizer'=>function($action,$database,$table,$column,$type,$value) { return $value===null?null:strip_tags($value); },
  32. 'input_validator'=>function($action,$database,$table,$column,$type,$value,$context) { return ($column=='category_id' && !is_numeric($value))?'must be numeric':true; },
  33. // for tests
  34. 'method' =>$method,
  35. 'request' =>$url['path'],
  36. 'post'=>$data,
  37. 'get' =>$get,
  38. ));
  39. return $this;
  40. }
  41. public function get($url)
  42. {
  43. return $this->action('GET',$url);
  44. }
  45. public function post($url,$data)
  46. {
  47. return $this->action('POST',$url,$data);
  48. }
  49. public function put($url,$data)
  50. {
  51. return $this->action('PUT',$url,$data);
  52. }
  53. public function delete($url)
  54. {
  55. return $this->action('DELETE',$url);
  56. }
  57. public function options($url)
  58. {
  59. return $this->action('OPTIONS',$url);
  60. }
  61. public function patch($url,$data)
  62. {
  63. return $this->action('PATCH',$url,$data);
  64. }
  65. public function expectAny()
  66. {
  67. ob_start();
  68. $this->api->executeCommand();
  69. ob_end_clean();
  70. return $this;
  71. }
  72. public function expect($output,$error=false)
  73. {
  74. $exception = false;
  75. ob_start();
  76. try {
  77. $this->api->executeCommand();
  78. } catch (\Exception $e) {
  79. $exception = $e->getMessage();
  80. }
  81. $data = ob_get_contents();
  82. ob_end_clean();
  83. if ($exception) $this->test->assertEquals($error, $exception);
  84. else $this->test->assertEquals($output, $data);
  85. return $this;
  86. }
  87. }
  88. class PHP_CRUD_API_Test extends PHPUnit_Framework_TestCase
  89. {
  90. public static function setUpBeforeClass()
  91. {
  92. if (PHP_CRUD_API_Config::$database=='{{test_database}}') {
  93. die("Configure database in 'config.php' before running tests.\n");
  94. }
  95. $dbengine = PHP_CRUD_API_Config::$dbengine;
  96. $hostname = PHP_CRUD_API_Config::$hostname;
  97. $username = PHP_CRUD_API_Config::$username;
  98. $password = PHP_CRUD_API_Config::$password;
  99. $database = PHP_CRUD_API_Config::$database;
  100. $fixture = __DIR__.'/blog_'.strtolower($dbengine).'.sql';
  101. if ($dbengine == 'MySQL') {
  102. $link = mysqli_connect($hostname, $username, $password, $database);
  103. if (mysqli_connect_errno()) {
  104. die("Connect failed: ".mysqli_connect_error()."\n");
  105. }
  106. mysqli_set_charset($link,'utf8');
  107. $i=0;
  108. if (mysqli_multi_query($link, file_get_contents($fixture))) {
  109. do { $i++; mysqli_next_result($link); } while (mysqli_more_results($link));
  110. }
  111. if (mysqli_errno($link)) {
  112. die("Loading '$fixture' failed on statemement #$i with error:\n".mysqli_error($link)."\n");
  113. }
  114. mysqli_close($link);
  115. } elseif ($dbengine == 'SQLServer') {
  116. $connectionInfo = array();
  117. $connectionInfo['UID']=$username;
  118. $connectionInfo['PWD']=$password;
  119. $connectionInfo['Database']=$database;
  120. $connectionInfo['CharacterSet']='UTF-8';
  121. $conn = sqlsrv_connect( $hostname, $connectionInfo);
  122. if (!$conn) {
  123. die("Connect failed: ".print_r( sqlsrv_errors(), true));
  124. }
  125. $queries = preg_split('/\n\s*GO\s*\n/', file_get_contents($fixture));
  126. array_pop($queries);
  127. foreach ($queries as $i=>$query) {
  128. if (!sqlsrv_query($conn, $query)) {
  129. $i++;
  130. die("Loading '$fixture' failed on statemement #$i with error:\n".print_r( sqlsrv_errors(), true)."\n");
  131. }
  132. }
  133. sqlsrv_close($conn);
  134. } elseif ($dbengine == 'PostgreSQL') {
  135. $e = function ($v) { return str_replace(array('\'','\\'),array('\\\'','\\\\'),$v); };
  136. $hostname = $e($hostname);
  137. $database = $e($database);
  138. $username = $e($username);
  139. $password = $e($password);
  140. $conn_string = "host='$hostname' dbname='$database' user='$username' password='$password' options='--client_encoding=UTF8'";
  141. $db = pg_connect($conn_string);
  142. if (!$db) {
  143. die("Connect failed: ". pg_last_error());
  144. }
  145. $queries = preg_split('/;\s*\n/', file_get_contents($fixture));
  146. array_pop($queries);
  147. foreach ($queries as $i=>$query) {
  148. if (!pg_query($db, $query.';')) {
  149. $i++;
  150. die("Loading '$fixture' failed on statemement #$i with error:\n".print_r( pg_last_error($db), true)."\n");
  151. }
  152. }
  153. pg_close($db);
  154. } elseif ($dbengine == 'SQLite') {
  155. $db = new SQLite3($database);
  156. if (!$db) {
  157. die("Could not open '$database' SQLite database: ".SQLite3::lastErrorMsg().' ('.SQLite3::lastErrorCode().")\n");
  158. }
  159. $queries = preg_split('/;\s*\n/', file_get_contents($fixture));
  160. array_pop($queries);
  161. foreach ($queries as $i=>$query) {
  162. if (!$db->query($query.';')) {
  163. $i++;
  164. die("Loading '$fixture' failed on statemement #$i with error:\n".$db->lastErrorCode().': '.$db->lastErrorMsg()."\n");
  165. }
  166. }
  167. $db->close();
  168. }
  169. }
  170. public function testListPosts()
  171. {
  172. $test = new API($this);
  173. $test->get('/posts');
  174. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[1,1,1,"blog started"],[2,1,2,"It works!"]]}}');
  175. }
  176. public function testListPostColumns()
  177. {
  178. $test = new API($this);
  179. $test->get('/posts?columns=id,content');
  180. $test->expect('{"posts":{"columns":["id","content"],"records":[[1,"blog started"],[2,"It works!"]]}}');
  181. }
  182. public function testListPostsWithTransform()
  183. {
  184. $test = new API($this);
  185. $test->get('/posts?transform=1');
  186. $test->expect('{"posts":[{"id":1,"user_id":1,"category_id":1,"content":"blog started"},{"id":2,"user_id":1,"category_id":2,"content":"It works!"}]}');
  187. }
  188. public function testReadPost()
  189. {
  190. $test = new API($this);
  191. $test->get('/posts/2');
  192. $test->expect('{"id":2,"user_id":1,"category_id":2,"content":"It works!"}');
  193. }
  194. public function testReadPosts()
  195. {
  196. $test = new API($this);
  197. $test->get('/posts/1,2');
  198. $test->expect('[{"id":1,"user_id":1,"category_id":1,"content":"blog started"},{"id":2,"user_id":1,"category_id":2,"content":"It works!"}]');
  199. }
  200. public function testReadPostColumns()
  201. {
  202. $test = new API($this);
  203. $test->get('/posts/2?columns=id,content');
  204. $test->expect('{"id":2,"content":"It works!"}');
  205. }
  206. public function testAddPost()
  207. {
  208. $test = new API($this);
  209. $test->post('/posts','{"user_id":1,"category_id":1,"content":"test"}');
  210. $test->expect('3');
  211. }
  212. public function testEditPost()
  213. {
  214. $test = new API($this);
  215. $test->put('/posts/3','{"user_id":1,"category_id":1,"content":"test (edited)"}');
  216. $test->expect('1');
  217. $test->get('/posts/3');
  218. $test->expect('{"id":3,"user_id":1,"category_id":1,"content":"test (edited)"}');
  219. }
  220. public function testEditPostColumnsMissingField()
  221. {
  222. $test = new API($this);
  223. $test->put('/posts/3?columns=id,content','{"content":"test (edited 2)"}');
  224. $test->expect('1');
  225. $test->get('/posts/3');
  226. $test->expect('{"id":3,"user_id":1,"category_id":1,"content":"test (edited 2)"}');
  227. }
  228. public function testEditPostColumnsExtraField()
  229. {
  230. $test = new API($this);
  231. $test->put('/posts/3?columns=id,content','{"user_id":2,"content":"test (edited 3)"}');
  232. $test->expect('1');
  233. $test->get('/posts/3');
  234. $test->expect('{"id":3,"user_id":1,"category_id":1,"content":"test (edited 3)"}');
  235. }
  236. public function testEditPostWithUtf8Content()
  237. {
  238. $utf8 = json_encode('Hello world, Καλημέρα κόσμε, コンニチハ');
  239. $test = new API($this);
  240. $test->put('/posts/2','{"content":'.$utf8.'}');
  241. $test->expect('1');
  242. $test->get('/posts/2');
  243. $test->expect('{"id":2,"user_id":1,"category_id":2,"content":'.$utf8.'}');
  244. }
  245. public function testEditPostWithUtf8ContentWithPost()
  246. {
  247. $utf8 = '€ Hello world, Καλημέρα κόσμε, コンニチハ';
  248. $url_encoded = urlencode($utf8);
  249. $json_encoded = json_encode($utf8);
  250. $test = new API($this);
  251. $test->put('/posts/2','content='.$url_encoded);
  252. $test->expect('1');
  253. $test->get('/posts/2');
  254. $test->expect('{"id":2,"user_id":1,"category_id":2,"content":'.$json_encoded.'}');
  255. }
  256. public function testDeletePost()
  257. {
  258. $test = new API($this);
  259. $test->delete('/posts/3');
  260. $test->expect('1');
  261. $test->get('/posts/3');
  262. $test->expect(false,'Not found (object)');
  263. }
  264. public function testAddPostWithPost()
  265. {
  266. $test = new API($this);
  267. $test->post('/posts','user_id=1&category_id=1&content=test');
  268. $test->expect('4');
  269. }
  270. public function testEditPostWithPost()
  271. {
  272. $test = new API($this);
  273. $test->put('/posts/4','user_id=1&category_id=1&content=test+(edited)');
  274. $test->expect('1');
  275. $test->get('/posts/4');
  276. $test->expect('{"id":4,"user_id":1,"category_id":1,"content":"test (edited)"}');
  277. }
  278. public function testDeletePostWithPost()
  279. {
  280. $test = new API($this);
  281. $test->delete('/posts/4');
  282. $test->expect('1');
  283. $test->get('/posts/4');
  284. $test->expect(false,'Not found (object)');
  285. }
  286. public function testListWithPaginate()
  287. {
  288. $test = new API($this);
  289. for ($i=1;$i<=10;$i++) {
  290. $test->post('/posts','{"user_id":1,"category_id":1,"content":"#'.$i.'"}');
  291. $test->expect(4+$i);
  292. }
  293. $test->get('/posts?page=2,2&order=id');
  294. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[5,1,1,"#1"],[6,1,1,"#2"]],"results":11}}');
  295. }
  296. public function testListWithPaginateInMultipleOrder()
  297. {
  298. $test = new API($this);
  299. $test->get('/posts?page=1,2&order[]=category_id,asc&order[]=id,desc');
  300. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[14,1,1,"#10"],[12,1,1,"#8"]],"results":11}}');
  301. }
  302. public function testListWithPaginateInDescendingOrder()
  303. {
  304. $test = new API($this);
  305. $test->get('/posts?page=2,2&order=id,desc');
  306. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[11,1,1,"#7"],[10,1,1,"#6"]],"results":11}}');
  307. }
  308. public function testListWithPaginateLastPage()
  309. {
  310. $test = new API($this);
  311. $test->get('/posts?page=3,5&order=id');
  312. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[14,1,1,"#10"]],"results":11}}');
  313. }
  314. public function testListExampleFromReadme()
  315. {
  316. $test = new API($this);
  317. $test->get('/posts?include=categories,tags,comments&filter=id,eq,1');
  318. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[[1,1,1,"blog started"]]},"post_tags":{"relations":{"post_id":"posts.id"},"columns":["id","post_id","tag_id"],"records":[[1,1,1],[2,1,2]]},"categories":{"relations":{"id":"posts.category_id"},"columns":["id","name","icon"],"records":[[1,"announcement",null]]},"tags":{"relations":{"id":"post_tags.tag_id"},"columns":["id","name"],"records":[[1,"funny"],[2,"important"]]},"comments":{"relations":{"post_id":"posts.id"},"columns":["id","post_id","message"],"records":[[1,1,"great"],[2,1,"fantastic"]]}}');
  319. }
  320. public function testListExampleFromReadmeWithTransform()
  321. {
  322. $test = new API($this);
  323. $test->get('/posts?include=categories,tags,comments&filter=id,eq,1&transform=1');
  324. $test->expect('{"posts":[{"id":1,"post_tags":[{"id":1,"post_id":1,"tag_id":1,"tags":[{"id":1,"name":"funny"}]},{"id":2,"post_id":1,"tag_id":2,"tags":[{"id":2,"name":"important"}]}],"comments":[{"id":1,"post_id":1,"message":"great"},{"id":2,"post_id":1,"message":"fantastic"}],"user_id":1,"category_id":1,"categories":[{"id":1,"name":"announcement","icon":null}],"content":"blog started"}]}');
  325. }
  326. public function testEditCategoryWithBinaryContent()
  327. {
  328. $binary = base64_encode("\0abc\0\n\r\b\0");
  329. $base64url = rtrim(strtr($binary, '+/', '-_'), '=');
  330. $test = new API($this);
  331. $test->put('/categories/2','{"icon":"'.$base64url.'"}');
  332. $test->expect('1');
  333. $test->get('/categories/2');
  334. $test->expect('{"id":2,"name":"article","icon":"'.$binary.'"}');
  335. }
  336. public function testEditCategoryWithNull()
  337. {
  338. $test = new API($this);
  339. $test->put('/categories/2','{"icon":null}');
  340. $test->expect('1');
  341. $test->get('/categories/2');
  342. $test->expect('{"id":2,"name":"article","icon":null}');
  343. }
  344. public function testEditCategoryWithBinaryContentWithPost()
  345. {
  346. $binary = base64_encode("€ \0abc\0\n\r\b\0");
  347. $base64url = rtrim(strtr($binary, '+/', '-_'), '=');
  348. $test = new API($this);
  349. $test->put('/categories/2','icon='.$base64url);
  350. $test->expect('1');
  351. $test->get('/categories/2');
  352. $test->expect('{"id":2,"name":"article","icon":"'.$binary.'"}');
  353. }
  354. public function testListCategoriesWithBinaryContent()
  355. {
  356. $test = new API($this);
  357. $test->get('/categories');
  358. $test->expect('{"categories":{"columns":["id","name","icon"],"records":[[1,"announcement",null],[2,"article","4oKsIABhYmMACg1cYgA="]]}}');
  359. }
  360. public function testEditCategoryWithNullWithPost()
  361. {
  362. $test = new API($this);
  363. $test->put('/categories/2','icon__is_null');
  364. $test->expect('1');
  365. $test->get('/categories/2');
  366. $test->expect('{"id":2,"name":"article","icon":null}');
  367. }
  368. public function testAddPostFailure()
  369. {
  370. $test = new API($this);
  371. $test->post('/posts','{"user_id":"a","category_id":1,"content":"tests"}');
  372. $test->expect('null');
  373. }
  374. public function testOptionsRequest()
  375. {
  376. $test = new API($this);
  377. $test->options('/posts/2');
  378. $test->expect('["Access-Control-Allow-Headers: Content-Type","Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST, DELETE, PATCH","Access-Control-Allow-Credentials: true","Access-Control-Max-Age: 1728000"]',false);
  379. }
  380. public function testHidingPasswordColumn()
  381. {
  382. $test = new API($this);
  383. $test->get('/users?filter=id,eq,1&transform=1');
  384. $test->expect('{"users":[{"id":1,"username":"user1","location":null}]}');
  385. }
  386. public function testValidatorErrorMessage()
  387. {
  388. $test = new API($this);
  389. $test->put('/posts/1','{"category_id":"a"}');
  390. $test->expect(false,'{"category_id":"must be numeric"}');
  391. }
  392. public function testSanitizerToStripTags()
  393. {
  394. $test = new API($this);
  395. $test->put('/categories/2','{"name":"<script>alert();</script>"}');
  396. $test->expect('1');
  397. $test->get('/categories/2');
  398. $test->expect('{"id":2,"name":"alert();","icon":null}');
  399. }
  400. public function testErrorOnInvalidJson()
  401. {
  402. $test = new API($this);
  403. $test->post('/posts','{"}');
  404. $test->expect(false,'Not found (input)');
  405. }
  406. public function testErrorOnDuplicatePrimaryKey()
  407. {
  408. $test = new API($this);
  409. $test->post('/posts','{"id":1,"user_id":1,"category_id":1,"content":"blog started (duplicate)"}');
  410. $test->expect('null');
  411. }
  412. public function testErrorOnFailingForeignKeyConstraint()
  413. {
  414. $test = new API($this);
  415. $test->post('/posts','{"user_id":3,"category_id":1,"content":"fk constraint"}');
  416. $test->expect('null');
  417. }
  418. public function testMissingIntermediateTable()
  419. {
  420. $test = new API($this);
  421. $test->get('/users?include=posts,tags');
  422. $test->expect('{"users":{"columns":["id","username","location"],"records":[[1,"user1",null]]},"posts":{"relations":{"user_id":"users.id"},"columns":["id","user_id","category_id","content"],"records":[[1,1,1,"blog started"],[2,1,2,"\u20ac Hello world, \u039a\u03b1\u03bb\u03b7\u03bc\u1f73\u03c1\u03b1 \u03ba\u1f79\u03c3\u03bc\u03b5, \u30b3\u30f3\u30cb\u30c1\u30cf"],[5,1,1,"#1"],[6,1,1,"#2"],[7,1,1,"#3"],[8,1,1,"#4"],[9,1,1,"#5"],[10,1,1,"#6"],[11,1,1,"#7"],[12,1,1,"#8"],[14,1,1,"#10"]]},"post_tags":{"relations":{"post_id":"posts.id"},"columns":["id","post_id","tag_id"],"records":[[1,1,1],[2,1,2],[3,2,1],[4,2,2]]},"tags":{"relations":{"id":"post_tags.tag_id"},"columns":["id","name"],"records":[[1,"funny"],[2,"important"]]}}');
  423. }
  424. public function testEditUserPassword()
  425. {
  426. $test = new API($this);
  427. $test->put('/users/1','{"password":"testtest"}');
  428. $test->expect('1');
  429. }
  430. public function testEditUserLocation()
  431. {
  432. $test = new API($this);
  433. $test->put('/users/1','{"location":"POINT(30 20)"}');
  434. $test->expect('1');
  435. $test->get('/users/1?columns=id,location');
  436. if (PHP_CRUD_API_Config::$dbengine=='SQLServer') {
  437. $test->expect('{"id":1,"location":"POINT (30 20)"}');
  438. } else {
  439. $test->expect('{"id":1,"location":"POINT(30 20)"}');
  440. }
  441. }
  442. public function testListUserLocations()
  443. {
  444. $test = new API($this);
  445. $test->get('/users?columns=id,location');
  446. if (PHP_CRUD_API_Config::$dbengine=='SQLServer') {
  447. $test->expect('{"users":{"columns":["id","location"],"records":[[1,"POINT (30 20)"]]}}');
  448. } else {
  449. $test->expect('{"users":{"columns":["id","location"],"records":[[1,"POINT(30 20)"]]}}');
  450. }
  451. }
  452. public function testEditUserWithId()
  453. {
  454. if (PHP_CRUD_API_Config::$dbengine!='SQLServer') {
  455. $test = new API($this);
  456. $test->put('/users/1','{"id":2,"password":"testtest2"}');
  457. $test->expect('1');
  458. $test->get('/users/1?columns=id,username,password');
  459. $test->expect('{"id":1,"username":"user1","password":"testtest2"}');
  460. }
  461. }
  462. public function testReadOtherUser()
  463. {
  464. $test = new API($this);
  465. $test->get('/users/2');
  466. $test->expect(false,'Not found (object)');
  467. }
  468. public function testEditOtherUser()
  469. {
  470. $test = new API($this);
  471. $test->put('/users/2','{"password":"testtest"}');
  472. $test->expect('0');
  473. }
  474. public function testFilterCategoryOnNullIcon()
  475. {
  476. $test = new API($this);
  477. $test->get('/categories?filter[]=icon,is,null&transform=1');
  478. $test->expect('{"categories":[{"id":1,"name":"announcement","icon":null},{"id":2,"name":"alert();","icon":null}]}');
  479. }
  480. public function testFilterCategoryOnNotNullIcon()
  481. {
  482. $test = new API($this);
  483. $test->get('/categories?filter[]=icon,nis,null&transform=1');
  484. $test->expect('{"categories":[]}');
  485. }
  486. public function testFilterPostsNotIn()
  487. {
  488. $test = new API($this);
  489. $test->get('/posts?filter[]=id,nin,1,2,3,4,7,8,9,10,11,12,13,14&transform=1');
  490. $test->expect('{"posts":[{"id":5,"user_id":1,"category_id":1,"content":"#1"},{"id":6,"user_id":1,"category_id":1,"content":"#2"}]}');
  491. }
  492. public function testFilterPostsBetween()
  493. {
  494. $test = new API($this);
  495. $test->get('/posts?filter[]=id,bt,5,6&transform=1');
  496. $test->expect('{"posts":[{"id":5,"user_id":1,"category_id":1,"content":"#1"},{"id":6,"user_id":1,"category_id":1,"content":"#2"}]}');
  497. }
  498. public function testFilterPostsNotBetween()
  499. {
  500. $test = new API($this);
  501. $test->get('/posts?filter[]=id,nbt,2,13&transform=1');
  502. $test->expect('{"posts":[{"id":1,"user_id":1,"category_id":1,"content":"blog started"},{"id":14,"user_id":1,"category_id":1,"content":"#10"}]}');
  503. }
  504. public function testColumnsWithTable()
  505. {
  506. $test = new API($this);
  507. $test->get('/posts?columns=posts.content&filter=id,eq,1&transform=1');
  508. $test->expect('{"posts":[{"content":"blog started"}]}');
  509. }
  510. public function testColumnsWithTableWildcard()
  511. {
  512. $test = new API($this);
  513. $test->get('/posts?columns=posts.*&filter=id,eq,1&transform=1');
  514. $test->expect('{"posts":[{"id":1,"user_id":1,"category_id":1,"content":"blog started"}]}');
  515. }
  516. public function testColumnsOnInclude()
  517. {
  518. $test = new API($this);
  519. $test->get('/posts?include=categories&columns=categories.name&filter=id,eq,1&transform=1');
  520. $test->expect('{"posts":[{"category_id":1,"categories":[{"id":1,"name":"announcement"}]}]}');
  521. }
  522. public function testFilterOnRelationAnd()
  523. {
  524. $test = new API($this);
  525. $test->get('/categories?include=posts&filter[]=id,ge,1&filter[]=id,le,1&filter[]=id,le,2&filter[]=posts.id,lt,8&filter[]=posts.id,gt,4');
  526. $test->expect('{"categories":{"columns":["id","name","icon"],"records":[[1,"announcement",null]]},"posts":{"relations":{"category_id":"categories.id"},"columns":["id","user_id","category_id","content"],"records":[[5,1,1,"#1"],[6,1,1,"#2"],[7,1,1,"#3"]]}}');
  527. }
  528. public function testFilterOnRelationOr()
  529. {
  530. $test = new API($this);
  531. $test->get('/categories?include=posts&filter[]=id,ge,1&filter[]=id,le,1&filter[]=posts.id,eq,5&filter[]=posts.id,eq,6&filter[]=posts.id,eq,7&satisfy=all,posts.any');
  532. $test->expect('{"categories":{"columns":["id","name","icon"],"records":[[1,"announcement",null]]},"posts":{"relations":{"category_id":"categories.id"},"columns":["id","user_id","category_id","content"],"records":[[5,1,1,"#1"],[6,1,1,"#2"],[7,1,1,"#3"]]}}');
  533. }
  534. public function testColumnsOnWrongInclude()
  535. {
  536. $test = new API($this);
  537. $test->get('/posts?include=categories&columns=categories&filter=id,eq,1&transform=1');
  538. $test->expect('{"posts":[{"category_id":1,"categories":[{"id":1}]}]}');
  539. }
  540. public function testColumnsOnImplicitJoin()
  541. {
  542. $test = new API($this);
  543. $test->get('/posts?include=tags&columns=posts.id,tags.name&filter=id,eq,1&transform=1');
  544. $test->expect('{"posts":[{"id":1,"post_tags":[{"post_id":1,"tag_id":1,"tags":[{"id":1,"name":"funny"}]},{"post_id":1,"tag_id":2,"tags":[{"id":2,"name":"important"}]}]}]}');
  545. }
  546. public function testSpatialFilterWithin()
  547. {
  548. if (PHP_CRUD_API_Config::$dbengine!='SQLite') {
  549. $test = new API($this);
  550. $test->get('/users?columns=id,username&filter=location,swi,POINT(30 20)');
  551. $test->expect('{"users":{"columns":["id","username"],"records":[[1,"user1"]]}}');
  552. }
  553. }
  554. public function testAddPostsWithNonExistingCategory()
  555. {
  556. $test = new API($this);
  557. $test->post('/posts','[{"user_id":1,"category_id":1,"content":"tests"},{"user_id":1,"category_id":15,"content":"tests"}]');
  558. $test->expect('null');
  559. $test->get('/posts?columns=content&filter=content,eq,tests');
  560. $test->expect('{"posts":{"columns":["content"],"records":[]}}');
  561. }
  562. public function testAddPosts()
  563. {
  564. $test = new API($this);
  565. $test->post('/posts','[{"user_id":1,"category_id":1,"content":"tests"},{"user_id":1,"category_id":1,"content":"tests"}]');
  566. $test->expectAny();
  567. $test->get('/posts?columns=content&filter=content,eq,tests');
  568. $test->expect('{"posts":{"columns":["content"],"records":[["tests"],["tests"]]}}');
  569. }
  570. public function testListEvents()
  571. {
  572. $test = new API($this);
  573. $test->get('/events?columns=datetime');
  574. $test->expect('{"events":{"columns":["datetime"],"records":[["2016-01-01 13:01:01.111"]]}}');
  575. }
  576. public function testIncrementEventVisitors()
  577. {
  578. $test = new API($this);
  579. $test->patch('/events/1','{"visitors":11}');
  580. $test->expect('1');
  581. $test->get('/events/1');
  582. $test->expect('{"id":1,"name":"Launch","datetime":"2016-01-01 13:01:01.111","visitors":11}');
  583. }
  584. public function testIncrementEventVisitorsWithZero()
  585. {
  586. $test = new API($this);
  587. $test->patch('/events/1','{"visitors":0}');
  588. $test->expect('1');
  589. $test->get('/events/1');
  590. $test->expect('{"id":1,"name":"Launch","datetime":"2016-01-01 13:01:01.111","visitors":11}');
  591. }
  592. public function testDecrementEventVisitors()
  593. {
  594. $test = new API($this);
  595. $test->patch('/events/1','{"visitors":-5}');
  596. $test->expect('1');
  597. $test->get('/events/1');
  598. $test->expect('{"id":1,"name":"Launch","datetime":"2016-01-01 13:01:01.111","visitors":6}');
  599. }
  600. public function testListTagUsage()
  601. {
  602. $test = new API($this);
  603. $test->get('/tag_usage');
  604. $test->expect('{"tag_usage":{"columns":["name","count"],"records":[["funny",2],["important",2]]}}');
  605. }
  606. public function testUpdateMultipleTags()
  607. {
  608. $test = new API($this);
  609. $test->get('/tags?transform=1');
  610. $test->expect('{"tags":[{"id":1,"name":"funny"},{"id":2,"name":"important"}]}');
  611. $test->put('/tags/1,2','[{"name":"funny"},{"name":"important"}]');
  612. $test->expect('[1,1]');
  613. }
  614. public function testUpdateMultipleTagsTooManyIds()
  615. {
  616. $test = new API($this);
  617. $test->put('/tags/1,2,3','[{"name":"funny!!!"},{"name":"important"}]');
  618. $test->expect(false,'Not found (subject)');
  619. $test->get('/tags?transform=1');
  620. $test->expect('{"tags":[{"id":1,"name":"funny"},{"id":2,"name":"important"}]}');
  621. }
  622. public function testUpdateMultipleTagsWithoutFields()
  623. {
  624. $test = new API($this);
  625. $test->put('/tags/1,2','[{"name":"funny!!!"},{}]');
  626. $test->expect('null');
  627. $test->get('/tags?transform=1');
  628. $test->expect('{"tags":[{"id":1,"name":"funny"},{"id":2,"name":"important"}]}');
  629. }
  630. public function testDeleteMultipleTags()
  631. {
  632. $test = new API($this);
  633. $test->post('/tags','[{"name":"extra"},{"name":"more"}]');
  634. $test->expect('[3,4]');
  635. $test->delete('/tags/3,4');
  636. $test->expect('[1,1]');
  637. $test->get('/tags?transform=1');
  638. $test->expect('{"tags":[{"id":1,"name":"funny"},{"id":2,"name":"important"}]}');
  639. }
  640. public function testListProducts()
  641. {
  642. $test = new API($this);
  643. $test->get('/products?transform=1');
  644. $test->expect('{"products":[{"id":1,"name":"Calculator","price":"23.01"}]}');
  645. }
  646. }