62 lines
4.8 KiB
PHP
62 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/** No app initialization, .env, network or real credentials. */
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
require dirname(__DIR__) . '/vendor/topthink/framework/src/helper.php';
|
|
use app\common\service\followupaudio\FollowupAudioProviderConfig as Provider;
|
|
use app\common\service\followupaudio\FollowupAudioException as AudioError;
|
|
new think\App();
|
|
$config = new think\Config();
|
|
think\Container::getInstance()->instance('config', $config);
|
|
$checks = 0;
|
|
$expect = static function (bool $ok, string $why) use (&$checks): void { if (!$ok) { throw new RuntimeException($why); } $checks++; };
|
|
$expectError = static function (callable $call, string $code) use ($expect): void {
|
|
try { $call(); } catch (AudioError $error) { $expect($error->errorCode === $code, $code); return; }
|
|
throw new RuntimeException('Expected ' . $code);
|
|
};
|
|
$legacy = ['base_url' => 'https://legacy.invalid/v1', 'models' => ['qwen' => ['api_key' => 'synthetic-legacy-key', 'name' => 'must-not-inherit']]];
|
|
$dify = Provider::resolve('qwen', [], $legacy);
|
|
$expect($dify['driver'] === 'dify' && $dify['base_url'] === $legacy['base_url'] && $dify['model'] === '', 'legacy Dify app identity allowed without fake internal model');
|
|
$slot = ['driver' => 'openai_audio', 'base_url' => 'https://audio.invalid/v1/chat/completions',
|
|
'api_key' => 'synthetic-provider-key', 'model' => 'configurable-audio-model', 'label' => 'Audio label'];
|
|
$settings = ['enabled' => false, 'audio_verified' => true, 'verified_profiles' => ['qwen'], 'providers' => ['qwen' => $slot]];
|
|
$resolved = Provider::resolve('qwen', $settings, $legacy);
|
|
$expect($resolved['base_url'] === 'https://audio.invalid/v1' && $resolved['model'] === $slot['model'], 'explicit selected driver model and normalized endpoint');
|
|
$settings['providers']['qwen']['verified_fingerprint'] = $resolved['fingerprint'];
|
|
$config->set($settings, 'followup_audio'); $config->set($legacy, 'prescription_ai');
|
|
$expect(Provider::isVerified('qwen'), 'verified state independent of disabled operational switch');
|
|
$expect(Provider::publicModels() === [['value' => 'qwen', 'label' => 'Audio label']], 'only verified slots exposed');
|
|
$status = Provider::status('qwen', $settings, $legacy);
|
|
$expect(array_keys($status) === ['configured', 'profile', 'code', 'application_fingerprint'], 'safe exact status keys');
|
|
$public = json_encode([$status, Provider::publicModels()]);
|
|
$expect(!str_contains($public, 'synthetic-provider-key') && !str_contains($public, 'audio.invalid'), 'public response contains no endpoint or key');
|
|
foreach (['driver' => 'dify', 'base_url' => 'https://changed.invalid/v1', 'model' => 'another-model', 'api_key' => 'other-synthetic-key'] as $field => $value) {
|
|
$changed = $settings; $changed['providers']['qwen'][$field] = $value;
|
|
$expect(!Provider::verified('qwen', $changed, $legacy), 'identity change invalidates ' . $field);
|
|
}
|
|
$changed = $settings; $changed['providers']['qwen']['label'] = 'Renamed display';
|
|
$expect(Provider::verified('qwen', $changed, $legacy), 'display-only label does not change request identity');
|
|
$changed = $settings; $changed['providers']['qwen']['label'] = '';
|
|
$expect(Provider::resolve('qwen', $changed, $legacy)['label'] === $slot['model'], 'empty label falls back to configured model');
|
|
$changed = $settings; $changed['providers']['qwen']['base_url'] = 'http://audio.invalid/v1';
|
|
$changed['providers']['qwen']['verified_fingerprint'] = Provider::resolve('qwen', $changed, $legacy)['fingerprint'];
|
|
$changed['allow_insecure_synthetic'] = true;
|
|
$expect(!Provider::verified('qwen', $changed, $legacy), 'HTTP never becomes production-verified');
|
|
foreach (['model', 'base_url', 'api_key'] as $field) {
|
|
$changed = $settings; $changed['providers']['qwen'][$field] = '';
|
|
$expectError(static fn () => Provider::resolve('qwen', $changed, $legacy), 'CONFIG_MISSING');
|
|
}
|
|
foreach (['ftp://audio.invalid/v1', 'https://user:password@audio.invalid/v1', 'https://audio.invalid/v1?token=x',
|
|
"https://audio.invalid/v1\n", 'https://audio.invalid/v1#fragment'] as $url) {
|
|
$changed = $settings; $changed['providers']['qwen']['base_url'] = $url;
|
|
$expectError(static fn () => Provider::resolve('qwen', $changed, []), 'CONFIG_INVALID');
|
|
}
|
|
$changed = $settings; $changed['providers']['qwen']['driver'] = 'automatic';
|
|
$expectError(static fn () => Provider::resolve('qwen', $changed, []), 'CONFIG_INVALID');
|
|
$changed = $settings; $changed['providers']['qwen']['verified_fingerprint'] = '';
|
|
$expect(!Provider::verified('qwen', $changed, $legacy), 'old unbound flags do not enable audio');
|
|
$expect(Provider::status('invalid', $settings, $legacy)['code'] === 'INVALID_PROFILE', 'logical slot allowlist retained');
|
|
echo 'FOLLOWUP_AUDIO_PROVIDER_CONFIG assertions=' . $checks . ' PASS explicit_driver=1 model_configurable=1 exact_fingerprint=1 https_verified=1 secrets_hidden=1' . PHP_EOL;
|