180 lines
13 KiB
C++
180 lines
13 KiB
C++
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||
|
|
|
||
|
|
// Code keys: a static code pass is keyed on its code, a functional pass on
|
||
|
|
// its code AND the range. Every rewrite of the commits fixture is checked in
|
||
|
|
// under tests/Goldens/fixture/code-keys/<rewrite>/.
|
||
|
|
|
||
|
|
import std;
|
||
|
|
import Crafter.Review;
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
using namespace Crafter::Review;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
std::int32_t Failures = 0;
|
||
|
|
const std::string Fn(Plan::FunctionalLens);
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
using Keys = std::map<std::string, std::string>;
|
||
|
|
|
||
|
|
Keys CodeKeys(const fs::path& d, const std::string& lens = "repo-bug-prover.md", std::string_view env = {}) {
|
||
|
|
const Inventory::Built built = Inventory::Build("commits", d.string());
|
||
|
|
const Plan::CommitCode cc = Plan::CommitCodeOf(Artifact::PatchTexts(d));
|
||
|
|
Keys out;
|
||
|
|
for (const Json& u : built.out.at("units")) {
|
||
|
|
const std::string id = u.at("id").get<std::string>();
|
||
|
|
if (Plan::KindFor(lens, u) != "code") continue;
|
||
|
|
if (lens == Fn && (id.ends_with(":outside") || id.ends_with(":absent") || id.ends_with("#hdr"))) continue;
|
||
|
|
const Json job = {{"kind", "block"}, {"unit", id}, {"unitKind", "code"}};
|
||
|
|
out[id] = Plan::BlockReuseKey(job, lens, Plan::UnitText(built.lines, u), cc, {}, "c", 1, env).value_or("");
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<std::string> WholeKey(const fs::path& d, const std::string& lens = "repo-better-solution.md", std::string_view env = {}) {
|
||
|
|
return Plan::WholeCodeKey({{"kind", "whole"}, {"unit", nullptr}}, lens, Plan::CommitCodeOf(Artifact::PatchTexts(d)), {}, "c", 1, env);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::set<std::string> Changed(const Keys& now, const Keys& before) {
|
||
|
|
std::set<std::string> out;
|
||
|
|
for (const auto& [id, key] : before) {
|
||
|
|
if (now.at(id) != key) out.insert(id);
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool AllChanged(const Keys& now, const Keys& before) {
|
||
|
|
return Changed(now, before).size() == before.size();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Show(const std::set<std::string>& ids) {
|
||
|
|
Json out = Json::array();
|
||
|
|
for (const std::string& id : ids) out.push_back(id);
|
||
|
|
return Dyn::ReprValue(out);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int main() {
|
||
|
|
std::println("reuse keys: a static code pass is keyed on its code; a functional pass on its code AND the range (fixtures/commits and rewrites of it)");
|
||
|
|
const fs::path fixture = fs::current_path() / "tests" / "Goldens" / "fixture";
|
||
|
|
const fs::path src = fixture / "commits";
|
||
|
|
auto rewrite = [&](std::string_view name) { return fixture / "code-keys" / name; };
|
||
|
|
const Keys base = CodeKeys(src);
|
||
|
|
const Keys fbase = CodeKeys(src, Fn);
|
||
|
|
const Keys sbase = CodeKeys(src, "repo-security.md");
|
||
|
|
std::set<std::string> baseValues;
|
||
|
|
std::set<std::string> sbaseValues;
|
||
|
|
for (const auto& [id, key] : base) baseValues.insert(key);
|
||
|
|
for (const auto& [id, key] : sbase) sbaseValues.insert(key);
|
||
|
|
const bool keysDiffer = std::ranges::all_of(base, [&](const auto& entry) { return sbase.contains(entry.first) && sbase.at(entry.first) != entry.second; });
|
||
|
|
Check("the security lens keys the same code units as the bug-prover, every key its own (the lens file is in the key): a verdict of one is never evidence for the other",
|
||
|
|
std::ranges::equal(std::views::keys(sbase), std::views::keys(base)) && keysDiffer && sbaseValues.size() == baseValues.size());
|
||
|
|
std::set<std::string> slots;
|
||
|
|
for (const auto& [id, key] : base) {
|
||
|
|
if (id.ends_with(":outside") || id.ends_with(":absent")) slots.insert(id);
|
||
|
|
}
|
||
|
|
std::set<std::string> fbaseValues;
|
||
|
|
for (const auto& [id, key] : fbase) fbaseValues.insert(key);
|
||
|
|
const std::set<std::string> functionalUnits = {"p1:hello.py#1", "p2:hello.py#1", "p2:hello.py#2"};
|
||
|
|
std::set<std::string> shared;
|
||
|
|
std::ranges::set_intersection(baseValues, fbaseValues, std::inserter(shared, shared.begin()));
|
||
|
|
Check("fixture: every static code unit has a key (4 slots, hunks, headers), the functional units are the hunks alone, no doc unit, no shared key",
|
||
|
|
std::ranges::all_of(base, [](const auto& entry) { return !entry.second.empty(); }) && slots.size() == 4 && base.contains("p2:hello.py#hdr")
|
||
|
|
&& std::set<std::string>(std::views::keys(fbase).begin(), std::views::keys(fbase).end()) == functionalUnits
|
||
|
|
&& std::ranges::none_of(base, [](const auto& entry) { return entry.first.contains(".md#"); }) && shared.empty());
|
||
|
|
|
||
|
|
const fs::path rw = rewrite("rewrite");
|
||
|
|
Check("a rewrite that leaves the code alone (ids, counter, blobs, line numbers, git version, a reworded message) -> every key the same, static and functional",
|
||
|
|
CodeKeys(rw) == base && CodeKeys(rw, Fn) == fbase && WholeKey(rw) == WholeKey(src) && WholeKey(rw, Fn) == WholeKey(src, Fn)
|
||
|
|
&& Artifact::ArtifactHash("commits", rw) != Artifact::ArtifactHash("commits", src));
|
||
|
|
const fs::path code = rewrite("code");
|
||
|
|
std::set<std::string> diff = Changed(CodeKeys(code), base);
|
||
|
|
std::set<std::string> expected = slots;
|
||
|
|
expected.insert("p2:hello.py#1");
|
||
|
|
Check("one changed line in p2:hello.py#1 -> static: that hunk and the four slots, nothing else", diff == expected, Show(diff));
|
||
|
|
Check("...functional: EVERY unit is asked again (the range is in its key)", AllChanged(CodeKeys(code, Fn), fbase));
|
||
|
|
Check("...and both whole passes", WholeKey(code) != WholeKey(src) && WholeKey(code, Fn) != WholeKey(src, Fn));
|
||
|
|
const Keys ctx = CodeKeys(rewrite("context"));
|
||
|
|
Check("a changed context line re-keys its hunk", ctx.at("p2:hello.py#1") != base.at("p2:hello.py#1") && ctx.at("p2:hello.py#2") == base.at("p2:hello.py#2"));
|
||
|
|
const fs::path nobody = rewrite("nobody");
|
||
|
|
Check("p2 loses its body -> p2:absent alone (static); nothing functional", Changed(CodeKeys(nobody), base) == std::set<std::string>{"p2:absent"} && CodeKeys(nobody, Fn) == fbase);
|
||
|
|
const fs::path bse = rewrite("base");
|
||
|
|
Check("another base -> every key changes, static, functional and both wholes", AllChanged(CodeKeys(bse), base) && AllChanged(CodeKeys(bse, Fn), fbase) && WholeKey(bse) != WholeKey(src));
|
||
|
|
const fs::path mode = rewrite("mode");
|
||
|
|
expected = slots;
|
||
|
|
expected.insert("p2:hello.py#hdr");
|
||
|
|
Check("a mode change re-keys the file header (and the slots), not the hunks; the functional hunks follow the range", Changed(CodeKeys(mode), base) == expected
|
||
|
|
&& AllChanged(CodeKeys(mode, Fn), fbase));
|
||
|
|
const fs::path docs = rewrite("docs");
|
||
|
|
Check("a reworded README sentence -> no key changes anywhere (documentation is nobody's code)", CodeKeys(docs) == base && CodeKeys(docs, Fn) == fbase && WholeKey(docs) == WholeKey(src));
|
||
|
|
const fs::path nodoc = rewrite("nodoc");
|
||
|
|
Check("p2 drops its README hunk -> p2's two slots and the whole passes are asked again, no hunk", Changed(CodeKeys(nodoc), base) == std::set<std::string>{"p2:absent", "p2:outside"}
|
||
|
|
&& WholeKey(nodoc) != WholeKey(src) && CodeKeys(nodoc, Fn) == fbase);
|
||
|
|
|
||
|
|
// The environment in the keys (plan.ENVIRONMENTS item 1).
|
||
|
|
const Keys sb = CodeKeys(src, "repo-bug-prover.md", "supabase");
|
||
|
|
const Keys fb = CodeKeys(src, Fn, "browser+supabase");
|
||
|
|
Check("environment: a lens in its default environment keeps the key every stored verdict was built with (none for the static lenses, browser for the functional lens)",
|
||
|
|
CodeKeys(src, "repo-bug-prover.md", "none") == base && CodeKeys(src, Fn, "browser") == fbase && WholeKey(src, "repo-better-solution.md", "none") == WholeKey(src)
|
||
|
|
&& WholeKey(src, Fn, "browser") == WholeKey(src, Fn) && Plan::EnvDeps("repo-bug-prover.md").empty() && Plan::EnvDeps(Fn, "browser").empty()
|
||
|
|
&& Plan::EnvDeps(Fn, "browser+supabase") == std::vector<std::string>{"env=browser+supabase"});
|
||
|
|
std::set<std::string> everyKey;
|
||
|
|
for (const Plan::Environment& environment : Plan::Environments) {
|
||
|
|
for (const auto& [id, key] : CodeKeys(src, "repo-bug-prover.md", environment.name)) everyKey.insert(key);
|
||
|
|
}
|
||
|
|
Check("environment: any other environment re-keys every unit, block and whole, static and functional - each environment its own keys, none shared",
|
||
|
|
AllChanged(sb, base) && AllChanged(fb, fbase) && WholeKey(src, "repo-better-solution.md", "supabase") != WholeKey(src)
|
||
|
|
&& WholeKey(src, Fn, "browser+supabase") != WholeKey(src, Fn) && everyKey.size() == Plan::Environments.size() * base.size());
|
||
|
|
expected = slots;
|
||
|
|
expected.insert("p2:hello.py#1");
|
||
|
|
Check("environment: the code rules hold inside an environment - a rewrite that leaves the code alone keeps every supabase key, a changed line re-keys that hunk and the slots alone",
|
||
|
|
CodeKeys(rw, "repo-bug-prover.md", "supabase") == sb && CodeKeys(rw, Fn, "browser+supabase") == fb
|
||
|
|
&& WholeKey(rw, "repo-better-solution.md", "supabase") == WholeKey(src, "repo-better-solution.md", "supabase")
|
||
|
|
&& Changed(CodeKeys(code, "repo-bug-prover.md", "supabase"), sb) == expected);
|
||
|
|
|
||
|
|
const Plan::CommitCode cc = Plan::CommitCodeOf(Artifact::PatchTexts(src));
|
||
|
|
Check("whole key: both wholes have keys and differ; a block never; the bug-prover has none", WholeKey(src) && WholeKey(src, Fn) && WholeKey(src) != WholeKey(src, Fn)
|
||
|
|
&& !WholeKey(src, "repo-bug-prover.md") && !Plan::WholeCodeKey({{"kind", "block"}, {"unit", "x"}}, "repo-better-solution.md", cc, {}, "c", 1));
|
||
|
|
const Json job = {{"kind", "whole"}, {"unit", nullptr}};
|
||
|
|
const std::vector<std::string> w1 = {"w1"};
|
||
|
|
Json withModel = job;
|
||
|
|
withModel["model"] = "m";
|
||
|
|
const std::set<std::optional<std::string>> wholes = {
|
||
|
|
WholeKey(src), Plan::WholeCodeKey(job, "repo-better-solution.md", cc, w1, "c", 1), Plan::WholeCodeKey(job, "repo-better-solution.md", cc, {}, "c2", 1),
|
||
|
|
Plan::WholeCodeKey(job, "repo-better-solution.md", cc, {}, "c", 2), Plan::WholeCodeKey(withModel, "repo-better-solution.md", cc, {}, "c", 1),
|
||
|
|
};
|
||
|
|
Check("whole key: dispositions, the charter, the prompt version and the model are in it", wholes.size() == 5);
|
||
|
|
Plan::CommitCode two;
|
||
|
|
two.base = "b";
|
||
|
|
two.range = "r";
|
||
|
|
two.code = {{3, Plan::Sha("")}, {4, Plan::Sha("")}};
|
||
|
|
two.body = {{3, true}, {4, true}};
|
||
|
|
two.docs = {{3, {"h"}}, {4, {"h"}}};
|
||
|
|
two.upto = {{3, "u"}, {4, "u"}};
|
||
|
|
Plan::CommitCode renamed = two;
|
||
|
|
renamed.docs = {{3, {"h2"}}, {4, {"h"}}};
|
||
|
|
Check("slot keys: two documentation-only commits do not share a slot key (the position is in it); a renamed doc file re-asks the slots of its commit",
|
||
|
|
Plan::CodeKeyParts("p3:absent", two) != Plan::CodeKeyParts("p4:absent", two)
|
||
|
|
&& Plan::CodeKeyParts("p3:outside", two) != Plan::CodeKeyParts("p3:outside", renamed) && Plan::CodeKeyParts("p3:absent", two) != Plan::CodeKeyParts("p3:absent", renamed));
|
||
|
|
Check("a hunk is keyed on its file and text, not on its number or its commit; the functional ident is its own", Plan::CodeKeyParts("p2:hello.py#2", cc) == Plan::CodeKeyParts("p7:hello.py#5", cc)
|
||
|
|
&& Plan::CodeKeyParts("p2:hello.py#2", cc).first != Plan::CodeKeyParts("p2:other.py#2", cc).first
|
||
|
|
&& Plan::CodeKeyParts("p2:hello.py#hdr", cc).first != Plan::CodeKeyParts("p2:hello.py#1", cc).first);
|
||
|
|
const std::vector<std::string> parts = {"repo-bug-prover.md", "U", Plan::Sha("T"), Plan::Sha("d"), "w1", "cs", "1", "", "max"};
|
||
|
|
Check("reuse key: the pre-2026-10-02 formula with the effort slot frozen at max", Plan::ReuseKey({{"kind", "block"}, {"unit", "U"}}, "repo-bug-prover.md", "T", {"d"}, {"w1"}, "cs", 1)
|
||
|
|
== Plan::Sha(Dyn::Join(parts, "|")).substr(0, 16) && Plan::ReuseKeyEffortSlot == "max");
|
||
|
|
Check("code_norm leaves a hunk body alone", Plan::CodeNorm("@@ -1,2 +1,2 @@ f\n-- \n+index 1..2\n+@@ -1 +1 @@") == "@@ f\n-- \n+index 1..2\n+@@ -1 +1 @@");
|
||
|
|
const Json e = {{"unit", "p2:hello.py#2"}, {"coverage", {{"item", "p2:hello.py#2"}, {"verdict", "clear"}, {"basis", "b"}}}, {"findings", Json::array()},
|
||
|
|
{"notes", {{{"note", "n"}, {"items", {"p2:hello.py#2", "p2:hello.py#1"}}}}}};
|
||
|
|
const Json r = Plan::Rebind(e, "p2:hello.py#3");
|
||
|
|
Json v = {{"verdict", "SEND-READY"}, {"findings", r.at("findings")}, {"notes", r.at("notes")}, {"coverage", {r.at("coverage")}}};
|
||
|
|
const std::vector<std::string> ids = {"p2:hello.py#3"};
|
||
|
|
const Json n = Normalize::NormalizeOutput(v, {}, "repo-bug-prover.md", "H", Json::array({{{"id", "p2:hello.py#3"}}}), ids);
|
||
|
|
Check("rebind: the stored result under the unit's id of today, and it covers the renumbered unit", r.at("unit") == "p2:hello.py#3"
|
||
|
|
&& r.at("notes").at(0).at("items") == Json::array({"p2:hello.py#3"}) && e.at("coverage").at("item") == "p2:hello.py#2" && n.at("verdict") == "SEND-READY"
|
||
|
|
&& n.at("coverageMissing").empty());
|
||
|
|
return Failures ? 1 : 0;
|
||
|
|
}
|