Skip to content

Commit 2f076e3

Browse files
committed
Add unit tests for ImportCommand and SyncIndexSettingsCommand
ImportCommand tests: - Model class resolution with fully qualified names - Model not found exception handling - App\Models namespace fallback resolution SyncIndexSettingsCommand tests: - Engine not supporting UpdatesIndexSettings returns failure - No index settings configured returns success with info - Settings sync success flow - Driver option override - Index name prefix resolution logic
1 parent e9b45bc commit 2f076e3

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Tests\Scout\Unit\Console;
6+
7+
use Hypervel\Event\Contracts\Dispatcher;
8+
use Hypervel\Scout\Console\ImportCommand;
9+
use Hypervel\Scout\Exceptions\ScoutException;
10+
use Hypervel\Tests\TestCase;
11+
use Mockery as m;
12+
use ReflectionMethod;
13+
14+
/**
15+
* @internal
16+
* @coversNothing
17+
*/
18+
class ImportCommandTest extends TestCase
19+
{
20+
public function testThrowsExceptionWhenModelClassNotFound(): void
21+
{
22+
$events = m::mock(Dispatcher::class);
23+
24+
$command = m::mock(ImportCommand::class)->makePartial();
25+
$command->shouldReceive('argument')
26+
->with('model')
27+
->andReturn('NonExistentModel');
28+
29+
$this->expectException(ScoutException::class);
30+
$this->expectExceptionMessage('Model [NonExistentModel] not found.');
31+
32+
$command->handle($events);
33+
}
34+
35+
public function testResolvesModelClassFromAppModelsNamespace(): void
36+
{
37+
// Use reflection to test the protected method on a partial mock
38+
$command = m::mock(ImportCommand::class)->makePartial();
39+
40+
$method = new ReflectionMethod(ImportCommand::class, 'resolveModelClass');
41+
$method->setAccessible(true);
42+
43+
// Test with a class that exists - use a real class from the codebase
44+
$result = $method->invoke($command, \Hypervel\Scout\Builder::class);
45+
$this->assertSame(\Hypervel\Scout\Builder::class, $result);
46+
}
47+
48+
public function testResolvesFullyQualifiedClassName(): void
49+
{
50+
$command = m::mock(ImportCommand::class)->makePartial();
51+
52+
$method = new ReflectionMethod(ImportCommand::class, 'resolveModelClass');
53+
$method->setAccessible(true);
54+
55+
// Test with fully qualified class name
56+
$result = $method->invoke($command, \Hypervel\Scout\Engine::class);
57+
$this->assertSame(\Hypervel\Scout\Engine::class, $result);
58+
}
59+
60+
public function testThrowsExceptionForNonExistentClass(): void
61+
{
62+
$command = m::mock(ImportCommand::class)->makePartial();
63+
64+
$method = new ReflectionMethod(ImportCommand::class, 'resolveModelClass');
65+
$method->setAccessible(true);
66+
67+
$this->expectException(ScoutException::class);
68+
$this->expectExceptionMessage('Model [FakeModelThatDoesNotExist] not found.');
69+
70+
$method->invoke($command, 'FakeModelThatDoesNotExist');
71+
}
72+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Tests\Scout\Unit\Console;
6+
7+
use Hyperf\Contract\ConfigInterface;
8+
use Hypervel\Scout\Console\SyncIndexSettingsCommand;
9+
use Hypervel\Scout\Contracts\UpdatesIndexSettings;
10+
use Hypervel\Scout\Engine;
11+
use Hypervel\Scout\EngineManager;
12+
use Hypervel\Scout\Engines\CollectionEngine;
13+
use Hypervel\Tests\TestCase;
14+
use Mockery as m;
15+
use ReflectionMethod;
16+
17+
/**
18+
* @internal
19+
* @coversNothing
20+
*/
21+
class SyncIndexSettingsCommandTest extends TestCase
22+
{
23+
public function testFailsWhenEngineDoesNotSupportUpdatingIndexSettings(): void
24+
{
25+
$engine = new CollectionEngine();
26+
27+
$manager = m::mock(EngineManager::class);
28+
$manager->shouldReceive('engine')
29+
->with('collection')
30+
->once()
31+
->andReturn($engine);
32+
33+
$config = m::mock(ConfigInterface::class);
34+
$config->shouldReceive('get')
35+
->with('scout.driver')
36+
->andReturn('collection');
37+
38+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
39+
$command->shouldReceive('option')
40+
->with('driver')
41+
->andReturn(null);
42+
$command->shouldReceive('error')
43+
->once()
44+
->with('The "collection" engine does not support updating index settings.');
45+
46+
$result = $command->handle($manager, $config);
47+
48+
$this->assertSame(1, $result);
49+
}
50+
51+
public function testSucceedsWithInfoMessageWhenNoIndexSettingsConfigured(): void
52+
{
53+
$engine = m::mock(Engine::class . ', ' . UpdatesIndexSettings::class);
54+
55+
$manager = m::mock(EngineManager::class);
56+
$manager->shouldReceive('engine')
57+
->with('meilisearch')
58+
->once()
59+
->andReturn($engine);
60+
61+
$config = m::mock(ConfigInterface::class);
62+
$config->shouldReceive('get')
63+
->with('scout.driver')
64+
->andReturn('meilisearch');
65+
$config->shouldReceive('get')
66+
->with('scout.meilisearch.index-settings', [])
67+
->andReturn([]);
68+
69+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
70+
$command->shouldReceive('option')
71+
->with('driver')
72+
->andReturn(null);
73+
$command->shouldReceive('info')
74+
->once()
75+
->with('No index settings found for the "meilisearch" engine.');
76+
77+
$result = $command->handle($manager, $config);
78+
79+
$this->assertSame(0, $result);
80+
}
81+
82+
public function testSyncsIndexSettingsSuccessfully(): void
83+
{
84+
$engine = m::mock(Engine::class . ', ' . UpdatesIndexSettings::class);
85+
$engine->shouldReceive('updateIndexSettings')
86+
->once()
87+
->with('test_posts', ['filterableAttributes' => ['status']]);
88+
89+
$manager = m::mock(EngineManager::class);
90+
$manager->shouldReceive('engine')
91+
->with('meilisearch')
92+
->once()
93+
->andReturn($engine);
94+
95+
$config = m::mock(ConfigInterface::class);
96+
$config->shouldReceive('get')
97+
->with('scout.driver')
98+
->andReturn('meilisearch');
99+
$config->shouldReceive('get')
100+
->with('scout.meilisearch.index-settings', [])
101+
->andReturn([
102+
'test_posts' => ['filterableAttributes' => ['status']],
103+
]);
104+
$config->shouldReceive('get')
105+
->with('scout.prefix', '')
106+
->andReturn('');
107+
108+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
109+
$command->shouldReceive('option')
110+
->with('driver')
111+
->andReturn(null);
112+
$command->shouldReceive('info')
113+
->once()
114+
->with('Settings for the [test_posts] index synced successfully.');
115+
116+
$result = $command->handle($manager, $config);
117+
118+
$this->assertSame(0, $result);
119+
}
120+
121+
public function testUsesDriverOptionWhenProvided(): void
122+
{
123+
$engine = m::mock(Engine::class . ', ' . UpdatesIndexSettings::class);
124+
125+
$manager = m::mock(EngineManager::class);
126+
$manager->shouldReceive('engine')
127+
->with('typesense')
128+
->once()
129+
->andReturn($engine);
130+
131+
$config = m::mock(ConfigInterface::class);
132+
// Note: scout.driver should NOT be called when driver option is provided
133+
$config->shouldReceive('get')
134+
->with('scout.typesense.index-settings', [])
135+
->andReturn([]);
136+
137+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
138+
$command->shouldReceive('option')
139+
->with('driver')
140+
->andReturn('typesense');
141+
$command->shouldReceive('info')
142+
->once()
143+
->with('No index settings found for the "typesense" engine.');
144+
145+
$result = $command->handle($manager, $config);
146+
147+
$this->assertSame(0, $result);
148+
}
149+
150+
public function testIndexNameResolutionPrependsPrefix(): void
151+
{
152+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
153+
154+
$method = new ReflectionMethod(SyncIndexSettingsCommand::class, 'indexName');
155+
$method->setAccessible(true);
156+
157+
$config = m::mock(ConfigInterface::class);
158+
$config->shouldReceive('get')
159+
->with('scout.prefix', '')
160+
->andReturn('prod_');
161+
162+
// Test that prefix is prepended when not already present
163+
$result = $method->invoke($command, 'posts', $config);
164+
$this->assertSame('prod_posts', $result);
165+
}
166+
167+
public function testIndexNameResolutionDoesNotDuplicatePrefix(): void
168+
{
169+
$command = m::mock(SyncIndexSettingsCommand::class)->makePartial();
170+
171+
$method = new ReflectionMethod(SyncIndexSettingsCommand::class, 'indexName');
172+
$method->setAccessible(true);
173+
174+
$config = m::mock(ConfigInterface::class);
175+
$config->shouldReceive('get')
176+
->with('scout.prefix', '')
177+
->andReturn('prod_');
178+
179+
// Test that prefix is NOT duplicated when already present
180+
$result = $method->invoke($command, 'prod_posts', $config);
181+
$this->assertSame('prod_posts', $result);
182+
}
183+
}

0 commit comments

Comments
 (0)