Skip to content

Commit 1e6fa72

Browse files
committed
fix: Comb search index delete() rewriting JSON when ref absent
1 parent 6abb6d6 commit 1e6fa72

2 files changed

Lines changed: 92 additions & 2 deletions

File tree

src/Search/Comb/Index.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,13 @@ public function delete($document)
9494
return;
9595
}
9696

97-
$data->forget($document->getSearchReference());
97+
$ref = $document->getSearchReference();
98+
99+
if (! $data->has($ref)) {
100+
return;
101+
}
102+
103+
$data->forget($ref);
98104

99105
$this->save($data);
100106
}

tests/Search/CombIndexTest.php

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@
44

55
use Illuminate\Contracts\Filesystem\Filesystem;
66
use Mockery;
7+
use PHPUnit\Framework\Attributes\Test;
8+
use Statamic\Contracts\Search\Searchable;
79
use Statamic\Search\Comb\Index;
810
use Tests\TestCase;
911

1012
class CombIndexTest extends TestCase
1113
{
12-
use IndexTests;
14+
use IndexTests {
15+
tearDown as protected indexTestsTearDown;
16+
}
1317

1418
private $fs;
1519

20+
private ?string $tmpDir = null;
21+
1622
public function setUp(): void
1723
{
1824
parent::setUp();
@@ -22,6 +28,18 @@ public function setUp(): void
2228
$this->instance('filesystem', $this->fs);
2329
}
2430

31+
public function tearDown(): void
32+
{
33+
if ($this->tmpDir && is_dir($this->tmpDir)) {
34+
foreach (glob($this->tmpDir.'/*') as $file) {
35+
@unlink($file);
36+
}
37+
@rmdir($this->tmpDir);
38+
}
39+
40+
$this->indexTestsTearDown();
41+
}
42+
2543
protected function beforeSearched()
2644
{
2745
$this->fs
@@ -39,4 +57,70 @@ public function getIndexClass()
3957
{
4058
return Index::class;
4159
}
60+
61+
#[Test]
62+
public function delete_does_not_rewrite_the_index_when_the_reference_is_absent()
63+
{
64+
$path = $this->createIndexFile(['entry::a' => ['title' => 'Foo']]);
65+
66+
$oldMtime = time() - 3600;
67+
touch($path, $oldMtime);
68+
clearstatcache();
69+
70+
$index = $this->getIndex('test', ['path' => $this->tmpDir], null);
71+
72+
$document = Mockery::mock(Searchable::class);
73+
$document->shouldReceive('getSearchReference')->andReturn('entry::missing');
74+
75+
$index->delete($document);
76+
77+
clearstatcache();
78+
$this->assertSame(
79+
$oldMtime,
80+
filemtime($path),
81+
'Comb\Index::delete() rewrote the index file even though the reference was not present.'
82+
);
83+
}
84+
85+
#[Test]
86+
public function delete_rewrites_the_index_when_the_reference_is_present()
87+
{
88+
$path = $this->createIndexFile([
89+
'entry::a' => ['title' => 'Foo'],
90+
'entry::b' => ['title' => 'Bar'],
91+
]);
92+
93+
$oldMtime = time() - 3600;
94+
touch($path, $oldMtime);
95+
clearstatcache();
96+
97+
$index = $this->getIndex('test', ['path' => $this->tmpDir], null);
98+
99+
$document = Mockery::mock(Searchable::class);
100+
$document->shouldReceive('getSearchReference')->andReturn('entry::a');
101+
102+
$index->delete($document);
103+
104+
clearstatcache();
105+
$this->assertGreaterThan(
106+
$oldMtime,
107+
filemtime($path),
108+
'Comb\Index::delete() did not rewrite the index file even though the reference was present.'
109+
);
110+
111+
$this->assertSame(
112+
['entry::b' => ['title' => 'Bar']],
113+
json_decode(file_get_contents($path), true)
114+
);
115+
}
116+
117+
private function createIndexFile(array $data): string
118+
{
119+
$this->tmpDir = sys_get_temp_dir().'/statamic_comb_test_'.uniqid();
120+
mkdir($this->tmpDir, 0777, true);
121+
$path = $this->tmpDir.'/test.json';
122+
file_put_contents($path, json_encode($data));
123+
124+
return $path;
125+
}
42126
}

0 commit comments

Comments
 (0)