diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 29bceef1655..ff28b7f3ea2 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -155,30 +155,6 @@ public function userIsParticipantOfRooms($user, $apiVersion = 'v1', TableNode $f private function assertRooms($rooms, TableNode $formData) { Assert::assertCount(count($formData->getHash()), $rooms, 'Room count does not match'); Assert::assertEquals($formData->getHash(), array_map(function ($room, $expectedRoom) { - $participantNames = array_map(function ($participant) { - return $participant['name']; - }, $room['participants']); - - // When participants have the same last ping the order in which they - // are returned from the server is undefined. That is the most - // common case during the tests, so by default the list of - // participants returned by the server is sorted alphabetically. In - // order to check the exact order of participants returned by the - // server " [exact order]" can be appended in the test definition to - // the list of expected participants of the room. - if (strpos($expectedRoom['participants'], ' [exact order]') === false) { - sort($participantNames); - } else { - // "end(array_keys(..." would generate the Strict Standards - // error "Only variables should be passed by reference". - $participantNamesKeys = array_keys($participantNames); - $lastParticipantKey = end($participantNamesKeys); - - // Append " [exact order]" to the last participant so the - // imploded string is the same as the expected one. - $participantNames[$lastParticipantKey] .= ' [exact order]'; - } - $data = []; if (isset($expectedRoom['id'])) { $data['id'] = self::$tokenToIdentifier[$room['token']]; @@ -199,8 +175,37 @@ private function assertRooms($rooms, TableNode $formData) { $data['participantType'] = (string) $room['participantType']; } if (isset($expectedRoom['participants'])) { + $participantNames = array_map(function ($participant) { + return $participant['name']; + }, $room['participants']); + + // When participants have the same last ping the order in which they + // are returned from the server is undefined. That is the most + // common case during the tests, so by default the list of + // participants returned by the server is sorted alphabetically. In + // order to check the exact order of participants returned by the + // server " [exact order]" can be appended in the test definition to + // the list of expected participants of the room. + if (strpos($expectedRoom['participants'], ' [exact order]') === false) { + sort($participantNames); + } else { + // "end(array_keys(..." would generate the Strict Standards + // error "Only variables should be passed by reference". + $participantNamesKeys = array_keys($participantNames); + $lastParticipantKey = end($participantNamesKeys); + + // Append " [exact order]" to the last participant so the + // imploded string is the same as the expected one. + $participantNames[$lastParticipantKey] .= ' [exact order]'; + } $data['participants'] = implode(', ', $participantNames); } + if (isset($expectedRoom['sipEnabled'])) { + $data['sipEnabled'] = (string) $room['sipEnabled']; + } + if (isset($expectedRoom['attendeePin'])) { + $data['attendeePin'] = $room['attendeePin'] ? '**PIN**' : ''; + } return $data; }, $rooms, $formData->getHash())); @@ -257,6 +262,72 @@ public function userIsParticipantOfRoom($user, $isOrNotParticipant, $identifier, Assert::assertEquals($isParticipant, false, 'Room ' . $identifier . ' not found in user“s room list'); } + /** + * @Then /^user "([^"]*)" sees the following attendees in room "([^"]*)" with (\d+)(?: \((v(1|2|3))\))?$/ + * + * @param string $user + * @param string $identifier + * @param string $statusCode + * @param string $apiVersion + * @param TableNode $formData + */ + public function userSeesAttendeesInRoom($user, $identifier, $statusCode, $apiVersion = 'v1', TableNode $formData = null) { + $this->setCurrentUser($user); + $this->sendRequest('GET', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/participants'); + $this->assertStatusCode($this->response, $statusCode); + + if ($formData instanceof TableNode) { + $attendees = $this->getDataFromResponse($this->response); + $expectedKeys = array_flip($formData->getRows()[0]); + + $result = []; + foreach ($attendees as $attendee) { + $data = []; + if (isset($expectedKeys['actorType'])) { + $data['actorType'] = $attendee['actorType']; + } + if (isset($expectedKeys['actorId'])) { + $data['actorId'] = $attendee['actorId']; + } + if (isset($expectedKeys['participantType'])) { + $data['participantType'] = (string) $attendee['participantType']; + } + if (isset($expectedKeys['inCall'])) { + $data['inCall'] = (string) $attendee['inCall']; + } + if (isset($expectedKeys['attendeePin'])) { + $data['attendeePin'] = $attendee['attendeePin'] ? '**PIN**' : ''; + } + + $result[] = $data; + } + + $expected = array_map(function ($attendee) { + if (isset($attendee['actorId']) && substr($attendee['actorId'], 0, strlen('"guest')) === '"guest') { + $attendee['actorId'] = sha1(self::$userToSessionId[trim($attendee['actorId'], '"')]); + } + return $attendee; + }, $formData->getHash()); + + usort($expected, [$this, 'sortAttendees']); + usort($result, [$this, 'sortAttendees']); + + Assert::assertEquals($result, $expected); + } else { + Assert::assertNull($formData); + } + } + + protected function sortAttendees(array $a1, array $a2): int { + if ($a1['participantType'] !== $a2['participantType']) { + return $a1['participantType'] <=> $a2['participantType']; + } + if ($a1['actorType'] !== $a2['actorType']) { + return $a1['actorType'] <=> $a2['actorType']; + } + return $a1['actorId'] <=> $a2['actorId']; + } + /** * @param string $guest * @param string $isOrNotParticipant @@ -625,17 +696,42 @@ public function userSetsLobbyStateForRoomTo($user, $identifier, $lobbyStateStrin $lobbyState = 1; } else { Assert::fail('Invalid lobby state'); - return; } $this->setCurrentUser($user); $this->sendRequest( - 'PUT', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/webinary/lobby', + 'PUT', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/webinar/lobby', new TableNode([['state', $lobbyState]]) ); $this->assertStatusCode($this->response, $statusCode); } + /** + * @When /^user "([^"]*)" sets SIP state for room "([^"]*)" to "([^"]*)" with (\d+)(?: \((v(1|2|3))\))?$/ + * + * @param string $user + * @param string $identifier + * @param string $SIPStateString + * @param string $statusCode + * @param string $apiVersion + */ + public function userSetsSIPStateForRoomTo($user, $identifier, $SIPStateString, $statusCode, $apiVersion = 'v1') { + if ($SIPStateString === 'disabled') { + $SIPState = 0; + } elseif ($SIPStateString === 'enabled') { + $SIPState = 1; + } else { + Assert::fail('Invalid SIP state'); + } + + $this->setCurrentUser($user); + $this->sendRequest( + 'PUT', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/webinar/sip', + new TableNode([['state', $SIPState]]) + ); + $this->assertStatusCode($this->response, $statusCode); + } + /** * @Then /^user "([^"]*)" makes room "([^"]*)" (public|private) with (\d+)(?: \((v(1|2|3))\))?$/ * @@ -690,6 +786,29 @@ public function userAddUserToRoom($user, $newUser, $identifier, $statusCode, $ap $this->assertStatusCode($this->response, $statusCode); } + /** + * @Then /^user "([^"]*)" adds (user|group|email|circle) "([^"]*)" to room "([^"]*)" with (\d+)(?: \((v(1|2|3))\))?$/ + * + * @param string $user + * @param string $newType + * @param string $newId + * @param string $identifier + * @param string $statusCode + * @param string $apiVersion + */ + public function userAddAttendeeToRoom($user, $newType, $newId, $identifier, $statusCode, $apiVersion = 'v1') { + var_dump($newType); + $this->setCurrentUser($user); + $this->sendRequest( + 'POST', '/apps/spreed/api/' . $apiVersion . '/room/' . self::$identifierToToken[$identifier] . '/participants', + new TableNode([ + ['source', $newType . 's'], + ['newParticipant', $newId], + ]) + ); + $this->assertStatusCode($this->response, $statusCode); + } + /** * @Then /^user "([^"]*)" (promotes|demotes) "([^"]*)" in room "([^"]*)" with (\d+)(?: \((v(1|2|3))\))?$/ * diff --git a/tests/integration/features/conversation/sip-dialin.feature b/tests/integration/features/conversation/sip-dialin.feature new file mode 100644 index 00000000000..f47448ed85f --- /dev/null +++ b/tests/integration/features/conversation/sip-dialin.feature @@ -0,0 +1,67 @@ +Feature: public + Background: + Given user "participant1" exists + Given user "participant2" exists + Given user "participant3" exists + Given group "group1" exists + Given user "participant1" is member of group "group1" + + Scenario: SIP admin enables SIP + Given the following app config is set + | sip_bridge_dialin_info | +49-1234-567890 | + | sip_bridge_shared_secret | 1234567890abcdef | + | sip_bridge_groups | ["group1"] | + Given user "participant1" creates room "room" + | roomType | 3 | + | roomName | room | + And user "participant1" is participant of the following rooms (v3) + | id | type | participantType | sipEnabled | attendeePin | + | room | 3 | 1 | 0 | | + When user "participant1" sets SIP state for room "room" to "enabled" with 200 (v3) + Then user "participant1" is participant of the following rooms (v3) + | id | type | participantType | sipEnabled | attendeePin | + | room | 3 | 1 | 1 | **PIN** | + When user "participant1" adds user "participant2" to room "room" with 200 (v3) + When user "participant1" adds "participant3" to room "room" with 200 (v3) + When user "participant1" adds email "test@example.tld" to room "room" with 200 (v3) + # Guests don't get a PIN as they can not be recognized and are deleted on leave + When user "guest" joins room "room" with 200 + Then user "participant1" sees the following attendees in room "room" with 200 (v3) + | participantType | inCall | actorType | actorId | attendeePin | + | 4 | 0 | emails | test@example.tld | **PIN** | + | 4 | 0 | guests | "guest" | | + | 1 | 0 | users | participant1 | **PIN** | + | 3 | 0 | users | participant2 | **PIN** | + | 3 | 0 | users | participant3 | **PIN** | + When user "participant2" sets SIP state for room "room" to "disabled" with 403 (v3) + Then user "participant1" sees the following attendees in room "room" with 200 (v3) + | participantType | inCall | actorType | actorId | attendeePin | + | 4 | 0 | emails | test@example.tld | **PIN** | + | 4 | 0 | guests | "guest" | | + | 1 | 0 | users | participant1 | **PIN** | + | 3 | 0 | users | participant2 | **PIN** | + | 3 | 0 | users | participant3 | **PIN** | + When user "participant1" sets SIP state for room "room" to "disabled" with 200 (v3) + Then user "participant1" sees the following attendees in room "room" with 200 (v3) + | participantType | inCall | actorType | actorId | attendeePin | + | 4 | 0 | emails | test@example.tld | | + | 4 | 0 | guests | "guest" | | + | 1 | 0 | users | participant1 | | + | 3 | 0 | users | participant2 | | + | 3 | 0 | users | participant3 | | + + Scenario: Non-SIP admin tries to enable SIP + Given the following app config is set + | sip_bridge_dialin_info | +49-1234-567890 | + | sip_bridge_shared_secret | 1234567890abcdef | + | sip_bridge_groups | ["group1"] | + Given user "participant2" creates room "room" + | roomType | 3 | + | roomName | room | + And user "participant2" is participant of the following rooms (v3) + | id | type | participantType | sipEnabled | attendeePin | + | room | 3 | 1 | 0 | | + When user "participant2" sets SIP state for room "room" to "enabled" with 403 (v3) + And user "participant2" is participant of the following rooms (v3) + | id | type | participantType | sipEnabled | attendeePin | + | room | 3 | 1 | 0 | |