1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
|
/**
* @file SdkTestCreateAccount_test.cpp
* @brief This file defines some tests that check account creation with different types of clients
*/
#include "env_var_accounts.h"
#include "megaapi.h"
#include "sdk_test_utils.h"
#include "SdkTest_test.h"
#include <gmock/gmock.h>
using namespace sdk_test;
/**
* @class SdkTesCreateAccount
* @brief An abstract class that provides a template fixture/test suite to test account creation
* with different client types
*
*/
class SdkTestCreateAccount: public SdkTest, virtual public ::testing::WithParamInterface<int>
{
public:
void TearDown() override;
void doCreateAccountTest(const std::string& testName, int clientType);
};
std::string getLinkFromMailbox(const std::string& exe, // Python
const std::string& script, // email_processor.py
const std::string& realAccount, // user
const std::string& realPswd, // password for user@host.domain
const std::string& toAddr, // user+testnewaccount@host.domain
const std::string& intent, // confirm / delete
const std::chrono::steady_clock::time_point& timeOfEmail)
{
using namespace std::chrono; // Just for this little scope
std::string command = exe + " \"" + script + "\" \"" + realAccount + "\" \"" + realPswd +
"\" \"" + toAddr + "\" " + intent;
std::string output;
// Wait for the link to be sent
constexpr seconds delta = 10s;
constexpr minutes maxTimeout = 10min;
seconds spentTime = 0s;
for (; spentTime < maxTimeout && output.empty(); spentTime += delta)
{
WaitMillisec(duration_cast<milliseconds>(delta).count());
// get time interval to look for emails, add some seconds to account for delays related to
// the python script call
constexpr seconds safetyDelay = 5s;
const auto attemptTime = steady_clock::now();
seconds timeSinceEmail = duration_cast<seconds>(attemptTime - timeOfEmail) + safetyDelay;
// Run Python script
output = runProgram(command + ' ' + std::to_string(timeSinceEmail.count()),
PROG_OUTPUT_TYPE::TEXT);
}
LOG_debug << "Time spent trying to get the email: " << spentTime.count() << "s";
// Print whatever was fetched from the mailbox
LOG_debug << "Link from email (" << intent << "): " << (output.empty() ? "[empty]" : output);
// Validate the link
constexpr char expectedLinkPrefix[] = "https://";
return output.substr(0, sizeof(expectedLinkPrefix) - 1) == expectedLinkPrefix ? output :
std::string();
}
std::string getUniqueAlias()
{
// use n random chars
int n = 4;
std::string alias;
auto t = std::time(nullptr);
srand((unsigned int)t);
for (int i = 0; i < n; ++i)
{
alias += static_cast<char>('a' + rand() % 26);
}
// add a timestamp
auto tm = *std::localtime(&t);
std::ostringstream oss;
oss << std::put_time(&tm, "%Y%m%d%H%M%S");
alias += oss.str();
return alias;
}
void SdkTestCreateAccount::TearDown()
{
releaseMegaApi(1);
releaseMegaApi(2);
if (!megaApi.empty() && megaApi[0])
{
releaseMegaApi(0);
}
out() << "Teardown done, test exiting";
}
/*
* doCreateAccountTest(testName, clientType)
*
* testName
* - Name of the concrete test invoking this function.
*
* clientType
* - The type of the client we want to perform this test on.
*
* This function tests the creation of a new account for a random user.
* - Create account and send confirmation link
* - Logout and resume the create-account process
* - Extract confirmation link from the mailbox
* - Use the link to confirm the account
*
* - Request a reset password link
* - Confirm the reset password
*
* - Login to the new account
* - Request cancel account link
* - Extract cancel account link from the mailbox
* - Use the link to cancel the account
*/
void SdkTestCreateAccount::doCreateAccountTest(const std::string& testName, int clientType)
{
LOG_info << "___TEST " << testName << "____";
// Make sure the new account details have been set up
const auto bufRealEmail = Utils::getenv("MEGA_REAL_EMAIL", ""); // user@host.domain
const auto bufRealPswd =
Utils::getenv("MEGA_REAL_PWD", ""); // email password of user@host.domain
fs::path bufScript = getLinkExtractSrciptPath();
ASSERT_TRUE(!bufRealEmail.empty() && !bufRealPswd.empty())
<< "MEGA_REAL_EMAIL, MEGA_REAL_PWD env vars must all be defined";
// test that Python 3 was installed
std::string pyExe = "python";
{
const std::string pyOpt = " -V";
const std::string pyExpected = "Python 3.";
std::string output = runProgram(pyExe + pyOpt, PROG_OUTPUT_TYPE::TEXT); // Python -V
if (output.substr(0, pyExpected.length()) != pyExpected)
{
pyExe += "3";
output = runProgram(pyExe + pyOpt, PROG_OUTPUT_TYPE::TEXT); // Python3 -V
ASSERT_EQ(pyExpected, output.substr(0, pyExpected.length()))
<< "Python 3 was not found.";
}
LOG_debug << "Using " << output;
}
megaApi.resize(1);
mApi.resize(1);
ASSERT_NO_FATAL_FAILURE(configureTestInstance(0, bufRealEmail, bufRealPswd, true, clientType));
// create the account
// ------------------
LOG_debug << testName << ": Start account creation";
const std::string realEmail(bufRealEmail); // user@host.domain
std::string::size_type pos = realEmail.find('@');
const std::string realAccount = realEmail.substr(0, pos); // user
[[maybe_unused]] const auto [testEmail, _] = getEnvVarAccounts().getVarValues(0);
const std::string newTestAcc = realAccount + '+' + testEmail.substr(0, testEmail.find("@")) +
'+' + getUniqueAlias() +
realEmail.substr(pos); // user+testUser+rand20210919@host.domain
LOG_info << "Creating Mega account " << newTestAcc;
const char* origTestPwd = "TestPswd!@#$"; // maybe this should be logged too, changed later
// save point in time for account init
std::chrono::time_point timeOfConfirmEmail = std::chrono::steady_clock::now();
// Create an ephemeral session internally and send a confirmation link to email
ASSERT_EQ(
API_OK,
synchronousCreateAccount(0, newTestAcc.c_str(), origTestPwd, "MyFirstname", "MyLastname"));
if (clientType == MegaApi::CLIENT_TYPE_PASSWORD_MANAGER)
{
RequestTracker rt{megaApi[0].get()};
megaApi[0]->getPasswordManagerBase(&rt);
EXPECT_EQ(API_OK, rt.waitForResult())
<< "Getting Password Manager Base node through shortcut failed";
EXPECT_NE(nullptr, rt.request);
EXPECT_NE(INVALID_HANDLE, rt.request->getNodeHandle())
<< "Invalid Password Manager Base node retrieved";
}
LOG_debug << testName << ": Logout and resume";
// Logout from ephemeral session and resume session
ASSERT_NO_FATAL_FAILURE(locallogout());
ASSERT_EQ(API_OK, synchronousResumeCreateAccount(0, mApi[0].getSid().c_str()));
// Get confirmation link from the email
{
LOG_debug << testName << ": Get confirmation link from email";
std::string conformLink = getLinkFromMailbox(pyExe,
bufScript.string(),
realAccount,
bufRealPswd,
newTestAcc,
MegaClient::confirmLinkPrefix(),
timeOfConfirmEmail);
ASSERT_FALSE(conformLink.empty()) << "Confirmation link was not found.";
LOG_debug << testName << ": Confirm account";
// create another connection to confirm the account
megaApi.resize(2);
mApi.resize(2);
ASSERT_NO_FATAL_FAILURE(
configureTestInstance(1, bufRealEmail, bufRealPswd, true, clientType));
PerApi& initialConn = mApi[0];
initialConn.resetlastEvent();
// Use confirmation link
ASSERT_EQ(API_OK, synchronousConfirmSignupLink(1, conformLink.c_str()));
// check for event triggered by 'uec' action packet received after the confirmation
EXPECT_TRUE(WaitFor(
[&initialConn]()
{
return initialConn.lastEventsContain(MegaEvent::EVENT_CONFIRM_USER_EMAIL);
},
10000))
<< "EVENT_CONFIRM_USER_EMAIL event triggered by 'uec' action packet was not received";
}
// Login to the new account
{
LOG_debug << testName << ": Login to the new account";
std::unique_ptr<RequestTracker> loginTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->login(newTestAcc.c_str(), origTestPwd, loginTracker.get());
ASSERT_EQ(API_OK, loginTracker->waitForResult())
<< " Failed to login to account " << newTestAcc.c_str();
}
// fetchnodes // needed internally to fill in user details, including email
{
LOG_debug << testName << ": fetch nodes from new account";
std::unique_ptr<RequestTracker> fetchnodesTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->fetchNodes(fetchnodesTracker.get());
ASSERT_EQ(API_OK, fetchnodesTracker->waitForResult())
<< " Failed to fetchnodes for account " << newTestAcc.c_str();
}
// test resetting the password
// ---------------------------
LOG_debug << testName << ": Start reset password";
std::chrono::time_point timeOfResetEmail = chrono::steady_clock::now();
ASSERT_EQ(synchronousResetPassword(0, newTestAcc.c_str(), true), MegaError::API_OK)
<< "resetPassword failed";
// Get password reset link from the mailbox
const char* newTestPwd = "PassAndGotHerPhoneNumber!#$**!";
{
LOG_debug << testName << ": Get password reset link from email";
std::string recoverink = getLinkFromMailbox(pyExe,
bufScript.string(),
realAccount,
bufRealPswd,
newTestAcc,
MegaClient::recoverLinkPrefix(),
timeOfResetEmail);
ASSERT_FALSE(recoverink.empty()) << "Recover account link was not found.";
LOG_debug << testName << ": Confirm reset password";
char* masterKey = megaApi[0]->exportMasterKey();
ASSERT_EQ(synchronousConfirmResetPassword(0, recoverink.c_str(), newTestPwd, masterKey),
MegaError::API_OK)
<< "confirmResetPassword failed";
}
// Login using new password
{
LOG_debug << testName << ": Login with new password";
std::unique_ptr<RequestTracker> loginTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->login(newTestAcc.c_str(), newTestPwd, loginTracker.get());
ASSERT_EQ(API_OK, loginTracker->waitForResult())
<< " Failed to login to account after change password with new password "
<< newTestAcc.c_str();
}
// fetchnodes - needed internally to fill in user details, to allow cancelAccount() to work
{
LOG_debug << testName << ": Fetching nodes";
std::unique_ptr<RequestTracker> fetchnodesTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->fetchNodes(fetchnodesTracker.get());
ASSERT_EQ(API_OK, fetchnodesTracker->waitForResult())
<< " Failed to fetchnodes after change password for account " << newTestAcc.c_str();
}
// test changing the email (check change with auxiliar instance)
// -----------------------
LOG_debug << testName << ": Start email change";
// login with auxiliar instance
LOG_debug << testName << ": Login auxiliar account";
megaApi.resize(2);
mApi.resize(2);
ASSERT_NO_FATAL_FAILURE(configureTestInstance(1, newTestAcc, newTestPwd, true, clientType));
{
std::unique_ptr<RequestTracker> loginTracker =
std::make_unique<RequestTracker>(megaApi[1].get());
megaApi[1]->login(newTestAcc.c_str(), newTestPwd, loginTracker.get());
ASSERT_EQ(API_OK, loginTracker->waitForResult()) << " Failed to login to auxiliar account ";
}
LOG_debug << testName << ": Send change email request";
const std::string changedTestAcc = Utils::replace(newTestAcc, "@", "-new@");
std::chrono::time_point timeOfChangeEmail = chrono::steady_clock::now();
ASSERT_EQ(synchronousChangeEmail(0, changedTestAcc.c_str()), MegaError::API_OK)
<< "changeEmail failed";
{
LOG_debug << testName << ": Get change email link from email inbox";
std::string changelink = getLinkFromMailbox(pyExe,
bufScript.string(),
realAccount,
bufRealPswd,
changedTestAcc,
MegaClient::verifyLinkPrefix(),
timeOfChangeEmail);
ASSERT_FALSE(changelink.empty()) << "Change email account link was not found.";
LOG_debug << testName << ": Confirm email change";
ASSERT_STRCASEEQ(newTestAcc.c_str(),
std::unique_ptr<char[]>{megaApi[0]->getMyEmail()}.get())
<< "email changed prematurely";
ASSERT_EQ(synchronousConfirmChangeEmail(0, changelink.c_str(), newTestPwd),
MegaError::API_OK)
<< "confirmChangeEmail failed";
}
{
// Check if our own email is updated after receive ug at auxiliar instance
LOG_debug << testName << ": Check email is updated";
std::unique_ptr<RequestTracker> userDataTracker =
std::make_unique<RequestTracker>(megaApi[1].get());
megaApi[1]->getUserData(userDataTracker.get());
ASSERT_EQ(API_OK, userDataTracker->waitForResult())
<< " Failed to get user data at auxiliar account";
ASSERT_EQ(changedTestAcc, std::unique_ptr<char[]>{megaApi[1]->getMyEmail()}.get())
<< "Email update error at auxiliar account";
logout(1, false, maxTimeout);
}
// Login using new email
ASSERT_STRCASEEQ(changedTestAcc.c_str(),
std::unique_ptr<char[]>{megaApi[0]->getMyEmail()}.get())
<< "email not changed correctly";
{
LOG_debug << testName << ": Login with new email";
std::unique_ptr<RequestTracker> loginTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->login(changedTestAcc.c_str(), newTestPwd, loginTracker.get());
ASSERT_EQ(API_OK, loginTracker->waitForResult())
<< " Failed to login to account after change email with new email "
<< changedTestAcc.c_str();
}
// fetchnodes - needed internally to fill in user details, to allow cancelAccount() to work
{
LOG_debug << testName << ": Fetching nodes";
std::unique_ptr<RequestTracker> fetchnodesTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->fetchNodes(fetchnodesTracker.get());
ASSERT_EQ(API_OK, fetchnodesTracker->waitForResult())
<< " Failed to fetchnodes after change password for account " << changedTestAcc.c_str();
}
ASSERT_STRCASEEQ(changedTestAcc.c_str(),
std::unique_ptr<char[]>{megaApi[0]->getMyEmail()}.get())
<< "my email not set correctly after changed";
// delete the account
// ------------------
// Request cancel account link
LOG_debug << testName << ": Start deleting account";
std::chrono::time_point timeOfDeleteEmail = std::chrono::steady_clock::now();
{
LOG_debug << testName << ": Request account cancel";
std::unique_ptr<RequestTracker> cancelLinkTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->cancelAccount(cancelLinkTracker.get());
ASSERT_EQ(API_OK, cancelLinkTracker->waitForResult())
<< " Failed to request cancel link for account " << changedTestAcc.c_str();
}
// Get cancel account link from the mailbox
{
LOG_debug << testName << ": Get cancel link from email";
std::string deleteLink = getLinkFromMailbox(pyExe,
bufScript.string(),
realAccount,
bufRealPswd,
changedTestAcc,
MegaClient::cancelLinkPrefix(),
timeOfDeleteEmail);
ASSERT_FALSE(deleteLink.empty()) << "Cancel account link was not found.";
// Use cancel account link
LOG_debug << testName << ": Confirm cancel link";
std::unique_ptr<RequestTracker> useCancelLinkTracker =
std::make_unique<RequestTracker>(megaApi[0].get());
megaApi[0]->confirmCancelAccount(deleteLink.c_str(),
newTestPwd,
useCancelLinkTracker.get());
// Allow API_ESID beside API_OK, due to the race between sc and cs channels
ASSERT_PRED3(
[](int t, int v1, int v2)
{
return t == v1 || t == v2;
},
useCancelLinkTracker->waitForResult(),
API_OK,
API_ESID)
<< " Failed to confirm cancel account " << changedTestAcc.c_str();
}
}
/**
* @brief Test_P CreateAccount
*
* Tests account creation for any client type client.
*
* See doCreateAccountTest(...).
*/
TEST_P(SdkTestCreateAccount, CreateAccount)
{
int clientType = GetParam();
ASSERT_NO_FATAL_FAILURE(doCreateAccountTest("SdkTestVPNCreateAccount", clientType));
}
INSTANTIATE_TEST_SUITE_P(CreateAccount,
SdkTestCreateAccount,
::testing::Values(MegaApi::CLIENT_TYPE_DEFAULT,
MegaApi::CLIENT_TYPE_VPN,
MegaApi::CLIENT_TYPE_PASSWORD_MANAGER));
|