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
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }