diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php index 0dcb77c0b97..0233bbea43b 100644 --- a/lib/Controller/ChatController.php +++ b/lib/Controller/ChatController.php @@ -132,7 +132,7 @@ protected function getActorInfo(string $actorDisplayName = ''): array { } return [Attendee::ACTOR_GUESTS, $this->participant->getAttendee()->getActorId()]; } - + if ($this->userId === MatterbridgeManager::BRIDGE_BOT_USERID && $actorDisplayName) { return [Attendee::ACTOR_BRIDGED, str_replace(['/', '"'], '', $actorDisplayName)]; } @@ -936,7 +936,6 @@ public function getObjectsSharedInRoomOverview(int $limit = 7): DataResponse { Attachment::TYPE_VOICE, ]; - $messages = []; $messageIdsByType = []; // Get all attachments foreach ($objectTypes as $objectType) { diff --git a/lib/Controller/ReactionController.php b/lib/Controller/ReactionController.php index cab0c27f20e..684f9f6dd29 100644 --- a/lib/Controller/ReactionController.php +++ b/lib/Controller/ReactionController.php @@ -59,7 +59,7 @@ public function __construct( * * @param int $messageId ID of the message * @param string $reaction Emoji to add - * @return DataResponse, array{}>|DataResponse, array{}> + * @return DataResponse|\stdClass, array{}>|DataResponse, array{}> * * 200: Reaction already existed * 201: Reaction added successfully @@ -89,7 +89,7 @@ public function react(int $messageId, string $reaction): DataResponse { return new DataResponse([], Http::STATUS_BAD_REQUEST); } $reactions = $this->reactionManager->retrieveReactionMessages($this->getRoom(), $this->getParticipant(), $messageId); - return new DataResponse($reactions, $status); + return new DataResponse($this->formatReactions($reactions), $status); } /** @@ -97,7 +97,7 @@ public function react(int $messageId, string $reaction): DataResponse { * * @param int $messageId ID of the message * @param string $reaction Emoji to remove - * @return DataResponse, array{}>|DataResponse, array{}> + * @return DataResponse|\stdClass, array{}>|DataResponse, array{}> * * 200: Reaction deleted successfully * 400: Deleting reaction is not possible @@ -124,7 +124,7 @@ public function delete(int $messageId, string $reaction): DataResponse { return new DataResponse([], Http::STATUS_BAD_REQUEST); } - return new DataResponse($reactions, Http::STATUS_OK); + return new DataResponse($this->formatReactions($reactions), Http::STATUS_OK); } /** @@ -132,7 +132,7 @@ public function delete(int $messageId, string $reaction): DataResponse { * * @param int $messageId ID of the message * @param string|null $reaction Emoji to filter - * @return DataResponse, array{}>|DataResponse, array{}> + * @return DataResponse|\stdClass, array{}>|DataResponse, array{}> * * 200: Reactions returned * 404: Message or reaction not found @@ -150,6 +150,21 @@ public function getReactions(int $messageId, ?string $reaction): DataResponse { $reactions = $this->reactionManager->retrieveReactionMessages($this->getRoom(), $this->getParticipant(), $messageId, $reaction); - return new DataResponse($reactions, Http::STATUS_OK); + return new DataResponse($this->formatReactions($reactions), Http::STATUS_OK); + } + + + /** + * @param array $reactions + * @return array|\stdClass + */ + public function formatReactions(array $reactions): array|\stdClass { + if ($this->getResponseFormat() === 'json' && empty($reactions)) { + // Cheating here to make sure the reactions array is always a + // JSON object on the API, even when there is no reaction at all. + return new \stdClass(); + } + + return $reactions; } } diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 78041860823..63d587e2ab4 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -2391,12 +2391,17 @@ public function userSeesTheFollowingSharedOverviewMediaInRoom($user, $identifier $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/chat/' . self::$identifierToToken[$identifier] . '/share/overview'); $this->assertStatusCode($this->response, $statusCode); - $overview = $this->getDataFromResponse($this->response); - $expected = $formData->getRowsHash(); - $summarized = array_map(function ($type) { - return (string) count($type); - }, $overview); - Assert::assertEquals($expected, $summarized); + $contents = $this->response->getBody()->getContents(); + $this->assertEmptyArrayIsNotAListButADictionary($formData, $contents); + $overview = $this->getDataFromResponseBody($contents); + + if ($formData instanceof TableNode) { + $expected = $formData->getRowsHash(); + $summarized = array_map(function ($type) { + return (string) count($type); + }, $overview); + Assert::assertEquals($expected, $summarized); + } } /** @@ -2985,7 +2990,16 @@ public function userCheckCapability($user, $capability, $value) { * @return array */ protected function getDataFromResponse(ResponseInterface $response) { - $jsonBody = json_decode($response->getBody()->getContents(), true); + return $this->getDataFromResponseBody($response->getBody()->getContents()); + } + + /** + * Parses the JSON answer to get the array of users returned. + * @param string $response + * @return array + */ + protected function getDataFromResponseBody(string $response) { + $jsonBody = json_decode($response, true); return $jsonBody['ocs']['data']; } @@ -3465,7 +3479,9 @@ public function userReactWithOnMessageToRoomWith(string $user, string $action, s 'reaction' => $reaction ]); $this->assertStatusCode($this->response, $statusCode); - $this->assertReactionList($formData); + if ($statusCode === 200 || $statusCode === 201) { + $this->assertReactionList($formData); + } } /** @@ -3483,10 +3499,15 @@ public function userRetrieveReactionsOfMessageInRoomWith(string $user, string $r } private function assertReactionList(?TableNode $formData): void { + $contents = $this->response->getBody()->getContents(); + $this->assertEmptyArrayIsNotAListButADictionary($formData, $contents); + $reactions = $this->getDataFromResponseBody($contents); + $expected = []; if (!$formData instanceof TableNode) { return; } + foreach ($formData->getHash() as $row) { $reaction = $row['reaction']; unset($row['reaction']); @@ -3501,8 +3522,7 @@ private function assertReactionList(?TableNode $formData): void { $expected[$reaction][] = $row; } - $result = $this->getDataFromResponse($this->response); - $result = array_map(static function ($reaction, $list) use ($expected): array { + $actual = array_map(static function ($reaction, $list) use ($expected): array { $list = array_map(function ($reaction) { unset($reaction['timestamp']); $reaction['actorId'] = ($reaction['actorType'] === 'guests') ? self::$sessionIdToUser[$reaction['actorId']] : (string) $reaction['actorId']; @@ -3515,8 +3535,8 @@ private function assertReactionList(?TableNode $formData): void { usort($list, [self::class, 'sortAttendees']); Assert::assertEquals($expected[$reaction], $list, 'Reaction list by type does not match'); return $list; - }, array_keys($result), array_values($result)); - Assert::assertCount(count($expected), $result, 'Reaction count does not match'); + }, array_keys($reactions), array_values($reactions)); + Assert::assertCount(count($expected), $actual, 'Reaction count does not match'); } /** @@ -3988,6 +4008,13 @@ public function getRoomListWithSpecificUserAgent(string $userAgent, int $statusC $this->assertStatusCode($this->response, $statusCode); } + protected function assertEmptyArrayIsNotAListButADictionary(?TableNode $formData, string $content) { + if (!$formData instanceof TableNode || empty($formData->getHash())) { + $data = json_decode($content); + Assert::assertIsNotArray($data->ocs->data, 'Response ocs.data should be an "object" to represent a JSON dictionary, not a list-array'); + } + } + /** * @Then the response error matches with :error */ diff --git a/tests/integration/features/chat-2/rich-object-share.feature b/tests/integration/features/chat-2/rich-object-share.feature index e8dcab5ff1f..7158b019e43 100644 --- a/tests/integration/features/chat-2/rich-object-share.feature +++ b/tests/integration/features/chat-2/rich-object-share.feature @@ -64,6 +64,7 @@ Feature: chat-2/rich-object-share Given user "participant1" creates room "public room" (v4) | roomType | 3 | | roomName | room | + Then user "participant1" sees the following shared summarized overview in room "public room" with 200 When user "participant1" shares rich-object "call" "R4nd0mT0k3n" '{"name":"Another room","call-type":"group"}' to room "public room" with 201 (v1) Then user "participant1" sees the following shared other in room "public room" with 200 | room | actorType | actorId | actorDisplayName | message | messageParameters |