MigrationsTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <?php
  2. /**
  3. * Copyright (c) 2016 Thomas Müller <thomas.mueller@tmit.eu>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace Test\DB;
  9. use Doctrine\DBAL\Platforms\OraclePlatform;
  10. use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
  11. use Doctrine\DBAL\Schema\Column;
  12. use Doctrine\DBAL\Schema\ForeignKeyConstraint;
  13. use Doctrine\DBAL\Schema\Index;
  14. use Doctrine\DBAL\Schema\Schema;
  15. use Doctrine\DBAL\Schema\Sequence;
  16. use Doctrine\DBAL\Schema\Table;
  17. use OC\DB\Connection;
  18. use OC\DB\MigrationService;
  19. use OC\DB\SchemaWrapper;
  20. use OCP\IDBConnection;
  21. use OCP\Migration\IMigrationStep;
  22. /**
  23. * Class MigrationsTest
  24. *
  25. * @package Test\DB
  26. */
  27. class MigrationsTest extends \Test\TestCase {
  28. /** @var MigrationService | \PHPUnit_Framework_MockObject_MockObject */
  29. private $migrationService;
  30. /** @var \PHPUnit_Framework_MockObject_MockObject | IDBConnection $db */
  31. private $db;
  32. public function setUp() {
  33. parent::setUp();
  34. $this->db = $this->createMock(Connection::class);
  35. $this->db->expects($this->any())->method('getPrefix')->willReturn('test_oc_');
  36. $this->migrationService = new MigrationService('testing', $this->db);
  37. }
  38. public function testGetters() {
  39. $this->assertEquals('testing', $this->migrationService->getApp());
  40. $this->assertEquals(\OC::$SERVERROOT . '/apps/testing/lib/Migration', $this->migrationService->getMigrationsDirectory());
  41. $this->assertEquals('OCA\Testing\Migration', $this->migrationService->getMigrationsNamespace());
  42. $this->assertEquals('test_oc_migrations', $this->migrationService->getMigrationsTableName());
  43. }
  44. public function testCore() {
  45. $this->migrationService = new MigrationService('core', $this->db);
  46. $this->assertEquals('core', $this->migrationService->getApp());
  47. $this->assertEquals(\OC::$SERVERROOT . '/core/Migrations', $this->migrationService->getMigrationsDirectory());
  48. $this->assertEquals('OC\Core\Migrations', $this->migrationService->getMigrationsNamespace());
  49. $this->assertEquals('test_oc_migrations', $this->migrationService->getMigrationsTableName());
  50. }
  51. /**
  52. * @expectedException \InvalidArgumentException
  53. * @expectedExceptionMessage Version 20170130180000 is unknown.
  54. */
  55. public function testExecuteUnknownStep() {
  56. $this->migrationService->executeStep('20170130180000');
  57. }
  58. /**
  59. * @expectedException \Exception
  60. * @expectedExceptionMessage App not found
  61. */
  62. public function testUnknownApp() {
  63. $migrationService = new MigrationService('unknown-bloody-app', $this->db);
  64. }
  65. /**
  66. * @expectedException \Exception
  67. * @expectedExceptionMessage Migration step 'X' is unknown
  68. */
  69. public function testExecuteStepWithUnknownClass() {
  70. $this->migrationService = $this->getMockBuilder(MigrationService::class)
  71. ->setMethods(['findMigrations'])
  72. ->setConstructorArgs(['testing', $this->db])
  73. ->getMock();
  74. $this->migrationService->expects($this->any())->method('findMigrations')->willReturn(
  75. ['20170130180000' => 'X', '20170130180001' => 'Y', '20170130180002' => 'Z', '20170130180003' => 'A']
  76. );
  77. $this->migrationService->executeStep('20170130180000');
  78. }
  79. public function testExecuteStepWithSchemaChange() {
  80. $schema = $this->createMock(Schema::class);
  81. $this->db->expects($this->any())
  82. ->method('createSchema')
  83. ->willReturn($schema);
  84. $this->db->expects($this->once())
  85. ->method('migrateToSchema');
  86. $wrappedSchema = $this->createMock(Schema::class);
  87. // TODO re-enable once stable14 is branched of: https://github.com/nextcloud/server/issues/10518
  88. /*$wrappedSchema->expects($this->once())
  89. ->method('getTables')
  90. ->willReturn([]);
  91. $wrappedSchema->expects($this->once())
  92. ->method('getSequences')
  93. ->willReturn([]);*/
  94. $schemaResult = $this->createMock(SchemaWrapper::class);
  95. $schemaResult->expects($this->once())
  96. ->method('getWrappedSchema')
  97. ->willReturn($wrappedSchema);
  98. $step = $this->createMock(IMigrationStep::class);
  99. $step->expects($this->at(0))
  100. ->method('preSchemaChange');
  101. $step->expects($this->at(1))
  102. ->method('changeSchema')
  103. ->willReturn($schemaResult);
  104. $step->expects($this->at(2))
  105. ->method('postSchemaChange');
  106. $this->migrationService = $this->getMockBuilder(MigrationService::class)
  107. ->setMethods(['createInstance'])
  108. ->setConstructorArgs(['testing', $this->db])
  109. ->getMock();
  110. $this->migrationService->expects($this->any())
  111. ->method('createInstance')
  112. ->with('20170130180000')
  113. ->willReturn($step);
  114. $this->migrationService->executeStep('20170130180000');
  115. }
  116. public function testExecuteStepWithoutSchemaChange() {
  117. $schema = $this->createMock(Schema::class);
  118. $this->db->expects($this->any())
  119. ->method('createSchema')
  120. ->willReturn($schema);
  121. $this->db->expects($this->never())
  122. ->method('migrateToSchema');
  123. $step = $this->createMock(IMigrationStep::class);
  124. $step->expects($this->at(0))
  125. ->method('preSchemaChange');
  126. $step->expects($this->at(1))
  127. ->method('changeSchema')
  128. ->willReturn(null);
  129. $step->expects($this->at(2))
  130. ->method('postSchemaChange');
  131. $this->migrationService = $this->getMockBuilder(MigrationService::class)
  132. ->setMethods(['createInstance'])
  133. ->setConstructorArgs(['testing', $this->db])
  134. ->getMock();
  135. $this->migrationService->expects($this->any())
  136. ->method('createInstance')
  137. ->with('20170130180000')
  138. ->willReturn($step);
  139. $this->migrationService->executeStep('20170130180000');
  140. }
  141. public function dataGetMigration() {
  142. return [
  143. ['current', '20170130180001'],
  144. ['prev', '20170130180000'],
  145. ['next', '20170130180002'],
  146. ['latest', '20170130180003'],
  147. ];
  148. }
  149. /**
  150. * @dataProvider dataGetMigration
  151. * @param string $alias
  152. * @param string $expected
  153. */
  154. public function testGetMigration($alias, $expected) {
  155. $this->migrationService = $this->getMockBuilder(MigrationService::class)
  156. ->setMethods(['getMigratedVersions', 'findMigrations'])
  157. ->setConstructorArgs(['testing', $this->db])
  158. ->getMock();
  159. $this->migrationService->expects($this->any())->method('getMigratedVersions')->willReturn(
  160. ['20170130180000', '20170130180001']
  161. );
  162. $this->migrationService->expects($this->any())->method('findMigrations')->willReturn(
  163. ['20170130180000' => 'X', '20170130180001' => 'Y', '20170130180002' => 'Z', '20170130180003' => 'A']
  164. );
  165. $this->assertEquals(
  166. ['20170130180000', '20170130180001', '20170130180002', '20170130180003'],
  167. $this->migrationService->getAvailableVersions());
  168. $migration = $this->migrationService->getMigration($alias);
  169. $this->assertEquals($expected, $migration);
  170. }
  171. public function testMigrate() {
  172. $this->migrationService = $this->getMockBuilder(MigrationService::class)
  173. ->setMethods(['getMigratedVersions', 'findMigrations', 'executeStep'])
  174. ->setConstructorArgs(['testing', $this->db])
  175. ->getMock();
  176. $this->migrationService->expects($this->any())->method('getMigratedVersions')->willReturn(
  177. ['20170130180000', '20170130180001']
  178. );
  179. $this->migrationService->expects($this->any())->method('findMigrations')->willReturn(
  180. ['20170130180000' => 'X', '20170130180001' => 'Y', '20170130180002' => 'Z', '20170130180003' => 'A']
  181. );
  182. $this->assertEquals(
  183. ['20170130180000', '20170130180001', '20170130180002', '20170130180003'],
  184. $this->migrationService->getAvailableVersions());
  185. $this->migrationService->expects($this->exactly(2))->method('executeStep')
  186. ->withConsecutive(['20170130180002'], ['20170130180003']);
  187. $this->migrationService->migrate();
  188. }
  189. public function testEnsureOracleIdentifierLengthLimitValid() {
  190. $column = $this->createMock(Column::class);
  191. $column->expects($this->once())
  192. ->method('getName')
  193. ->willReturn(\str_repeat('a', 30));
  194. $index = $this->createMock(Index::class);
  195. $index->expects($this->once())
  196. ->method('getName')
  197. ->willReturn(\str_repeat('a', 30));
  198. $foreignKey = $this->createMock(ForeignKeyConstraint::class);
  199. $foreignKey->expects($this->once())
  200. ->method('getName')
  201. ->willReturn(\str_repeat('a', 30));
  202. $table = $this->createMock(Table::class);
  203. $table->expects($this->once())
  204. ->method('getName')
  205. ->willReturn(\str_repeat('a', 30));
  206. $sequence = $this->createMock(Sequence::class);
  207. $sequence->expects($this->once())
  208. ->method('getName')
  209. ->willReturn(\str_repeat('a', 30));
  210. $table->expects($this->once())
  211. ->method('getColumns')
  212. ->willReturn([$column]);
  213. $table->expects($this->once())
  214. ->method('getIndexes')
  215. ->willReturn([$index]);
  216. $table->expects($this->once())
  217. ->method('getForeignKeys')
  218. ->willReturn([$foreignKey]);
  219. $table->expects($this->once())
  220. ->method('getPrimaryKey')
  221. ->willReturn(null);
  222. $schema = $this->createMock(Schema::class);
  223. $schema->expects($this->once())
  224. ->method('getTables')
  225. ->willReturn([$table]);
  226. $schema->expects($this->once())
  227. ->method('getSequences')
  228. ->willReturn([$sequence]);
  229. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  230. }
  231. public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKey() {
  232. $index = $this->createMock(Index::class);
  233. $index->expects($this->any())
  234. ->method('getName')
  235. ->willReturn(\str_repeat('a', 30));
  236. $table = $this->createMock(Table::class);
  237. $table->expects($this->any())
  238. ->method('getName')
  239. ->willReturn(\str_repeat('a', 26));
  240. $table->expects($this->once())
  241. ->method('getColumns')
  242. ->willReturn([]);
  243. $table->expects($this->once())
  244. ->method('getIndexes')
  245. ->willReturn([]);
  246. $table->expects($this->once())
  247. ->method('getForeignKeys')
  248. ->willReturn([]);
  249. $table->expects($this->once())
  250. ->method('getPrimaryKey')
  251. ->willReturn($index);
  252. $schema = $this->createMock(Schema::class);
  253. $schema->expects($this->once())
  254. ->method('getTables')
  255. ->willReturn([$table]);
  256. $schema->expects($this->once())
  257. ->method('getSequences')
  258. ->willReturn([]);
  259. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  260. }
  261. public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKeyDefault() {
  262. $defaultName = 'PRIMARY';
  263. if ($this->db->getDatabasePlatform() instanceof PostgreSqlPlatform) {
  264. $defaultName = \str_repeat('a', 26) . '_' . \str_repeat('b', 30) . '_seq';
  265. } else if ($this->db->getDatabasePlatform() instanceof OraclePlatform) {
  266. $defaultName = \str_repeat('a', 26) . '_seq';
  267. }
  268. $index = $this->createMock(Index::class);
  269. $index->expects($this->any())
  270. ->method('getName')
  271. ->willReturn($defaultName);
  272. $index->expects($this->any())
  273. ->method('getColumns')
  274. ->willReturn([\str_repeat('b', 30)]);
  275. $table = $this->createMock(Table::class);
  276. $table->expects($this->any())
  277. ->method('getName')
  278. ->willReturn(\str_repeat('a', 26));
  279. $table->expects($this->once())
  280. ->method('getColumns')
  281. ->willReturn([]);
  282. $table->expects($this->once())
  283. ->method('getIndexes')
  284. ->willReturn([]);
  285. $table->expects($this->once())
  286. ->method('getForeignKeys')
  287. ->willReturn([]);
  288. $table->expects($this->once())
  289. ->method('getPrimaryKey')
  290. ->willReturn($index);
  291. $schema = $this->createMock(Schema::class);
  292. $schema->expects($this->once())
  293. ->method('getTables')
  294. ->willReturn([$table]);
  295. $schema->expects($this->once())
  296. ->method('getSequences')
  297. ->willReturn([]);
  298. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  299. }
  300. /**
  301. * @expectedException \InvalidArgumentException
  302. */
  303. public function testEnsureOracleIdentifierLengthLimitTooLongTableName() {
  304. $table = $this->createMock(Table::class);
  305. $table->expects($this->any())
  306. ->method('getName')
  307. ->willReturn(\str_repeat('a', 31));
  308. $schema = $this->createMock(Schema::class);
  309. $schema->expects($this->once())
  310. ->method('getTables')
  311. ->willReturn([$table]);
  312. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  313. }
  314. /**
  315. * @expectedException \InvalidArgumentException
  316. */
  317. public function testEnsureOracleIdentifierLengthLimitTooLongPrimaryWithDefault() {
  318. $defaultName = 'PRIMARY';
  319. if ($this->db->getDatabasePlatform() instanceof PostgreSqlPlatform) {
  320. $defaultName = \str_repeat('a', 27) . '_' . \str_repeat('b', 30) . '_seq';
  321. } else if ($this->db->getDatabasePlatform() instanceof OraclePlatform) {
  322. $defaultName = \str_repeat('a', 27) . '_seq';
  323. }
  324. $index = $this->createMock(Index::class);
  325. $index->expects($this->any())
  326. ->method('getName')
  327. ->willReturn($defaultName);
  328. $index->expects($this->any())
  329. ->method('getColumns')
  330. ->willReturn([\str_repeat('b', 30)]);
  331. $table = $this->createMock(Table::class);
  332. $table->expects($this->any())
  333. ->method('getName')
  334. ->willReturn(\str_repeat('a', 27));
  335. $table->expects($this->once())
  336. ->method('getColumns')
  337. ->willReturn([]);
  338. $table->expects($this->once())
  339. ->method('getIndexes')
  340. ->willReturn([]);
  341. $table->expects($this->once())
  342. ->method('getForeignKeys')
  343. ->willReturn([]);
  344. $table->expects($this->once())
  345. ->method('getPrimaryKey')
  346. ->willReturn($index);
  347. $schema = $this->createMock(Schema::class);
  348. $schema->expects($this->once())
  349. ->method('getTables')
  350. ->willReturn([$table]);
  351. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  352. }
  353. /**
  354. * @expectedException \InvalidArgumentException
  355. */
  356. public function testEnsureOracleIdentifierLengthLimitTooLongPrimaryWithName() {
  357. $index = $this->createMock(Index::class);
  358. $index->expects($this->any())
  359. ->method('getName')
  360. ->willReturn(\str_repeat('a', 31));
  361. $table = $this->createMock(Table::class);
  362. $table->expects($this->any())
  363. ->method('getName')
  364. ->willReturn(\str_repeat('a', 26));
  365. $table->expects($this->once())
  366. ->method('getColumns')
  367. ->willReturn([]);
  368. $table->expects($this->once())
  369. ->method('getIndexes')
  370. ->willReturn([]);
  371. $table->expects($this->once())
  372. ->method('getForeignKeys')
  373. ->willReturn([]);
  374. $table->expects($this->once())
  375. ->method('getPrimaryKey')
  376. ->willReturn($index);
  377. $schema = $this->createMock(Schema::class);
  378. $schema->expects($this->once())
  379. ->method('getTables')
  380. ->willReturn([$table]);
  381. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  382. }
  383. /**
  384. * @expectedException \InvalidArgumentException
  385. */
  386. public function testEnsureOracleIdentifierLengthLimitTooLongColumnName() {
  387. $column = $this->createMock(Column::class);
  388. $column->expects($this->any())
  389. ->method('getName')
  390. ->willReturn(\str_repeat('a', 31));
  391. $table = $this->createMock(Table::class);
  392. $table->expects($this->any())
  393. ->method('getName')
  394. ->willReturn(\str_repeat('a', 30));
  395. $table->expects($this->once())
  396. ->method('getColumns')
  397. ->willReturn([$column]);
  398. $schema = $this->createMock(Schema::class);
  399. $schema->expects($this->once())
  400. ->method('getTables')
  401. ->willReturn([$table]);
  402. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  403. }
  404. /**
  405. * @expectedException \InvalidArgumentException
  406. */
  407. public function testEnsureOracleIdentifierLengthLimitTooLongIndexName() {
  408. $index = $this->createMock(Index::class);
  409. $index->expects($this->any())
  410. ->method('getName')
  411. ->willReturn(\str_repeat('a', 31));
  412. $table = $this->createMock(Table::class);
  413. $table->expects($this->any())
  414. ->method('getName')
  415. ->willReturn(\str_repeat('a', 30));
  416. $table->expects($this->once())
  417. ->method('getColumns')
  418. ->willReturn([]);
  419. $table->expects($this->once())
  420. ->method('getIndexes')
  421. ->willReturn([$index]);
  422. $schema = $this->createMock(Schema::class);
  423. $schema->expects($this->once())
  424. ->method('getTables')
  425. ->willReturn([$table]);
  426. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  427. }
  428. /**
  429. * @expectedException \InvalidArgumentException
  430. */
  431. public function testEnsureOracleIdentifierLengthLimitTooLongForeignKeyName() {
  432. $foreignKey = $this->createMock(ForeignKeyConstraint::class);
  433. $foreignKey->expects($this->any())
  434. ->method('getName')
  435. ->willReturn(\str_repeat('a', 31));
  436. $table = $this->createMock(Table::class);
  437. $table->expects($this->any())
  438. ->method('getName')
  439. ->willReturn(\str_repeat('a', 30));
  440. $table->expects($this->once())
  441. ->method('getColumns')
  442. ->willReturn([]);
  443. $table->expects($this->once())
  444. ->method('getIndexes')
  445. ->willReturn([]);
  446. $table->expects($this->once())
  447. ->method('getForeignKeys')
  448. ->willReturn([$foreignKey]);
  449. $schema = $this->createMock(Schema::class);
  450. $schema->expects($this->once())
  451. ->method('getTables')
  452. ->willReturn([$table]);
  453. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  454. }
  455. /**
  456. * @expectedException \InvalidArgumentException
  457. */
  458. public function testEnsureOracleIdentifierLengthLimitTooLongSequenceName() {
  459. $sequence = $this->createMock(Sequence::class);
  460. $sequence->expects($this->any())
  461. ->method('getName')
  462. ->willReturn(\str_repeat('a', 31));
  463. $schema = $this->createMock(Schema::class);
  464. $schema->expects($this->once())
  465. ->method('getTables')
  466. ->willReturn([]);
  467. $schema->expects($this->once())
  468. ->method('getSequences')
  469. ->willReturn([$sequence]);
  470. self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
  471. }
  472. }