From 9c602233a9ca0ffc4587ea7db47d5588d63799ab Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 8 Nov 2024 16:49:02 +0100 Subject: [PATCH] test: Add integration test for summary Signed-off-by: Joas Schilling --- .../features/bootstrap/FeatureContext.php | 107 +++++++++++++++++- .../features/chat-4/summary.feature | 23 ++++ 2 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 tests/integration/features/chat-4/summary.feature diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index f0402bdbda83..3b2034c10b08 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -40,6 +40,8 @@ class FeatureContext implements Context, SnippetAcceptingContext { /** @var array */ protected static array $messageIdToText; /** @var array */ + protected static array $aiTaskIds; + /** @var array */ protected static array $remoteToInviteId; /** @var array */ protected static array $inviteIdToRemote; @@ -113,6 +115,8 @@ class FeatureContext implements Context, SnippetAcceptingContext { private ?SharingContext $sharingContext; private array $guestsAppWasEnabled = []; + private array $testingAppWasEnabled = []; + private array $taskProcessingProviderPreference = []; private array $guestsOldWhitelist = []; @@ -184,6 +188,7 @@ public function __construct() { foreach (['LOCAL', 'REMOTE'] as $server) { $this->changedConfigs[$server] = []; $this->guestsAppWasEnabled[$server] = null; + $this->testingAppWasEnabled[$server] = null; $this->guestsOldWhitelist[$server] = ''; } } @@ -2422,6 +2427,43 @@ public function userSharesRichObjectToRoom($user, $type, $id, $metaData, $identi } } + /** + * @Then /^user "([^"]*)" requests summary for "([^"]*)" starting from ("[^"]*"|'[^']*') with (\d+)(?: \((v1)\))?$/ + */ + public function userSummarizesRoom(string $user, string $identifier, string $message, string $statusCode, string $apiVersion = 'v1', ?TableNode $tableNode = null): void { + $message = substr($message, 1, -1); + $fromMessageId = self::$textToMessageId[$message]; + + $this->setCurrentUser($user, $identifier); + $this->sendRequest( + 'POST', '/apps/spreed/api/' . $apiVersion . '/chat/' . self::$identifierToToken[$identifier] . '/summarize', + ['fromMessageId' => $fromMessageId], + ); + $this->assertStatusCode($this->response, $statusCode); + sleep(1); // make sure Postgres manages the order of the messages + + $response = $this->getDataFromResponse($this->response); + self::$aiTaskIds[$user . '/summary/' . self::$identifierToToken[$identifier]] = $response['taskId']; + if (isset($tableNode?->getRowsHash()['nextOffset'])) { + Assert::assertSame(self::$textToMessageId[$tableNode->getRowsHash()['nextOffset']], $response['nextOffset'], 'Offset ID does not match'); + } elseif (isset($response['nextOffset'])) { + Assert::assertArrayNotHasKey('nextOffset', $response, 'Did not expect a follow-up offset key on response, but received: ' . self::$messageIdToText[$response['nextOffset']]); + } + } + + /** + * @Then /^user "([^"]*)" receives summary for "([^"]*)" with (\d+)$/ + */ + public function userReceivesSummary(string $user, string $identifier, string $statusCode, ?TableNode $tableNode = null): void { + $this->sendRequest( + 'GET', '/taskprocessing/task/' . self::$aiTaskIds[$user . '/summary/' . self::$identifierToToken[$identifier]], + ); + $this->assertStatusCode($this->response, $statusCode); + $response = $this->getDataFromResponse($this->response); + Assert::assertNotNull($response['task']['output'], 'Task output should not be null'); + Assert::assertStringContainsString($tableNode->getRowsHash()['contains'], $response['task']['output']['output']); + } + /** * @Then /^user "([^"]*)" creates a poll in room "([^"]*)" with (\d+)(?: \((v1)\))?$/ * @@ -3881,6 +3923,31 @@ public function allowGuestAccountsCreation(): void { $this->setCurrentUser($currentUser); } + /** + * @Given /^Fake summary task provider is enabled$/ + */ + public function enableTestingApp(): void { + $currentUser = $this->setCurrentUser('admin'); + + // save old state and restore at the end + $this->sendRequest('GET', '/cloud/apps?filter=enabled'); + $this->assertStatusCode($this->response, 200); + $data = $this->getDataFromResponse($this->response); + $this->testingAppWasEnabled[$this->currentServer] = in_array('testing', $data['apps'], true); + + if (!$this->testingAppWasEnabled[$this->currentServer]) { + $this->runOcc(['app:enable', 'testing']); + } + + $this->runOcc(['config:app:get', 'core', 'ai.taskprocessing_provider_preferences']); + $this->taskProcessingProviderPreference[$this->currentServer] = $this->lastStdOut; + $preferences = json_decode($this->lastStdOut ?: '[]', true, flags: JSON_THROW_ON_ERROR); + $preferences['core:text2text:summary'] = 'testing-text2text-summary'; + $this->runOcc(['config:app:set', 'core', 'ai.taskprocessing_provider_preferences', '--value', json_encode($preferences)]); + + $this->setCurrentUser($currentUser); + } + /** * @BeforeScenario * @AfterScenario @@ -3909,13 +3976,13 @@ public function resetSpreedAppData() { /** * @AfterScenario */ - public function resetGuestsAppState() { + public function resetAppsState() { foreach (['LOCAL', 'REMOTE'] as $server) { $this->usingServer($server); if ($this->guestsAppWasEnabled[$server] === null) { // Guests app was not touched - return; + continue; } $currentUser = $this->setCurrentUser('admin'); @@ -3937,6 +4004,30 @@ public function resetGuestsAppState() { $this->guestsAppWasEnabled[$server] = null; } + + foreach (['LOCAL', 'REMOTE'] as $server) { + $this->usingServer($server); + + if ($this->testingAppWasEnabled[$server] === null) { + // Testing app was not touched + continue; + } + + $currentUser = $this->setCurrentUser('admin'); + + if ($this->taskProcessingProviderPreference[$server]) { + $this->runOcc(['config:app:set', 'core', 'ai.taskprocessing_provider_preferences', '--value', $this->taskProcessingProviderPreference[$server]]); + } else { + $this->runOcc(['config:app:delete', 'core', 'ai.taskprocessing_provider_preferences']); + } + + // restore app's enabled state + $this->sendRequest($this->testingAppWasEnabled[$server] ? 'POST' : 'DELETE', '/cloud/apps/testing'); + + $this->setCurrentUser($currentUser); + + $this->testingAppWasEnabled[$server] = null; + } } /* @@ -4762,12 +4853,16 @@ public function userShareLastNotificationFile(string $user, string $firstLast, s } /** - * @When /^(force run|run) "([^"]*)" background jobs$/ + * @When /^(force run|run|repeating run) "([^"]*)" background jobs$/ */ - public function runReminderBackgroundJobs(string $useForce, string $class): void { + public function runReminderBackgroundJobs(string $useForce, string $class, bool $repeated = false): void { $this->runOcc(['background-job:list', '--output=json_pretty', '--class=' . $class]); $list = json_decode($this->lastStdOut, true, 512, JSON_THROW_ON_ERROR); + if ($repeated && empty($list)) { + return; + } + Assert::assertNotEmpty($list, 'List of ' . $class . ' should not be empty'); foreach ($list as $job) { @@ -4781,6 +4876,10 @@ public function runReminderBackgroundJobs(string $useForce, string $class): void throw new \RuntimeException($this->lastStdErr); } } + + if ($useForce === 'repeating run') { + $this->runReminderBackgroundJobs($useForce, $class, true); + } } /** diff --git a/tests/integration/features/chat-4/summary.feature b/tests/integration/features/chat-4/summary.feature new file mode 100644 index 000000000000..4367b776ee67 --- /dev/null +++ b/tests/integration/features/chat-4/summary.feature @@ -0,0 +1,23 @@ +Feature: chat-4/summary + Background: + Given user "participant1" exists + Given user "participant2" exists + + Scenario: sending a message clears unread counter for sender + Given Fake summary task provider is enabled + And the following spreed app config is set + | ai_unread_summary_batch_size | 3 | + And user "participant1" creates room "room" (v4) + | roomType | 2 | + | roomName | room | + And user "participant1" adds user "participant2" to room "room" with 200 (v4) + When user "participant1" sends message "Message 1" to room "room" with 201 + And user "participant1" sends message "Message 2" to room "room" with 201 + And user "participant1" sends message "Message 3" to room "room" with 201 + And user "participant1" sends message "Message 4" to room "room" with 201 + And user "participant2" requests summary for "room" starting from "Message 1" with 201 + | nextOffset | Message 3 | + And repeating run "OC\TaskProcessing\SynchronousBackgroundJob" background jobs + Then user "participant2" receives summary for "room" with 200 + | contains | This is a fake summary | + And user "participant2" requests summary for "room" starting from "Message 3" with 201