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 13KB

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