diff --git a/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/ApigeeEdgeTeamsFunctionalTestBase.php b/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/ApigeeEdgeTeamsFunctionalTestBase.php new file mode 100644 index 00000000..3d93be03 --- /dev/null +++ b/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/ApigeeEdgeTeamsFunctionalTestBase.php @@ -0,0 +1,76 @@ +testTokenData = [ + 'access_token' => mb_strtolower($this->randomMachineName(32)), + 'token_type' => 'bearer', + 'expires_in' => 300, + 'refresh_token' => mb_strtolower($this->randomMachineName(32)), + 'scope' => 'create', + ]; + $storage = $this->tokenStorage(); + + // Save the token. + $storage->saveToken($this->testTokenData); + } + + /** + * Returns a pre-configured token storage service for testing. + * + * @param bool $rebuild + * Enforces rebuild of the container and with the the token storage + * service. + * + * @return \Drupal\apigee_edge\OauthTokenFileStorage + * The configured and initialized OAuth file token storage service. + * + * @throws \Exception + */ + private function tokenStorage(bool $rebuild = FALSE): OauthTokenFileStorage { + $config = $this->config('apigee_edge.auth'); + $config->set('oauth_token_storage_location', OauthTokenFileStorage::DEFAULT_DIRECTORY)->save(); + if ($rebuild) { + $this->container->get('kernel')->rebuildContainer(); + } + return $this->container->get('apigee_edge.authentication.oauth_token_storage'); + } + +} diff --git a/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/TeamInvitationsTest.php b/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/TeamInvitationsTest.php new file mode 100644 index 00000000..42f3d05d --- /dev/null +++ b/modules/apigee_edge_teams/tests/src/Functional/ApigeeX/TeamInvitationsTest.php @@ -0,0 +1,236 @@ +storeToken(); + $this->addApigeexOrganizationMatchedResponse(); + $this->teamA = $this->createApigeexTeam(); + $this->teamB = $this->createApigeexTeam(); + + $this->accountAdmin = $this->createAccount(['administer team']); + $this->accountUser = $this->createAccount([ + 'accept own team invitation', + ]); + + if (!$this->integration_enabled) { + $this->queueDeveloperResponse($this->accountAdmin); + Developer::load($this->accountAdmin->getEmail()); + $this->queueDeveloperResponse($this->accountUser); + Developer::load($this->accountUser->getEmail()); + } + } + + /** + * {@inheritdoc} + */ + protected function tearDown(): void { + if (!$this->integration_enabled) { + return; + } + else { + $teams = [ + $this->teamA, + $this->teamB, + ]; + foreach ($teams as $team) { + if ($team !== NULL) { + try { + $team->delete(); + } + catch (\Exception $exception) { + $this->logException($exception); + } + } + } + + $accounts = [ + $this->accountAdmin, + $this->accountUser, + ]; + foreach ($accounts as $account) { + if ($account !== NULL) { + try { + $account->delete(); + } + catch (\Exception $exception) { + $this->logException($exception); + } + } + } + } + + parent::tearDown(); + } + + /** + * Tests that an email can be invited to one or more teams. + */ + public function testMultipleInvitations() { + $this->drupalLogin($this->accountAdmin); + + $teams = [ + $this->teamA, + $this->teamB, + ]; + $appgroups = [ + $this->teamA->decorated(), + $this->teamB->decorated(), + ]; + + $inCache = FALSE; + foreach ($teams as $team) { + if (!$inCache) { + $this->queueAppGroupResponse($team->decorated()); + } + $this->drupalGet(Url::fromRoute('entity.team.add_members', [ + 'team' => $team->id(), + ])); + + $this->assertSession()->pageTextContains('Invite members'); + + $this->queueAppGroupsResponse($appgroups); + $this->queueAppGroupsResponse($appgroups); + $this->queueDevsInCompanyResponse([]); + $this->submitForm([ + 'developers' => $this->accountUser->getEmail(), + ], 'Invite members'); + + $successMessage = t('The following developer has been invited to the @team @team_label: @developer.', [ + '@developer' => $this->accountUser->getEmail(), + '@team' => $team->label(), + '@team_label' => mb_strtolower($team->getEntityType()->getSingularLabel()), + ]); + + $this->assertSession()->pageTextContains($successMessage); + $inCache = TRUE; + } + } + + /** + * Tests that a user can see their list of invitations. + */ + public function testInvitationsList() { + // Ensure "team_invitations" views page is installed. + $this->assertNotNull(Views::getView('team_invitations')); + + /** @var \Drupal\apigee_edge_teams\Entity\Storage\TeamInvitationStorageInterface $teamInvitationStorage */ + $teamInvitationStorage = $this->entityTypeManager->getStorage('team_invitation'); + $selected_roles = [TeamRoleInterface::TEAM_MEMBER_ROLE => TeamRoleInterface::TEAM_MEMBER_ROLE]; + $teams = [ + $this->teamA, + $this->teamB, + ]; + $appgroups = [ + $this->teamA->decorated(), + $this->teamB->decorated(), + ]; + + // Invite user to both teams. + foreach ($teams as $team) { + $this->queueAppGroupResponse($team->decorated()); + $teamInvitationStorage->create([ + 'team' => ['target_id' => $team->id()], + 'team_roles' => array_values(array_map(function (string $role) { + return ['target_id' => $role]; + }, $selected_roles)), + 'recipient' => $this->accountUser->getEmail(), + ])->save(); + } + + // Check that user can see both invitations. + $this->drupalLogin($this->accountUser); + $invitationsUrl = Url::fromRoute('view.team_invitations.user', [ + 'user' => $this->accountUser->id(), + ]); + + $this->queueAppGroupsResponse($appgroups); + $this->queueDevsInCompanyResponse([]); + $this->drupalGet($invitationsUrl); + foreach ($teams as $team) { + $this->assertSession()->pageTextContains('Invitation to join ' . $team->label()); + } + } + +} diff --git a/tests/modules/apigee_mock_api_client/tests/response-templates/appgroup.json.twig b/tests/modules/apigee_mock_api_client/tests/response-templates/appgroup.json.twig index d4f4719d..16c5675e 100644 --- a/tests/modules/apigee_mock_api_client/tests/response-templates/appgroup.json.twig +++ b/tests/modules/apigee_mock_api_client/tests/response-templates/appgroup.json.twig @@ -12,12 +12,18 @@ */ #} { - "apps": [], "name": "{{ appgroup.name|default('foo') }}", + "channelUri": "http:\/\/localhost:8080\/test\/web\/teams\/drupalteam", + "channelId": "devportal", "displayName": "{{ appgroup.displayName }}", "organization": "{{ org_name }}", "status": "active", - "attributes": [], + "attributes": [ + { + "name": "__apigee_reserved__developer_details", + "value": "[{\"developer\":\"doe@example.com\",\"roles\":[\"admin\"]}]" + } + ], "createdAt": 1506959878351, "createdBy" : "user@example.com", "lastModifiedAt": 1506959878351, diff --git a/tests/modules/apigee_mock_api_client/tests/response-templates/appgroups.json.twig b/tests/modules/apigee_mock_api_client/tests/response-templates/appgroups.json.twig new file mode 100644 index 00000000..322717ef --- /dev/null +++ b/tests/modules/apigee_mock_api_client/tests/response-templates/appgroups.json.twig @@ -0,0 +1,19 @@ +{# +/** + * @file + * AppGroups + * + * Usage: + * @code {% include 'appgroups.json.twig' %} @endcode + * + * Variables: + * - appgroups: an array of appgroup objects. + */ +#} +{ + "appGroups" : [ + {% for appgroup in appgroups %} + {% include 'appgroup.json.twig' with {'appgroup': appgroup} %}{{ loop.last ? '' : ',' }} + {% endfor %} + ] +} diff --git a/tests/modules/apigee_mock_api_client/tests/src/Traits/ApigeeMockApiClientHelperTrait.php b/tests/modules/apigee_mock_api_client/tests/src/Traits/ApigeeMockApiClientHelperTrait.php index 8c7ec5ef..3a8dc53b 100644 --- a/tests/modules/apigee_mock_api_client/tests/src/Traits/ApigeeMockApiClientHelperTrait.php +++ b/tests/modules/apigee_mock_api_client/tests/src/Traits/ApigeeMockApiClientHelperTrait.php @@ -298,6 +298,21 @@ protected function queueCompaniesResponse(array $companies, $response_code = NUL $this->stack->queueMockResponse(['companies' => $context]); } + /** + * Queues up a mock appgroups response. + * + * @param array $appgroups + * An array of appgroup objects. + * @param string|null $response_code + * Add a response code to override the default. + */ + protected function queueAppGroupsResponse(array $appgroups, $response_code = NULL) { + $context = empty($response_code) ? [] : ['status_code' => $response_code]; + $context['appgroups'] = $appgroups; + + $this->stack->queueMockResponse(['appgroups' => $context]); + } + /** * Queues up a mock developers in a company response. * diff --git a/tests/src/Functional/ApigeeX/ApigeeEdgeFunctionalTestBase.php b/tests/src/Functional/ApigeeX/ApigeeEdgeFunctionalTestBase.php new file mode 100644 index 00000000..36722a64 --- /dev/null +++ b/tests/src/Functional/ApigeeX/ApigeeEdgeFunctionalTestBase.php @@ -0,0 +1,53 @@ +markTestSkipped('This test suite is expecting a HYBRID instance type.'); + } + parent::setUp(); + $this->initTestEnv(); + } + +} diff --git a/tests/src/Traits/ApigeeEdgeFunctionalTestTrait.php b/tests/src/Traits/ApigeeEdgeFunctionalTestTrait.php index ecfbdaf8..bb076c23 100644 --- a/tests/src/Traits/ApigeeEdgeFunctionalTestTrait.php +++ b/tests/src/Traits/ApigeeEdgeFunctionalTestTrait.php @@ -100,7 +100,7 @@ protected function createAccount(array $permissions = [], bool $status = TRUE, s $edit = [ 'first_name' => $this->randomMachineName(), 'last_name' => $this->randomMachineName(), - 'name' => $this->randomMachineName(), + 'name' => strtolower($this->randomMachineName()), 'pass' => \Drupal::service('password_generator')->generate(), 'status' => $status, ];