chore(chat): убрать настройки Jivo — чат теперь свой; экскурсии выключены
Config: jivo_bot -> bot (убраны неиспользуемые webhook_secret/outbound_url/token - транспорт Jivo уже удалён), jivosite убран целиком (виджет больше не нужен). Env: JIVO_BOT_* -> BOT_*; JIVO_WIDGET_ID/VITE_JIVO_WIDGET_ID удалены. tours_enabled выключен по умолчанию (BOT_TOURS_ENABLED=false) — экскурсии «Показать на портале» уходили не по теме вопроса (живая проверка 13.07.2026), чинить релевантность отдельно; сторож-тест не даёт включить незаметно. Заодно убраны обнаруженные хвосты того же виджета, оставшиеся от прежних задач: JivoLivenessProbe (класс+тест+регистрация в мониторинге внешних сервисов), плитка «JivoSite» в админ-дашборде, встроенный скрипт виджета в welcome.blade.php, осиротевший тест resources/js/.../JivoWidget.vue (компонент уже был удалён ранее), declaration VITE_JIVO_WIDGET_ID. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-5
@@ -83,11 +83,7 @@ MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
SUPPORT_EMAIL=support@liderra.ru
|
||||
JIVO_WIDGET_ID=
|
||||
JIVO_BOT_WEBHOOK_SECRET=
|
||||
JIVO_BOT_OUTBOUND_URL=
|
||||
JIVO_BOT_TOKEN=
|
||||
JIVO_BOT_TOURS_ENABLED=false
|
||||
BOT_TOURS_ENABLED=false
|
||||
YANDEX_GPT_API_KEY=
|
||||
YANDEX_GPT_FOLDER_ID=
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use App\Services\Dashboard\BalanceHealth;
|
||||
use App\Services\External\BalanceProvider;
|
||||
use App\Services\External\CaptchaLivenessProbe;
|
||||
use App\Services\External\DadataBalanceProvider;
|
||||
use App\Services\External\JivoLivenessProbe;
|
||||
use App\Services\External\LivenessProbe;
|
||||
use App\Services\External\SupplierBalanceProvider;
|
||||
use App\Services\External\Yandex360BalanceProvider;
|
||||
@@ -71,7 +70,6 @@ class RefreshExternalBalancesJob implements ShouldQueue
|
||||
|
||||
return [
|
||||
app(YooKassaLivenessProbe::class),
|
||||
app(JivoLivenessProbe::class),
|
||||
app(CaptchaLivenessProbe::class),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
SymfonyProcessFactory::class,
|
||||
);
|
||||
|
||||
// Мозг бота: российская модель или зарубежная через шлюз AITunnel (services.jivo_bot.llm).
|
||||
// Мозг бота: российская модель или зарубежная через шлюз AITunnel (services.bot.llm).
|
||||
$this->app->bind(
|
||||
LlmClient::class,
|
||||
fn ($app) => config('services.jivo_bot.llm') === 'aitunnel'
|
||||
fn ($app) => config('services.bot.llm') === 'aitunnel'
|
||||
? $app->make(AitunnelChatClient::class)
|
||||
: $app->make(YandexGptClient::class),
|
||||
);
|
||||
|
||||
@@ -36,10 +36,10 @@ class AitunnelChatClient implements LlmClient
|
||||
$messages[] = ['role' => 'user', 'content' => $this->mask->hide($userText)];
|
||||
|
||||
try {
|
||||
$response = Http::timeout((int) config('services.jivo_bot.timeout_seconds', 10))
|
||||
$response = Http::timeout((int) config('services.bot.timeout_seconds', 10))
|
||||
->withToken($key)
|
||||
->post(rtrim((string) ($cfg['base_url'] ?? ''), '/').'/chat/completions', [
|
||||
'model' => (string) config('services.jivo_bot.aitunnel_model', 'gpt-4o-mini'),
|
||||
'model' => (string) config('services.bot.aitunnel_model', 'gpt-4o-mini'),
|
||||
'temperature' => 0.2,
|
||||
'max_tokens' => 500,
|
||||
'messages' => $messages,
|
||||
|
||||
@@ -301,7 +301,7 @@ class BotAnswerService
|
||||
// Экскурсию предлагаем, только если статья реально по теме вопроса. Иначе клиент
|
||||
// спрашивает про регистрацию, а ссылка ведёт на импорт данных (прогон 12.07.2026).
|
||||
$tour = $this->search->isOnTopic($chunks[0], $question) ? $chunks[0]->tour : null;
|
||||
if ($tour !== null && (bool) config('services.jivo_bot.tours_enabled')) {
|
||||
if ($tour !== null && (bool) config('services.bot.tours_enabled')) {
|
||||
$text .= "\n\n👉 Показать на портале: ".rtrim((string) config('app.url'), '/').'/?tour='.$tour;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class ChatRateLimiter
|
||||
|
||||
private function store(): \Illuminate\Contracts\Cache\Repository
|
||||
{
|
||||
return Cache::store((string) config('services.jivo_bot.limiter_store'));
|
||||
return Cache::store((string) config('services.bot.limiter_store'));
|
||||
}
|
||||
|
||||
/** Атомарная прибавка с временем жизни. Возвращает новое значение счётчика. */
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace App\Services\Bot;
|
||||
* Модель под капотом бота. Реализации: {@see YandexGptClient} (Яндекс, данные не покидают РФ)
|
||||
* и {@see AitunnelChatClient} (зарубежные модели через российский шлюз AITunnel).
|
||||
*
|
||||
* Какая используется — настройка `services.jivo_bot.llm` (yandex | aitunnel).
|
||||
* Какая используется — настройка `services.bot.llm` (yandex | aitunnel).
|
||||
*/
|
||||
interface LlmClient
|
||||
{
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\External;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
/**
|
||||
* Живость чата JivoSite: HTTP GET виджет-скрипта по widget_id. 200 → жив.
|
||||
* Нет widget_id → grey. Денег не тратит (публичный статик).
|
||||
*/
|
||||
class JivoLivenessProbe implements LivenessProbe
|
||||
{
|
||||
public function serviceKey(): string
|
||||
{
|
||||
return 'jivosite';
|
||||
}
|
||||
|
||||
public function check(): LivenessReading
|
||||
{
|
||||
$id = (string) config('services.jivosite.widget_id');
|
||||
if ($id === '') {
|
||||
return LivenessReading::unknown('jivosite', 'widget_id не задан');
|
||||
}
|
||||
|
||||
try {
|
||||
$tpl = (string) config('services.jivosite.widget_url_template', 'https://code.jivo.ru/widget/{id}');
|
||||
$url = str_replace('{id}', $id, $tpl);
|
||||
$resp = Http::timeout(5)->get($url);
|
||||
if ($resp->ok()) {
|
||||
return LivenessReading::alive('jivosite', 'виджет доступен');
|
||||
}
|
||||
|
||||
return LivenessReading::down('jivosite', 'HTTP '.$resp->status());
|
||||
} catch (\Throwable $e) {
|
||||
return LivenessReading::down('jivosite', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ namespace App\Services\External;
|
||||
*/
|
||||
interface LivenessProbe
|
||||
{
|
||||
/** email | yookassa | jivosite | captcha */
|
||||
/** email | yookassa | captcha */
|
||||
public function serviceKey(): string;
|
||||
|
||||
public function check(): LivenessReading;
|
||||
|
||||
+10
-17
@@ -141,31 +141,24 @@ return [
|
||||
'support' => [
|
||||
'email' => env('SUPPORT_EMAIL', 'support@liderra.ru'),
|
||||
],
|
||||
'jivosite' => [
|
||||
'widget_id' => env('JIVO_WIDGET_ID'),
|
||||
'widget_url_template' => env('JIVO_WIDGET_URL_TEMPLATE', 'https://code.jivo.ru/widget/{id}'),
|
||||
],
|
||||
|
||||
// Яндекс.Метрика портала (lk.liderra.ru). Пусто = Метрика не грузится.
|
||||
// Вебвизор пишет только кабинет клиента (см. resources/js/plugins/metrika.ts + router).
|
||||
'metrika' => [
|
||||
'counter_id' => env('METRIKA_COUNTER_ID'),
|
||||
],
|
||||
|
||||
// ИИ-бот техподдержки в чате Jivo (спека 2026-07-02-jivo-ai-support-bot-design).
|
||||
// webhook_secret — входящий секрет в URL (≥32 симв., по образцу supplier webhook).
|
||||
// outbound_url/token — выдаёт Jivo письмом при подключении Bot API; пусто → отправка
|
||||
// событий отключена (dev/CI), бот пишет только в журнал.
|
||||
'jivo_bot' => [
|
||||
'webhook_secret' => env('JIVO_BOT_WEBHOOK_SECRET', ''),
|
||||
'outbound_url' => env('JIVO_BOT_OUTBOUND_URL', ''),
|
||||
'token' => env('JIVO_BOT_TOKEN', ''),
|
||||
'tours_enabled' => env('JIVO_BOT_TOURS_ENABLED', false),
|
||||
// ИИ-бот техподдержки в СВОЁМ окошке чата на сайте (спека 2026-07-13-own-chat-widget-design;
|
||||
// до 07.07.2026 жил внутри чата Jivo — эта интеграция закрыта, см. docs/CHANGELOG).
|
||||
// tours_enabled — «экскурсии» (ссылки «Показать на портале» в ответах бота): ВЫКЛЮЧЕНЫ
|
||||
// по умолчанию, ссылки уходили не по теме вопроса (живая проверка 13.07.2026). Чинить
|
||||
// релевантность — отдельно, механизм в коде остаётся.
|
||||
'bot' => [
|
||||
'tours_enabled' => env('BOT_TOURS_ENABLED', false),
|
||||
// Мозг бота: yandex (данные не покидают РФ) | aitunnel (зарубежные модели через
|
||||
// российский шлюз; ПДн вырезаются PersonalDataMask до отправки).
|
||||
'llm' => env('JIVO_BOT_LLM', 'yandex'),
|
||||
'aitunnel_model' => env('JIVO_BOT_AITUNNEL_MODEL', 'gpt-4o-mini'),
|
||||
'timeout_seconds' => env('JIVO_BOT_LLM_TIMEOUT', 10),
|
||||
'llm' => env('BOT_LLM', 'yandex'),
|
||||
'aitunnel_model' => env('BOT_AITUNNEL_MODEL', 'gpt-4o-mini'),
|
||||
'timeout_seconds' => env('BOT_LLM_TIMEOUT', 10),
|
||||
// Счётчики чата должны считать АТОМАРНО: файловый кэш под параллельной нагрузкой
|
||||
// теряет до 74% прибавок (проверено), и защита от накрутки счёта становится дырявой.
|
||||
// Redis (INCR) атомарен. В тестах — 'array' (один процесс, гонки не бывает).
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="JIVO_BOT_LLM" value="yandex"/>
|
||||
<env name="BOT_LLM" value="yandex"/>
|
||||
<env name="BOT_LIMITER_STORE" value="array"/>
|
||||
<env name="AITUNNEL_API_KEY" value=""/>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
|
||||
@@ -105,7 +105,6 @@ const SERVICE_LABELS: Record<string, string> = {
|
||||
yandex_cloud: 'Yandex Cloud',
|
||||
email: 'Почта',
|
||||
yookassa: 'ЮKassa',
|
||||
jivosite: 'JivoSite',
|
||||
captcha: 'Капча',
|
||||
};
|
||||
const SERVICE_ICONS: Record<string, string> = {
|
||||
@@ -114,7 +113,6 @@ const SERVICE_ICONS: Record<string, string> = {
|
||||
yandex_cloud: '☁️',
|
||||
email: '✉️',
|
||||
yookassa: '💳',
|
||||
jivosite: '💬',
|
||||
captcha: '🛡',
|
||||
};
|
||||
|
||||
@@ -131,7 +129,7 @@ function daysLeftLabel(days: number | null): string {
|
||||
}
|
||||
|
||||
/** Сервисы БЕЗ денежного баланса — следим только за живостью (не за деньгами). */
|
||||
const LIVENESS_ONLY_KEYS = new Set(['yookassa', 'jivosite', 'captcha']);
|
||||
const LIVENESS_ONLY_KEYS = new Set(['yookassa', 'captcha']);
|
||||
function isLivenessOnly(key: string): boolean {
|
||||
return LIVENESS_ONLY_KEYS.has(key);
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -2,7 +2,6 @@
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_YANDEX_SMARTCAPTCHA_SITEKEY?: string;
|
||||
readonly VITE_JIVO_WIDGET_ID?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
<meta name="metrika-id" content="{{ config('services.metrika.counter_id') }}">
|
||||
@endif
|
||||
@vite(['resources/css/app.css', 'resources/js/app.ts'])
|
||||
@if(config('services.jivosite.widget_id'))
|
||||
<script src="https://code.jivo.ru/widget/{{ config('services.jivosite.widget_id') }}" async></script>
|
||||
@endif
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -88,7 +88,7 @@ it('LLM ответил «в инструкции этого нет» → чес
|
||||
// Живой урок 12.07.2026: «а что такое объединить дубли?» → слабое совпадение поиска
|
||||
// («что», «такое») → LLM честно сказал «В инструкции этого нет», а мы приляпали
|
||||
// кнопку «Показать на портале». Незнание = эскалация, без кнопок.
|
||||
config()->set('services.jivo_bot.tours_enabled', true);
|
||||
config()->set('services.bot.tours_enabled', true);
|
||||
Http::fake([
|
||||
'llm.api.cloud.yandex.net/*' => Http::response([
|
||||
'result' => ['alternatives' => [['message' => ['role' => 'assistant', 'text' => 'В инструкции этого нет.']]]],
|
||||
@@ -169,7 +169,7 @@ it('обычный вопрос → ответ LLM по контексту, бе
|
||||
});
|
||||
|
||||
it('tour-ссылка добавляется только при включённом tours_enabled', function () {
|
||||
config()->set('services.jivo_bot.tours_enabled', true);
|
||||
config()->set('services.bot.tours_enabled', true);
|
||||
config()->set('app.url', 'https://liderra.ru');
|
||||
Http::fake([
|
||||
'llm.api.cloud.yandex.net/*' => Http::response([
|
||||
@@ -180,7 +180,7 @@ it('tour-ссылка добавляется только при включён
|
||||
$withTours = app(BotAnswerService::class)->answer('что такое проект?');
|
||||
expect($withTours->text)->toContain('https://liderra.ru/?tour=create-project');
|
||||
|
||||
config()->set('services.jivo_bot.tours_enabled', false);
|
||||
config()->set('services.bot.tours_enabled', false);
|
||||
$without = app(BotAnswerService::class)->answer('что такое проект?');
|
||||
expect($without->text)->not->toContain('?tour=');
|
||||
});
|
||||
|
||||
@@ -85,9 +85,9 @@ it('счётчики чата живут в атомарном хранилищ
|
||||
// и защита от накрутки счёта становится дырявой. Хранилище счётчиков должно быть
|
||||
// атомарным (Redis INCR). Сторож ловит и подмену на 'file', и пропажу самого ключа:
|
||||
// без ключа config() вернул бы null, и проверка «не file» прошла бы впустую.
|
||||
expect(config('services.jivo_bot.limiter_store'))->toBeIn(['redis', 'array']);
|
||||
expect(config('services.bot.limiter_store'))->toBeIn(['redis', 'array']);
|
||||
|
||||
// И сам класс обязан брать это хранилище, а не общий кэш приложения.
|
||||
expect(file_get_contents(app_path('Services/Bot/ChatRateLimiter.php')))
|
||||
->toContain("Cache::store((string) config('services.jivo_bot.limiter_store'))");
|
||||
->toContain("Cache::store((string) config('services.bot.limiter_store'))");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
it('экскурсии «Показать на портале» пока выключены — ссылки не по теме', function () {
|
||||
// Живая проверка 13.07.2026: бот приписывал «👉 Показать на портале: …?tour=support»
|
||||
// к ответу не по теме вопроса. По спеке §10 в первом заходе экскурсий нет —
|
||||
// чиним релевантность отдельно. Сторож не даёт включить их незаметно.
|
||||
expect(config('services.bot.tours_enabled'))->toBeFalse();
|
||||
});
|
||||
@@ -14,10 +14,10 @@ use Illuminate\Support\Facades\Http;
|
||||
* клиент пишет в чат свой телефон. Маскируем до отправки.
|
||||
*/
|
||||
it('провайдер выбирается настройкой: яндекс или зарубежная модель через AITunnel', function () {
|
||||
config()->set('services.jivo_bot.llm', 'yandex');
|
||||
config()->set('services.bot.llm', 'yandex');
|
||||
expect(app(LlmClient::class))->toBeInstanceOf(YandexGptClient::class);
|
||||
|
||||
config()->set('services.jivo_bot.llm', 'aitunnel');
|
||||
config()->set('services.bot.llm', 'aitunnel');
|
||||
expect(app(LlmClient::class))->toBeInstanceOf(AitunnelChatClient::class);
|
||||
});
|
||||
|
||||
@@ -32,9 +32,9 @@ it('маскирует телефоны и почту до отправки в
|
||||
});
|
||||
|
||||
it('зарубежная модель получает вопрос БЕЗ телефона клиента', function () {
|
||||
config()->set('services.jivo_bot.llm', 'aitunnel');
|
||||
config()->set('services.bot.llm', 'aitunnel');
|
||||
config()->set('services.aitunnel.key', 'test-key');
|
||||
config()->set('services.jivo_bot.aitunnel_model', 'claude-sonnet-5');
|
||||
config()->set('services.bot.aitunnel_model', 'claude-sonnet-5');
|
||||
|
||||
Http::fake(['*' => Http::response([
|
||||
'choices' => [['message' => ['content' => 'Передал специалисту.']]],
|
||||
|
||||
@@ -327,7 +327,7 @@ it('не предлагает экскурсию, если статья подо
|
||||
// «Показать на портале» на экскурсию не по теме — спросили про регистрацию,
|
||||
// а бот вёл на импорт данных. Экскурсию даём, только если статья реально по теме:
|
||||
// слово из вопроса попало в её заголовок или синонимы, а не просто в текст.
|
||||
config()->set('services.jivo_bot.tours_enabled', true);
|
||||
config()->set('services.bot.tours_enabled', true);
|
||||
config()->set('app.url', 'https://liderra.ru');
|
||||
|
||||
KnowledgeChunk::query()->delete();
|
||||
|
||||
@@ -28,7 +28,7 @@ it('шлёт одно письмо, когда сервис впервые по
|
||||
Mail::fake();
|
||||
stubBalanceProvidersOk();
|
||||
RefreshExternalBalancesJob::useLivenessProbes([
|
||||
fakeProbe('jivosite', LivenessReading::down('jivosite', 'HTTP 500')),
|
||||
fakeProbe('demo_probe', LivenessReading::down('demo_probe', 'HTTP 500')),
|
||||
]);
|
||||
|
||||
(new RefreshExternalBalancesJob)->handle();
|
||||
@@ -40,14 +40,14 @@ it('не шлёт письмо, если сервис уже был красны
|
||||
Mail::fake();
|
||||
stubBalanceProvidersOk();
|
||||
|
||||
// Предзаливаем строку: jivosite уже красный.
|
||||
// Предзаливаем строку: demo_probe уже красный.
|
||||
DB::connection('pgsql_supplier')->table('external_service_balances')->updateOrInsert(
|
||||
['service_key' => 'jivosite'],
|
||||
['service_key' => 'demo_probe'],
|
||||
['light' => 'red', 'ok' => true, 'currency' => 'RUB', 'checked_at' => now(), 'updated_at' => now()],
|
||||
);
|
||||
|
||||
RefreshExternalBalancesJob::useLivenessProbes([
|
||||
fakeProbe('jivosite', LivenessReading::down('jivosite', 'HTTP 500')),
|
||||
fakeProbe('demo_probe', LivenessReading::down('demo_probe', 'HTTP 500')),
|
||||
]);
|
||||
|
||||
(new RefreshExternalBalancesJob)->handle();
|
||||
@@ -59,7 +59,7 @@ it('не шлёт письмо, когда всё зелёное', function () {
|
||||
Mail::fake();
|
||||
stubBalanceProvidersOk();
|
||||
RefreshExternalBalancesJob::useLivenessProbes([
|
||||
fakeProbe('jivosite', LivenessReading::alive('jivosite', 'ок')),
|
||||
fakeProbe('demo_probe', LivenessReading::alive('demo_probe', 'ок')),
|
||||
]);
|
||||
|
||||
(new RefreshExternalBalancesJob)->handle();
|
||||
|
||||
@@ -85,7 +85,7 @@ it('пишет строки живости: balance_amount NULL, цвет из
|
||||
app()->instance(Yandex360BalanceProvider::class, fakeProvider('email', BalanceReading::ok('email', 777, 'RUB', null)));
|
||||
|
||||
RefreshExternalBalancesJob::useLivenessProbes([
|
||||
fakeProbe('jivosite', LivenessReading::down('jivosite', 'HTTP 500')),
|
||||
fakeProbe('demo_probe', LivenessReading::down('demo_probe', 'HTTP 500')),
|
||||
fakeProbe('captcha', LivenessReading::unknown('captcha', 'выключена')),
|
||||
]);
|
||||
|
||||
@@ -95,9 +95,9 @@ it('пишет строки живости: balance_amount NULL, цвет из
|
||||
expect($rows)->toHaveCount(6); // 4 деньги (вкл. email) + 2 живость
|
||||
expect((float) $rows['email']->balance_amount)->toBe(777.0);
|
||||
expect((bool) $rows['email']->ok)->toBeTrue();
|
||||
expect($rows['jivosite']->light)->toBe('red');
|
||||
expect((bool) $rows['jivosite']->ok)->toBeTrue(); // ok=true: статус свежий и определённый (упал)
|
||||
expect($rows['jivosite']->error)->toContain('500');
|
||||
expect($rows['demo_probe']->light)->toBe('red');
|
||||
expect((bool) $rows['demo_probe']->ok)->toBeTrue(); // ok=true: статус свежий и определённый (упал)
|
||||
expect($rows['demo_probe']->error)->toContain('500');
|
||||
expect($rows['captcha']->light)->toBe('grey');
|
||||
expect((bool) $rows['captcha']->ok)->toBeFalse(); // grey = не смогли/не применимо
|
||||
});
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import JivoWidget from '../../resources/js/components/support/JivoWidget.vue';
|
||||
|
||||
beforeEach(() => {
|
||||
document.querySelectorAll('#jivosite-widget-js').forEach((n) => n.remove());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
describe('JivoWidget', () => {
|
||||
it('подгружает скрипт Jivo, когда задан VITE_JIVO_WIDGET_ID', () => {
|
||||
vi.stubEnv('VITE_JIVO_WIDGET_ID', 'abc123');
|
||||
mount(JivoWidget);
|
||||
const script = document.getElementById('jivosite-widget-js') as HTMLScriptElement | null;
|
||||
expect(script).not.toBeNull();
|
||||
expect(script?.src).toContain('code.jivosite.com/widget/abc123');
|
||||
});
|
||||
|
||||
it('ничего не грузит, когда ключ пуст (виджет спит)', () => {
|
||||
vi.stubEnv('VITE_JIVO_WIDGET_ID', '');
|
||||
mount(JivoWidget);
|
||||
expect(document.getElementById('jivosite-widget-js')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Services\External\JivoLivenessProbe;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class); // нужен booted-app: config()/Http::fake()
|
||||
|
||||
beforeEach(function () {
|
||||
config()->set('services.jivosite.widget_id', 'ABC123');
|
||||
config()->set('services.jivosite.widget_url_template', 'https://code.jivo.ru/widget/{id}');
|
||||
});
|
||||
|
||||
it('зелёный при 200 на виджет-скрипт', function () {
|
||||
Http::fake(['code.jivo.ru/widget/ABC123' => Http::response('/* jivo */', 200)]);
|
||||
$r = (new JivoLivenessProbe)->check();
|
||||
expect($r->serviceKey)->toBe('jivosite');
|
||||
expect($r->light)->toBe('green');
|
||||
});
|
||||
|
||||
it('красный при 404', function () {
|
||||
Http::fake(['code.jivo.ru/widget/ABC123' => Http::response('', 404)]);
|
||||
expect((new JivoLivenessProbe)->check()->light)->toBe('red');
|
||||
});
|
||||
|
||||
it('серый, когда widget_id не задан', function () {
|
||||
config()->set('services.jivosite.widget_id', '');
|
||||
expect((new JivoLivenessProbe)->check()->light)->toBe('grey');
|
||||
});
|
||||
Reference in New Issue
Block a user