From b93df9f83b15d0c3b62018b945e56dab01817e0f Mon Sep 17 00:00:00 2001 From: proggeler Date: Thu, 19 Dec 2019 14:07:00 +0100 Subject: [PATCH] #98: SftpAdapter tries to create a missing directory when rename is called --- src/SftpAdapter.php | 7 ++ tests/SftpAdapterTests.php | 162 ++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 65 deletions(-) diff --git a/src/SftpAdapter.php b/src/SftpAdapter.php index 013e13e..150c5d4 100644 --- a/src/SftpAdapter.php +++ b/src/SftpAdapter.php @@ -518,6 +518,13 @@ public function rename($path, $newpath) { $connection = $this->getConnection(); + $dirname = dirname($newpath); + $metadata = $this->getMetadata($dirname); + + if ($metadata === false || $metadata['type'] !== 'dir') { + $this->createDir($dirname, new Config()); + } + return $connection->rename($path, $newpath); } diff --git a/tests/SftpAdapterTests.php b/tests/SftpAdapterTests.php index e6cf3f4..03d4d32 100644 --- a/tests/SftpAdapterTests.php +++ b/tests/SftpAdapterTests.php @@ -18,7 +18,10 @@ class SftpTests extends TestCase protected function setup() { - if (! defined('NET_SFTP_TYPE_DIRECTORY')) { + if (!defined('NET_SFTP_TYPE_REGULAR')) { + define('NET_SFTP_TYPE_REGULAR', 1); + } + if (!defined('NET_SFTP_TYPE_DIRECTORY')) { define('NET_SFTP_TYPE_DIRECTORY', 2); } } @@ -44,9 +47,9 @@ public function adapterProvider() public function testHas($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); @@ -96,9 +99,9 @@ public function testDelete($filesystem, $adapter, $mock) { $mock->shouldReceive('delete')->andReturn(true, false); $mock->shouldReceive('stat')->andReturn([ - 'type' => 1, - 'mtime' => time(), - 'size' => 20, + 'type' => 1, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $this->assertTrue($filesystem->delete('something')); @@ -112,9 +115,9 @@ public function testUpdate(FilesystemInterface $filesystem, $adapter, $mock) { $mock->shouldReceive('put')->andReturn(true, false); $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $this->assertTrue($filesystem->update('something', 'something')); @@ -129,9 +132,9 @@ public function testUpdateStream(FilesystemInterface $filesystem, $adapter, $moc $stream = tmpfile(); $mock->shouldReceive('put')->andReturn(true, false); $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $this->assertTrue($filesystem->updateStream('something', $stream)); @@ -145,9 +148,9 @@ public function testUpdateStream(FilesystemInterface $filesystem, $adapter, $moc public function testSetVisibility($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => 1, // file - 'mtime' => time(), - 'size' => 20, + 'type' => 1, // file + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $mock->shouldReceive('chmod')->twice()->andReturn(true, false); @@ -162,9 +165,9 @@ public function testSetVisibility($filesystem, $adapter, $mock) public function testSetVisibilityInvalid($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => 1, // file - 'mtime' => time(), - 'size' => 20, + 'type' => 1, // file + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $mock->shouldReceive('stat')->once()->andReturn(true); @@ -176,15 +179,44 @@ public function testSetVisibilityInvalid($filesystem, $adapter, $mock) */ public function testRename($filesystem, $adapter, $mock) { - $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + $mock->shouldReceive('stat')->with('old')->andReturn([ + 'type' => NET_SFTP_TYPE_REGULAR, // file + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, - ], false); + ]); + $mock->shouldReceive('stat')->with('old')->andReturn(false); + $mock->shouldReceive('stat')->with('.')->andReturn([ + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, + 'permissions' => 0777, + ]); + $mock->shouldReceive('rename')->andReturn(true); $result = $filesystem->rename('old', 'new'); $this->assertTrue($result); + $mock->shouldNotHaveReceived('mkdir'); + } + + /** + * @dataProvider adapterProvider + */ + public function testRenameCreatesDirectory($filesystem, $adapter, $mock) + { + $mock->shouldReceive('stat')->with('old_dir/file.ext')->andReturn([ + 'type' => NET_SFTP_TYPE_REGULAR, // file + 'mtime' => time(), + 'size' => 20, + 'permissions' => 0777, + ]); + $mock->shouldReceive('stat')->with('new_dir/file.ext')->andReturn(false); + $mock->shouldReceive('stat')->with('new_dir')->andReturn(false); + + $mock->shouldReceive('rename')->andReturn(true); + $result = $filesystem->rename('old_dir/file.ext', 'new_dir/file.ext'); + $this->assertTrue($result); + $mock->shouldHaveReceived('mkdir'); } /** @@ -203,19 +235,19 @@ public function testDeleteDir($filesystem, $adapter, $mock) public function testListContents($filesystem, $adapter, $mock) { $mock->shouldReceive('rawlist')->andReturn(false, [ - '.' => [], + '.' => [], 'dirname' => [ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ], ], [ - '..' => [], + '..' => [], 'dirname' => [ - 'type' => 1, - 'mtime' => time(), - 'size' => 20, + 'type' => 1, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ], ]); @@ -245,12 +277,12 @@ public function methodProvider() public function testMetaMethods($filesystem, $adapter, $mock, $method, $type) { $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); - $result = $filesystem->{$method}(uniqid().'object.ext'); + $result = $filesystem->{$method}(uniqid() . 'object.ext'); $this->assertInternalType($type, $result); } @@ -260,12 +292,12 @@ public function testMetaMethods($filesystem, $adapter, $mock, $method, $type) public function testGetVisibility($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); - $result = $adapter->getVisibility(uniqid().'object.ext'); + $result = $adapter->getVisibility(uniqid() . 'object.ext'); $this->assertInternalType('array', $result); $result = $result['visibility']; $this->assertInternalType('string', $result); @@ -278,9 +310,9 @@ public function testGetVisibility($filesystem, $adapter, $mock) public function testGetTimestamp($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => $time = time(), - 'size' => 20, + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => $time = time(), + 'size' => 20, 'permissions' => 0777, ]); $result = $adapter->getTimestamp('object.ext'); @@ -309,9 +341,9 @@ public function testCreateDir($filesystem, $adapter, $mock) public function testRead($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => 1, - 'mtime' => time(), - 'size' => 20, + 'type' => 1, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); $mock->shouldReceive('get')->andReturn('file contents', false); @@ -343,9 +375,9 @@ public function testReadStream($filesystem, $adapter, $mock) public function testGetMimetype($filesystem, $adapter, $mock) { $mock->shouldReceive('stat')->andReturn([ - 'type' => 1, - 'mtime' => time(), - 'size' => 20, + 'type' => 1, + 'mtime' => time(), + 'size' => 20, 'permissions' => 0777, ]); @@ -389,7 +421,7 @@ public function testAgentSetGet($filesystem, SftpAdapter $adapter, $mock) */ public function testPrivateKeyFileSetGet($filesystem, $adapter, $mock) { - file_put_contents($key = __DIR__.'/some.key', 'key contents'); + file_put_contents($key = __DIR__ . '/some.key', 'key contents'); $this->assertEquals($adapter, $adapter->setPrivateKey($key)); $this->assertInstanceOf('phpseclib\Crypt\RSA', $adapter->getPrivateKey()); @unlink($key); @@ -489,7 +521,7 @@ public function testConnectWithDoubleAuthentication($filesystem, $adapter, $mock $adapter->setNetSftpConnection($mock); $expectedAuths = [$adapter->getPrivateKey(), 'test']; - $mock->shouldReceive('login')->with('test', Mockery::on(function($auth) use (&$expectedAuths) { + $mock->shouldReceive('login')->with('test', Mockery::on(function ($auth) use (&$expectedAuths) { return $auth == array_shift($expectedAuths); }))->twice()->andReturn(false, true); @@ -574,17 +606,17 @@ public function testListContentsDir($filesystem, $adapter, $mock) [ 'dirname' => [ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), - 'permissions' => 0777, - 'filename' => 'dirname' + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), + 'permissions' => 0777, + 'filename' => 'dirname' ], 'filename' => [ - 'mtime' => time(), - 'size' => 20, - 'permissions' => 0777, - 'filename' => 'filename' + 'mtime' => time(), + 'size' => 20, + 'permissions' => 0777, + 'filename' => 'filename' ], ] ); @@ -607,7 +639,7 @@ public function testNetSftpConnectionSetter() $this->assertEquals($mock, $adapter->getConnection()); } - public function testHostFingerprintIsVerifiedIfProvided () + public function testHostFingerprintIsVerifiedIfProvided() { $adapter = new SftpAdapter([ 'host' => 'example.org', @@ -630,7 +662,7 @@ public function testHostFingerprintIsVerifiedIfProvided () $adapter->connect(); } - public function testHostFingerprintNotIsVerifiedIfNotProvided () + public function testHostFingerprintNotIsVerifiedIfNotProvided() { $adapter = new SftpAdapter([ 'host' => 'example.org', @@ -656,7 +688,7 @@ public function testHostFingerprintNotIsVerifiedIfNotProvided () * @expectedException LogicException * @expectedExceptionMessage The authenticity of host example.org can't be established. */ - public function testMisMatchingHostFingerprintAbortsLogin () + public function testMisMatchingHostFingerprintAbortsLogin() { $adapter = new SftpAdapter([ 'host' => 'example.org', @@ -721,10 +753,10 @@ public function testListContentsWithZeroNamedDir($filesystem, $adapter, $mock) [ '0' => [ - 'type' => NET_SFTP_TYPE_DIRECTORY, - 'mtime' => time(), + 'type' => NET_SFTP_TYPE_DIRECTORY, + 'mtime' => time(), 'permissions' => 0777, - 'filename' => '0' + 'filename' => '0' ] ] );