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
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

tests.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. $data = 'data://text/plain;base64,'.base64_encode($data);
  21. $this->api = new PHP_CRUD_API(array(
  22. 'dbengine'=>MySQL_CRUD_API_Config::$dbengine,
  23. 'hostname'=>MySQL_CRUD_API_Config::$hostname,
  24. 'username'=>MySQL_CRUD_API_Config::$username,
  25. 'password'=>MySQL_CRUD_API_Config::$password,
  26. 'database'=>MySQL_CRUD_API_Config::$database,
  27. // callbacks
  28. 'table_authorizer'=>function($action,$database,$table) { return true; },
  29. 'column_authorizer'=>function($action,$database,$table,$column) { return !($column=='password'&&$action=='list'); },
  30. 'record_filter'=>function($action,$database,$table) { return ($table=='posts')?array('id,ne,13'):false; },
  31. 'tenancy_function'=>function($action,$database,$table,$column) { return ($table=='users'&&$column=='id')?1:null; },
  32. 'input_sanitizer'=>function($action,$database,$table,$column,$type,$value) { return $value===null?null:strip_tags($value); },
  33. 'input_validator'=>function($action,$database,$table,$column,$type,$value,$context) { return ($column=='category_id' && !is_numeric($value))?'must be numeric':true; },
  34. // for tests
  35. 'method' =>$method,
  36. 'request' =>$url['path'],
  37. 'post'=>$data,
  38. 'get' =>$get,
  39. ));
  40. return $this;
  41. }
  42. public function get($url)
  43. {
  44. return $this->action('GET',$url);
  45. }
  46. public function post($url,$data)
  47. {
  48. return $this->action('POST',$url,$data);
  49. }
  50. public function put($url,$data)
  51. {
  52. return $this->action('PUT',$url,$data);
  53. }
  54. public function delete($url)
  55. {
  56. return $this->action('DELETE',$url);
  57. }
  58. public function options($url)
  59. {
  60. return $this->action('OPTIONS',$url);
  61. }
  62. public function expect($output,$error=false)
  63. {
  64. $exception = false;
  65. ob_start();
  66. try {
  67. $this->api->executeCommand();
  68. } catch (\Exception $e) {
  69. $exception = $e->getMessage();
  70. }
  71. $data = ob_get_contents();
  72. ob_end_clean();
  73. if ($exception) $this->test->assertEquals($error, $exception);
  74. else $this->test->assertEquals($output, $data);
  75. return $this;
  76. }
  77. }
  78. class MySQL_CRUD_API_Test extends PHPUnit_Framework_TestCase
  79. {
  80. public static function setUpBeforeClass()
  81. {
  82. if (MySQL_CRUD_API_Config::$database=='{{test_database}}') {
  83. die("Configure database in 'config.php' before running tests.\n");
  84. }
  85. $dbengine = MySQL_CRUD_API_Config::$dbengine;
  86. $hostname = MySQL_CRUD_API_Config::$hostname;
  87. $username = MySQL_CRUD_API_Config::$username;
  88. $password = MySQL_CRUD_API_Config::$password;
  89. $database = MySQL_CRUD_API_Config::$database;
  90. $fixture = __DIR__.'/blog_'.strtolower($dbengine).'.sql';
  91. if ($dbengine == 'MySQL') {
  92. $link = mysqli_connect($hostname, $username, $password, $database);
  93. if (mysqli_connect_errno()) {
  94. die("Connect failed: ".mysqli_connect_error()."\n");
  95. }
  96. mysqli_set_charset($link,'utf8');
  97. $i=0;
  98. if (mysqli_multi_query($link, file_get_contents($fixture))) {
  99. do { $i++; mysqli_next_result($link); } while (mysqli_more_results($link));
  100. }
  101. if (mysqli_errno($link)) {
  102. die("Loading '$fixture' failed on statemement #$i with error:\n".mysqli_error($link)."\n");
  103. }
  104. mysqli_close($link);
  105. } elseif ($dbengine == 'SQLServer') {
  106. $connectionInfo = array();
  107. $connectionInfo['UID']=$username;
  108. $connectionInfo['PWD']=$password;
  109. $connectionInfo['Database']=$database;
  110. $connectionInfo['CharacterSet']='UTF-8';
  111. $conn = sqlsrv_connect( $hostname, $connectionInfo);
  112. if (!$conn) {
  113. die("Connect failed: ".print_r( sqlsrv_errors(), true));
  114. }
  115. $queries = preg_split('/\n\s*GO\s*\n/', file_get_contents($fixture));
  116. array_pop($queries);
  117. foreach ($queries as $i=>$query) {
  118. if (!sqlsrv_query($conn, $query)) {
  119. $i++;
  120. die("Loading '$fixture' failed on statemement #$i with error:\n".print_r( sqlsrv_errors(), true)."\n");
  121. }
  122. }
  123. sqlsrv_close($conn);
  124. } elseif ($dbengine == 'PostgreSQL') {
  125. $e = function ($v) { return str_replace(array('\'','\\'),array('\\\'','\\\\'),$v); };
  126. $hostname = $e($hostname);
  127. $database = $e($database);
  128. $username = $e($username);
  129. $password = $e($password);
  130. $conn_string = "host='$hostname' dbname='$database' user='$username' password='$password' options='--client_encoding=UTF8'";
  131. $db = pg_connect($conn_string);
  132. if (!$db) {
  133. die("Connect failed: ".print_r( sqlsrv_errors(), true));
  134. }
  135. $queries = preg_split('/;\s*\n/', file_get_contents($fixture));
  136. array_pop($queries);
  137. foreach ($queries as $i=>$query) {
  138. if (!pg_query($db, $query.';')) {
  139. $i++;
  140. die("Loading '$fixture' failed on statemement #$i with error:\n".print_r( pg_last_error($db), true)."\n");
  141. }
  142. }
  143. pg_close($db);
  144. }
  145. }
  146. public function testListPosts()
  147. {
  148. $test = new API($this);
  149. $test->get('/posts');
  150. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[["1","1","1","blog started"],["2","1","2","It works!"]]}}');
  151. }
  152. public function testListPostColumns()
  153. {
  154. $test = new API($this);
  155. $test->get('/posts?columns=id,content');
  156. $test->expect('{"posts":{"columns":["id","content"],"records":[["1","blog started"],["2","It works!"]]}}');
  157. }
  158. public function testListPostsWithTransform()
  159. {
  160. $test = new API($this);
  161. $test->get('/posts?transform=1');
  162. $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!"}]}');
  163. }
  164. public function testReadPost()
  165. {
  166. $test = new API($this);
  167. $test->get('/posts/2');
  168. $test->expect('{"id":"2","user_id":"1","category_id":"2","content":"It works!"}');
  169. }
  170. public function testReadPostColumns()
  171. {
  172. $test = new API($this);
  173. $test->get('/posts/2?columns=id,content');
  174. $test->expect('{"id":"2","content":"It works!"}');
  175. }
  176. public function testAddPost()
  177. {
  178. $test = new API($this);
  179. $test->post('/posts','{"user_id":"1","category_id":"1","content":"test"}');
  180. $test->expect('3');
  181. }
  182. public function testEditPost()
  183. {
  184. $test = new API($this);
  185. $test->put('/posts/3','{"user_id":"1","category_id":"1","content":"test (edited)"}');
  186. $test->expect('1');
  187. $test->get('/posts/3');
  188. $test->expect('{"id":"3","user_id":"1","category_id":"1","content":"test (edited)"}');
  189. }
  190. public function testEditPostColumnsMissingField()
  191. {
  192. $test = new API($this);
  193. $test->put('/posts/3?columns=id,content','{"content":"test (edited 2)"}');
  194. $test->expect('1');
  195. $test->get('/posts/3');
  196. $test->expect('{"id":"3","user_id":"1","category_id":"1","content":"test (edited 2)"}');
  197. }
  198. public function testEditPostColumnsExtraField()
  199. {
  200. $test = new API($this);
  201. $test->put('/posts/3?columns=id,content','{"user_id":"2","content":"test (edited 3)"}');
  202. $test->expect('1');
  203. $test->get('/posts/3');
  204. $test->expect('{"id":"3","user_id":"1","category_id":"1","content":"test (edited 3)"}');
  205. }
  206. public function testEditPostWithUtf8Content()
  207. {
  208. $utf8 = json_encode('Hello world, Καλημέρα κόσμε, コンニチハ');
  209. $test = new API($this);
  210. $test->put('/posts/2','{"content":'.$utf8.'}');
  211. $test->expect('1');
  212. $test->get('/posts/2');
  213. $test->expect('{"id":"2","user_id":"1","category_id":"2","content":'.$utf8.'}');
  214. }
  215. public function testEditPostWithUtf8ContentWithPost()
  216. {
  217. $utf8 = '€ Hello world, Καλημέρα κόσμε, コンニチハ';
  218. $url_encoded = urlencode($utf8);
  219. $json_encoded = json_encode($utf8);
  220. $test = new API($this);
  221. $test->put('/posts/2','content='.$url_encoded);
  222. $test->expect('1');
  223. $test->get('/posts/2');
  224. $test->expect('{"id":"2","user_id":"1","category_id":"2","content":'.$json_encoded.'}');
  225. }
  226. public function testDeletePost()
  227. {
  228. $test = new API($this);
  229. $test->delete('/posts/3');
  230. $test->expect('1');
  231. $test->get('/posts/3');
  232. $test->expect(false,'Not found (object)');
  233. }
  234. public function testAddPostWithPost()
  235. {
  236. $test = new API($this);
  237. $test->post('/posts','user_id=1&category_id=1&content=test');
  238. $test->expect('4');
  239. }
  240. public function testEditPostWithPost()
  241. {
  242. $test = new API($this);
  243. $test->put('/posts/4','user_id=1&category_id=1&content=test+(edited)');
  244. $test->expect('1');
  245. $test->get('/posts/4');
  246. $test->expect('{"id":"4","user_id":"1","category_id":"1","content":"test (edited)"}');
  247. }
  248. public function testDeletePostWithPost()
  249. {
  250. $test = new API($this);
  251. $test->delete('/posts/4');
  252. $test->expect('1');
  253. $test->get('/posts/4');
  254. $test->expect(false,'Not found (object)');
  255. }
  256. public function testListWithPaginate()
  257. {
  258. $test = new API($this);
  259. for ($i=1;$i<=10;$i++) {
  260. $test->post('/posts','{"user_id":"1","category_id":"1","content":"#'.$i.'"}');
  261. $test->expect(4+$i);
  262. }
  263. $test->get('/posts?page=2,2&order=id');
  264. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[["5","1","1","#1"],["6","1","1","#2"]],"results":11}}');
  265. }
  266. public function testListWithPaginateLastPage()
  267. {
  268. $test = new API($this);
  269. $test->get('/posts?page=3,5&order=id');
  270. $test->expect('{"posts":{"columns":["id","user_id","category_id","content"],"records":[["14","1","1","#10"]],"results":11}}');
  271. }
  272. public function testListExampleFromReadme()
  273. {
  274. $test = new API($this);
  275. $test->get('/posts,categories,tags,comments?filter=id,eq,1');
  276. $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","anouncement",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"]]}}');
  277. }
  278. public function testListExampleFromReadmeWithTransform()
  279. {
  280. $test = new API($this);
  281. $test->get('/posts,categories,tags,comments?filter=id,eq,1&transform=1');
  282. $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":"anouncement","icon":null}],"content":"blog started"}]}');
  283. }
  284. public function testEditCategoryWithBinaryContent()
  285. {
  286. $binary = base64_encode("\0abc\0\n\r\b\0");
  287. $base64url = rtrim(strtr($binary, '+/', '-_'), '=');
  288. $test = new API($this);
  289. $test->put('/categories/2','{"icon":"'.$base64url.'"}');
  290. $test->expect('1');
  291. $test->get('/categories/2');
  292. $test->expect('{"id":"2","name":"article","icon":"'.$binary.'"}');
  293. }
  294. public function testEditCategoryWithNull()
  295. {
  296. $test = new API($this);
  297. $test->put('/categories/2','{"icon":null}');
  298. $test->expect('1');
  299. $test->get('/categories/2');
  300. $test->expect('{"id":"2","name":"article","icon":null}');
  301. }
  302. public function testEditCategoryWithBinaryContentWithPost()
  303. {
  304. $binary = base64_encode("€ \0abc\0\n\r\b\0");
  305. $base64url = rtrim(strtr($binary, '+/', '-_'), '=');
  306. $test = new API($this);
  307. $test->put('/categories/2','icon='.$base64url);
  308. $test->expect('1');
  309. $test->get('/categories/2');
  310. $test->expect('{"id":"2","name":"article","icon":"'.$binary.'"}');
  311. }
  312. public function testEditCategoryWithNullWithPost()
  313. {
  314. $test = new API($this);
  315. $test->put('/categories/2','icon__is_null');
  316. $test->expect('1');
  317. $test->get('/categories/2');
  318. $test->expect('{"id":"2","name":"article","icon":null}');
  319. }
  320. public function testAddPostFailure()
  321. {
  322. $test = new API($this);
  323. $test->post('/posts','{"user_id":"a","category_id":"1","content":"tests"}');
  324. $test->expect('null');
  325. }
  326. public function testOptionsRequest()
  327. {
  328. $test = new API($this);
  329. $test->options('/posts/2');
  330. $test->expect('["Access-Control-Allow-Headers: Content-Type","Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST, DELETE","Access-Control-Max-Age: 1728000"]',false);
  331. }
  332. public function testHidingPasswordColumn()
  333. {
  334. $test = new API($this);
  335. $test->get('/users?filter=id,eq,1&transform=1');
  336. $test->expect('{"users":[{"id":"1","username":"user1"}]}');
  337. }
  338. public function testValidatorErrorMessage()
  339. {
  340. $test = new API($this);
  341. $test->put('/posts/1','{"category_id":"a"}');
  342. $test->expect(false,'{"category_id":"must be numeric"}');
  343. }
  344. public function testSanitizerToStripTags()
  345. {
  346. $test = new API($this);
  347. $test->put('/categories/2','{"name":"<script>alert();</script>"}');
  348. $test->expect('1');
  349. $test->get('/categories/2');
  350. $test->expect('{"id":"2","name":"alert();","icon":null}');
  351. }
  352. public function testErrorOnInvalidJson()
  353. {
  354. $test = new API($this);
  355. $test->post('/posts','{"}');
  356. $test->expect(false,'Not found (input)');
  357. }
  358. public function testErrorOnDuplicatePrimaryKey()
  359. {
  360. $test = new API($this);
  361. $test->post('/posts','{"id":"1","user_id":"1","category_id":"1","content":"blog started (duplicate)"}');
  362. $test->expect('null');
  363. }
  364. public function testErrorOnFailingForeignKeyConstraint()
  365. {
  366. $test = new API($this);
  367. $test->post('/posts','{"user_id":"3","category_id":"1","content":"fk constraint"}');
  368. $test->expect('null');
  369. }
  370. public function testForJsonpCallbackOnList()
  371. {
  372. $test = new API($this);
  373. $test->get('/posts?filter[]=id,eq,1&callback=test_jsonp_fn&transform=1');
  374. $test->expect('test_jsonp_fn({"posts":[{"id":"1","user_id":"1","category_id":"1","content":"blog started"}]});');
  375. }
  376. public function testMissingIntermediateTable()
  377. {
  378. $test = new API($this);
  379. $test->get('/users,posts,tags');
  380. $test->expect('{"users":{"columns":["id","username"],"records":[["1","user1"]]},"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"]]}}');
  381. }
  382. public function testEditUser()
  383. {
  384. $test = new API($this);
  385. $test->put('/users/1','{"password":"testtest"}');
  386. $test->expect('1');
  387. }
  388. public function testEditUserWithId()
  389. {
  390. $test = new API($this);
  391. $test->put('/users/1','{"id":"2","password":"testtest2"}');
  392. $test->expect('1');
  393. $test->get('/users/1');
  394. $test->expect('{"id":"1","username":"user1","password":"testtest2"}');
  395. }
  396. public function testReadOtherUser()
  397. {
  398. $test = new API($this);
  399. $test->get('/users/2');
  400. $test->expect(false,'Not found (object)');
  401. }
  402. public function testEditOtherUser()
  403. {
  404. $test = new API($this);
  405. $test->put('/users/2','{"password":"testtest"}');
  406. $test->expect('0');
  407. }
  408. }