// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.acceptance.testsuite.request;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth.assert_;
import static com.google.common.truth.Truth8.assertThat;

import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.CurrentUser.PropertyKey;
import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;

@UseSsh
public class RequestScopeOperationsImplTest extends AbstractDaemonTest {
  private static final AtomicInteger changeCounter = new AtomicInteger();

  @Inject private AccountOperations accountOperations;
  @Inject private Provider<CurrentUser> userProvider;
  @Inject private RequestScopeOperationsImpl requestScopeOperations;
  @Inject private Sequences sequences;

  @Test
  public void setApiUserToExistingUserById() throws Exception {
    fastCheckCurrentUser(admin.id());
    AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(user.id());
    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
    checkCurrentUser(user.id());
  }

  @Test
  public void setApiUserToExistingUserByTestAccount() throws Exception {
    fastCheckCurrentUser(admin.id());
    TestAccount testAccount =
        accountOperations.account(accountOperations.newAccount().username("tester").create()).get();
    AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(testAccount);
    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
    checkCurrentUser(testAccount.accountId());
  }

  @Test
  public void setApiUserToNonExistingUser() throws Exception {
    fastCheckCurrentUser(admin.id());
    try {
      requestScopeOperations.setApiUser(Account.id(sequences.nextAccountId()));
      assert_().fail("expected RuntimeException");
    } catch (RuntimeException e) {
      // Expected.
    }
    checkCurrentUser(admin.id());
  }

  @Test
  public void resetCurrentApiUserClearsCachedState() throws Exception {
    requestScopeOperations.setApiUser(user.id());
    PropertyKey<String> key = PropertyKey.create();
    atrScope.get().getUser().put(key, "foo");
    assertThat(atrScope.get().getUser().get(key)).hasValue("foo");

    AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.resetCurrentApiUser();
    checkCurrentUser(user.id());
    assertThat(atrScope.get().getUser().get(key)).isEmpty();
    assertThat(oldCtx.getUser().get(key)).hasValue("foo");
  }

  @Test
  public void setApiUserAnonymousSetsAnonymousUser() throws Exception {
    fastCheckCurrentUser(admin.id());
    requestScopeOperations.setApiUserAnonymous();
    assertThat(userProvider.get()).isInstanceOf(AnonymousUser.class);
  }

  private void fastCheckCurrentUser(Account.Id expected) {
    // Check current user quickly, since the full check requires creating changes and is quite slow.
    assertWithMessage("user from provider is an IdentifiedUser")
        .that(userProvider.get().isIdentifiedUser())
        .isTrue();
    assertWithMessage("user from provider")
        .that(userProvider.get().getAccountId())
        .isEqualTo(expected);
  }

  private void checkCurrentUser(Account.Id expected) throws Exception {
    // Test all supported ways that an acceptance test might query the active user.
    fastCheckCurrentUser(expected);
    assertWithMessage("user from GerritApi")
        .that(gApi.accounts().self().get()._accountId)
        .isEqualTo(expected.get());
    AcceptanceTestRequestScope.Context ctx = atrScope.get();
    assertWithMessage("user from AcceptanceTestRequestScope.Context is an IdentifiedUser")
        .that(ctx.getUser().isIdentifiedUser())
        .isTrue();
    assertWithMessage("user from AcceptanceTestRequestScope.Context")
        .that(ctx.getUser().getAccountId())
        .isEqualTo(expected);
    checkSshUser(expected);
  }

  private void checkSshUser(Account.Id expected) throws Exception {
    // No "gerrit whoami" command, so the simplest way to check who the user is over SSH is to query
    // for owner:self.
    ChangeInput cin = new ChangeInput();
    cin.project = project.get();
    cin.branch = "master";
    cin.subject = "Test change " + changeCounter.incrementAndGet();
    String changeId = gApi.changes().create(cin).get().changeId;
    assertThat(gApi.changes().id(changeId).get().owner._accountId).isEqualTo(expected.get());
    String queryResults =
        atrScope.get().getSession().exec("gerrit query owner:self change:" + changeId);
    assertWithMessage("Change-Ids in query results:\n%s", queryResults)
        .that(findDistinct(queryResults, "I[0-9a-f]{40}"))
        .containsExactly(changeId);
  }

  private static ImmutableSet<String> findDistinct(String input, String pattern) {
    Matcher m = Pattern.compile(pattern).matcher(input);
    ImmutableSet.Builder<String> b = ImmutableSet.builder();
    while (m.find()) {
      b.add(m.group(0));
    }
    return b.build();
  }
}

@@ -825,16 +825,20 @@
 ruth
+8
 .assert
-_
+That
 ;%0Aim
@@ -860,35 +860,42 @@
 .google.
-common.truth.Truth8
+gerrit.testing.GerritJUnit
 .assertT
@@ -895,18 +895,20 @@
 assertTh
-at
+rows
 ;%0A%0Aimpor
@@ -3037,19 +3037,67 @@
     
-try %7B%0A     
+assertThrows(%0A        RuntimeException.class,%0A        () -%3E
  req
@@ -3169,120 +3169,10 @@
 ()))
+)
 ;
-%0A      assert_().fail(%22expected RuntimeException%22);%0A    %7D catch (RuntimeException e) %7B%0A      // Expected.%0A    %7D
 %0A   

Migrate from try-catch-fail to assertThrows