Files
whetstone_DSL/editor/tests/step614_test.cpp
2026-02-17 20:41:10 -07:00

194 lines
6.8 KiB
C++

// Step 614: whetstone_architect_intake MCP Tool (12 tests)
#include "MCPServer.h"
#include <iostream>
#include <string>
static int passed = 0, failed = 0;
#define TEST(name) { std::cout << " " << #name << "... "; }
#define PASS() { std::cout << "PASS\n"; ++passed; }
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
static json callTool(MCPServer& mcp, const std::string& name, const json& args) {
json req = {
{"jsonrpc", "2.0"},
{"id", 1},
{"method", "tools/call"},
{"params", {
{"name", name},
{"arguments", args}
}}
};
json resp = mcp.handleRequest(req);
std::string text = resp["result"]["content"][0].value("text", "{}");
return json::parse(text);
}
static std::string validSpec() {
return
"## Goals\n"
"- Build reporting dashboard\n"
"\n"
"## Constraints\n"
"- Do not use dynamic allocation in hot path\n"
"- Use dynamic allocation in hot path for cache warmup\n"
"\n"
"## Dependencies\n"
"- sqlite\n"
"\n"
"## Acceptance Criteria\n"
"- p95 latency under 50ms\n"
"- maybe include export csv support\n";
}
void test_tool_registered_accessor() {
TEST(tool_registered_accessor);
MCPServer mcp;
bool found = false;
for (const auto& tool : mcp.getTools()) {
if (tool.name == "whetstone_architect_intake") found = true;
}
CHECK(found, "tool should be registered");
PASS();
}
void test_tool_visible_in_tools_list() {
TEST(tool_visible_in_tools_list);
MCPServer mcp;
json req = {{"jsonrpc", "2.0"}, {"id", 1}, {"method", "tools/list"}};
json resp = mcp.handleRequest(req);
bool found = false;
for (const auto& tool : resp["result"]["tools"]) {
if (tool.value("name", "") == "whetstone_architect_intake") found = true;
}
CHECK(found, "tools/list should include tool");
PASS();
}
void test_tool_schema_requires_markdown() {
TEST(tool_schema_requires_markdown);
MCPServer mcp;
bool hasRequired = false;
for (const auto& tool : mcp.getTools()) {
if (tool.name != "whetstone_architect_intake") continue;
if (tool.inputSchema.contains("required")) {
for (const auto& req : tool.inputSchema["required"]) {
if (req == "markdown") hasRequired = true;
}
}
}
CHECK(hasRequired, "input schema should require markdown");
PASS();
}
void test_success_for_valid_markdown() {
TEST(success_for_valid_markdown);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", validSpec()}});
CHECK(out.value("success", false), "success should be true");
PASS();
}
void test_parsed_spec_has_expected_sections() {
TEST(parsed_spec_has_expected_sections);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", validSpec()}});
int sections = (int)out["parsedSpec"]["sections"].size();
CHECK(sections == 4, "expected 4 sections");
PASS();
}
void test_normalized_requirement_count_matches_inputs() {
TEST(normalized_requirement_count_matches_inputs);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", validSpec()}});
int count = (int)out["normalizedRequirements"].size();
CHECK(count == 6, "expected 6 normalized requirements");
PASS();
}
void test_detects_constraint_conflict_signal() {
TEST(detects_constraint_conflict_signal);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", validSpec()}});
CHECK(out["conflictSignals"].value("hasConflicts", false), "expected conflict signal");
CHECK(out["conflictSignals"].value("conflictCount", 0) >= 1, "expected conflict count >= 1");
PASS();
}
void test_detects_ambiguous_requirement_count() {
TEST(detects_ambiguous_requirement_count);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", validSpec()}});
CHECK(out["conflictSignals"].value("ambiguousRequirementCount", 0) >= 1,
"expected ambiguous requirement count >= 1");
PASS();
}
void test_missing_markdown_field_errors() {
TEST(missing_markdown_field_errors);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake", json::object());
CHECK(!out.value("success", true), "success should be false");
CHECK(out.value("error", "") == "markdown_missing", "wrong error");
PASS();
}
void test_markdown_must_be_string() {
TEST(markdown_must_be_string);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake", {{"markdown", 123}});
CHECK(!out.value("success", true), "success should be false");
CHECK(out.value("error", "") == "markdown_not_string", "wrong error");
PASS();
}
void test_parser_rejects_no_sections() {
TEST(parser_rejects_no_sections);
MCPServer mcp;
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", "- only bullet no section"}});
CHECK(!out.value("success", true), "success should be false");
CHECK(out.value("error", "") == "no_sections_found", "wrong error");
PASS();
}
void test_normalize_rejects_no_requirements() {
TEST(normalize_rejects_no_requirements);
MCPServer mcp;
std::string spec = "## Goals\nNarrative only.\n";
json out = callTool(mcp, "whetstone_architect_intake",
{{"markdown", spec}});
CHECK(!out.value("success", true), "success should be false");
CHECK(out.value("error", "") == "no_requirements_found", "wrong error");
CHECK(out.contains("parsedSpec"), "parsed spec should be included on normalize failure");
PASS();
}
int main() {
std::cout << "Step 614: whetstone_architect_intake MCP Tool\n";
test_tool_registered_accessor(); // 1
test_tool_visible_in_tools_list(); // 2
test_tool_schema_requires_markdown(); // 3
test_success_for_valid_markdown(); // 4
test_parsed_spec_has_expected_sections(); // 5
test_normalized_requirement_count_matches_inputs(); // 6
test_detects_constraint_conflict_signal(); // 7
test_detects_ambiguous_requirement_count(); // 8
test_missing_markdown_field_errors(); // 9
test_markdown_must_be_string(); // 10
test_parser_rejects_no_sections(); // 11
test_normalize_rejects_no_requirements(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
return failed == 0 ? 0 : 1;
}