Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/Halcyon/Datasource/Datasource.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@ public function forceDelete(string $dirName, string $fileName, string $extension
{
$this->forceDeleting = true;

$success = $this->delete($dirName, $fileName, $extension);

$this->forceDeleting = false;

return $success;
try {
return $this->delete($dirName, $fileName, $extension);
} finally {
// A delete that throws must not leave the datasource in force-deleting mode,
// or every later ordinary delete on this instance becomes a hard delete.
$this->forceDeleting = false;
}
}

/**
Expand Down
66 changes: 66 additions & 0 deletions tests/Halcyon/DatasourceForceDeleteTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

use Winter\Storm\Filesystem\Filesystem;
use Winter\Storm\Halcyon\Datasource\Datasource;
use Winter\Storm\Halcyon\Datasource\FileDatasource;

class DatasourceForceDeleteTest extends \Winter\Storm\Tests\TestCase
{
protected function getForceDeleting(Datasource $datasource): bool
{
return (new ReflectionProperty(Datasource::class, 'forceDeleting'))->getValue($datasource);
}

public function testForceDeleteResetsTheFlagWhenDeleteThrows()
{
$datasource = new class ('/tmp', new Filesystem) extends FileDatasource
{
public function delete(string $dirName, string $fileName, string $extension): bool
{
throw new RuntimeException('delete failed');
}
};

$caught = null;

try {
$datasource->forceDelete('pages', 'index', 'htm');
}
catch (RuntimeException $ex) {
$caught = $ex;
}

// Asserted outside the catch: PHPUnit's own assertion failures extend RuntimeException,
// so a fail() inside the try would be swallowed by the catch above.
$this->assertInstanceOf(RuntimeException::class, $caught, 'forceDelete() must propagate the failure');
$this->assertSame('delete failed', $caught->getMessage());

$this->assertFalse(
$this->getForceDeleting($datasource),
'A failed force delete must not leave the datasource in force-deleting mode, or every '
. 'later ordinary delete on this instance becomes a hard delete.'
);
}

public function testForceDeleteReturnsTheDeleteResultAndResetsTheFlag()
{
$datasource = new class ('/tmp', new Filesystem) extends FileDatasource
{
/**
* @var bool The flag value observed while delete() ran.
*/
public $flagDuringDelete = false;

public function delete(string $dirName, string $fileName, string $extension): bool
{
$this->flagDuringDelete = $this->forceDeleting;

return true;
}
};

$this->assertTrue($datasource->forceDelete('pages', 'index', 'htm'));
$this->assertTrue($datasource->flagDuringDelete, 'delete() should run in force-deleting mode');
$this->assertFalse($this->getForceDeleting($datasource));
}
}
Loading