diff --git a/psicash.cpp b/psicash.cpp index 794a726..05d6a2b 100644 --- a/psicash.cpp +++ b/psicash.cpp @@ -80,7 +80,11 @@ constexpr int HTTPResult::CRITICAL_ERROR; constexpr int HTTPResult::RECOVERABLE_ERROR; PsiCash::PsiCash() - : server_port_(0), make_http_request_fn_(nullptr) { + : test_(false), + initialized_(false), + server_port_(0), + user_data_(std::make_unique()), + make_http_request_fn_(nullptr) { } PsiCash::~PsiCash() { @@ -111,9 +115,16 @@ Error PsiCash::Init(const string& user_agent, const string& file_store_root, // May still be null. make_http_request_fn_ = std::move(make_http_request_fn); - user_data_ = std::make_unique(); - auto err = user_data_->Init(file_store_root, test); - return PassError(err); + if (auto err = user_data_->Init(file_store_root, test)) { + return PassError(err); + } + + initialized_ = true; + return error::nullerr; +} + +bool PsiCash::Initialized() const { + return initialized_; } Error PsiCash::Reset(const string& file_store_root, bool test) { @@ -598,6 +609,10 @@ Result PsiCash::RefreshState( 6. If there are still no valid tokens, then things are horribly wrong. Return error. */ + if (!initialized_) { + return MakeCriticalError("PsiCash is uninitialized"); + } + auto auth_tokens = user_data_->GetAuthTokens(); if (auth_tokens.empty()) { // No tokens. @@ -728,8 +743,9 @@ Result PsiCash::RefreshState( return RefreshState(purchase_classes, true); } else if (result->code == kHTTPStatusUnauthorized) { // This can only happen if the tokens we sent didn't all belong to same user. - // This really should never happen. - user_data_->Clear(); + // This really should never happen. We're not checking the return value, as there + // isn't a sane response to a failure at this point. + (void)user_data_->Clear(); return Status::InvalidTokens; } else if (IsServerError(result->code)) { return Status::ServerError; @@ -743,6 +759,10 @@ Result PsiCash::NewExpiringPurchase( const string& transaction_class, const string& distinguisher, const int64_t expected_price) { + if (!initialized_) { + return MakeCriticalError("PsiCash is uninitialized"); + } + auto result = MakeHTTPRequestWithRetry( kMethodPOST, "/transaction", diff --git a/psicash.hpp b/psicash.hpp index a0112ad..6a674fc 100644 --- a/psicash.hpp +++ b/psicash.hpp @@ -172,6 +172,8 @@ class PsiCash { /// filesystem). /// If `test` is true, then the test server will be used, and other testing interfaces /// will be available. Should only be used for testing. + /// When uninitialized, data accessors will return zero values, and operations (e.g., + /// RefreshState and NewExpiringPurchase) will return errors. error::Error Init(const std::string& user_agent, const std::string& file_store_root, MakeHTTPRequestFn make_http_request_fn, bool test=false); @@ -179,6 +181,9 @@ class PsiCash { /// Returns an error if the reset failed, likely indicating a filesystem problem. error::Error Reset(const std::string& file_store_root, bool test=false); + /// Returns true if the library has been successfully initialized (i.e., Init called). + bool Initialized() const; + /// Can be used for updating the HTTP requester function pointer. void SetHTTPRequestFn(MakeHTTPRequestFn make_http_request_fn); @@ -380,6 +385,7 @@ class PsiCash { protected: bool test_; + bool initialized_; std::string user_agent_; std::string server_scheme_; std::string server_hostname_; diff --git a/psicash_test.cpp b/psicash_test.cpp index b72fe95..87df3c3 100644 --- a/psicash_test.cpp +++ b/psicash_test.cpp @@ -156,6 +156,33 @@ TEST_F(TestPsiCash, InitFail) { } } +TEST_F(TestPsiCash, UninitializedBehaviour) { + { + // No Init + PsiCashTester pc; + ASSERT_FALSE(pc.Initialized()); + + ASSERT_EQ(pc.Balance(), 0); + + auto res = pc.RefreshState({"speed-boost"}); + ASSERT_FALSE(res); + } + { + // Failed Init + auto bad_dir = GetTempDir() + "/a/b/c/d/f/g"; + PsiCashTester pc; + auto err = pc.Init(user_agent_, bad_dir.c_str(), nullptr, true); + ASSERT_TRUE(err) << bad_dir; + + ASSERT_FALSE(pc.Initialized()); + + ASSERT_EQ(pc.Balance(), 0); + + auto res = pc.RefreshState({"speed-boost"}); + ASSERT_FALSE(res); + } +} + TEST_F(TestPsiCash, Clear) { int64_t want_balance = 123; auto temp_dir = GetTempDir();