Stream, Normalize, Merge, Dispositions, Config, CodeKeys and the pure half of CommitsMode, each a test of its own (131 checks). The fixture rewrites are checked in (code-keys/<name>/, commits3/), so the suite reads fixtures and edits nothing. The allowlist, command-line and MCP checks of CommitsMode come with the pool they test. Among them: this binary's `<exe> sql` spelling counts as a SQL call, and a result line with a raw U+2028 in a JSON string is still the result line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
5.2 KiB
C++
76 lines
5.2 KiB
C++
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
// Merging a lens's parts: the dedupe, the cross-unit rule, the strongest
|
|
// verdict, missing units; the matrix with units nobody reviews.
|
|
|
|
import std;
|
|
import Crafter.Review;
|
|
using namespace Crafter::Review;
|
|
|
|
namespace {
|
|
std::int32_t Failures = 0;
|
|
|
|
void Check(std::string_view name, bool cond, std::string_view detail = {}) {
|
|
std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string());
|
|
if (!cond) ++Failures;
|
|
}
|
|
|
|
// {item: verdict} of a merged result.
|
|
std::map<std::string, std::string> Verdicts(const Json& merged) {
|
|
std::map<std::string, std::string> out;
|
|
for (const Json& c : merged.at("coverage")) out[c.at("item").get<std::string>()] = c.at("verdict").get<std::string>();
|
|
return out;
|
|
}
|
|
|
|
Json With(Json value, const Json& changes) {
|
|
value.update(changes);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
std::println("merge_parts: dedupe, cross-unit rule, strongest verdict, missing units; the matrix with not-reviewed units");
|
|
const std::string lens = "repo-bug-prover.md";
|
|
const Json note = {{"kind", "info"}, {"items", {"A", "B"}}, {"note", "shared"}};
|
|
const Json found = {{"finding", "x"}, {"items", {"A", "B"}}, {"discharged", true}, {"dischargedBy", "w1"}};
|
|
const std::vector<Json> parts = {
|
|
{{"partLabel", "a"}, {"expectedIds", {"A"}}, {"findings", {found}}, {"notes", {note, note}}, {"coverage", {{{"item", "A"}, {"verdict", "clear"}}}}},
|
|
{{"partLabel", "b"}, {"expectedIds", {"B"}}, {"findings", {found}}, {"notes", {note}}, {"coverage", {{{"item", "B"}, {"verdict", "clear"}}}}},
|
|
};
|
|
Json m = Normalize::MergeParts("L", lens, parts, {});
|
|
Check("one note and one finding after the merge, verdict unchanged", m.at("notes").size() == 1 && m.at("findings").size() == 1 && m.at("verdict") == "SEND-READY");
|
|
const Json pa = {{"partLabel", "L:block:A"}, {"partKind", "block"}, {"partUnit", "A"}, {"verdict", "FIX FIRST"},
|
|
{"findings", {{{"finding", "B contradicts A"}, {"evidence", "e"}, {"proposedAction", "p"}, {"items", {"A", "B"}}, {"discharged", false}}}},
|
|
{"notes", Json::array()}, {"coverage", {{{"item", "A"}, {"verdict", "defect"}, {"basis", "b"}}}}, {"coverageMissing", Json::array()}, {"undischarged", 1}};
|
|
const Json pb = {{"partLabel", "L:block:B"}, {"partKind", "block"}, {"partUnit", "B"}, {"verdict", "SEND-READY"}, {"findings", Json::array()}, {"notes", Json::array()},
|
|
{"coverage", {{{"item", "B"}, {"verdict", "clear"}, {"basis", "checked"}}}}, {"coverageMissing", Json::array()}};
|
|
m = Normalize::MergeParts("L", lens, std::vector<Json>{pa, pb}, {});
|
|
Json basisOfB;
|
|
for (const Json& c : m.at("coverage")) {
|
|
if (c.at("item") == "B") basisOfB = c;
|
|
}
|
|
const bool raised = std::ranges::any_of(m.at("normalizeLogs"), [](const Json& l) { return l.get<std::string>().contains("raised to defect"); });
|
|
Check("B raised to defect by the finding citing it; lens FIX FIRST", basisOfB.at("verdict") == "defect" && basisOfB.at("basis").get<std::string>().starts_with("cited by finding")
|
|
&& m.at("verdict") == "FIX FIRST" && raised);
|
|
Json dischargedFinding = pa.at("findings").at(0);
|
|
dischargedFinding["discharged"] = true;
|
|
dischargedFinding["dischargedBy"] = "w1";
|
|
const Json pc = With(pa, {{"findings", {dischargedFinding}}, {"coverage", {{{"item", "A"}, {"verdict", "clear"}, {"basis", "discharged"}}}}, {"verdict", "SEND-READY"}, {"undischarged", 0}});
|
|
Check("a discharged finding does not raise B", Verdicts(Normalize::MergeParts("L", lens, std::vector<Json>{pc, pb}, {}))["B"] == "clear");
|
|
const std::vector<std::string> missingA = {"A"};
|
|
const Json m3 = Normalize::MergeParts("L", lens, std::vector<Json>{pb}, missingA);
|
|
Check("missing unit -> coverageMissing + contract violation + verdict NOT RETURNED", m3.at("coverageMissing") == Json::array({"A"}) && !m3.at("contractViolations").empty()
|
|
&& m3.at("verdict") == "NOT RETURNED");
|
|
const Json pd = With(pb, {{"coverage", {{{"item", "B"}, {"verdict", "immaterial"}, {"basis", "w"}}}}});
|
|
Check("strongest verdict per unit wins (immaterial > clear)", Verdicts(Normalize::MergeParts("L", lens, std::vector<Json>{pb, pd}, {}))["B"] == "immaterial");
|
|
const Json result = Normalize::MergeParts("L", lens, std::vector<Json>{pa, pb}, {});
|
|
const std::vector<std::string> notReviewed = {"p1:msg"};
|
|
const Json mx = Normalize::Matrix(Json::array({{{"id", "A"}}, {{"id", "B"}}, {{"id", "C"}}, {{"id", "p1:msg"}}}), std::vector<Json>{result}, notReviewed);
|
|
std::set<std::string> defects;
|
|
for (auto it = mx.at("defects").begin(); it != mx.at("defects").end(); ++it) defects.insert(it.key());
|
|
Check("matrix: a unit without an entry is MISSING and a hole; a not-reviewed unit is listed, never a hole; defects per unit",
|
|
mx.at("matrix").at("C").at("L").at("verdict") == "MISSING" && mx.at("holes") == Json::array({"C"}) && mx.at("notReviewed") == Json::array({"p1:msg"})
|
|
&& defects == std::set<std::string>{"A", "B"}, DumpCompactSorted(mx.at("holes")));
|
|
return Failures ? 1 : 0;
|
|
}
|