diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 342f5c58e..1d2ad53df 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -894,3 +894,213 @@ jobs: run: | echo "Pull request:" >> $GITHUB_STEP_SUMMARY echo "[[#${{ fromJSON(steps.pull_request.outputs.data)[0].number }}] ${{ fromJSON(steps.pull_request.outputs.data)[0].title }}](${{ fromJSON(steps.pull_request.outputs.data)[0].html_url }})" >> $GITHUB_STEP_SUMMARY + +# ------------------------------------------------------ + + Test-Linux: + runs-on: ubuntu-latest + + env: + QT_VERSION: 6.10.1 + + steps: + - name: 'Get sources' + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'linux' + target: 'desktop' + arch: 'linux_gcc_64' + modules: 'qtremoteobjects qt5compat qtshadertools' + dir: ${{ runner.temp }} + set-env: 'true' + + - name: 'Install dependencies' + run: | + sudo apt-get update + sudo apt-get install -y \ + cmake \ + ninja-build \ + libsecret-1-dev \ + libxcb-cursor0 \ + libxkbcommon-x11-0 \ + libxcb-xinerama0 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-xfixes0 \ + libxcb-shape0 \ + libxcb-sync1 \ + libxcb-xkb1 \ + libgl1 \ + libglib2.0-0 + + - name: 'Setup python' + uses: actions/setup-python@v6 + with: + python-version: 3.14 + + - name: 'Install conan' + run: pip install "conan==2.26.2" + + - name: 'Configure' + run: | + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON + + - name: 'Build' + run: cmake --build build + + - name: 'Run tests' + run: | + QT_ROOT=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64 + + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:\ + $QT_ROOT/lib:\ + $PWD/client/3rd-prebuilt/deploy-prebuilt/linux/x64 + + export QT_QPA_PLATFORM=offscreen + + ctest --test-dir build/client --output-on-failure + +# ------------------------------------------------------ + + Test-Windows: + runs-on: windows-latest + + env: + QT_VERSION: 6.10.1 + + steps: + - name: 'Get sources' + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + host: 'windows' + target: 'desktop' + arch: 'win64_msvc2022_64' + modules: 'qtremoteobjects qt5compat qtshadertools' + dir: ${{ runner.temp }} + set-env: 'true' + + - name: 'Setup MSVC' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: 'x64' + + - name: 'Setup python' + uses: actions/setup-python@v6 + with: + python-version: 3.14 + + - name: 'Install conan' + run: pip install "conan==2.26.2" + + - name: 'Install dependencies' + shell: bash + run: | + choco install ninja -y + + - name: 'Configure' + shell: cmd + run: | + cmake -B build -G Ninja ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DBUILD_TESTING=ON + + - name: 'Build' + shell: cmd + run: cmake --build build --config Release + + - name: 'Deploy Qt dependencies' + shell: cmd + run: | + for %%f in (build\client\test_*.exe) do (windeployqt %%f) + + - name: 'Run tests' + shell: cmd + run: | + set PATH=%PATH%;%CD%\client\3rd-prebuilt\deploy-prebuilt\windows\x64 + set PATH=%PATH%;%Qt6_DIR%\..\..\..\bin + ctest --test-dir build\client --output-on-failure + +# ------------------------------------------------------ + + Test-MacOS: + runs-on: macos-latest + + env: + QT_VERSION: 6.10.1 + + steps: + - name: 'Setup xcode' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.2.0' + + - name: 'Get sources' + uses: actions/checkout@v4 + with: + submodules: 'true' + fetch-depth: 10 + + - name: 'Install Qt' + uses: jurplel/install-qt-action@v4 + with: + version: ${{ env.QT_VERSION }} + host: 'mac' + target: 'desktop' + arch: 'clang_64' + modules: 'qtremoteobjects qt5compat qtshadertools' + dir: ${{ runner.temp }} + set-env: 'true' + + - name: 'Install dependencies' + run: | + brew update + brew install cmake ninja + + - name: 'Setup python' + uses: actions/setup-python@v6 + with: + python-version: 3.14 + + - name: 'Install conan' + run: pip install "conan==2.26.2" + + - name: 'Configure' + run: | + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON + + - name: 'Build' + run: cmake --build build + + - name: 'Run tests' + run: | + QT_ROOT=${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/macos + + export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:\ + $QT_ROOT/lib:\ + $PWD/client/3rd-prebuilt/deploy-prebuilt/macos + + export QT_QPA_PLATFORM=offscreen + + ctest --test-dir build/client --output-on-failure + diff --git a/.gitignore b/.gitignore index 90430b7ee..90df4676e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,8 @@ deploy/build_64/* winbuild*.bat .cache/ .vscode/ - +.venv/ +.cursor/ # Qt-es /.qmake.cache diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2f138157d..41df05087 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -193,6 +193,8 @@ elseif(APPLE) include(cmake/macos.cmake) endif() +include(CTest) + if(NOT IOS AND NOT ANDROID AND NOT MACOS_NE) add_subdirectory(tests) endif() diff --git a/client/core/controllers/coreController.h b/client/core/controllers/coreController.h index 4b3ed2831..9897ee78e 100644 --- a/client/core/controllers/coreController.h +++ b/client/core/controllers/coreController.h @@ -82,12 +82,21 @@ class TestAdminSelfHostedExport; class TestServerEdit; class TestDefaultServerChange; class TestServerEdgeCases; +class TestGatewayStacks; class TestSignalOrder; class TestServersModelSync; class TestComplexOperations; class TestSettingsSignals; class TestUiServersModelAndController; class TestSelfHostedServerSetup; +class TestMultipleExports; +class TestSerialization; +class TestUiLanguageModelAndController; +class TestUiIpModelAndController; +class TestUiAppSTModelAndController; +class TestUiAllowedDnsModelAndController; +class TestUiNewsModelAndController; +class TestUiApiServicesModelAndController; class CoreController : public QObject { @@ -98,12 +107,21 @@ class CoreController : public QObject friend class TestServerEdit; friend class TestDefaultServerChange; friend class TestServerEdgeCases; + friend class TestGatewayStacks; friend class TestSignalOrder; friend class TestServersModelSync; friend class TestComplexOperations; friend class TestSettingsSignals; friend class TestUiServersModelAndController; friend class TestSelfHostedServerSetup; + friend class TestMultipleExports; + friend class TestSerialization; + friend class TestUiLanguageModelAndController; + friend class TestUiIpModelAndController; + friend class TestUiAppSTModelAndController; + friend class TestUiAllowedDnsModelAndController; + friend class TestUiNewsModelAndController; + friend class TestUiApiServicesModelAndController; public: explicit CoreController(const QSharedPointer &vpnConnection, SecureQSettings* settings, diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index a8ba12246..48039d130 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -4,8 +4,11 @@ project(AmneziaVPN_Tests) find_package(Qt6 REQUIRED COMPONENTS Test) +include(CTest) +set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) qt6_add_resources(TEST_QRC ${CLIENT_ROOT_DIR}/server_scripts/serverScripts.qrc @@ -77,6 +80,15 @@ target_link_libraries(test_server_edge_cases PRIVATE test_common ) +add_executable(test_gateway_stacks + testGatewayStacks.cpp +) + +target_link_libraries(test_gateway_stacks PRIVATE + Qt6::Test + test_common +) + add_executable(test_signal_order testSignalOrder.cpp ) @@ -131,15 +143,85 @@ target_link_libraries(test_self_hosted_server_setup PRIVATE test_common ) +add_executable(test_exports + testMultipleExports.cpp +) + +target_link_libraries(test_exports PRIVATE + Qt6::Test + test_common +) + +add_executable(test_serialization + testSerialization.cpp +) + +target_link_libraries(test_serialization PRIVATE + Qt6::Test + test_common +) + +add_executable(test_ui_language_model_and_controller + testUiLanguageModelAndController.cpp +) + +target_link_libraries(test_ui_language_model_and_controller PRIVATE + Qt6::Test + test_common +) + +add_executable(test_ui_ip_model_and_controller + testUiIpModelAndController.cpp +) + +target_link_libraries(test_ui_ip_model_and_controller PRIVATE + Qt6::Test + test_common +) + +add_executable(test_ui_app_st_model_and_controller + testUiAppSTModelAndController.cpp +) + +target_link_libraries(test_ui_app_st_model_and_controller PRIVATE + Qt6::Test + test_common +) + +add_executable(test_ui_allowed_dns_model_and_controller + testUiAllowedDnsModelAndController.cpp +) + +target_link_libraries(test_ui_allowed_dns_model_and_controller PRIVATE + Qt6::Test + test_common +) + +add_executable(test_ui_api_services_model_and_controller + api/testUiApiServicesModelAndController.cpp +) + +target_link_libraries(test_ui_api_services_model_and_controller PRIVATE + Qt6::Test + test_common +) + enable_testing() add_test(NAME ImportExportTest COMMAND test_import_export) add_test(NAME MultipleImportsTest COMMAND test_multiple_imports) add_test(NAME ServerEditTest COMMAND test_server_edit) add_test(NAME DefaultServerChangeTest COMMAND test_default_server_change) add_test(NAME ServerEdgeCasesTest COMMAND test_server_edge_cases) +add_test(NAME GatewayStacksTest COMMAND test_gateway_stacks) add_test(NAME SignalOrderTest COMMAND test_signal_order) add_test(NAME ServersModelSyncTest COMMAND test_servers_model_sync) add_test(NAME ComplexOperationsTest COMMAND test_complex_operations) add_test(NAME SettingsSignalsTest COMMAND test_settings_signals) add_test(NAME UiServersModelAndControllerTest COMMAND test_ui_servers_model_and_controller) add_test(NAME SelfHostedServerSetupTest COMMAND test_self_hosted_server_setup) +add_test(NAME MultipleExportsTest COMMAND test_exports) +add_test(NAME SerializationTest COMMAND test_serialization) +add_test(NAME UiLanguageModelAndControllerTest COMMAND test_ui_language_model_and_controller) +add_test(NAME UiIpModelAndControllerTest COMMAND test_ui_ip_model_and_controller) +add_test(NAME UiAppSTModelAndControllerTest COMMAND test_ui_app_st_model_and_controller) +add_test(NAME UiAllowedDnsModelAndControllerTest COMMAND test_ui_allowed_dns_model_and_controller) diff --git a/client/tests/api/testUiApiServicesModelAndController.cpp b/client/tests/api/testUiApiServicesModelAndController.cpp new file mode 100644 index 000000000..da4692c26 --- /dev/null +++ b/client/tests/api/testUiApiServicesModelAndController.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiApiServicesModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + // TODO: add env vars for api + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testRolesAndSignals() + { + QSignalSpy errorOccurredSpy(m_coreController->m_servicesCatalogUiController, &ServicesCatalogUiController::errorOccurred); + + /* TODO: + m_coreController->m_servicesCatalogUiController->fillAvailableServices(); + QVERIFY(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + + QModelIndex serviceModelIndex = m_coreController->m_apiServicesModel->index(0, 0); + QVERIFY2(serviceModelIndex.isValid(), "Service model index should be valid"); + + auto serviceName = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::NameRole); + QCOMPARE(serviceName, ); + + auto serviceCardDescription = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::CardDescriptionRole); + QCOMPARE(serviceCardDescription, ); + + auto isServiceAvailable = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::IsServiceAvailableRole); + QCOMPARE(isServiceAvailable, ); + + auto serviceSpeed = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::SpeedRole); + QCOMPARE(serviceSpeed, ); + + auto serviceTimeLimit = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::TimeLimitRole); + QCOMPARE(serviceTimeLimit, ); + + auto serviceRegion = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::RegionRole); + QCOMPARE(serviceRegion, ); + + auto serviceFeatures = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::FeaturesRole); + QCOMPARE(serviceFeatures, ); + + auto servicePrice = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::PriceRole); + QCOMPARE(servicePrice, ); + + auto serviceEndDate = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::EndDateRole); + QCOMPARE(serviceEndDate, ); + + auto serviceOrder = m_coreController->m_apiServicesModel->data(serviceModelIndex, ApiServicesModel::OrderRole); + QCOMPARE(serviceOrder, ); + */ + } +}; + +QTEST_MAIN(TestUiApiServicesModelAndController) +#include "testUiApiServicesModelAndController.moc" diff --git a/client/tests/api/testUiNewsModelAndController.cpp b/client/tests/api/testUiNewsModelAndController.cpp new file mode 100644 index 000000000..c51a1c711 --- /dev/null +++ b/client/tests/api/testUiNewsModelAndController.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiNewsModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + // TODO: add env vars for api + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testRolesAndSignals() + { + QSignalSpy fetchNewsFinishedSpy(m_coreController->m_apiNewsUiController, &ApiNewsUiController::fetchNewsFinished); + QSignalSpy errorOccurredSpy(m_coreController->m_apiNewsUiController, &ApiNewsUiController::errorOccurred); + QSignalSpy processedIndexChangedSpy(m_coreController->m_newsModel, &NewsModel::processedIndexChanged); + QSignalSpy hasUnreadChangedSpy(m_coreController->m_newsModel, &NewsModel::hasUnreadChanged); + + /* TODO: + m_coreController->m_apiNewsUiController->fetchNews(false); + QVERIFY(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY(fetchNewsFinishedSpy.count() == 1, "fetchNewsFinished signal should be emitted"); + + m_coreController->m_newsModel->updateModel(); + QVERIFY(hasUnreadChangedSpy.count() == 1, "hasUnreadChanged signal should be emitted"); + + QModelIndex newsModelIndex = m_coreController->m_newsModel->index(0, 0); + QVERIFY2(newsModelIndex.isValid(), "News model index should be valid"); + + auto newsId = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::IdRole); + QCOMPARE(newsId, ); + + auto newsTitle = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::TitleRole); + QCOMPARE(newsTitle, ); + + auto newsContent = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::ContentRole); + QCOMPARE(newsContent, ); + + auto newsTimestamp = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::TimestampRole); + QCOMPARE(newsTimestamp, ); + + auto newsIsRead = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::IsReadRole); + QCOMPARE(newsIsRead, false); + + auto newsIsProcessed = m_coreController->m_newsModel->data(newsModelIndex, NewsModel::IsProcessedRole); + QCOMPARE(newsIsProcessed, ); + + m_coreController->m_newsModel->markAsRead(0); + ? m_coreController->m_newsModel->updateModel(); ? + QVERIFY(hasUnreadChangedSpy.count() == 2, "hasUnreadChanged signal should be emitted"); + QCOMPARE(newsIsRead, true); + */ + } +}; + +QTEST_MAIN(TestUiNewsModelAndController) +#include "testUiNewsModelAndController.moc" diff --git a/client/tests/testAdminSelfHostedExport.cpp b/client/tests/testAdminSelfHostedExport.cpp index c2a4065f8..69971364d 100644 --- a/client/tests/testAdminSelfHostedExport.cpp +++ b/client/tests/testAdminSelfHostedExport.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include "core/controllers/coreController.h" #include "core/utils/constants/configKeys.h" @@ -20,6 +20,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + QJsonObject decodeVpnKey(const QString &vpnKey) { QString key = vpnKey; key.replace("vpn://", ""); @@ -97,7 +103,7 @@ private slots: } void testAdminSelfHostedExport() { - QString vpnKey = "vpn://AAABTXjarZIxT8MwEIX_Cro5jbDjQunKUhhYyoZQZZKjRGpsy3baQtT_zp2bJh3oACLLPfvz3bOe00FpTdS1QR9g_tKB3q1h3sFCwBzEdf9N5ElBBgtJqBiQOkcFoemAbs6RInQ7oNkZemAvrrKvRV9VX6fH-lhSVSwavU9GSdcmXZX0UqSbseJRMqlioDxuSsJZH1mKWTrhvI22tJvVljKoLU-TtB3aN4NxpavKYwhpSD7LRc4t0WsTeMwqNRNsKweHbAyTtnRj8KvWE0pUEut-hNah2TpDM0-Kwu8vKMSd-ttFLrntao_rVvuKWkc9OnIk4n8t915_Ulcqo5FSxa9tYsk2rxlU-K7bTby_lDWfCKWvXTy-5jOGeLVET-9L7MOG-KQbJEBx57jXjdtgXtqG_wUdws5yJhCpa1iefhopM2gD-n4An-ElHL4BvzD6nw"; + QString vpnKey = getValueFromIni("configs/TEST_CONFIG_ANY"); QSignalSpy importFinishedSpy(m_coreController->m_importCoreController, &ImportController::importFinished); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); diff --git a/client/tests/testComplexOperations.cpp b/client/tests/testComplexOperations.cpp index 1117b5e13..b0866be41 100644 --- a/client/tests/testComplexOperations.cpp +++ b/client/tests/testComplexOperations.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -20,6 +20,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -43,9 +49,9 @@ private slots: } void testComplexOperationSequence() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; - QString wgKey = "vpn://AAAAwXjahY89a8NADIb_StDsHLFDIHjt0C1LhgwlBNWnpgfx3SHp6hDj_15dacnYTS_Po68ZhhQVQyQW6N_mZ4QecIz0CLieAtO1IHto4Fn3M-TEat6u3XetMSnvkfSC3jOJjYN24_audRtjyhil-pfMSZPB4jMsy7kBTx9Ybvryz2ZPMnDIGlI042TktZLVkfjLmhr4TKIHHMnodHV0xzHfyA1pNJZRZEr1alAS_Yvbin6e6LoGihD_DqhSjbB8AyB_ZI8"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_WG"); + QString wgKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); QSignalSpy importFinishedSpy(m_coreController->m_importCoreController, &ImportController::importFinished); QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded); diff --git a/client/tests/testDefaultServerChange.cpp b/client/tests/testDefaultServerChange.cpp index f0fb6163b..d3c28f7fd 100644 --- a/client/tests/testDefaultServerChange.cpp +++ b/client/tests/testDefaultServerChange.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -21,6 +21,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -45,9 +51,9 @@ private slots: } void testSetDefaultServerIndex() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; - QString wgKey = "vpn://AAAAwXjahY89a8NADIb_StDsHLFDIHjt0C1LhgwlBNWnpgfx3SHp6hDj_15dacnYTS_Po68ZhhQVQyQW6N_mZ4QecIz0CLieAtO1IHto4Fn3M-TEat6u3XetMSnvkfSC3jOJjYN24_audRtjyhil-pfMSZPB4jMsy7kBTx9Ybvryz2ZPMnDIGlI042TktZLVkfjLmhr4TKIHHMnodHV0xzHfyA1pNJZRZEr1alAS_Yvbin6e6LoGihD_DqhSjbB8AyB_ZI8"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_WG"); + QString wgKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); auto importResult1 = m_coreController->m_importCoreController->extractConfigFromData(awgKey); m_coreController->m_importCoreController->importConfig(importResult1.config); @@ -80,9 +86,9 @@ private slots: } void testDefaultServerChangeOnRemoveEdgeCases() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; - QString wgKey = "vpn://AAAAwXjahY89a8NADIb_StDsHLFDIHjt0C1LhgwlBNWnpgfx3SHp6hDj_15dacnYTS_Po68ZhhQVQyQW6N_mZ4QecIz0CLieAtO1IHto4Fn3M-TEat6u3XetMSnvkfSC3jOJjYN24_audRtjyhil-pfMSZPB4jMsy7kBTx9Ybvryz2ZPMnDIGlI042TktZLVkfjLmhr4TKIHHMnodHV0xzHfyA1pNJZRZEr1alAS_Yvbin6e6LoGihD_DqhSjbB8AyB_ZI8"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_WG"); + QString wgKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); auto importResult1 = m_coreController->m_importCoreController->extractConfigFromData(awgKey); m_coreController->m_importCoreController->importConfig(importResult1.config); diff --git a/client/tests/testGatewayStacks.cpp b/client/tests/testGatewayStacks.cpp new file mode 100644 index 000000000..a3f8991a1 --- /dev/null +++ b/client/tests/testGatewayStacks.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "ui/controllers/serversUiController.h" +#include "ui/models/serversModel.h" +#include "vpnConnection.h" +#include "secureQSettings.h" + +using namespace amnezia; + +class TestGatewayStacks : public QObject +{ + Q_OBJECT + +private: + CoreController* m_coreController; + SecureQSettings* m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + +private slots: + void initTestCase() { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() { + m_settings->clearSettings(); + m_coreController->m_serversRepository->invalidateCache(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testGatewayStacksRecomputeOnServerOperations() { + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + + QSignalSpy serverAddedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverAdded); + QSignalSpy serverEditedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverEdited); + QSignalSpy serverRemovedSpy(m_coreController->m_serversRepository, &SecureServersRepository::serverRemoved); + QSignalSpy gatewayServersChangedSpy(m_coreController->m_serversUiController, + &ServersUiController::hasServersFromGatewayApiChanged); + + auto importResult = m_coreController->m_importCoreController->extractConfigFromData(awgKey); + m_coreController->m_importCoreController->importConfig(importResult.config); + + QVERIFY2(serverAddedSpy.count() == 1, "serverAdded signal should be emitted"); + QVERIFY2(!m_coreController->m_serversUiController->hasServersFromGatewayApi(), + "Self-hosted imports should not be treated as gateway API servers"); + + const QString serverId = m_coreController->m_serversController->getServerId(0); + QVERIFY2(m_coreController->m_serversController->renameServer(serverId, QStringLiteral("Edited Server")), + "Server rename should succeed"); + + QVERIFY2(serverEditedSpy.count() == 1, "serverEdited signal should be emitted"); + + m_coreController->m_serversController->removeServer(serverId); + + QVERIFY2(serverRemovedSpy.count() == 1, "serverRemoved signal should be emitted"); + QVERIFY2(!m_coreController->m_serversUiController->hasServersFromGatewayApi(), + "Gateway API server state should remain empty"); + QVERIFY2(gatewayServersChangedSpy.count() == 0, + "Self-hosted server operations should not emit gateway API state changes"); + } +}; + +QTEST_MAIN(TestGatewayStacks) +#include "testGatewayStacks.moc" + diff --git a/client/tests/testMultipleExports.cpp b/client/tests/testMultipleExports.cpp new file mode 100644 index 000000000..696bc86e2 --- /dev/null +++ b/client/tests/testMultipleExports.cpp @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestMultipleExports : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + + QString vpnKey = getValueFromIni("configs/TEST_SELF_HOSTED_CONFIG"); + QJsonObject importedConfig = m_coreController->m_importCoreController->extractConfigFromData(vpnKey).config; + + m_coreController->m_importCoreController->importConfig(importedConfig); + + qDebug() << "SELF-HOSTED ADMIN SERVER IMPORTED\n"; + } + + void cleanupTestCase() + { + int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + + for (int containerIndex = 1; containerIndex < 7; ++containerIndex) + m_coreController->m_installUiController->clearCachedProfile(serverId, containerIndex); + + m_coreController->m_serversController->removeServer(serverId); + + qDebug() << "SERVER REMOVED\n"; + + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testMultipleExports() + { + int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + + QString clientName = "MultipleExports Test Client"; + + for (int containerIndex = 1; containerIndex < 7; ++containerIndex) { + + QString containerName; + + switch (containerIndex) { + case 1: containerName = "AwgLegacy"; break; + case 2: containerName = "Awg2"; break; + case 3: containerName = "WireGuard"; break; + case 4: containerName = "OpenVPN"; break; + case 5: continue; break; // skipping IPsec + case 6: containerName = "XRay"; break; + } + + if (!m_coreController->m_containersModel->data(containerIndex, ContainersModel::Roles::IsInstalledRole).toBool()) { + qDebug() << QStringLiteral("%1: Not installed").arg(containerName).toUtf8().constData(); + continue; + } + + auto exportResult = m_coreController->m_exportController->generateConnectionConfig(serverId, containerIndex, clientName); + + QVERIFY2(exportResult.errorCode == ErrorCode::NoError, + QStringLiteral("\n%1: Export should succeed").arg(containerName).toUtf8().constData()); + QVERIFY2(!exportResult.config.isEmpty(), + QStringLiteral("%1: Exported config should not be empty\n").arg(containerName).toUtf8().constData()); + } + } + + void testMultipleExportsNative() + { + int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + + QString clientName = "MultipleExports Test Client"; + + auto exportResultAwg = m_coreController->m_exportController->generateAwgConfig(serverId, DockerContainer::Awg2, clientName); + auto exportResultWg = m_coreController->m_exportController->generateWireGuardConfig(serverId, clientName); + auto exportResultOvpn = m_coreController->m_exportController->generateOpenVpnConfig(serverId, clientName); + auto exportResultXray = m_coreController->m_exportController->generateXrayConfig(serverId, clientName); + + QVERIFY2(exportResultAwg.errorCode == ErrorCode::NoError, "\nAwg (native): Export should succeed"); + QVERIFY2(exportResultWg.errorCode == ErrorCode::NoError, "\nWg (native): Export should succeed"); + QVERIFY2(exportResultOvpn.errorCode == ErrorCode::NoError, "\nOvpn (native): Export should succeed"); + QVERIFY2(exportResultXray.errorCode == ErrorCode::NoError, "\nXray (native): Export should succeed"); + + QVERIFY2(!exportResultAwg.config.isEmpty(), "Awg (native): Exported config should not be empty\n"); + QVERIFY2(!exportResultWg.config.isEmpty(), "Wg (native): Exported config should not be empty\n"); + QVERIFY2(!exportResultOvpn.config.isEmpty(), "Ovpn (native): Exported config should not be empty\n"); + QVERIFY2(!exportResultXray.config.isEmpty(), "Xray (native): Exported config should not be empty\n"); + } + +}; + +QTEST_MAIN(TestMultipleExports) +#include "testMultipleExports.moc" diff --git a/client/tests/testMultipleImports.cpp b/client/tests/testMultipleImports.cpp index 932c0c902..03c5f14f6 100644 --- a/client/tests/testMultipleImports.cpp +++ b/client/tests/testMultipleImports.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -21,6 +21,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -46,9 +52,9 @@ private slots: } void testMultipleImports() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; - QString wgKey = "vpn://AAAAwXjahY89a8NADIb_StDsHLFDIHjt0C1LhgwlBNWnpgfx3SHp6hDj_15dacnYTS_Po68ZhhQVQyQW6N_mZ4QecIz0CLieAtO1IHto4Fn3M-TEat6u3XetMSnvkfSC3jOJjYN24_audRtjyhil-pfMSZPB4jMsy7kBTx9Ybvryz2ZPMnDIGlI042TktZLVkfjLmhr4TKIHHMnodHV0xzHfyA1pNJZRZEr1alAS_Yvbin6e6LoGihD_DqhSjbB8AyB_ZI8"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_WG"); + QString wgKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); QSignalSpy importFinishedSpy(m_coreController->m_importCoreController, &ImportController::importFinished); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); @@ -126,8 +132,8 @@ private slots: } void testMultipleImportsRemoval() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); QSignalSpy importFinishedSpy(m_coreController->m_importCoreController, &ImportController::importFinished); QSignalSpy defaultServerChangedSpy(m_coreController->m_serversRepository, &SecureServersRepository::defaultServerChanged); diff --git a/client/tests/testSelfHostedServerSetup.cpp b/client/tests/testSelfHostedServerSetup.cpp index fa1a783fb..580dfcf3e 100644 --- a/client/tests/testSelfHostedServerSetup.cpp +++ b/client/tests/testSelfHostedServerSetup.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -31,13 +31,19 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + ServerCredentials getCredentialsFromEnv() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString hostName = env.value("TEST_SERVER_HOST"); - QString userName = env.value("TEST_SERVER_USER"); - QString password = env.value("TEST_SERVER_PASSWORD"); - QString portStr = env.value("TEST_SERVER_PORT", "22"); + QString hostName = getValueFromIni("secrets/TEST_SERVER_HOST"); + QString userName = getValueFromIni("secrets/TEST_SERVER_USER"); + QString password = getValueFromIni("secrets/TEST_SERVER_PASSWORD"); + QString portStr = getValueFromIni("secrets/TEST_SERVER_PORT"); int port = portStr.toInt(); ServerCredentials credentials; diff --git a/client/tests/testSerialization.cpp b/client/tests/testSerialization.cpp new file mode 100644 index 000000000..bc12cf344 --- /dev/null +++ b/client/tests/testSerialization.cpp @@ -0,0 +1,287 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "core/utils/serialization/serialization.h" +#include "core/utils/utilities.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestSerialization : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + + QJsonObject extractXrayConfig(const QString &data, ConfigTypes configType, const QString &description = "") const + { + QJsonParseError parserErr; + QJsonDocument jsonConf = QJsonDocument::fromJson(data.toLocal8Bit(), &parserErr); + + QJsonObject xrayVpnConfig; + xrayVpnConfig[configKey::config] = jsonConf.toJson().constData(); + QJsonObject lastConfig; + lastConfig[configKey::lastConfig] = jsonConf.toJson().constData(); + lastConfig[configKey::isThirdPartyConfig] = true; + + QJsonObject containers; + if (configType == ConfigTypes::ShadowSocks) { + containers.insert(configKey::ssxray, QJsonValue(lastConfig)); + containers.insert(configKey::container, QJsonValue(configKey::amneziaSsxray)); + } else { + containers.insert(configKey::container, QJsonValue(configKey::amneziaXray)); + containers.insert(configKey::xray, QJsonValue(lastConfig)); + } + + QJsonArray arr; + arr.push_back(containers); + + QString hostName; + + const static QRegularExpression hostNameRegExp("\"address\":\\s*\"([^\"]+)"); + QRegularExpressionMatch hostNameMatch = hostNameRegExp.match(data); + if (hostNameMatch.hasMatch()) { + hostName = hostNameMatch.captured(1); + } + + QJsonObject config; + config[configKey::containers] = arr; + config[configKey::defaultContainer] = + (configType == ConfigTypes::ShadowSocks) ? configKey::amneziaSsxray : configKey::amneziaXray; + if (description.isEmpty()) { + config[configKey::description] = m_coreController->m_appSettingsRepository->nextAvailableServerName(); + } else { + config[configKey::description] = description; + } + config[configKey::hostName] = hostName; + + return config; + } + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + + QString vpnKey = getValueFromIni("configs/TEST_SELF_HOSTED_CONFIG"); + QJsonObject importedConfig = m_coreController->m_importCoreController->extractConfigFromData(vpnKey).config; + + m_coreController->m_importCoreController->importConfig(importedConfig); + + qDebug() << "SELF-HOSTED ADMIN SERVER IMPORTED\n"; + } + + void cleanupTestCase() + { + int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + + for (int containerIndex = 1; containerIndex < 7; ++containerIndex) + m_coreController->m_installUiController->clearCachedProfile(serverId, containerIndex); + + m_coreController->m_serversController->removeServer(serverId); + + qDebug() << "SERVER REMOVED\n"; + + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testVless() + { + int serverIndex = m_coreController->m_serversRepository->defaultServerIndex(); + const QString serverId = m_coreController->m_serversRepository->serverIdAt(serverIndex); + + QString clientName = "Test Client (vless (de)serialization)"; + + ExportController::ExportResult exportResult = m_coreController->m_exportController->generateXrayConfig(serverId, clientName); + + ImportController::ImportResult importResult; + + QString config = exportResult.config; + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("vless://")) { + configType = ConfigTypes::Xray; + importResult.config = extractXrayConfig( + Utils::JsonToString(serialization::vless::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), + configType, prefix); + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with vless://"); + } + + QCOMPARE(importResult.config, config); + } + + void testVmessNew() + { + QString clientName = "Test Client (vmess_new deserialization)"; + + ImportController::ImportResult importResult; + + m_coreController->m_importController->extractConfigFromData(getValueFromIni("configs/TEST_CONFIG_VMESS_NEW")); + + QString config = m_coreController->m_importController->getConfig(); + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("vmess://") && config.contains("@")) { + configType = ConfigTypes::Xray; + importResult.config = extractXrayConfig( + Utils::JsonToString(serialization::vmess_new::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), + configType, prefix); + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with vmess:// or not contain @"); + } + + QCOMPARE(importResult.config, config); + } + + void testVmess() + { + QString clientName = "Test Client (vmess deserialization)"; + + ImportController::ImportResult importResult; + + m_coreController->m_importController->extractConfigFromData(getValueFromIni("configs/TEST_CONFIG_VMESS")); + + QString config = m_coreController->m_importController->getConfig(); + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("vmess://")) { + configType = ConfigTypes::Xray; + importResult.config = extractXrayConfig( + Utils::JsonToString(serialization::vmess::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), + configType, prefix); + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with vmess://"); + } + + QCOMPARE(importResult.config, config); + } + + void testTrojan() + { + QString clientName = "Test Client (trojan deserialization)"; + + ImportController::ImportResult importResult; + + m_coreController->m_importController->extractConfigFromData(getValueFromIni("configs/TEST_CONFIG_TROJAN")); + + QString config = m_coreController->m_importController->getConfig(); + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("trojan://")) { + configType = ConfigTypes::Xray; + importResult.config = extractXrayConfig( + Utils::JsonToString(serialization::trojan::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), + configType, prefix); + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with trojan://"); + } + + QCOMPARE(importResult.config, config); + } + + void testSS() + { + QString clientName = "Test Client (ss deserialization)"; + + ImportController::ImportResult importResult; + + m_coreController->m_importController->extractConfigFromData(getValueFromIni("configs/TEST_CONFIG_SS")); + + QString config = m_coreController->m_importController->getConfig(); + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("ss://") && !config.contains("plugin=")) { + configType = ConfigTypes::ShadowSocks; + importResult.config = extractXrayConfig( + Utils::JsonToString(serialization::ss::Deserialize(config, &prefix, &errormsg), QJsonDocument::JsonFormat::Compact), + configType, prefix); + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with ss:// or contain plugin="); + } + + QCOMPARE(importResult.config, config); + } + + void testSSd() + { + QString clientName = "Test Client (ssd deserialization)"; + + ImportController::ImportResult importResult; + + m_coreController->m_importController->extractConfigFromData(getValueFromIni("configs/TEST_CONFIG_SSD")); + + QString config = m_coreController->m_importController->getConfig(); + QString prefix; + QString errormsg; + ConfigTypes configType = ConfigTypes::Invalid; + + if (config.startsWith("ssd://")) { + QStringList tmp; + QList> servers = serialization::ssd::Deserialize(config, &prefix, &tmp); + configType = ConfigTypes::ShadowSocks; + // Took only first config from list + if (!servers.isEmpty()) { + importResult.config = extractXrayConfig(servers.first().first, configType); + } + if (!importResult.config.empty()) { + importResult.configType = configType; + } + QVERIFY2(!importResult.config.empty(), "Config shouldn't be empty"); + } else { + QSKIP("Config not starts with ssd://"); + } + + QCOMPARE(importResult.config, config); + } +}; + +QTEST_MAIN(TestSerialization) +#include "testSerialization.moc" diff --git a/client/tests/testServerEdgeCases.cpp b/client/tests/testServerEdgeCases.cpp index 91dba71fd..0bbe4339f 100644 --- a/client/tests/testServerEdgeCases.cpp +++ b/client/tests/testServerEdgeCases.cpp @@ -1,7 +1,9 @@ +#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/repositories/secureServersRepository.h" @@ -21,6 +23,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -45,7 +53,7 @@ private slots: } void testInvalidIndexOperations() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); auto importResult = m_coreController->m_importCoreController->extractConfigFromData(awgKey); m_coreController->m_importCoreController->importConfig(importResult.config); diff --git a/client/tests/testServerEdit.cpp b/client/tests/testServerEdit.cpp index 4e101eb6e..cf5835100 100644 --- a/client/tests/testServerEdit.cpp +++ b/client/tests/testServerEdit.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -21,6 +21,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -45,7 +51,7 @@ private slots: } void testServerEditTriggersHandlers() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); QSignalSpy importFinishedSpy(m_coreController->m_importCoreController, &ImportController::importFinished); auto importResult = m_coreController->m_importCoreController->extractConfigFromData(awgKey); @@ -73,8 +79,8 @@ private slots: } void testServerEditPreservesDefault() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); auto importResult1 = m_coreController->m_importCoreController->extractConfigFromData(awgKey); m_coreController->m_importCoreController->importConfig(importResult1.config); diff --git a/client/tests/testServersModelSync.cpp b/client/tests/testServersModelSync.cpp index 12b1288fe..528c00c9d 100644 --- a/client/tests/testServersModelSync.cpp +++ b/client/tests/testServersModelSync.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" @@ -21,6 +21,12 @@ private: CoreController* m_coreController; SecureQSettings* m_settings; + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + private slots: void initTestCase() { QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); @@ -44,7 +50,7 @@ private slots: } void testServersModelSyncOnOperations() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); if (!m_coreController->m_serversModel) { QSKIP("ServersModel not available"); @@ -71,9 +77,9 @@ private slots: } void testServersModelDefaultIndexSync() { - QString awgKey = "vpn://AAABFHjadZBBT4QwEIX_ipkzS2wBJdyMB1cPXvbgwRgyQnclgZa0RTYS_rszXRa52Mt77TfzOu0EldEeG62sg-J9AhxPUEywF1CAuF3WTl4dRLCXhJIVpVuUEMpWdLdFKaH7FeUb9Mx3scpFk0XTRbOLvlSkKZsOz-Gi4BsdRiV_EGEydhwlg0tWynEZmd5Yz1bkoaK3xpvKtOU3_UFjOE3SsRs-tfIl1rVVzoWQOI9FzC3eonYcU4ZmgkPdwxz9fSYdYafVT4M7-lEJ80cEtTri0PrH_2q4wlW26f1lioe3p5uDsjQWoS_j_Ct2ipvGU6zO2PWtiivT8RPQudHYmqBXzl-3Yn2slBEMTtklgYt4C_Mv3ROMwA"; - QString xrayKey = "vpn://AAAAtXjadY7NCsJADIRfRXKui1YP0qt3L14EkRK7EQt2d0lS_0rf3awonjyFmW-YyQBNDIptIBao9sNPQgXYBXq2OL0zPqCA96kGSJHV6HK5MFP6YyCt0XsmsQqYz9zKzd3MmDIGyek6cdRoUJsE43gowNMJ-4uu_695kobbpG0MBndmTrbEV4sWcI6iG-zIQE47umOXLuSa2BlNKHKL7PMeiX5lmdH79bIsoBfiT0UOZQnjCw_AXRQ"; - QString wgKey = "vpn://AAAAwXjahY89a8NADIb_StDsHLFDIHjt0C1LhgwlBNWnpgfx3SHp6hDj_15dacnYTS_Po68ZhhQVQyQW6N_mZ4QecIz0CLieAtO1IHto4Fn3M-TEat6u3XetMSnvkfSC3jOJjYN24_audRtjyhil-pfMSZPB4jMsy7kBTx9Ybvryz2ZPMnDIGlI042TktZLVkfjLmhr4TKIHHMnodHV0xzHfyA1pNJZRZEr1alAS_Yvbin6e6LoGihD_DqhSjbB8AyB_ZI8"; + QString awgKey = getValueFromIni("configs/TEST_CONFIG_AWG"); + QString xrayKey = getValueFromIni("configs/TEST_CONFIG_WG"); + QString wgKey = getValueFromIni("configs/TEST_CONFIG_XRAY"); if (!m_coreController->m_serversModel) { QSKIP("ServersModel not available"); diff --git a/client/tests/testSettingsSignals.cpp b/client/tests/testSettingsSignals.cpp index 22c5c0677..52ec0ce84 100644 --- a/client/tests/testSettingsSignals.cpp +++ b/client/tests/testSettingsSignals.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "ui/controllers/settingsUiController.h" diff --git a/client/tests/testSignalOrder.cpp b/client/tests/testSignalOrder.cpp index 8c6cf0d65..cc999f1b6 100644 --- a/client/tests/testSignalOrder.cpp +++ b/client/tests/testSignalOrder.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" diff --git a/client/tests/testUiAllowedDnsModelAndController.cpp b/client/tests/testUiAllowedDnsModelAndController.cpp new file mode 100644 index 000000000..558dcca62 --- /dev/null +++ b/client/tests/testUiAllowedDnsModelAndController.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiAllowedDnsModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testRolesAndSignals() + { + QSignalSpy finishedSpy(m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::finished); + QSignalSpy errorOccurredSpy(m_coreController->m_allowedDnsUiController, &AllowedDnsUiController::errorOccurred); + + QString ip = "188.40.167.81"; + + m_coreController->m_allowedDnsUiController->addDns(ip); + m_coreController->m_allowedDnsUiController->updateModel(); + QVERIFY2(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY2(finishedSpy.count() == 1, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_allowedDnsModel->rowCount() == 1, "AllowedDnsModel should have 1 row"); + + QModelIndex allowedDnsModelIndex = m_coreController->m_allowedDnsModel->index(0, 0); + QVERIFY2(allowedDnsModelIndex.isValid(), "Site model index should be valid"); + + auto dnsIp = m_coreController->m_allowedDnsModel->data(allowedDnsModelIndex, AllowedDnsModel::IpRole); + QString msg = QString("dns ip should be %1, got %2").arg(ip, dnsIp.toString()); + QVERIFY2(dnsIp == ip, msg.toLocal8Bit().constData()); + + m_coreController->m_allowedDnsUiController->importDns(getValueFromIni("paths/TEST_DNS_LIST_PATH"), true); + m_coreController->m_allowedDnsUiController->updateModel(); + QVERIFY2(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY2(finishedSpy.count() == 2, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_allowedDnsModel->rowCount() > 1, "AllowedDnsModel should have more than 1 row"); + + m_coreController->m_allowedDnsUiController->exportDns(getValueFromIni("paths/TEST_EXPORT_PATH") + "test_dns_export.json"); + QVERIFY2(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY2(finishedSpy.count() == 3, "finished signal should be emitted"); + + m_coreController->m_allowedDnsUiController->removeDns(0); + m_coreController->m_allowedDnsUiController->updateModel(); + QVERIFY2(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY2(finishedSpy.count() == 4, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_allowedDnsModel->rowCount() == 0, "AllowedDnsModel should have 0 rows"); + } +}; + +QTEST_MAIN(TestUiAllowedDnsModelAndController) +#include "testUiAllowedDnsModelAndController.moc" diff --git a/client/tests/testUiAppSTModelAndController.cpp b/client/tests/testUiAppSTModelAndController.cpp new file mode 100644 index 000000000..9a2d86c6c --- /dev/null +++ b/client/tests/testUiAppSTModelAndController.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiAppSTModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testRolesAndSignals() + { + QSignalSpy finishedSpy(m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::finished); + QSignalSpy errorOccurredSpy(m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::errorOccurred); + QSignalSpy isSplitTunnelingChangedSpy(m_coreController->m_appSplitTunnelingUiController, &AppSplitTunnelingUiController::isSplitTunnelingEnabledChanged); + + m_coreController->m_appSplitTunnelingUiController->toggleSplitTunneling(true); + QVERIFY2(isSplitTunnelingChangedSpy.count() == 1, "isSplitTunnelingChangedSpy signal should be emitted"); + QVERIFY2(m_coreController->m_appSplitTunnelingUiController->isSplitTunnelingEnabled() == true, "AppSplitTunneling should be enabled"); + + m_coreController->m_appSplitTunnelingUiController->toggleSplitTunneling(false); + QVERIFY2(isSplitTunnelingChangedSpy.count() == 2, "isSplitTunnelingChangedSpy signal should be emitted 2nd time"); + QVERIFY2(m_coreController->m_appSplitTunnelingUiController->isSplitTunnelingEnabled() == false, "AppSplitTunneling should be disabled"); + + QString app = getValueFromIni("paths/TEST_APP_PATH"); + + m_coreController->m_appSplitTunnelingUiController->addApp(app); + m_coreController->m_appSplitTunnelingUiController->updateModel(); + QVERIFY2(finishedSpy.count() == 1, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_appSplitTunnelingModel->rowCount() == 1, "AppSplitTunnelingModel should have 1 row"); + + QModelIndex appSTModelIndex = m_coreController->m_appSplitTunnelingModel->index(0, 0); + QVERIFY2(appSTModelIndex.isValid(), "Site model index should be valid"); + + auto appPath = m_coreController->m_appSplitTunnelingModel->data(appSTModelIndex, AppSplitTunnelingModel::AppPathRole); + QString msg = QString("app path should be %1, got %2").arg(app, appPath.toString()); + QVERIFY2(app.contains(appPath.toString()) == true, msg.toLocal8Bit().constData()); + + auto pkgAppName = m_coreController->m_appSplitTunnelingModel->data(appSTModelIndex, AppSplitTunnelingModel::PackageAppNameRole); + QVERIFY2(pkgAppName == true, "app name should be set"); + + auto pkgAppIcon = m_coreController->m_appSplitTunnelingModel->data(appSTModelIndex, AppSplitTunnelingModel::PackageAppIconRole); + QVERIFY2(pkgAppIcon == true, "app image should be set"); + + m_coreController->m_appSplitTunnelingUiController->addApp(app); + m_coreController->m_appSplitTunnelingUiController->updateModel(); + QVERIFY2(errorOccurredSpy.count() == 1, "errorOccurred signal should be emitted"); + QVERIFY2(m_coreController->m_appSplitTunnelingModel->rowCount() == 1, "AppSplitTunnelingModel should have 3 rows (same app should not be added)"); + + m_coreController->m_appSplitTunnelingUiController->removeApp(0); + m_coreController->m_appSplitTunnelingUiController->updateModel(); + QVERIFY2(finishedSpy.count() == 2, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_appSplitTunnelingModel->rowCount() == 0, "AppSplitTunnelingModel should have 0 rows"); + } +}; + +QTEST_MAIN(TestUiAppSTModelAndController) +#include "testUiAppSTModelAndController.moc" diff --git a/client/tests/testUiIpModelAndController.cpp b/client/tests/testUiIpModelAndController.cpp new file mode 100644 index 000000000..f58b57e67 --- /dev/null +++ b/client/tests/testUiIpModelAndController.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiIpModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + + QString getValueFromIni(const QString &key) + { + QSettings settings("test_vars.ini", QSettings::IniFormat); + return settings.value(key).toString(); + } + + QString normalizeHostname(const QString &hostname) const + { + QString normalized = hostname; + normalized.replace("https://", ""); + normalized.replace("http://", ""); + normalized.replace("ftp://", ""); + normalized = normalized.split("/", Qt::SkipEmptyParts).first(); + return normalized; + } + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testRolesAndSignals() + { + QSignalSpy finishedSpy(m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::finished); + QSignalSpy errorOccurredSpy(m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::errorOccurred); + QSignalSpy isSplitTunnelingChangedSpy(m_coreController->m_ipSplitTunnelingUiController, &IpSplitTunnelingUiController::isSplitTunnelingEnabledChanged); + + m_coreController->m_ipSplitTunnelingUiController->toggleSplitTunneling(true); + QVERIFY2(isSplitTunnelingChangedSpy.count() == 1, "isSplitTunnelingChangedSpy signal should be emitted"); + QVERIFY2(m_coreController->m_ipSplitTunnelingUiController->isSplitTunnelingEnabled() == true, "ipSplitTunneling should be enabled"); + + m_coreController->m_ipSplitTunnelingUiController->toggleSplitTunneling(false); + QVERIFY2(isSplitTunnelingChangedSpy.count() == 2, "isSplitTunnelingChangedSpy signal should be emitted 2nd time"); + QVERIFY2(m_coreController->m_ipSplitTunnelingUiController->isSplitTunnelingEnabled() == false, "ipSplitTunneling should be disabled"); + + QString site = "2ip.io"; + + m_coreController->m_ipSplitTunnelingUiController->addSite(site); + m_coreController->m_ipSplitTunnelingUiController->addSite("whatismyipaddress.com"); + m_coreController->m_ipSplitTunnelingUiController->updateModel(); + QVERIFY2(finishedSpy.count() == 2, "finished signal should be emitted 2 times"); + QVERIFY2(m_coreController->m_ipSplitTunnelingModel->rowCount() == 2, "IpSplitTunnelingModel should have 2 rows"); + + QModelIndex siteModelIndex = m_coreController->m_ipSplitTunnelingModel->index(0, 0); + QVERIFY2(siteModelIndex.isValid(), "Ip model index should be valid"); + + auto siteUrl = m_coreController->m_ipSplitTunnelingModel->data(siteModelIndex, IpSplitTunnelingModel::UrlRole); + QCOMPARE(siteUrl, normalizeHostname(site)); + + auto siteIp = m_coreController->m_ipSplitTunnelingModel->data(siteModelIndex, IpSplitTunnelingModel::IpRole); + QVERIFY2(siteIp.isNull() == false, "Ip should not be empty"); + + m_coreController->m_ipSplitTunnelingUiController->removeSite(0); + m_coreController->m_ipSplitTunnelingUiController->updateModel(); + QVERIFY2(finishedSpy.count() == 3, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_ipSplitTunnelingModel->rowCount() == 1, "IpSplitTunnelingModel should have 1 row"); + + m_coreController->m_ipSplitTunnelingUiController->importSites(getValueFromIni("paths/TEST_SITES_LIST_PATH"), true); + m_coreController->m_ipSplitTunnelingUiController->updateModel(); + QVERIFY2(errorOccurredSpy.count() == 0, "errorOccurred signal should not be emitted"); + QVERIFY2(finishedSpy.count() == 4, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_ipSplitTunnelingModel->rowCount() > 1, "IpSplitTunnelingModel should have more than 1 row"); + + m_coreController->m_ipSplitTunnelingUiController->exportSites(getValueFromIni("paths/TEST_EXPORT_PATH") + "test_ips_export.json"); + QVERIFY2(finishedSpy.count() == 5, "finished signal should be emitted"); + + m_coreController->m_ipSplitTunnelingUiController->removeSites(); + m_coreController->m_ipSplitTunnelingUiController->updateModel(); + QVERIFY2(finishedSpy.count() == 6, "finished signal should be emitted"); + QVERIFY2(m_coreController->m_ipSplitTunnelingModel->rowCount() == 0, "IpSplitTunnelingModel should have 0 rows"); + } +}; + +QTEST_MAIN(TestUiIpModelAndController) +#include "testUiIpModelAndController.moc" diff --git a/client/tests/testUiLanguageModelAndController.cpp b/client/tests/testUiLanguageModelAndController.cpp new file mode 100644 index 000000000..45f62b1cf --- /dev/null +++ b/client/tests/testUiLanguageModelAndController.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "core/controllers/coreController.h" +#include "core/models/serverDescription.h" +#include "secureQSettings.h" +#include "vpnConnection.h" + +using namespace amnezia; + +class TestUiLanguageModelAndController : public QObject +{ + Q_OBJECT + +private: + CoreController *m_coreController; + SecureQSettings *m_settings; + +private slots: + void initTestCase() + { + QString testOrg = "AmneziaVPN-Test-" + QUuid::createUuid().toString(); + m_settings = new SecureQSettings(testOrg, "amnezia-client", nullptr, false); + + auto vpnConnection = QSharedPointer::create(nullptr, nullptr); + + m_coreController = new CoreController(vpnConnection, m_settings, nullptr, this); + } + + void cleanupTestCase() + { + m_settings->clearSettings(); + delete m_coreController; + delete m_settings; + } + + void init() + { + m_settings->clearSettings(); + if (m_coreController->m_serversModel) { + m_coreController->m_serversModel->updateModel(QVector(), -1); + } + } + + void testChangeLanguage() + { + QVERIFY2(m_coreController->m_languageModel->rowCount() > 0, "Language model should not be empty"); + + QSignalSpy updateTranslationsSpy(m_coreController->m_languageUiController, &LanguageUiController::updateTranslations); + QSignalSpy translationsUpdatedSpy(m_coreController->m_languageUiController, &LanguageUiController::translationsUpdated); + + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::China_cn); + QVERIFY2(updateTranslationsSpy.count() == 1, "updateTranslations signal should be emitted"); + QVERIFY2(translationsUpdatedSpy.count() == 1, "translationsUpdated signal should be emitted"); + + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::English); + QVERIFY2(updateTranslationsSpy.count() == 2, "updateTranslations signal should be emitted"); + QVERIFY2(translationsUpdatedSpy.count() == 2, "translationsUpdated signal should be emitted"); + } + + void testUrl() + { + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::Russian); + QString siteRU = m_coreController->m_languageUiController->getCurrentSiteUrl("test_path"); + QString docsRU = m_coreController->m_languageUiController->getCurrentDocsUrl("test_path"); + + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::English); + QString siteEN = m_coreController->m_languageUiController->getCurrentSiteUrl("test_path"); + QString docsEN = m_coreController->m_languageUiController->getCurrentDocsUrl("test_path"); + + QVERIFY2(siteRU != siteEN, "site url's should not be same"); + QVERIFY2(docsRU != docsEN, "docs url's should not be same"); + } + + void testLineHeight() + { + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::Burmese); + QVERIFY2(m_coreController->m_languageUiController->getLineHeightAppend() == 10, "line height should be 10"); + + m_coreController->m_languageUiController->changeLanguage(LanguageSettings::AvailableLanguageEnum::English); + QVERIFY2(m_coreController->m_languageUiController->getLineHeightAppend() == 0, "line height should be 0"); + } +}; + +QTEST_MAIN(TestUiLanguageModelAndController) +#include "testUiLanguageModelAndController.moc" diff --git a/client/tests/testUiServersModelAndController.cpp b/client/tests/testUiServersModelAndController.cpp index ca67ab612..8584af307 100644 --- a/client/tests/testUiServersModelAndController.cpp +++ b/client/tests/testUiServersModelAndController.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include "core/controllers/coreController.h" #include "core/models/serverDescription.h" diff --git a/client/tests/test_vars.ini b/client/tests/test_vars.ini new file mode 100644 index 000000000..2413b41cb --- /dev/null +++ b/client/tests/test_vars.ini @@ -0,0 +1,23 @@ +[configs] +TEST_SELF_HOSTED_CONFIG = self-hosted_config_string +TEST_CONFIG_ANY = any_config_string +TEST_CONFIG_AWG = awg_config_string +TEST_CONFIG_WG = wg_config_string +TEST_CONFIG_XRAY = xray_config_string +TEST_CONFIG_VMESS_NEW = vmess_new_config_string +TEST_CONFIG_VMESS = vmess_config_string +TEST_CONFIG_TROJAN = trojan_config_string +TEST_CONFIG_SS = ss_config_string +TEST_CONFIG_SSD = ssd_config_string + +[paths] +TEST_APP_PATH = path/to/app +TEST_SITES_LIST_PATH = path/to/file/with/sites_list +TEST_DNS_LIST_PATH = path/to/file/with/dns_list +TEST_EXPORT_PATH = path/to/export_directory + +[secrets] +TEST_SERVER_HOST = server_host +TEST_SERVER_USER = server_user +TEST_SERVER_PASSWORD = server_password +TEST_SERVER_PORT = 22