92 خطوط
3.3 KiB
PHP
92 خطوط
3.3 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\AI;
|
|
|
|
use App\Modules\AI\Infrastructure\AiEndpointPolicy;
|
|
use App\Modules\AI\Infrastructure\OpenAiCompatibleProvider;
|
|
use Illuminate\Http\Client\ConnectionException;
|
|
use Illuminate\Support\Facades\Http;
|
|
use RuntimeException;
|
|
use Tests\TestCase;
|
|
|
|
class OpenAiCompatibleProviderTest extends TestCase
|
|
{
|
|
public function test_openai_environment_contract_is_defined_in_config(): void
|
|
{
|
|
$this->assertNull(config('ai.provider'));
|
|
$this->assertNull(config('ai.ollama'));
|
|
$this->assertIsString(config('ai.openai.base_url'));
|
|
$this->assertIsArray(config('ai.openai.models'));
|
|
$this->assertArrayHasKey('fast', config('ai.openai.models'));
|
|
$this->assertArrayHasKey('balanced', config('ai.openai.models'));
|
|
$this->assertArrayHasKey('advanced', config('ai.openai.models'));
|
|
$this->assertIsInt(config('ai.openai.timeout'));
|
|
}
|
|
|
|
public function test_ollama_reachable_and_unavailable_health_states_are_reported(): void
|
|
{
|
|
Http::fakeSequence()
|
|
->push(['data' => [['id' => 'qwen']]])
|
|
->push([], 503);
|
|
|
|
$this->assertTrue($this->ollama()->health()['connected']);
|
|
$this->assertFalse($this->ollama()->health()['connected']);
|
|
}
|
|
|
|
public function test_invalid_model_fails_without_silent_fallback(): void
|
|
{
|
|
Http::fake(['*/chat/completions' => Http::response(['error' => 'model not found'], 404)]);
|
|
|
|
$this->expectException(RuntimeException::class);
|
|
$this->expectExceptionMessage('404');
|
|
$this->ollama()->chat('hello');
|
|
}
|
|
|
|
public function test_provider_timeout_is_not_silently_swallowed(): void
|
|
{
|
|
Http::fake(fn () => throw new ConnectionException('timeout'));
|
|
|
|
$this->expectException(ConnectionException::class);
|
|
$this->ollama()->chat('hello');
|
|
}
|
|
|
|
public function test_openai_compatible_assist_and_invalid_credentials_are_handled(): void
|
|
{
|
|
$provider = $this->online();
|
|
Http::fake(['*/chat/completions' => Http::response(['choices' => [['message' => ['content' => '{"result":"ok"}']]]])]);
|
|
$this->assertSame('ok', $provider->assist('rewrite', 'content', [])['proposal']['result']);
|
|
|
|
Http::fake(['*/models' => Http::response(['error' => 'unauthorized'], 401)]);
|
|
$this->assertFalse($provider->health()['connected']);
|
|
}
|
|
|
|
private function ollama(): OpenAiCompatibleProvider
|
|
{
|
|
return new OpenAiCompatibleProvider(new AiEndpointPolicy(customProviderHosts: []), [
|
|
'id' => 'ollama',
|
|
'provider' => 'ollama',
|
|
'mode' => 'local',
|
|
'base_url' => 'http://127.0.0.1:11434/v1',
|
|
'api_key' => '',
|
|
'default_model' => 'qwen',
|
|
'timeout_seconds' => 10,
|
|
]);
|
|
}
|
|
|
|
private function online(): OpenAiCompatibleProvider
|
|
{
|
|
return new OpenAiCompatibleProvider(new AiEndpointPolicy(
|
|
resolver: fn (string $host): array => ['93.184.216.34'],
|
|
customProviderHosts: ['ai.example.test'],
|
|
), [
|
|
'id' => 'online',
|
|
'provider' => 'openai_compatible',
|
|
'mode' => 'online',
|
|
'base_url' => 'https://ai.example.test/v1',
|
|
'api_key' => 'secret',
|
|
'default_model' => 'model-a',
|
|
'timeout_seconds' => 10,
|
|
]);
|
|
}
|
|
}
|