blob: b6f048426b7b85ddeeb3651a33ea213a83408026 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ledger/lib/firebase_auth/firebase_auth_impl.h"
#include <fuchsia/auth/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/gtest/test_loop_fixture.h>
#include <utility>
#include "src/ledger/lib/firebase_auth/testing/test_token_manager.h"
#include "src/lib/backoff/testing/test_backoff.h"
#include "src/lib/callback/capture.h"
#include "src/lib/callback/set_when_called.h"
#include "src/lib/timekeeper/clock.h"
#include "src/lib/timekeeper/test_clock.h"
namespace firebase_auth {
namespace {
class MockCobaltLogger : public cobalt::CobaltLogger {
public:
~MockCobaltLogger() override = default;
explicit MockCobaltLogger(int* called) : called_(called) {}
void LogEvent(uint32_t metric_id, uint32_t event_code) override {}
void LogEventCount(uint32_t metric_id, uint32_t /*event_code*/, const std::string& component,
zx::duration /*period_duration*/, int64_t /*count*/) override {
EXPECT_EQ(4u, metric_id);
EXPECT_TRUE(component.find("firebase-test") != std::string::npos);
*called_ += 1;
}
void LogElapsedTime(uint32_t metric_id, uint32_t event_code, const std::string& component,
zx::duration elapsed_time) override {}
void LogFrameRate(uint32_t metric_id, uint32_t event_code, const std::string& component,
float fps) override {}
void LogMemoryUsage(uint32_t metric_id, uint32_t event_code, const std::string& component,
int64_t bytes) override {}
void LogString(uint32_t metric_id, const std::string& s) override {}
void StartTimer(uint32_t metric_id, uint32_t event_code, const std::string& component,
const std::string& timer_id, zx::time timestamp, zx::duration timeout) override {}
void EndTimer(const std::string& timer_id, zx::time timestamp, zx::duration timeout) override {}
void LogIntHistogram(uint32_t metric_id, uint32_t event_code, const std::string& component,
std::vector<fuchsia::cobalt::HistogramBucket> histogram) override {}
void LogCustomEvent(uint32_t metric_id,
std::vector<fuchsia::cobalt::CustomEventValue> event_values) override {}
void LogCobaltEvent(fuchsia::cobalt::CobaltEvent event) override {}
void LogCobaltEvents(std::vector<fuchsia::cobalt::CobaltEvent> events) override {}
private:
int* called_;
};
class FirebaseAuthImplTest : public gtest::TestLoopFixture {
public:
FirebaseAuthImplTest()
: token_manager_(dispatcher()),
token_manager_binding_(&token_manager_),
firebase_auth_(
{"api_key", "user_id", /* collect_cobalt_metrics */ true, "firebase-test", 1},
dispatcher(), &clock_, token_manager_binding_.NewBinding().Bind(), InitBackoff(),
std::make_unique<MockCobaltLogger>(&report_observation_count_)) {}
~FirebaseAuthImplTest() override = default;
protected:
std::unique_ptr<backoff::Backoff> InitBackoff() {
// TODO(LE-630): Don't use a backoff duration of 0.
auto backoff = std::make_unique<backoff::TestBackoff>(zx::sec(0));
backoff_ = backoff.get();
return backoff;
}
timekeeper::TestClock clock_;
TestTokenManager token_manager_;
fidl::Binding<fuchsia::auth::TokenManager> token_manager_binding_;
FirebaseAuthImpl firebase_auth_;
int report_observation_count_ = 0;
backoff::TestBackoff* backoff_;
private:
FXL_DISALLOW_COPY_AND_ASSIGN(FirebaseAuthImplTest);
};
const uint64_t kTokenExpiry = 3600;
TEST_F(FirebaseAuthImplTest, GetFirebaseToken) {
clock_.Set(zx::time());
token_manager_.Set("this is a token", "some id", "me@example.com", kTokenExpiry);
bool called;
AuthStatus auth_status;
std::string firebase_token;
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::OK);
EXPECT_EQ(firebase_token, "this is a token");
EXPECT_EQ(report_observation_count_, 0);
}
TEST_F(FirebaseAuthImplTest, GetFirebaseTokenRetryOnError) {
bool called;
AuthStatus auth_status;
std::string firebase_token;
clock_.Set(zx::time());
token_manager_.SetError(fuchsia::auth::Status::NETWORK_ERROR);
backoff_->SetOnGetNext(QuitLoopClosure());
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_FALSE(called);
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 0);
EXPECT_EQ(report_observation_count_, 0);
token_manager_.Set("this is a token", "some id", "me@example.com", kTokenExpiry);
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::OK);
EXPECT_EQ(firebase_token, "this is a token");
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 1);
EXPECT_EQ(report_observation_count_, 0);
}
TEST_F(FirebaseAuthImplTest, GetFirebaseUserId) {
clock_.Set(zx::time());
token_manager_.Set("this is a token", "some id", "me@example.com", kTokenExpiry);
bool called;
AuthStatus auth_status;
std::string firebase_user_id;
firebase_auth_.GetFirebaseUserId(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_user_id));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::OK);
EXPECT_EQ(firebase_user_id, "some id");
EXPECT_EQ(report_observation_count_, 0);
}
TEST_F(FirebaseAuthImplTest, GetFirebaseUserIdRetryOnError) {
bool called;
AuthStatus auth_status;
std::string firebase_id;
clock_.Set(zx::time());
token_manager_.SetError(fuchsia::auth::Status::NETWORK_ERROR);
backoff_->SetOnGetNext(QuitLoopClosure());
firebase_auth_.GetFirebaseUserId(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_id));
RunLoopUntilIdle();
EXPECT_FALSE(called);
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 0);
EXPECT_EQ(report_observation_count_, 0);
token_manager_.Set("this is a token", "some id", "me@example.com", kTokenExpiry);
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::OK);
EXPECT_EQ(firebase_id, "some id");
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 1);
EXPECT_EQ(report_observation_count_, 0);
}
TEST_F(FirebaseAuthImplTest, GetFirebaseUserIdMaxRetry) {
bool called;
AuthStatus auth_status;
clock_.Set(zx::time());
token_manager_.SetError(fuchsia::auth::Status::NETWORK_ERROR);
backoff_->SetOnGetNext(QuitLoopClosure());
firebase_auth_.GetFirebaseUserId(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &std::ignore));
RunLoopUntilIdle();
EXPECT_FALSE(called);
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 0);
EXPECT_EQ(report_observation_count_, 0);
// Exceeding the maximum number of retriable errors returns an error.
token_manager_.SetError(fuchsia::auth::Status::INTERNAL_ERROR);
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::ERROR);
EXPECT_EQ(backoff_->get_next_count, 1);
EXPECT_EQ(backoff_->reset_count, 1);
EXPECT_EQ(report_observation_count_, 1);
}
TEST_F(FirebaseAuthImplTest, GetFirebaseUserIdNonRetriableError) {
bool called;
AuthStatus auth_status;
clock_.Set(zx::time());
token_manager_.SetError(fuchsia::auth::Status::INVALID_REQUEST);
backoff_->SetOnGetNext(QuitLoopClosure());
firebase_auth_.GetFirebaseUserId(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &std::ignore));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(auth_status, AuthStatus::ERROR);
EXPECT_EQ(backoff_->get_next_count, 0);
EXPECT_EQ(backoff_->reset_count, 1);
EXPECT_EQ(report_observation_count_, 1);
}
TEST_F(FirebaseAuthImplTest, GetCachedFirebaseToken) {
clock_.Set(zx::time());
token_manager_.Set("this is the first token", "some id", "me@example.com", kTokenExpiry);
bool called;
AuthStatus auth_status;
std::string firebase_token;
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(AuthStatus::OK, auth_status);
EXPECT_EQ("this is the first token", firebase_token);
EXPECT_EQ(0, report_observation_count_);
token_manager_.Set("this is the second token", "some id", "me@example.com", kTokenExpiry);
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(AuthStatus::OK, auth_status);
EXPECT_EQ("this is the first token", firebase_token);
EXPECT_EQ(0, report_observation_count_);
}
TEST_F(FirebaseAuthImplTest, GetFreshFirebaseTokenAfterExpiry) {
clock_.Set(zx::time());
token_manager_.Set("this is the first token", "some id", "me@example.com", kTokenExpiry);
bool called;
AuthStatus auth_status;
std::string firebase_token;
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(AuthStatus::OK, auth_status);
EXPECT_EQ("this is the first token", firebase_token);
EXPECT_EQ(0, report_observation_count_);
clock_.Set(clock_.Now() + zx::sec(kTokenExpiry));
token_manager_.Set("this is the second token", "some id", "me@example.com", kTokenExpiry);
firebase_auth_.GetFirebaseToken(
callback::Capture(callback::SetWhenCalled(&called), &auth_status, &firebase_token));
RunLoopUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(AuthStatus::OK, auth_status);
EXPECT_EQ("this is the second token", firebase_token);
EXPECT_EQ(0, report_observation_count_);
}
} // namespace
} // namespace firebase_auth