From 53abfb4785cb3a9a75bc0fe8e59ae263a1793e06 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Fri, 21 Oct 2022 14:45:00 +0200 Subject: [PATCH] Add an end to end test utils file Signed-off-by: Claudio Cambra --- src/gui/folderman.h | 2 + test/CMakeLists.txt | 1 + test/endtoendtestutils.cpp | 174 +++++++++++++++++++++++++++++++++++++ test/endtoendtestutils.h | 98 +++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 test/endtoendtestutils.cpp create mode 100644 test/endtoendtestutils.h diff --git a/src/gui/folderman.h b/src/gui/folderman.h index 5d85b18d1..ee39c259f 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -29,6 +29,7 @@ class TestFolderMan; class TestCfApiShellExtensionsIPC; class TestShareModel; class ShareTestHelper; +class EndToEndTestHelper; namespace OCC { @@ -385,6 +386,7 @@ private: friend class ::TestFolderMan; friend class ::TestCfApiShellExtensionsIPC; friend class ::ShareTestHelper; + friend class ::EndToEndTestHelper; }; } // namespace OCC diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a66133905..cf6599d2b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(testutils themeutils.cpp testhelper.cpp sharetestutils.cpp + endtoendtestutils.cpp ) target_link_libraries(testutils PUBLIC Nextcloud::sync Qt5::Test) diff --git a/test/endtoendtestutils.cpp b/test/endtoendtestutils.cpp new file mode 100644 index 000000000..a25a7e145 --- /dev/null +++ b/test/endtoendtestutils.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) by Claudio Cambra + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "endtoendtestutils.h" + +#include +#include +#include + +#include "cmd/simplesslerrorhandler.h" +#include "creds/httpcredentials.h" +#include "gui/accountmanager.h" +#include "libsync/theme.h" +#include "accessmanager.h" +#include "httplogger.h" +#include "syncenginetestutils.h" +#include "testhelper.h" + +constexpr auto serverUrl = "https://server"; + +Q_LOGGING_CATEGORY(lcEndToEndTestUtils, "nextcloud.gui.endtoendtestutils", QtInfoMsg) + +/** End to end test credentials access manager class **/ + +class EndToEndTestCredentialsAccessManager : public OCC::AccessManager +{ +public: + EndToEndTestCredentialsAccessManager(const EndToEndTestCredentials *cred, QObject *parent = nullptr) + : OCC::AccessManager(parent) + , _cred(cred) + { + } + +protected: + QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) override + { + if(!_cred) { + qCWarning(lcEndToEndTestUtils) << "Could not create request -- null creds!"; + return {}; + } + + QNetworkRequest req(request); + QByteArray credHash = QByteArray(_cred->user().toUtf8() + ":" + _cred->password().toUtf8()).toBase64(); + req.setRawHeader("Authorization", "Basic " + credHash); + + return OCC::AccessManager::createRequest(op, req, outgoingData); + } + +private: + // The credentials object dies along with the account, while the QNAM might + // outlive both. + QPointer _cred; +}; + +/** End to end test credentials class **/ + +QNetworkAccessManager *EndToEndTestCredentials::createQNAM() const +{ + return new EndToEndTestCredentialsAccessManager(this); +} + +/** End to end test helper class **/ + +EndToEndTestHelper::~EndToEndTestHelper() +{ + removeConfiguredSyncFolder(); + removeConfiguredAccount(); + + OCC::AccountManager::instance()->shutdown(); +} + +void EndToEndTestHelper::startAccountConfig() +{ + const auto accountManager = OCC::AccountManager::instance(); + _account = accountManager->createAccount(); + + _account->setCredentials(new EndToEndTestCredentials); + _account->setUrl(OCC::Theme::instance()->overrideServerUrl()); + + const auto serverUrlString = QString(serverUrl); + _account->setUrl(serverUrlString); + + _account->networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); + _account->setSslConfiguration(QSslConfiguration::defaultConfiguration()); + _account->setSslErrorHandler(new OCC::SimpleSslErrorHandler); + _account->setTrustCertificates(true); + + slotConnectToNCUrl(serverUrlString); +} + +void EndToEndTestHelper::slotConnectToNCUrl(const QString &url) +{ + qCDebug(lcEndToEndTestUtils) << "Connect to url: " << url; + + const auto fetchUserNameJob = new OCC::JsonApiJob(_account->sharedFromThis(), QStringLiteral("/ocs/v1.php/cloud/user")); + connect(fetchUserNameJob, &OCC::JsonApiJob::jsonReceived, this, [this, url](const QJsonDocument &json, const int statusCode) { + if (statusCode != 100) { + qCDebug(lcEndToEndTestUtils) << "Could not fetch username."; + } + + const auto objData = json.object().value("ocs").toObject().value("data").toObject(); + const auto userId = objData.value("id").toString(""); + const auto displayName = objData.value("display-name").toString(""); + _account->setDavUser(userId); + _account->setDavDisplayName(displayName); + + _accountState = new OCC::AccountState(_account); + + emit accountReady(_account); + }); + fetchUserNameJob->start(); +} + +void EndToEndTestHelper::removeConfiguredAccount() +{ + OCC::AccountManager::instance()->deleteAccount(_accountState.data()); +} + +OCC::Folder *EndToEndTestHelper::configureSyncFolder(const QString &targetPath) +{ + if(_syncFolder) { + removeConfiguredSyncFolder(); + } + + qCDebug(lcEndToEndTestUtils) << "Creating temp end-to-end test folder."; + Q_ASSERT(_tempDir.isValid()); + OCC::FileSystem::setFolderMinimumPermissions(_tempDir.path()); + qCDebug(lcEndToEndTestUtils) << "Created temp end-to-end test folder at:" << _tempDir.path(); + + setupFolderMan(); + + OCC::FolderDefinition definition; + definition.localPath = _tempDir.path(); + definition.targetPath = targetPath; + _syncFolder = _folderMan->addFolder(_accountState.data(), definition); + + return _syncFolder; +} + +void EndToEndTestHelper::removeConfiguredSyncFolder() +{ + if(!_syncFolder || !_folderMan) { + return; + } + + QSignalSpy folderSyncFinished(_syncFolder, &OCC::Folder::syncFinished); + _folderMan->forceSyncForFolder(_syncFolder); + Q_ASSERT(folderSyncFinished.wait(3000)); + _folderMan->unloadAndDeleteAllFolders(); + _syncFolder = nullptr; +} + +void EndToEndTestHelper::setupFolderMan() +{ + if(_folderMan) { + return; + } + + auto folderMan = new OCC::FolderMan; + Q_ASSERT(folderMan); + folderMan->setSyncEnabled(true); + _folderMan.reset(folderMan); +} diff --git a/test/endtoendtestutils.h b/test/endtoendtestutils.h new file mode 100644 index 000000000..c1a48329a --- /dev/null +++ b/test/endtoendtestutils.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) by Claudio Cambra + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#pragma once + +#include +#include + +#include "gui/accountstate.h" +#include "gui/folderman.h" +#include "libsync/account.h" + +constexpr auto testUsername = "test"; +constexpr auto testPassword = "test"; + +class QNetworkReply; + +namespace OCC +{ +class Folder; +class FolderMan; +} + +class EndToEndTestCredentials : public OCC::AbstractCredentials +{ + Q_OBJECT + +public: + explicit EndToEndTestCredentials() + : OCC::AbstractCredentials() + , _user(testUsername) + , _password(testPassword) + { + _wasFetched = true; + }; + + [[nodiscard]] QString authType() const override { return QStringLiteral("http"); } + [[nodiscard]] QString user() const override { return _user; } + [[nodiscard]] QString password() const override { return _password; } + [[nodiscard]] bool ready() const override { return true; } + bool stillValid(QNetworkReply *) override { return true; } + void askFromUser() override {}; + void fetchFromKeychain() override { _wasFetched = true; Q_EMIT fetched(); }; + void persist() override {}; + void invalidateToken() override {}; + void forgetSensitiveData() override {}; + + [[nodiscard]] QNetworkAccessManager *createQNAM() const override; + +private: + QString _user; + QString _password; +}; + +class EndToEndTestHelper : public QObject +{ + Q_OBJECT + +public: + EndToEndTestHelper() = default; + ~EndToEndTestHelper() override; + + [[nodiscard]] OCC::AccountPtr account() const { return _account; } + [[nodiscard]] OCC::AccountStatePtr accountState() const { return _accountState; } + + OCC::Folder *configureSyncFolder(const QString &targetPath = QStringLiteral("")); + +signals: + void accountReady(const OCC::AccountPtr &account); + +public slots: + void startAccountConfig(); + void removeConfiguredAccount(); + void removeConfiguredSyncFolder(); + +private slots: + void slotConnectToNCUrl(const QString &url); + void setupFolderMan(); + +private: + OCC::AccountPtr _account; + OCC::AccountStatePtr _accountState; + QScopedPointer _folderMan; + QTemporaryDir _tempDir; + + OCC::Folder* _syncFolder = nullptr; +};