new unit tests, hehe 67
tests / linux (push) Failing after 2m18s
tests / windows (push) Skipped
tests / macos (push) Skipped
tests / package-smoke-linux (push) Skipped
tests / package-smoke-windows (push) Skipped
tests / package-smoke-macos (push) Skipped

Signed-off-by: galeon <dertiellis@gmail.com>
This commit is contained in:
galeon committed 2026-10-12 13:14:08 +03:00
1 parent ebf8ff7869
commit 41599fa82f
67 files changed
+8775 -4

No files matched your search

+136
View File
@@ -0,0 +1,136 @@
#pragma once
// «Фейковый интернет» для тестов сетевых сервисов без правки их кода.
//
// Сервисы лаунчера ходят на захардкоженные https-адреса через собственные
// QNetworkAccessManager. Прокси и SSL-конфигурация по умолчанию у Qt общие
// на весь процесс, поэтому start():
// - ставит QNetworkProxy::setApplicationProxy() на локальный порт;
// - отключает проверку сертификата в QSslConfiguration::defaultConfiguration().
// Дальше каждое https-соединение приходит сюда как CONNECT host:443; сокет
// переводится в TLS с тестовым самоподписанным сертификатом и обслуживается
// как обычный HTTP/1.1 — по таблице маршрутов (host + path).
//
// Запрос на неизвестный адрес получает 404 и попадает в unmatched(): тест
// может проверить, что код не ушёл куда-то, куда не ожидали.
#include <QByteArray>
#include <QHash>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QPair>
#include <QPointer>
#include <QString>
#include <QStringList>
#include <QTcpServer>
#include <QUrl>
#include <QUrlQuery>
#include <functional>
class QSslSocket;
class QTcpSocket;
struct FakeRequest
{
QByteArray method;
QString scheme; // https | http
QString host;
QString path;
QString query; // без «?»
QHash<QByteArray, QByteArray> headers; // имена в нижнем регистре
QByteArray body;
QUrl url() const;
QUrlQuery queryItems() const { return QUrlQuery(query); }
QUrlQuery formItems() const { return QUrlQuery(QString::fromUtf8(body)); }
QByteArray header(const QByteArray &name) const { return headers.value(name.toLower()); }
};
struct FakeResponse
{
int status = 200;
QByteArray body;
QByteArray contentType = "application/octet-stream";
QList<QPair<QByteArray, QByteArray>> headers;
int delayMs = 0; // ответить с задержкой
bool hang = false; // не отвечать вовсе (для отмены и таймаутов)
bool drop = false; // оборвать соединение без ответа
int truncateTo = -1; // объявить полный размер, отдать столько байт и оборвать
static FakeResponse json(const QByteArray &body, int status = 200);
static FakeResponse json(const class QJsonObject &object, int status = 200);
static FakeResponse text(const QByteArray &body, int status = 200);
static FakeResponse bytes(const QByteArray &body);
static FakeResponse error(int status, const QByteArray &body = {});
};
class FakeInternet : public QTcpServer
{
Q_OBJECT
public:
using Handler = std::function<FakeResponse(const FakeRequest &)>;
explicit FakeInternet(QObject *parent = nullptr);
~FakeInternet() override;
// Слушает 127.0.0.1 и перенаправляет на себя весь трафик процесса.
// При ошибке пишет причину в error (например, нет TLS-бэкенда с поддержкой сервера).
bool start(QString *error = nullptr);
void stop();
// Есть ли TLS вообще (без него сетевые тесты пропускаются).
static bool tlsAvailable(QString *why = nullptr);
// Маршрут: "https://host/path" — точное совпадение пути (query не учитывается),
// "https://host/prefix/*" — всё, что начинается с prefix/.
void route(const QString &pattern, const Handler &handler);
void route(const QString &pattern, const FakeResponse &response);
void unroute(const QString &pattern);
// Ответ на всё, что не подошло ни к одному маршруту (по умолчанию 404).
void setFallback(const FakeResponse &response) { m_fallback = response; }
// Сбросить маршруты и журнал.
void reset();
QList<FakeRequest> requests() const;
// Сколько раз был запрошен адрес "https://host/path" (query не учитывается).
int hits(const QString &url) const;
QStringList unmatched() const;
QStringList requestedUrls() const;
signals:
void requestServed(const QString &url);
protected:
void incomingConnection(qintptr descriptor) override;
private:
struct Connection;
struct Route
{
QString pattern;
QString host;
QString path;
bool prefix = false;
Handler handler;
};
void onReadyRead(Connection *connection);
void processBuffer(Connection *connection);
void dispatch(Connection *connection, FakeRequest request);
void respond(QPointer<QTcpSocket> socket, const FakeResponse &response, bool keepAlive);
void startTls(Connection *connection);
const Route *match(const FakeRequest &request) const;
QList<Route> m_routes;
FakeResponse m_fallback;
QList<FakeRequest> m_requests;
QStringList m_unmatched;
QByteArray m_certificate;
QByteArray m_key;
bool m_started = false;
};