Files
BiLauncher/tests/net/tst_seasonalbuildservice.cpp
T
galeon 41599fa82f
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
new unit tests, hehe 67
Signed-off-by: galeon <dertiellis@gmail.com>
2026-10-12 13:14:08 +03:00

198 lines
7.3 KiB
C++

#include "testenv.h"
#include "fixtures.h"
#include "nettest.h"
#include "launcherpaths.h"
#include "localization.h"
#include "seasonalbuildservice.h"
#include <QJsonArray>
#include <QSignalSpy>
namespace {
const QUrl kBase(QStringLiteral("https://launcher-builds.galeonworx.com"));
const QString kIndex = QStringLiteral("https://launcher-builds.galeonworx.com/index.json");
}
class TestSeasonalBuildService : public QObject
{
Q_OBJECT
FakeInternet m_net;
private slots:
void initTestCase() { BILAUNCHER_START_FAKE_INTERNET(m_net); }
void init()
{
TestEnv::resetLauncherRoot();
m_net.reset();
}
void withoutBaseUrlFails()
{
SeasonalBuildService service;
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 5000);
QVERIFY(!r.ok);
QCOMPARE(r.warning, Loc::text("seasonal.error.noServerUrl"));
QCOMPARE(service.lastError(), Loc::text("seasonal.error.noServerUrl"));
QVERIFY(m_net.requests().isEmpty());
}
void parsesValidatesAndSorts()
{
m_net.route(kIndex, FakeResponse::json(Fixtures::read("seasonal/index.json")));
SeasonalBuildService service;
service.setBaseUrl(kBase);
QCOMPARE(service.baseUrl(), kBase);
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QVERIFY2(r.ok, qPrintable(r.warning));
QStringList ids;
for (const SeasonalBuildEntry &entry : service.builds())
ids << entry.id;
// Открытые сезоны первыми (новые выше), закрытые — после; всё невалидное отброшено.
QCOMPARE(ids, QStringList({"season-5", "vanilla-plus", "season-4"}));
const auto s5 = service.find("season-5");
QVERIFY(s5.has_value());
QVERIFY(s5->isValid());
QCOMPARE(s5->name, QStringLiteral("Сезон 5: Пустоши"));
QCOMPARE(s5->revision, 3);
QCOMPARE(s5->minecraftVersion, QStringLiteral("1.21.1"));
QCOMPARE(s5->loader, QStringLiteral("fabric"));
QCOMPARE(s5->loaderVersion, QStringLiteral("0.16.10"));
QCOMPARE(s5->javaMajor, 21);
QCOMPARE(s5->modCount, 120);
QCOMPARE(s5->description, QStringLiteral("Пустоши")); // обрезаны пробелы
QCOMPARE(s5->seasonStart, QDate(2025, 7, 1));
QVERIFY(!s5->seasonEnd.isValid());
QCOMPARE(s5->archiveSize, qint64(123456));
QCOMPARE(s5->archiveSha256, QString("abcdef" + QString(58, u'0'))); // к нижнему регистру
QCOMPARE(s5->archiveUrl, QUrl("https://launcher-builds.galeonworx.com/packs/s5.zip"));
QCOMPARE(service.find("season-4")->seasonEnd, QDate(2025, 6, 1));
QVERIFY(service.find("vanilla-plus")->loader.isEmpty());
QVERIFY(QFile::exists(LauncherPaths::seasonalCatalogFile()));
}
void baseUrlPathIsKept()
{
m_net.route("https://launcher-builds.galeonworx.com/beta/index.json",
FakeResponse::json(QJsonObject{{"formatVersion", 1}, {"builds", QJsonArray{}}}));
SeasonalBuildService service;
service.setBaseUrl(QUrl("https://launcher-builds.galeonworx.com/beta"));
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QCOMPARE(m_net.hits("https://launcher-builds.galeonworx.com/beta/index.json"), 1);
QVERIFY(!r.ok);
QCOMPARE(r.warning, Loc::text("seasonal.error.catalogEmpty"));
}
void formatVersionChecks_data()
{
QTest::addColumn<int>("format");
QTest::addColumn<QString>("error");
QTest::newRow("too new") << 2 << Loc::text("seasonal.error.formatTooNew").arg(2);
QTest::newRow("unknown") << 0 << Loc::text("seasonal.error.formatUnknown").arg(0);
}
void formatVersionChecks()
{
QFETCH(int, format);
QFETCH(QString, error);
QJsonObject catalog = Fixtures::readJson("seasonal/index.json");
catalog["formatVersion"] = format;
m_net.route(kIndex, FakeResponse::json(catalog));
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QVERIFY(!r.ok);
QCOMPARE(r.warning, error);
QCOMPARE(service.lastError(), error);
}
void cacheIsBoundToBaseUrl()
{
m_net.route(kIndex, FakeResponse::json(Fixtures::read("seasonal/index.json")));
{
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
}
// Тот же адрес — свежий кеш, без сети.
{
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback());
QVERIFY(service.hasData());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 5000);
QCOMPARE(m_net.hits(kIndex), 1);
}
// Другой адрес — чужой кеш не подхватывается.
SeasonalBuildService other;
other.setBaseUrl(QUrl("https://mirror.example"));
m_net.route("https://mirror.example/index.json", FakeResponse::error(503));
CallResult r;
other.ensureLoaded(r.callback());
QVERIFY(!other.hasData());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QVERIFY(!r.ok);
QVERIFY(r.warning.startsWith(Loc::text("seasonal.error.catalogFetchFailed").section(u'%', 0, 0)));
}
void setBaseUrlResetsData()
{
m_net.route(kIndex, FakeResponse::json(Fixtures::read("seasonal/index.json")));
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QVERIFY(service.hasData());
QSignalSpy changed(&service, &SeasonalBuildService::buildsChanged);
service.setBaseUrl(kBase); // тот же адрес — ничего не происходит
QCOMPARE(changed.count(), 0);
service.setBaseUrl(QUrl("https://mirror.example"));
QCOMPARE(changed.count(), 1);
QVERIFY(!service.hasData());
QVERIFY(!service.find("season-5").has_value());
}
void offlineWithCacheKeepsBuilds()
{
m_net.route(kIndex, FakeResponse::json(Fixtures::read("seasonal/index.json")));
{
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback());
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
}
m_net.route(kIndex, FakeResponse::error(502));
SeasonalBuildService service;
service.setBaseUrl(kBase);
CallResult r;
service.ensureLoaded(r.callback(), /*forceRefresh=*/true);
QTRY_COMPARE_WITH_TIMEOUT(r.calls, 1, 15000);
QVERIFY(r.ok);
QVERIFY(!r.warning.isEmpty());
QCOMPARE(service.builds().size(), 3);
QCOMPARE(service.lastError(), r.warning);
}
};
BILAUNCHER_TEST_MAIN(TestSeasonalBuildService)
#include "tst_seasonalbuildservice.moc"