diff --git a/src/Storage/Device/S3.php b/src/Storage/Device/S3.php index cab37c82..36bc48b6 100644 --- a/src/Storage/Device/S3.php +++ b/src/Storage/Device/S3.php @@ -636,8 +636,8 @@ public function exists(string $path): bool $prefix = $root.'/'.ltrim($path, '/'); } - if (! empty($path) && ! str_ends_with($prefix, '/')) { - $prefix .= '/'; + if (! empty($path) && str_ends_with($path, '/')) { + $prefix = rtrim($prefix, '/').'/'; } $objects = $this->listObjects($prefix, 1); diff --git a/tests/Storage/S3Base.php b/tests/Storage/S3Base.php index 338537a3..ec70fe8c 100644 --- a/tests/Storage/S3Base.php +++ b/tests/Storage/S3Base.php @@ -165,6 +165,25 @@ public function testDirectoryExists() $this->assertEquals(false, $this->object->exists($this->object->getPath('nested/deep/structure'))); } + /** + * Test that file paths without trailing slash don't incorrectly match directories + * Verifies fix: exists('file.html') should not return true when directory 'file.html/' exists + */ + public function testFileExistsDoesNotMatchDirectoryPrefix() + { + $this->object->write($this->object->getPath('builds/index.html'), 'content', 'text/html'); + $this->object->write($this->object->getPath('builds/other.css'), 'body {}', 'text/css'); + + $this->assertEquals(true, $this->object->exists($this->object->getPath('builds/index.html'))); + + $this->object->delete($this->object->getPath('builds/index.html')); + + // File should not exist even though directory 'builds/' still contains other.css + $this->assertEquals(false, $this->object->exists($this->object->getPath('builds/index.html'))); + + $this->object->delete($this->object->getPath('builds/other.css')); + } + public function testMove() { $this->assertEquals(true, $this->object->write($this->object->getPath('text-for-move.txt'), 'Hello World', 'text/plain'));