// Step 1976: DispatchJustificationReport // // t1: add entries, finalize computes averageScore correctly // t2: serialize returns valid JSON string (parseable by nlohmann) // t3: JSON contains all required top-level fields // t4: unenriched entry (empty selectedLibrary) not counted in enrichedCount // t5: projectId present in JSON and entries array present #include "DispatchJustificationReport.h" #include namespace ws = whetstone; static int p = 0, f = 0; #define T(n) { std::cout << " " << #n << "... "; } #define P() { std::cout << "PASS\n"; ++p; } #define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; } #define C(c, m) if (!(c)) { F(m); return; } static ws::JustificationEntry makeEntry(const std::string& task, const std::string& lib, const std::string& domain, float score) { ws::JustificationEntry e; e.taskDescription = task; e.selectedLibrary = lib; e.operationDomain = domain; e.capabilityScore = score; e.justification = lib + " scores " + std::to_string(score); return e; } void t1() { T(finalize_computes_average_score); ws::DispatchJustificationReport r; r.projectId = "test-project"; r.add(makeEntry("task A", "nlohmann_json", "serialization.json", 0.92f)); r.add(makeEntry("task B", "cublas", "compute.matrix", 0.99f)); r.finalize(); C(r.totalTasks == 2, "2 total tasks"); C(r.enrichedCount == 2, "2 enriched"); // averageScore = (0.92 + 0.99) / 2 = 0.955 C(r.averageScore > 0.9f && r.averageScore < 1.0f, "averageScore in (0.9, 1.0)"); P(); } void t2() { T(serialize_returns_valid_json); ws::DispatchJustificationReport r; r.projectId = "parse-test"; r.add(makeEntry("task X", "thrust", "compute.parallel", 0.97f)); r.finalize(); std::string json = r.serialize(); C(!json.empty(), "JSON non-empty"); // Must be parseable bool ok = false; try { auto parsed = nlohmann::json::parse(json); ok = true; } catch (...) {} C(ok, "JSON parseable"); P(); } void t3() { T(json_has_all_required_fields); ws::DispatchJustificationReport r; r.projectId = "fields-test"; r.add(makeEntry("task Y", "protobuf", "serialization.binary", 0.95f)); r.finalize(); auto j = nlohmann::json::parse(r.serialize()); C(j.contains("projectId"), "projectId"); C(j.contains("totalTasks"), "totalTasks"); C(j.contains("enrichedCount"), "enrichedCount"); C(j.contains("averageScore"), "averageScore"); C(j.contains("entries"), "entries"); C(j["entries"].is_array(), "entries is array"); P(); } void t4() { T(unenriched_entry_not_counted_in_enriched_count); ws::DispatchJustificationReport r; r.projectId = "mixed"; r.add(makeEntry("enriched task", "thrust", "compute.parallel", 0.97f)); // Unenriched entry: empty selectedLibrary ws::JustificationEntry ue; ue.taskDescription = "unenriched task"; r.add(ue); r.finalize(); C(r.totalTasks == 2, "2 total"); C(r.enrichedCount == 1, "1 enriched"); // averageScore should be based on enriched entry only C(r.averageScore > 0.9f, "averageScore based on enriched only"); P(); } void t5() { T(project_id_in_json_and_entries_array); ws::DispatchJustificationReport r; r.projectId = "my-special-project"; r.add(makeEntry("a", "cublas", "compute.matrix", 0.99f)); r.add(makeEntry("b", "nlohmann_json", "serialization.json", 0.92f)); r.finalize(); auto j = nlohmann::json::parse(r.serialize()); C(j["projectId"] == "my-special-project", "projectId value"); C(j["entries"].size() == 2, "2 entries"); C(j["entries"][0].contains("taskDescription"), "entry has taskDescription"); C(j["entries"][0].contains("avoidedLibraries"), "entry has avoidedLibraries"); P(); } int main() { std::cout << "Step 1976: DispatchJustificationReport\n"; t1(); t2(); t3(); t4(); t5(); std::cout << "\n" << p << "/" << (p + f) << " passed\n"; return f > 0 ? 1 : 0; }