2017-06-01 10:56:34 -04:00
< ? php
/**
2024-05-10 09:09:14 -04:00
* SPDX - FileCopyrightText : 2018 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - or - later
2017-06-01 10:56:34 -04:00
*/
namespace Test\DB ;
2018-07-18 04:42:32 -04:00
use Doctrine\DBAL\Schema\Column ;
use Doctrine\DBAL\Schema\ForeignKeyConstraint ;
use Doctrine\DBAL\Schema\Index ;
2017-06-01 10:56:34 -04:00
use Doctrine\DBAL\Schema\Schema ;
2018-10-10 04:45:10 -04:00
use Doctrine\DBAL\Schema\SchemaException ;
2018-07-18 04:42:32 -04:00
use Doctrine\DBAL\Schema\Sequence ;
use Doctrine\DBAL\Schema\Table ;
2020-11-11 08:34:24 -05:00
use Doctrine\DBAL\Types\Type ;
2017-06-01 10:56:34 -04:00
use OC\DB\Connection ;
use OC\DB\MigrationService ;
2017-06-09 10:45:12 -04:00
use OC\DB\SchemaWrapper ;
2025-07-31 09:14:48 -04:00
use OCP\App\AppPathNotFoundException ;
2017-06-01 10:56:34 -04:00
use OCP\IDBConnection ;
2017-06-09 10:45:12 -04:00
use OCP\Migration\IMigrationStep ;
2025-03-19 16:03:11 -04:00
use PHPUnit\Framework\Attributes\DataProvider ;
2025-09-17 08:45:48 -04:00
use PHPUnit\Framework\Attributes\TestWith ;
2024-07-29 07:14:29 -04:00
use PHPUnit\Framework\MockObject\MockObject ;
2025-03-19 16:03:11 -04:00
use Psr\Log\LoggerInterface ;
2017-06-01 10:56:34 -04:00
/**
2025-03-19 16:03:11 -04:00
* Class MigrationServiceTest
2017-06-01 10:56:34 -04:00
*
* @ package Test\DB
*/
2025-03-19 16:03:11 -04:00
class MigrationServiceTest extends \Test\TestCase {
private Connection & MockObject $db ;
private MigrationService $migrationService ;
2017-06-01 10:56:34 -04:00
2019-11-27 09:27:18 -05:00
protected function setUp () : void {
2017-06-01 10:56:34 -04:00
parent :: setUp ();
$this -> db = $this -> createMock ( Connection :: class );
2025-03-19 16:03:11 -04:00
$this -> db
-> expects ( $this -> any ())
-> method ( 'getPrefix' )
-> willReturn ( 'test_oc_' );
2024-07-29 07:14:29 -04:00
2025-03-19 16:03:11 -04:00
$this -> migrationService = new MigrationService ( 'testing' , $this -> db );
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testGetters () : void {
2017-06-01 10:56:34 -04:00
$this -> assertEquals ( 'testing' , $this -> migrationService -> getApp ());
2017-06-09 10:45:12 -04:00
$this -> assertEquals ( \OC :: $SERVERROOT . '/apps/testing/lib/Migration' , $this -> migrationService -> getMigrationsDirectory ());
$this -> assertEquals ( 'OCA\Testing\Migration' , $this -> migrationService -> getMigrationsNamespace ());
2017-06-01 10:56:34 -04:00
$this -> assertEquals ( 'test_oc_migrations' , $this -> migrationService -> getMigrationsTableName ());
}
2024-09-15 16:32:31 -04:00
public function testCore () : void {
2025-03-19 16:03:11 -04:00
$migrationService = new MigrationService ( 'core' , $this -> db );
2017-06-01 10:56:34 -04:00
2025-03-19 16:03:11 -04:00
$this -> assertEquals ( 'core' , $migrationService -> getApp ());
$this -> assertEquals ( \OC :: $SERVERROOT . '/core/Migrations' , $migrationService -> getMigrationsDirectory ());
$this -> assertEquals ( 'OC\Core\Migrations' , $migrationService -> getMigrationsNamespace ());
$this -> assertEquals ( 'test_oc_migrations' , $migrationService -> getMigrationsTableName ());
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testExecuteUnknownStep () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
$this -> expectExceptionMessage ( 'Version 20170130180000 is unknown.' );
2017-06-01 10:56:34 -04:00
$this -> migrationService -> executeStep ( '20170130180000' );
}
2024-09-15 16:32:31 -04:00
public function testUnknownApp () : void {
2025-07-31 09:14:48 -04:00
$this -> expectException ( AppPathNotFoundException :: class );
$this -> expectExceptionMessage ( 'Could not find path for unknown_bloody_app' );
2019-11-27 09:27:18 -05:00
2025-03-19 16:03:11 -04:00
new MigrationService ( 'unknown_bloody_app' , $this -> db );
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testExecuteStepWithUnknownClass () : void {
2019-11-27 09:27:18 -05:00
$this -> expectException ( \Exception :: class );
$this -> expectExceptionMessage ( 'Migration step \'X\' is unknown' );
2025-03-19 16:03:11 -04:00
$migrationService = $this -> getMockBuilder ( MigrationService :: class )
2025-04-30 02:29:47 -04:00
-> onlyMethods ([ 'findMigrations' ])
2017-06-01 10:56:34 -04:00
-> setConstructorArgs ([ 'testing' , $this -> db ])
-> getMock ();
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ()) -> method ( 'findMigrations' ) -> willReturn (
2017-06-01 10:56:34 -04:00
[ '20170130180000' => 'X' , '20170130180001' => 'Y' , '20170130180002' => 'Z' , '20170130180003' => 'A' ]
);
2025-03-19 16:03:11 -04:00
$migrationService -> executeStep ( '20170130180000' );
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testExecuteStepWithSchemaChange () : void {
2017-06-01 10:56:34 -04:00
$schema = $this -> createMock ( Schema :: class );
2017-06-09 10:45:12 -04:00
$this -> db -> expects ( $this -> any ())
-> method ( 'createSchema' )
-> willReturn ( $schema );
$this -> db -> expects ( $this -> once ())
-> method ( 'migrateToSchema' );
2018-07-18 04:42:32 -04:00
$wrappedSchema = $this -> createMock ( Schema :: class );
2025-03-19 16:03:11 -04:00
$wrappedSchema -> expects ( $this -> atLeast ( 2 ))
2018-07-18 04:42:32 -04:00
-> method ( 'getTables' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$wrappedSchema -> expects ( $this -> atLeast ( 2 ))
2018-07-18 04:42:32 -04:00
-> method ( 'getSequences' )
2018-10-10 04:45:10 -04:00
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2017-06-09 10:45:12 -04:00
$schemaResult = $this -> createMock ( SchemaWrapper :: class );
$schemaResult -> expects ( $this -> once ())
-> method ( 'getWrappedSchema' )
2018-07-18 04:42:32 -04:00
-> willReturn ( $wrappedSchema );
2017-06-09 10:45:12 -04:00
$step = $this -> createMock ( IMigrationStep :: class );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'preSchemaChange' );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'changeSchema' )
-> willReturn ( $schemaResult );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'postSchemaChange' );
2017-06-01 10:56:34 -04:00
2025-03-19 16:03:11 -04:00
$migrationService = $this -> getMockBuilder ( MigrationService :: class )
2025-04-30 02:29:47 -04:00
-> onlyMethods ([ 'createInstance' ])
2017-06-01 10:56:34 -04:00
-> setConstructorArgs ([ 'testing' , $this -> db ])
-> getMock ();
2017-06-09 10:45:12 -04:00
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ())
2017-06-09 10:45:12 -04:00
-> method ( 'createInstance' )
-> with ( '20170130180000' )
-> willReturn ( $step );
2025-03-19 16:03:11 -04:00
$migrationService -> executeStep ( '20170130180000' );
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testExecuteStepWithoutSchemaChange () : void {
2017-06-09 10:45:12 -04:00
$schema = $this -> createMock ( Schema :: class );
$this -> db -> expects ( $this -> any ())
-> method ( 'createSchema' )
-> willReturn ( $schema );
$this -> db -> expects ( $this -> never ())
-> method ( 'migrateToSchema' );
$step = $this -> createMock ( IMigrationStep :: class );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'preSchemaChange' );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'changeSchema' )
-> willReturn ( null );
2022-06-20 04:53:06 -04:00
$step -> expects ( $this -> once ())
2017-06-09 10:45:12 -04:00
-> method ( 'postSchemaChange' );
2017-06-01 10:56:34 -04:00
2025-03-19 16:03:11 -04:00
$migrationService = $this -> getMockBuilder ( MigrationService :: class )
2025-04-30 02:29:47 -04:00
-> onlyMethods ([ 'createInstance' ])
2017-06-01 10:56:34 -04:00
-> setConstructorArgs ([ 'testing' , $this -> db ])
-> getMock ();
2017-06-09 10:45:12 -04:00
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ())
2017-06-09 10:45:12 -04:00
-> method ( 'createInstance' )
-> with ( '20170130180000' )
-> willReturn ( $step );
2025-03-19 16:03:11 -04:00
$migrationService -> executeStep ( '20170130180000' );
2017-06-01 10:56:34 -04:00
}
2025-05-13 04:10:13 -04:00
public static function dataGetMigration () : array {
2017-06-09 10:45:12 -04:00
return [
[ 'current' , '20170130180001' ],
[ 'prev' , '20170130180000' ],
[ 'next' , '20170130180002' ],
[ 'latest' , '20170130180003' ],
];
}
/**
* @ param string $alias
* @ param string $expected
*/
2025-06-30 10:56:59 -04:00
#[\PHPUnit\Framework\Attributes\DataProvider('dataGetMigration')]
2024-09-15 16:32:31 -04:00
public function testGetMigration ( $alias , $expected ) : void {
2025-03-19 16:03:11 -04:00
$migrationService = $this -> getMockBuilder ( MigrationService :: class )
2025-04-30 02:29:47 -04:00
-> onlyMethods ([ 'getMigratedVersions' , 'findMigrations' ])
2017-06-01 10:56:34 -04:00
-> setConstructorArgs ([ 'testing' , $this -> db ])
-> getMock ();
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ()) -> method ( 'getMigratedVersions' ) -> willReturn (
2017-06-01 10:56:34 -04:00
[ '20170130180000' , '20170130180001' ]
);
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ()) -> method ( 'findMigrations' ) -> willReturn (
2017-06-01 10:56:34 -04:00
[ '20170130180000' => 'X' , '20170130180001' => 'Y' , '20170130180002' => 'Z' , '20170130180003' => 'A' ]
);
$this -> assertEquals (
[ '20170130180000' , '20170130180001' , '20170130180002' , '20170130180003' ],
2025-03-19 16:03:11 -04:00
$migrationService -> getAvailableVersions ());
2017-06-01 10:56:34 -04:00
2025-03-19 16:03:11 -04:00
$migration = $migrationService -> getMigration ( $alias );
2017-06-09 10:45:12 -04:00
$this -> assertEquals ( $expected , $migration );
2017-06-01 10:56:34 -04:00
}
2024-09-15 16:32:31 -04:00
public function testMigrate () : void {
2025-03-19 16:03:11 -04:00
$migrationService = $this -> getMockBuilder ( MigrationService :: class )
2025-04-30 02:29:47 -04:00
-> onlyMethods ([ 'getMigratedVersions' , 'findMigrations' , 'executeStep' ])
2017-06-01 10:56:34 -04:00
-> setConstructorArgs ([ 'testing' , $this -> db ])
-> getMock ();
2025-03-19 16:03:11 -04:00
$migrationService -> expects ( $this -> any ()) -> method ( 'getMigratedVersions' ) -> willReturn (
[ '20170130180000' , '20170130180001' ]
);
$migrationService -> expects ( $this -> any ()) -> method ( 'findMigrations' ) -> willReturn (
[ '20170130180000' => 'X' , '20170130180001' => 'Y' , '20170130180002' => 'Z' , '20170130180003' => 'A' ]
);
2017-06-01 10:56:34 -04:00
$this -> assertEquals (
[ '20170130180000' , '20170130180001' , '20170130180002' , '20170130180003' ],
2025-03-19 16:03:11 -04:00
$migrationService -> getAvailableVersions ());
2017-06-01 10:56:34 -04:00
2025-03-19 16:03:11 -04:00
$calls = [];
$migrationService
-> expects ( $this -> exactly ( 2 ))
2025-04-30 02:29:47 -04:00
-> method ( 'executeStep' )
2025-03-19 16:03:11 -04:00
-> willReturnCallback ( function ( string $migration ) use ( & $calls ) {
$calls [] = $migration ;
2025-04-30 02:29:47 -04:00
});
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$migrationService -> migrate ();
self :: assertEquals ([ '20170130180002' , '20170130180003' ], $calls );
}
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
#[DataProvider('dataEnsureNamingConstraintsTableName')]
public function testEnsureNamingConstraintsTableName ( string $name , int $prefixLength , bool $tableExists , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
}
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
2018-10-10 04:45:10 -04:00
$table -> expects ( $this -> atLeastOnce ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $name );
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getColumns' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getIndexes' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getForeignKeys' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getSequences' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
2025-03-19 16:03:11 -04:00
-> willReturnCallback ( fn () => match ( $tableExists ) {
false => throw new SchemaException (),
true => $table ,
});
2018-10-10 04:45:10 -04:00
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , $prefixLength );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
public static function dataEnsureNamingConstraintsTableName () : array {
return [
'valid name' => [
\str_repeat ( 'x' , 60 ), // table name
3 , // prefix length
false , // has this table
false , // throws
],
'valid name - long prefix' => [
\str_repeat ( 'x' , 55 ),
8 ,
false ,
false ,
],
'too long but not a new table' => [
\str_repeat ( 'x' , 61 ),
3 ,
true ,
false ,
],
'too long' => [
\str_repeat ( 'x' , 61 ),
3 ,
false ,
true ,
],
'too long with prefix' => [
\str_repeat ( 'x' , 60 ),
4 ,
false ,
true ,
],
];
}
#[DataProvider('dataEnsureNamingConstraintsPrimaryDefaultKey')]
public function testEnsureNamingConstraintsPrimaryDefaultKey ( string $tableName , int $prefixLength , string $platform , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
}
$this -> db -> expects ( self :: atLeastOnce ())
-> method ( 'getDatabaseProvider' )
-> willReturn ( $platform );
$defaultName = match ( $platform ) {
IDBConnection :: PLATFORM_POSTGRES => $tableName . '_pkey' ,
IDBConnection :: PLATFORM_ORACLE => $tableName . '_seq' ,
default => 'PRIMARY' ,
};
2018-07-18 04:42:32 -04:00
$index = $this -> createMock ( Index :: class );
2018-07-19 04:28:52 -04:00
$index -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $defaultName );
$index -> expects ( $this -> any ())
-> method ( 'getColumns' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
2018-07-19 04:28:52 -04:00
$table -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $tableName );
2018-07-19 04:28:52 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
-> method ( 'getIndexes' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
-> method ( 'getForeignKeys' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
-> method ( 'getPrimaryKey' )
-> willReturn ( $index );
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( $this -> atMost ( 1 ))
2018-07-19 04:28:52 -04:00
-> method ( 'getSequences' )
-> willReturn ([]);
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , $prefixLength );
2018-07-19 04:28:52 -04:00
}
2025-03-19 16:03:11 -04:00
public static function dataEnsureNamingConstraintsPrimaryDefaultKey () : array {
foreach ([ IDBConnection :: PLATFORM_MYSQL , IDBConnection :: PLATFORM_ORACLE , IDBConnection :: PLATFORM_POSTGRES , IDBConnection :: PLATFORM_SQLITE ] as $engine ) {
$testcases [ " $engine valid " ] = [
str_repeat ( 'x' , 55 ),
3 ,
$engine ,
false ,
];
$testcases [ " $engine too long " ] = [
str_repeat ( 'x' , 56 ), // 56 (name) + 3 (prefix) + 5 ('_pkey')= 64 > 63
3 ,
$engine ,
true ,
];
$testcases [ " $engine too long prefix " ] = [
str_repeat ( 'x' , 55 ),
4 ,
$engine ,
true ,
];
}
return $testcases ;
}
#[DataProvider('dataEnsureNamingConstraintsPrimaryCustomKey')]
public function testEnsureNamingConstraintsPrimaryCustomKey ( string $name , int $prefixLength , bool $newIndex , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
2018-07-19 04:28:52 -04:00
}
$index = $this -> createMock ( Index :: class );
$index -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $name );
2018-07-19 04:28:52 -04:00
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getColumns' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getIndexes' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getForeignKeys' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( $this -> atLeastOnce ())
2018-07-18 04:42:32 -04:00
-> method ( 'getPrimaryKey' )
-> willReturn ( $index );
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([]);
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
2025-03-19 16:03:11 -04:00
-> willReturnCallback ( fn () => match ( $newIndex ) {
true => throw new SchemaException (),
false => $table ,
});
2018-10-10 04:45:10 -04:00
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , $prefixLength );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
public static function dataEnsureNamingConstraintsPrimaryCustomKey () : array {
return [
'valid name' => [
str_repeat ( 'x' , 60 ),
3 ,
true ,
false ,
],
'valid name - prefix does not matter' => [
str_repeat ( 'x' , 63 ),
3 ,
true ,
false ,
],
'invalid name - but not new' => [
str_repeat ( 'x' , 64 ),
3 ,
false ,
false ,
],
'too long name' => [
str_repeat ( 'x' , 64 ),
3 ,
true ,
true ,
],
];
}
2019-12-05 08:38:28 -05:00
2025-03-19 16:03:11 -04:00
#[DataProvider('dataEnsureNamingConstraints')]
public function testEnsureNamingConstraintsColumnName ( string $name , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
}
$column = $this -> createMock ( Column :: class );
$column -> expects ( self :: atLeastOnce ())
-> method ( 'getName' )
-> willReturn ( $name );
2019-11-27 09:27:18 -05:00
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'valid' );
$table -> expects ( self :: once ())
-> method ( 'getColumns' )
-> willReturn ([ $column ]);
$table -> expects ( self :: atMost ( 1 ))
-> method ( 'getIndexes' )
-> willReturn ([]);
$table -> expects ( self :: atMost ( 1 ))
-> method ( 'getForeignKeys' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
2025-03-19 16:03:11 -04:00
$schema -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( self :: once ())
-> method ( 'getSequences' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
2025-03-19 16:03:11 -04:00
$sourceSchema -> expects ( self :: any ())
2018-10-10 04:45:10 -04:00
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
2025-03-19 16:03:11 -04:00
$sourceSchema -> expects ( self :: any ())
2018-10-10 04:45:10 -04:00
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , 3 );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
#[DataProvider('dataEnsureNamingConstraints')]
public function testEnsureNamingConstraintsIndexName ( string $name , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
2018-07-19 04:28:52 -04:00
}
2018-07-18 04:42:32 -04:00
$index = $this -> createMock ( Index :: class );
2025-03-19 16:03:11 -04:00
$index -> expects ( self :: atLeastOnce ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $name );
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'valid' );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: atMost ( 1 ))
2018-07-18 04:42:32 -04:00
-> method ( 'getColumns' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getIndexes' )
2025-03-19 16:03:11 -04:00
-> willReturn ([ $index ]);
$table -> expects ( self :: atMost ( 1 ))
2018-07-18 04:42:32 -04:00
-> method ( 'getForeignKeys' )
-> willReturn ([]);
$schema = $this -> createMock ( Schema :: class );
2025-03-19 16:03:11 -04:00
$schema -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( self :: once ())
-> method ( 'getSequences' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
2025-03-19 16:03:11 -04:00
$sourceSchema -> expects ( self :: any ())
2018-10-10 04:45:10 -04:00
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
2025-03-19 16:03:11 -04:00
$sourceSchema -> expects ( self :: any ())
2018-10-10 04:45:10 -04:00
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , 3 );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
#[DataProvider('dataEnsureNamingConstraints')]
public function testEnsureNamingConstraintsForeignKeyName ( string $name , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
}
2019-12-05 08:38:28 -05:00
2025-03-19 16:03:11 -04:00
$foreignKey = $this -> createMock ( ForeignKeyConstraint :: class );
$foreignKey -> expects ( self :: any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $name );
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'valid' );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getColumns' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getIndexes' )
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$table -> expects ( self :: once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getForeignKeys' )
2025-03-19 16:03:11 -04:00
-> willReturn ([ $foreignKey ]);
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( self :: once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
$schema -> expects ( self :: once ())
-> method ( 'getSequences' )
2018-07-18 04:42:32 -04:00
-> willReturn ([]);
2025-03-19 16:03:11 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( self :: any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( self :: any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , 3 );
}
#[DataProvider('dataEnsureNamingConstraints')]
public function testEnsureNamingConstraintsSequenceName ( string $name , bool $throws ) : void {
if ( $throws ) {
$this -> expectException ( \InvalidArgumentException :: class );
}
$sequence = $this -> createMock ( Sequence :: class );
$sequence -> expects ( $this -> any ())
-> method ( 'getName' )
-> willReturn ( $name );
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([ $sequence ]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureNamingConstraints ( $sourceSchema , $schema , 3 );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
public static function dataEnsureNamingConstraints () : array {
return [
'valid length' => [ \str_repeat ( 'x' , 63 ), false ],
'too long' => [ \str_repeat ( 'x' , 64 ), true ],
];
}
2019-12-05 08:38:28 -05:00
2025-03-19 16:03:11 -04:00
public function testEnsureOracleConstraintsValid () : void {
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> atLeastOnce ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$primaryKey = $this -> createMock ( Index :: class );
$primaryKey -> expects ( $this -> once ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'primary_key' );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$column = $this -> createMock ( Column :: class );
2018-07-18 04:42:32 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([ $column ]);
2025-03-19 16:03:11 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getPrimaryKey' )
-> willReturn ( $primaryKey );
2018-07-18 04:42:32 -04:00
2025-03-19 16:03:11 -04:00
$sequence = $this -> createMock ( Sequence :: class );
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([ $sequence ]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
public function testEnsureOracleConstraintsValidWithPrimaryKey () : void {
2018-07-18 04:42:32 -04:00
$index = $this -> createMock ( Index :: class );
$index -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( \str_repeat ( 'a' , 30 ));
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( \str_repeat ( 'a' , 26 ));
2018-07-18 04:42:32 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
2025-03-19 16:03:11 -04:00
-> method ( 'getPrimaryKey' )
-> willReturn ( $index );
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
public function testEnsureOracleConstraintsValidWithPrimaryKeyDefault () : void {
$defaultName = 'PRIMARY' ;
if ( $this -> db -> getDatabaseProvider () === IDBConnection :: PLATFORM_POSTGRES ) {
$defaultName = \str_repeat ( 'a' , 26 ) . '_' . \str_repeat ( 'b' , 30 ) . '_seq' ;
} elseif ( $this -> db -> getDatabaseProvider () === IDBConnection :: PLATFORM_ORACLE ) {
$defaultName = \str_repeat ( 'a' , 26 ) . '_seq' ;
}
2019-12-05 08:38:28 -05:00
2025-03-19 16:03:11 -04:00
$index = $this -> createMock ( Index :: class );
$index -> expects ( $this -> any ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( $defaultName );
$index -> expects ( $this -> any ())
-> method ( 'getColumns' )
-> willReturn ([ \str_repeat ( 'b' , 30 )]);
2018-07-18 04:42:32 -04:00
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( \str_repeat ( 'a' , 25 ));
2018-07-18 04:42:32 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
2025-03-19 16:03:11 -04:00
-> method ( 'getPrimaryKey' )
-> willReturn ( $index );
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-03-19 16:03:11 -04:00
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2022-03-10 08:04:04 -05:00
}
2024-09-15 16:32:31 -04:00
public function testEnsureOracleConstraintsNoPrimaryKey () : void {
2022-03-22 12:07:48 -04:00
$this -> markTestSkipped ( 'Test disabled for now due to multiple reasons, see https://github.com/nextcloud/server/pull/31580#issuecomment-1069182234 for details.' );
2022-03-10 08:04:04 -05:00
$this -> expectException ( \InvalidArgumentException :: class );
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> atLeastOnce ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
2022-03-10 08:04:04 -05:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
-> method ( 'getPrimaryKey' )
-> willReturn ( null );
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
-> willReturn ([]);
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2018-07-18 04:42:32 -04:00
}
2025-03-19 16:03:11 -04:00
/**
* Alternative for testEnsureOracleConstraintsNoPrimaryKey until we enforce it .
*/
public function testEnsureOracleConstraintsNoPrimaryKeyLogging () : void {
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> atLeastOnce ())
2018-07-18 04:42:32 -04:00
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([]);
$table -> expects ( $this -> once ())
-> method ( 'getPrimaryKey' )
-> willReturn ( null );
2018-07-18 04:42:32 -04:00
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
2025-03-19 16:03:11 -04:00
-> willReturn ([ $table ]);
2018-07-18 04:42:32 -04:00
$schema -> expects ( $this -> once ())
-> method ( 'getSequences' )
2025-03-19 16:03:11 -04:00
-> willReturn ([]);
2018-07-18 04:42:32 -04:00
2018-10-10 04:45:10 -04:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$logger = $this -> createMock ( LoggerInterface :: class );
$logger -> expects ( self :: once ())
-> method ( 'error' );
$this -> overwriteService ( LoggerInterface :: class , $logger );
2020-11-11 08:34:24 -05:00
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
}
2020-11-11 08:34:24 -05:00
2025-09-17 08:45:48 -04:00
#[TestWith([true])]
#[TestWith([false])]
public function testEnsureOracleConstraintsBooleanNotNull ( bool $isOracle ) : void {
2025-10-10 10:09:18 -04:00
$this -> db -> method ( 'getDatabaseProvider' )
-> willReturn ( $isOracle ? IDBConnection :: PLATFORM_ORACLE : IDBConnection :: PLATFORM_MARIADB );
2020-11-11 08:34:24 -05:00
$column = $this -> createMock ( Column :: class );
$column -> expects ( $this -> any ())
-> method ( 'getName' )
-> willReturn ( 'aaaa' );
$column -> expects ( $this -> any ())
-> method ( 'getType' )
-> willReturn ( Type :: getType ( 'boolean' ));
$column -> expects ( $this -> any ())
-> method ( 'getNotnull' )
-> willReturn ( true );
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
2025-09-26 11:07:26 -04:00
$table -> method ( 'getIndexes' ) -> willReturn ([]);
$table -> method ( 'getForeignKeys' ) -> willReturn ([]);
2020-11-11 08:34:24 -05:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([ $column ]);
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
2025-09-26 11:07:26 -04:00
$schema -> method ( 'getSequences' ) -> willReturn ([]);
2020-11-11 08:34:24 -05:00
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-09-17 08:45:48 -04:00
if ( $isOracle ) {
$column -> expects ( $this -> once ())
-> method ( 'setNotnull' )
-> with ( false );
} else {
$column -> expects ( $this -> never ())
-> method ( 'setNotnull' );
}
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2020-11-11 08:34:24 -05:00
}
2022-03-23 10:04:18 -04:00
2024-09-15 16:32:31 -04:00
public function testEnsureOracleConstraintsStringLength4000 () : void {
2022-03-23 10:04:18 -04:00
$this -> expectException ( \InvalidArgumentException :: class );
$column = $this -> createMock ( Column :: class );
$column -> expects ( $this -> any ())
-> method ( 'getName' )
-> willReturn ( 'aaaa' );
$column -> expects ( $this -> any ())
-> method ( 'getType' )
-> willReturn ( Type :: getType ( 'string' ));
$column -> expects ( $this -> any ())
-> method ( 'getLength' )
-> willReturn ( 4001 );
$table = $this -> createMock ( Table :: class );
$table -> expects ( $this -> any ())
-> method ( 'getName' )
2025-03-19 16:03:11 -04:00
-> willReturn ( 'tablename' );
2022-03-23 10:04:18 -04:00
$table -> expects ( $this -> once ())
-> method ( 'getColumns' )
-> willReturn ([ $column ]);
$schema = $this -> createMock ( Schema :: class );
$schema -> expects ( $this -> once ())
-> method ( 'getTables' )
-> willReturn ([ $table ]);
$sourceSchema = $this -> createMock ( Schema :: class );
$sourceSchema -> expects ( $this -> any ())
-> method ( 'getTable' )
-> willThrowException ( new SchemaException ());
$sourceSchema -> expects ( $this -> any ())
-> method ( 'hasSequence' )
-> willReturn ( false );
2025-03-19 16:03:11 -04:00
$this -> migrationService -> ensureOracleConstraints ( $sourceSchema , $schema );
2024-07-29 07:14:29 -04:00
}
2017-06-01 10:56:34 -04:00
}