80 خطوط
3.1 KiB
PHP
80 خطوط
3.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\AI;
|
|
|
|
use App\Modules\AI\Application\AiProviderResolver;
|
|
use App\Modules\Organizations\Domain\Organization;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\DB;
|
|
use RuntimeException;
|
|
use Tests\TestCase;
|
|
|
|
class AiProviderResolverTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_resolver_deterministically_selects_the_default_enabled_connection(): void
|
|
{
|
|
$organization = Organization::factory()->create();
|
|
$this->connection($organization->id, '01J00000000000000000000001', enabled: true, default: false, createdAt: '2026-01-01 00:00:00');
|
|
$this->connection($organization->id, '01J00000000000000000000002', enabled: true, default: true, createdAt: '2026-01-02 00:00:00');
|
|
|
|
$provider = app(AiProviderResolver::class)->forOrganization($organization->id);
|
|
|
|
$this->assertSame('connection:01J00000000000000000000002', $provider->id());
|
|
}
|
|
|
|
public function test_disabled_deleted_and_cross_tenant_connections_cannot_be_resolved(): void
|
|
{
|
|
$first = Organization::factory()->create();
|
|
$second = Organization::factory()->create();
|
|
$id = '01J00000000000000000000003';
|
|
$this->connection($first->id, $id, enabled: false, default: false);
|
|
|
|
$resolver = app(AiProviderResolver::class);
|
|
|
|
foreach ([$first->id, $second->id] as $organizationId) {
|
|
try {
|
|
$resolver->byId('connection:'.$id, $organizationId);
|
|
$this->fail('Unavailable connection must not resolve.');
|
|
} catch (RuntimeException) {
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
}
|
|
|
|
DB::table('ai_provider_connections')->where('id', $id)->delete();
|
|
$this->expectException(RuntimeException::class);
|
|
$resolver->byId('connection:'.$id, $first->id);
|
|
}
|
|
|
|
public function test_failed_external_provider_falls_back_to_the_local_provider_deterministically(): void
|
|
{
|
|
$organization = Organization::factory()->create();
|
|
|
|
$fallback = app(AiProviderResolver::class)->fallbackForOrganization($organization->id, 'connection:failed');
|
|
|
|
$this->assertNotNull($fallback);
|
|
$this->assertSame('local-structuring-v1', $fallback->id());
|
|
$this->assertNull(app(AiProviderResolver::class)->fallbackForOrganization($organization->id, 'local'));
|
|
}
|
|
|
|
private function connection(string $organizationId, string $id, bool $enabled, bool $default, string $createdAt = '2026-01-01 00:00:00'): void
|
|
{
|
|
DB::table('ai_provider_connections')->insert([
|
|
'id' => $id,
|
|
'organization_id' => $organizationId,
|
|
'name' => $id,
|
|
'provider' => 'ollama',
|
|
'mode' => 'local',
|
|
'base_url' => 'http://127.0.0.1:11434/v1',
|
|
'models' => json_encode(['qwen']),
|
|
'default_model' => 'qwen',
|
|
'timeout_seconds' => 30,
|
|
'enabled' => $enabled,
|
|
'is_default' => $default,
|
|
'created_at' => $createdAt,
|
|
'updated_at' => $createdAt,
|
|
]);
|
|
}
|
|
}
|