Files
nc-talk-ai/tests/unit/Jobs/ProcessQueuedRequestsJobTest.php
Pascal Kienast 0739d3da6a Initial open-source release of Talk AI
Talk AI is a multi-bot AI assistant manager for Nextcloud Talk:
per-bot prompts and models, agentic tool calling (MCP + built-in
tools), RAG over Nextcloud files, room-document search, vision and
speech-to-text attachments, persistent bot wikis, approval workflows,
rate limiting, and multi-provider LLM support (any OpenAI-compatible
endpoint).

Developed within EDUC - the European Digital UniverCity
(https://educalliance.eu), where it runs as the 'EDUC AI' assistant on
the alliance-wide Nextcloud portal. This public repository is the
upstream point of truth; deployment-specific tools plug in via the
tool-provider extension point (docs/TOOL_PROVIDERS.md).

License: AGPL-3.0-or-later.
2026-07-08 21:13:13 +02:00

87 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\EducAI\Tests\Unit\Jobs;
use OCA\EducAI\Db\Bot;
use OCA\EducAI\Db\BotMapper;
use OCA\EducAI\Db\QueuedRequest;
use OCA\EducAI\Jobs\ProcessQueuedRequestsJob;
use OCA\EducAI\Service\BotService;
use OCA\EducAI\Service\RateLimitService;
use OCA\EducAI\Webhook\TalkHandler;
use OCP\AppFramework\Utility\ITimeFactory;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class ProcessQueuedRequestsJobTest extends TestCase {
public function testPermanentQueueFailureDoesNotExposeProviderEndpointInTalk(): void {
$request = new QueuedRequest();
$request->setId(99);
$request->setBotId(7);
$request->setRoomToken('room-token');
$request->setUserId('owner');
$request->setMessage('Hello');
$request->setOriginalMessage('@bot Hello');
$request->setReplyToMessageId(123);
$request->setAttempts(3);
$bot = new Bot();
$bot->setId(7);
$bot->setIsActive(true);
$rateLimitService = $this->createMock(RateLimitService::class);
$rateLimitService->expects($this->once())
->method('markProcessing')
->with($request);
$rateLimitService->expects($this->once())
->method('recordUsage');
$rateLimitService->expects($this->once())
->method('markFailed')
->with(
$request,
$this->stringContains('Max retries exceeded')
);
$botMapper = $this->createMock(BotMapper::class);
$botMapper->expects($this->once())
->method('findById')
->with(7)
->willReturn($bot);
$botService = $this->createMock(BotService::class);
$botService->expects($this->once())
->method('processMessage')
->willThrowException(new \Exception('Failed to get response from AI: cURL error 7: Failed to connect to https://secret.example.invalid/v1/chat/completions'));
$talkHandler = $this->createMock(TalkHandler::class);
$talkHandler->expects($this->once())
->method('sendReplyToTalk')
->with(
'room-token',
$this->callback(function (string $message): bool {
$this->assertStringContainsString('Your request could not be processed after multiple attempts. Please try again later.', $message);
$this->assertStringNotContainsString('https://secret.example.invalid', $message);
$this->assertStringNotContainsString('Error:', $message);
return true;
}),
123
)
->willReturn(true);
$job = new ProcessQueuedRequestsJob(
$this->createMock(ITimeFactory::class),
$rateLimitService,
$botService,
$botMapper,
$talkHandler,
$this->createMock(LoggerInterface::class)
);
$method = new \ReflectionMethod(ProcessQueuedRequestsJob::class, 'processQueuedRequest');
$method->setAccessible(true);
$method->invoke($job, $request);
}
}