From 308444f39d7c2a654c33145090e8b9a0a9990253 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Sat, 17 Oct 2026 21:48:30 +0200 Subject: [PATCH] =?UTF-8?q?test:=20traces=20r=C3=A9elles=20du=20moteur=20j?= =?UTF-8?q?ug=C3=A9es=20contre=20le=20contrat=20(refoptimizer)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Les 16 lignes de testTracesReplayWithZeroDivergence : chaque requête, et chaque réponse de l'optimiseur rejugée sur sa requête (--trace --semantic). Co-Authored-By: Claude Opus 5.5 --- tests/auto/common/etm-powersync-contrat.h | 44 +++++++++++++------- tests/auto/refoptimizer/testrefoptimizer.cpp | 10 +++++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/tests/auto/common/etm-powersync-contrat.h b/tests/auto/common/etm-powersync-contrat.h index 9087bc3..2259ff8 100644 --- a/tests/auto/common/etm-powersync-contrat.h +++ b/tests/auto/common/etm-powersync-contrat.h @@ -43,29 +43,16 @@ struct Verdict { * Juge une session (un message JSON-RPC par élément, dans l'ordre) contre \p schema. * \p arguments : options en plus (`--requests-only`, `--semantic`). */ -inline Verdict juger(const QList &messages, const QString &schema, const QStringList &arguments = {}) +/*! Lance le validateur avec \p arguments (après `--schema`). */ +inline Verdict lancer(const QString &schema, const QStringList &arguments) { Verdict v; - QTemporaryDir dir; - if (!dir.isValid()) { - v.sortie = QStringLiteral("répertoire temporaire impossible"); - return v; - } - QFile f(dir.filePath(QStringLiteral("session.ndjson"))); - if (!f.open(QIODevice::WriteOnly)) { - v.sortie = QStringLiteral("écriture de la session impossible"); - return v; - } - for (const QJsonObject &m : messages) - f.write(QJsonDocument(m).toJson(QJsonDocument::Compact) + '\n'); - f.close(); QProcess p; p.setProcessChannelMode(QProcess::MergedChannels); p.start(QStringLiteral("python3"), QStringList{QStringLiteral("-I"), QStringLiteral("-B"), racineSources() + QStringLiteral("/tools/etm-powersync-schema-check"), - QStringLiteral("--schema"), schema, QStringLiteral("--ndjson"), f.fileName()} - + arguments); + QStringLiteral("--schema"), schema} + arguments); if (!p.waitForFinished(60000)) { p.kill(); v.sortie = QStringLiteral("validateur sans réponse en 60 s"); @@ -79,6 +66,31 @@ inline Verdict juger(const QList &messages, const QString &schema, return v; } +/*! + * Juge une session (un message JSON-RPC par élément, dans l'ordre) contre \p schema. + * \p arguments : options en plus (`--requests-only`, `--semantic`). + */ +inline Verdict juger(const QList &messages, const QString &schema, const QStringList &arguments = {}) +{ + QTemporaryDir dir; + QFile f(dir.filePath(QStringLiteral("session.ndjson"))); + if (!dir.isValid() || !f.open(QIODevice::WriteOnly)) { + Verdict v; + v.sortie = QStringLiteral("écriture de la session impossible"); + return v; + } + for (const QJsonObject &m : messages) + f.write(QJsonDocument(m).toJson(QJsonDocument::Compact) + '\n'); + f.close(); + return lancer(schema, QStringList{QStringLiteral("--ndjson"), f.fileName()} + arguments); +} + +/*! Juge un fichier de traces de décision (annexe D) contre \p schema. */ +inline Verdict jugerTraces(const QString &trace, const QString &schema, const QStringList &arguments = {}) +{ + return lancer(schema, QStringList{QStringLiteral("--trace"), trace} + arguments); +} + /*! Écrit \p octets dans \p dir et rend le chemin : le contrat EMBARQUÉ, passé au validateur. */ inline QString ecrireSchema(const QTemporaryDir &dir, const QByteArray &octets) { diff --git a/tests/auto/refoptimizer/testrefoptimizer.cpp b/tests/auto/refoptimizer/testrefoptimizer.cpp index 84df205..30bfe7a 100644 --- a/tests/auto/refoptimizer/testrefoptimizer.cpp +++ b/tests/auto/refoptimizer/testrefoptimizer.cpp @@ -836,6 +836,16 @@ void TestRefOptimizer::testTracesReplayWithZeroDivergence() const QStringList fichiers = QDir(d).entryList({"etm-powersync-optimizer-trace-*.ndjson"}, QDir::Files); QCOMPARE(fichiers.count(), 1); const QString trace = QDir(d).filePath(fichiers.first()); + // Lot contrat OpenRPC : les traces RÉELLES du moteur — requêtes des 16 cycles, réponses de + // l'optimiseur rejugées sur leur requête — sont conformes au contrat embarqué. + { + QTemporaryDir dir; + const Contrat::Verdict v = Contrat::jugerTraces(trace, Contrat::ecrireSchema(dir, OptimizerProtocol::schemaDocumentBytes()), + {QStringLiteral("--semantic")}); + QVERIFY2(v.code == 0, qUtf8Printable(v.sortie.right(4000))); + QCOMPARE(v.compte("GetPlan.params"), lignes.count()); + QCOMPARE(v.compte("GetPlan.result"), surplus.count()); // les lignes à latencyMs : celles de l'optimiseur + } RefOptimizer neuf; QString erreur;