CodingSqlTest.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /* Copyright (C) 2013 Laurent Destailleur <eldy@users.sourceforge.net>
  3. * Copyright (C) 2023 Alexandre Janniaux <alexandre.janniaux@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. * or see https://www.gnu.org/
  18. */
  19. /**
  20. * \file test/phpunit/CodingSqlTest.php
  21. * \ingroup test
  22. * \brief PHPUnit test
  23. * \remarks To run this script as CLI: phpunit filename.php
  24. */
  25. global $conf,$user,$langs,$db;
  26. //define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
  27. //require_once 'PHPUnit/Autoload.php';
  28. require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
  29. require_once dirname(__FILE__).'/../../htdocs/core/lib/security.lib.php';
  30. require_once dirname(__FILE__).'/../../htdocs/core/lib/security2.lib.php';
  31. if (! defined('NOREQUIREUSER')) {
  32. define('NOREQUIREUSER', '1');
  33. }
  34. if (! defined('NOREQUIREDB')) {
  35. define('NOREQUIREDB', '1');
  36. }
  37. if (! defined('NOREQUIRESOC')) {
  38. define('NOREQUIRESOC', '1');
  39. }
  40. if (! defined('NOREQUIRETRAN')) {
  41. define('NOREQUIRETRAN', '1');
  42. }
  43. if (! defined('NOCSRFCHECK')) {
  44. define('NOCSRFCHECK', '1');
  45. }
  46. if (! defined('NOTOKENRENEWAL')) {
  47. define('NOTOKENRENEWAL', '1');
  48. }
  49. if (! defined('NOREQUIREMENU')) {
  50. define('NOREQUIREMENU', '1'); // If there is no menu to show
  51. }
  52. if (! defined('NOREQUIREHTML')) {
  53. define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php
  54. }
  55. if (! defined('NOREQUIREAJAX')) {
  56. define('NOREQUIREAJAX', '1');
  57. }
  58. if (! defined("NOLOGIN")) {
  59. define("NOLOGIN", '1'); // If this page is public (can be called outside logged session)
  60. }
  61. if (empty($user->id)) {
  62. print "Load permissions for admin user nb 1\n";
  63. $user->fetch(1);
  64. $user->getrights();
  65. }
  66. $conf->global->MAIN_DISABLE_ALL_MAILS=1;
  67. /**
  68. * Class for PHPUnit tests
  69. *
  70. * @backupGlobals disabled
  71. * @backupStaticAttributes enabled
  72. * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
  73. */
  74. class CodingSqlTest extends PHPUnit\Framework\TestCase
  75. {
  76. protected $savconf;
  77. protected $savuser;
  78. protected $savlangs;
  79. protected $savdb;
  80. /**
  81. * Constructor
  82. * We save global variables into local variables
  83. *
  84. * @param string $name Name
  85. * @return SecurityTest
  86. */
  87. public function __construct($name = '')
  88. {
  89. parent::__construct($name);
  90. //$this->sharedFixture
  91. global $conf,$user,$langs,$db;
  92. $this->savconf=$conf;
  93. $this->savuser=$user;
  94. $this->savlangs=$langs;
  95. $this->savdb=$db;
  96. print __METHOD__." db->type=".$db->type." user->id=".$user->id;
  97. //print " - db ".$db->db;
  98. print "\n";
  99. }
  100. /**
  101. * setUpBeforeClass
  102. *
  103. * @return void
  104. */
  105. public static function setUpBeforeClass(): void
  106. {
  107. global $conf,$user,$langs,$db;
  108. $db->begin(); // This is to have all actions inside a transaction even if test launched without suite.
  109. print __METHOD__."\n";
  110. }
  111. /**
  112. * tearDownAfterClass
  113. *
  114. * @return void
  115. */
  116. public static function tearDownAfterClass(): void
  117. {
  118. global $conf,$user,$langs,$db;
  119. $db->rollback();
  120. print __METHOD__."\n";
  121. }
  122. /**
  123. * Init phpunit tests
  124. *
  125. * @return void
  126. */
  127. protected function setUp(): void
  128. {
  129. global $conf,$user,$langs,$db;
  130. $conf=$this->savconf;
  131. $user=$this->savuser;
  132. $langs=$this->savlangs;
  133. $db=$this->savdb;
  134. print __METHOD__."\n";
  135. }
  136. /**
  137. * End phpunit tests
  138. *
  139. * @return void
  140. */
  141. protected function tearDown(): void
  142. {
  143. print __METHOD__."\n";
  144. }
  145. /**
  146. * testEscape
  147. *
  148. * @return string
  149. */
  150. public function testEscape()
  151. {
  152. global $conf,$user,$langs,$db;
  153. $conf=$this->savconf;
  154. $user=$this->savuser;
  155. $langs=$this->savlangs;
  156. $db=$this->savdb;
  157. if ($db->type == 'mysqli') {
  158. $a = 'abc"\'def'; // string is abc"'def
  159. print $a;
  160. $result = $db->escape($a); // $result must be abc\"\'def
  161. $this->assertEquals('abc\"\\\'def', $result);
  162. }
  163. if ($db->type == 'pgsql') {
  164. $a = 'abc"\'def'; // string is abc"'def
  165. print $a;
  166. $result = $db->escape($a); // $result must be abc"''def
  167. $this->assertEquals('abc"\'\'def', $result);
  168. }
  169. }
  170. /**
  171. * testEscapeForLike
  172. *
  173. * @return string
  174. */
  175. public function testEscapeForLike()
  176. {
  177. global $conf,$user,$langs,$db;
  178. $conf=$this->savconf;
  179. $user=$this->savuser;
  180. $langs=$this->savlangs;
  181. $db=$this->savdb;
  182. $a = 'abc"\'def_ghi%klm\\nop';
  183. //print $a;
  184. $result = $db->escapeforlike($a); // $result must be abc"'def\_ghi\%klm\\nop with mysql
  185. $this->assertEquals('abc"\'def\_ghi\%klm\\\\nop', $result);
  186. }
  187. /**
  188. * testSql
  189. *
  190. * @return string
  191. */
  192. public function testSql()
  193. {
  194. global $conf,$user,$langs,$db;
  195. $conf=$this->savconf;
  196. $user=$this->savuser;
  197. $langs=$this->savlangs;
  198. $db=$this->savdb;
  199. $listofsqldir = array(DOL_DOCUMENT_ROOT.'/install/mysql/data', DOL_DOCUMENT_ROOT.'/install/mysql/tables', DOL_DOCUMENT_ROOT.'/install/mysql/migration');
  200. foreach ($listofsqldir as $dir) {
  201. print 'Process dir '.$dir."\n";
  202. $filesarray = scandir($dir);
  203. foreach ($filesarray as $key => $file) {
  204. if (! preg_match('/\.sql$/', $file)) {
  205. continue;
  206. }
  207. print 'Check sql file '.$file."\n";
  208. $filecontent = file_get_contents($dir.'/'.$file);
  209. // Allow ` for 'rank' column name
  210. $filecontent = str_replace('`rank`', '_rank_', $filecontent);
  211. $result=strpos($filecontent, '`');
  212. //print __METHOD__." Result for checking we don't have back quote = ".$result."\n";
  213. $this->assertTrue($result===false, 'Found back quote into '.$file.'. Bad.');
  214. $result=strpos($filecontent, '"');
  215. if ($result) {
  216. $result=(! strpos($filecontent, '["') && ! strpos($filecontent, '{"') && ! strpos($filecontent, '("'));
  217. }
  218. //print __METHOD__." Result for checking we don't have double quote = ".$result."\n";
  219. $this->assertTrue($result===false, 'Found double quote that is not [" neither {" (used for json content) neither (" (used for content with string like isModEnabled("")) into '.$file.'. Bad.');
  220. $result=strpos($filecontent, 'int(');
  221. //print __METHOD__." Result for checking we don't have 'int(' instead of 'integer' = ".$result."\n";
  222. $this->assertTrue($result===false, 'Found int(x) or tinyint(x) instead of integer or tinyint into '.$file.'. Bad.');
  223. $result=strpos($filecontent, 'ON DELETE CASCADE');
  224. //print __METHOD__." Result for checking we don't have 'ON DELETE CASCADE' = ".$result."\n";
  225. $this->assertTrue($result===false, 'Found ON DELETE CASCADE into '.$file.'. Bad.');
  226. $result=strpos($filecontent, 'NUMERIC(');
  227. //print __METHOD__." Result for checking we don't have 'NUMERIC(' = ".$result."\n";
  228. $this->assertTrue($result===false, 'Found NUMERIC( into '.$file.'. Bad.');
  229. $result=strpos($filecontent, 'NUMERIC(');
  230. //print __METHOD__." Result for checking we don't have 'curdate(' = ".$result."\n";
  231. $this->assertTrue($result===false, 'Found curdate( into '.$file.'. Bad. Current date must be generated with PHP.');
  232. $result=strpos($filecontent, 'integer(');
  233. //print __METHOD__." Result for checking we don't have 'integer(' = ".$result."\n";
  234. $this->assertTrue($result===false, 'Found value in parenthesis after the integer. It must be integer not integer(x) into '.$file.'. Bad.');
  235. $result=strpos($filecontent, 'timestamp,');
  236. //print __METHOD__." Result for checking we don't have 'NUMERIC(' = ".$result."\n";
  237. $this->assertTrue($result===false, 'Found type timestamp with option DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP after into '.$file.'. Bad.');
  238. if ($dir == DOL_DOCUMENT_ROOT.'/install/mysql/migration') {
  239. // Test for migration files only
  240. } elseif ($dir == DOL_DOCUMENT_ROOT.'/install/mysql/data') {
  241. // Test for data files only
  242. } else {
  243. if (preg_match('/\.key\.sql$/', $file)) {
  244. // Test for key files only
  245. } else {
  246. // Test for non key files only
  247. $result=(strpos($filecontent, 'KEY ') && strpos($filecontent, 'PRIMARY KEY') == 0);
  248. //print __METHOD__." Result for checking we don't have ' KEY ' instead of a sql file to create index = ".$result."\n";
  249. $this->assertTrue($result===false, 'Found KEY into '.$file.'. Bad.');
  250. $result=stripos($filecontent, 'ENGINE=innodb');
  251. //print __METHOD__." Result for checking we have the ENGINE=innodb string = ".$result."\n";
  252. $this->assertGreaterThan(0, $result, 'The ENGINE=innodb was not found into '.$file.'. Add it or just fix syntax to match case.');
  253. }
  254. }
  255. }
  256. }
  257. return;
  258. }
  259. /**
  260. * testInitData
  261. *
  262. * @return string
  263. */
  264. public function testInitData()
  265. {
  266. global $conf,$user,$langs,$db;
  267. $conf=$this->savconf;
  268. $user=$this->savuser;
  269. $langs=$this->savlangs;
  270. $db=$this->savdb;
  271. $filesarray = scandir(DOL_DOCUMENT_ROOT.'/../dev/initdemo');
  272. foreach ($filesarray as $key => $file) {
  273. if (! preg_match('/\.sql$/', $file)) {
  274. continue;
  275. }
  276. print 'Check sql file '.$file."\n";
  277. $filecontent=file_get_contents(DOL_DOCUMENT_ROOT.'/../dev/initdemo/'.$file);
  278. $result=strpos($filecontent, '@gmail.com');
  279. print __METHOD__." Result for checking we don't have personal data = ".$result."\n";
  280. $this->assertTrue($result===false, 'Found a bad key @gmail into file '.$file);
  281. $result=strpos($filecontent, 'eldy@');
  282. print __METHOD__." Result for checking we don't have personal data = ".$result."\n";
  283. $this->assertTrue($result===false, 'Found a bad key eldy@ into file '.$file);
  284. $result=strpos($filecontent, 'INSERT INTO `llx_oauth_token`');
  285. print __METHOD__." Result for checking we don't have data into llx_oauth_token = ".$result."\n";
  286. $this->assertTrue($result===false, 'Found a non expected insert into file '.$file);
  287. }
  288. return;
  289. }
  290. }