#include "TestHarness.h" #include "src/config/ConfigManager.h" #include "src/reminder/ReminderStore.h" #include "src/weather/WeatherStore.h" #include "src/web/WebStore.h" #include #include #include #include namespace { bool writeBrokenJson(const QString &filePath) { QDir().mkpath(QFileInfo(filePath).absolutePath()); QFile file(filePath); return file.open(QIODevice::WriteOnly | QIODevice::Truncate) && file.write(QByteArrayLiteral("{broken")) == 7; } QString qaConfigDirectory() { return QFileInfo(ConfigManager().appConfigPath()).absolutePath(); } bool isQaConfigDirectory(const QString &path) { return path.contains(QStringLiteral("QtDesktopPetInternalQA"), Qt::CaseInsensitive) || path.contains(QStringLiteral("qttest"), Qt::CaseInsensitive); } void clearQaConfigFiles() { const QString path = qaConfigDirectory(); if (!isQaConfigDirectory(path)) { return; } QDir directory(path); const QStringList filters = { QStringLiteral("app_config.json"), QStringLiteral("app_config.broken.*.json"), QStringLiteral("ai_config.json"), QStringLiteral("ai_config.broken.*.json"), QStringLiteral("weather_config.json"), QStringLiteral("weather_config.broken.*.json"), QStringLiteral("web_config.json"), QStringLiteral("web_config.broken.*.json"), QStringLiteral("reminders.json"), QStringLiteral("reminders.broken.*.json"), }; for (const QString &fileName : directory.entryList(filters, QDir::Files)) { directory.remove(fileName); } } int backupCount(const QString &pattern) { return QDir(qaConfigDirectory()).entryList({pattern}, QDir::Files).size(); } class QaConfigFiles { public: QaConfigFiles() { QA_EXPECT(isQaConfigDirectory(qaConfigDirectory())); clearQaConfigFiles(); } ~QaConfigFiles() { clearQaConfigFiles(); } }; } QA_TEST(ConfigRecovery, AppConfigFallsBackAndBacksUpBrokenJson) { QaConfigFiles files; ConfigManager manager; QA_EXPECT(writeBrokenJson(manager.appConfigPath())); const AppConfig config = manager.loadAppConfig(); QA_EXPECT_EQ(config.windowPosition, QPoint(100, 100)); QA_EXPECT(config.alwaysOnTop); QA_EXPECT(!QFile::exists(manager.appConfigPath())); QA_EXPECT_EQ(backupCount(QStringLiteral("app_config.broken.*.json")), 1); } QA_TEST(ConfigRecovery, AiConfigFallsBackAndBacksUpBrokenJson) { QaConfigFiles files; ConfigManager manager; QA_EXPECT(writeBrokenJson(manager.aiConfigPath())); const AIConfigStore store = manager.loadAIConfigStore(); QA_EXPECT_EQ(store.activeProvider, QStringLiteral("custom")); QA_EXPECT(store.providers.isEmpty()); QA_EXPECT(!QFile::exists(manager.aiConfigPath())); QA_EXPECT_EQ(backupCount(QStringLiteral("ai_config.broken.*.json")), 1); } QA_TEST(ConfigRecovery, WeatherAndWebConfigsFallBackAndBackUpBrokenJson) { QaConfigFiles files; WeatherStore weatherStore; WebStore webStore; QA_EXPECT(writeBrokenJson(weatherStore.storePath())); QA_EXPECT(writeBrokenJson(webStore.storePath())); QString weatherError; QString webError; const WeatherConfig weather = weatherStore.load(&weatherError); const WebConfig web = webStore.load(&webError); QA_EXPECT_EQ(weather.provider, QStringLiteral("open-meteo")); QA_EXPECT(weather.autoLocateWhenNoDefault); QA_EXPECT(web.enabled); QA_EXPECT(!web.defaultToggleOn); QA_EXPECT(!weatherError.isEmpty()); QA_EXPECT(!webError.isEmpty()); QA_EXPECT_EQ(backupCount(QStringLiteral("weather_config.broken.*.json")), 1); QA_EXPECT_EQ(backupCount(QStringLiteral("web_config.broken.*.json")), 1); } QA_TEST(ConfigRecovery, ReminderStoreFallsBackToEmptyAndBacksUpBrokenJson) { QaConfigFiles files; ReminderStore store; QA_EXPECT(writeBrokenJson(store.storePath())); QString error; QA_EXPECT(store.load(&error).isEmpty()); QA_EXPECT(!error.isEmpty()); QA_EXPECT(!QFile::exists(store.storePath())); QA_EXPECT_EQ(backupCount(QStringLiteral("reminders.broken.*.json")), 1); } QA_TEST(ConfigRecovery, SameSecondBackupsNeverOverwriteEarlierFiles) { QaConfigFiles files; WeatherStore store; QA_EXPECT(writeBrokenJson(store.storePath())); store.load(); QA_EXPECT(writeBrokenJson(store.storePath())); store.load(); QA_EXPECT_EQ(backupCount(QStringLiteral("weather_config.broken.*.json")), 2); }