Files
vdm/gui/tests/tst_optionsdialog.cpp
samiandClaude Sonnet 5 1fd2e0a0db gui: Options — Capture tab, saveTo.allowedRoots, lock coverage to the schema
Options was missing 8 of the 43 real settings.* keys: capture.enabled,
monitoredExtensions, monitoredMimeTypes, minSizeBytes, excludedHosts,
bypassModifier, autoStartTypes, and saveTo.allowedRoots. The capture.* keys
were dropped in an earlier pass on the mistaken read that the spec's "File
Types" tab meant per-category extension lists (which do live on Category,
not settings.*) — they're real settings.* keys for a real daemon feature
(the extension's auto-capture policy), so the tab exists now, named
"Capture" to match what it actually configures rather than the spec's
label.

New tst_optionsdialog case (allKeysMatchesTheSchemaExactly) loads
Settings.schema.json itself at test time and diffs its property set against
OptionsDialog::allKeys() — this drifted silently once already, so the
regression is now a build-time gate an unused import or a future key
addition would trip, not something that needs re-discovering by hand again.

Verified against a real veloxd (not just mockd): settings.get across all
43 keys, a settings.set/get round trip on a scalar (connection.timeoutSec)
and on array-valued keys in the shapes OptionsDialog::currentValues()
actually produces (capture.monitoredExtensions, proxy.bypassHosts,
saveTo.allowedRoots), and event.settings.changed fanning out to a second
subscribed client — all round-tripped and restored to their original
values afterward. The real OptionsDialog widget also loads and renders
correctly against that same daemon's live defaults with no crash under
ASan+UBSan.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
2026-09-12 21:29:19 +04:00

77 lines
3.1 KiB
C++

// OptionsDialog::diffChanged unit tests. Lane GUI.
//
// Red when a key that didn't actually change starts getting resent to settings.set (it
// would still work, but it defeats the fixture's "changed[] names exactly what took
// effect" contract and spams event.settings.changed with noise), or a key present in
// `current` but absent from `original` stops being treated as changed.
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QtTest>
#include "dialogs/OptionsDialog.hpp"
using velox::gui::OptionsDialog;
class TstOptionsDialog : public QObject {
Q_OBJECT
private slots:
void identicalValuesProduceNoDiff();
void onlyChangedKeysAreReturned();
void keyAbsentFromOriginalCountsAsChanged();
void allKeysAreNonEmptyAndUnique();
void allKeysMatchesTheSchemaExactly();
};
void TstOptionsDialog::identicalValuesProduceNoDiff() {
const QJsonObject original{{"connection.timeoutSec", 30}, {"proxy.mode", "system"}};
const QJsonObject current = original;
QVERIFY(OptionsDialog::diffChanged(original, current).isEmpty());
}
void TstOptionsDialog::onlyChangedKeysAreReturned() {
const QJsonObject original{{"connection.timeoutSec", 30}, {"proxy.mode", "system"}};
const QJsonObject current{{"connection.timeoutSec", 60}, {"proxy.mode", "system"}};
const QJsonObject changed = OptionsDialog::diffChanged(original, current);
QCOMPARE(changed.size(), 1);
QCOMPARE(changed.value("connection.timeoutSec").toInt(), 60);
}
void TstOptionsDialog::keyAbsentFromOriginalCountsAsChanged() {
const QJsonObject original{{"proxy.mode", "system"}};
const QJsonObject current{{"proxy.mode", "system"}, {"proxy.port", 1080}};
const QJsonObject changed = OptionsDialog::diffChanged(original, current);
QCOMPARE(changed.size(), 1);
QVERIFY(changed.contains("proxy.port"));
}
void TstOptionsDialog::allKeysAreNonEmptyAndUnique() {
const QStringList keys = OptionsDialog::allKeys();
QVERIFY(!keys.isEmpty());
QCOMPARE(QSet<QString>(keys.begin(), keys.end()).size(), keys.size());
}
// Red when a key is added to (or removed from) Settings.schema.json without the same
// change landing here — either direction is a real bug: an invented key settings.set
// would reject with -32602, or a real key the dialog silently never shows.
void TstOptionsDialog::allKeysMatchesTheSchemaExactly() {
QFile f(QStringLiteral(VELOX_REPO_ROOT "/contracts/schema/types/Settings.schema.json"));
QVERIFY2(f.open(QIODevice::ReadOnly), qUtf8Printable(f.errorString()));
const QJsonObject schema = QJsonDocument::fromJson(f.readAll()).object();
const QJsonObject properties = schema.value("properties").toObject();
QVERIFY(!properties.isEmpty());
QSet<QString> schemaKeys;
for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) {
schemaKeys.insert(it.key());
}
const QStringList dialogKeysList = OptionsDialog::allKeys();
const QSet<QString> dialogKeys(dialogKeysList.begin(), dialogKeysList.end());
QCOMPARE(dialogKeys, schemaKeys);
}
QTEST_MAIN(TstOptionsDialog)
#include "tst_optionsdialog.moc"