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
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tests.php 27KB

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