set('services.support.email', 'support@liderra.ru'); config()->set('services.yandexgpt', [ 'api_key' => 'k', 'folder_id' => 'f', 'model' => 'yandexgpt-lite/latest', 'endpoint' => 'https://llm.api.cloud.yandex.net/foundationModels/v1/completion', 'timeout_seconds' => 8, ]); KnowledgeChunk::create([ 'source_path' => 'help/b.md', 'title' => 'Как пополнить баланс', 'tour' => null, 'topics' => 'пополнить, баланс', 'chunk_index' => 0, 'content' => 'Пополнить баланс можно картой или по счёту.', ]); }); it('прогоняет вопросы и пишет ответы построчно', function () { Http::fake(['llm.api.cloud.yandex.net/*' => Http::response([ 'result' => ['alternatives' => [['message' => ['role' => 'assistant', 'text' => 'Пополнить баланс можно в разделе «Биллинг».']]]], ])]); $dir = sys_get_temp_dir().'/bot-run-'.uniqid(); mkdir($dir); $file = $dir.'/q.json'; $out = $dir.'/out.jsonl'; file_put_contents($file, json_encode(['как пополнить баланс?', 'а картой можно?'], JSON_UNESCAPED_UNICODE)); $this->artisan('bot:run-questions', ['--file' => $file, '--out' => $out, '--as' => 'guest']) ->assertExitCode(0); $lines = array_filter(explode("\n", (string) file_get_contents($out))); expect($lines)->toHaveCount(2); $first = json_decode((string) $lines[0], true); expect($first['n'])->toBe(1) ->and($first['as'])->toBe('guest') ->and($first['q'])->toBe('как пополнить баланс?') ->and($first['a'])->toContain('Биллинг'); }); it('без файла вопросов честно падает, а не делает вид, что прогнал', function () { $this->artisan('bot:run-questions', ['--file' => 'нет-такого.json', '--out' => 'x.jsonl']) ->assertExitCode(1); });