1 parent
ebf8ff7869
commit
41599fa82f
67 files changed
+8775
-4
No files matched your search
@@ -0,0 +1,197 @@
|
||||
#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"
|
||||
Reference in new issue
Block a user