Files
whetstone_DSL/editor/tests/step1991_test.cpp
Bill 410bb2d78c Steps 1991-1993: AST sidecar lifecycle + schema stub fixes + IR chain
Step 1991: Wire AST sidecar lifecycle hooks
- openFile (DispatchPart2.h): auto-calls loadSidecarAST() after parse,
  returns annotationsRestored + staleAnnotations
- saveBuffer (DispatchPart3.h): auto-calls saveSidecarAST() +
  saveSemannoSidecar() after write, returns annotationsSaved +
  sidecarPath + semannoPath

Step 1992: Fix 9 schema stubs in Sprint 142/143/144 tool registrations
- All 9 text/AST sync and merge tools now have proper inputSchema with
  required id and documented optional parameters (language, text, mode)

Step 1993: Fix ingest_legacy_to_ir → generate_cpp_from_ir chain
- ingest_legacy_to_ir now emits ir field (SemanticCoreIR format)
  alongside existing graph/api_intent/assumption/readiness fields
- RecoveryNode → IRNode mapping: intent→kind, confidence in metadata
- Neighbor lists become inferred edges; moduleId = "legacy:" + slug
- generate_cpp_from_ir now accepts result["ir"] directly

15/15 tests passing across all three steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 02:38:43 -06:00

147 lines
5.2 KiB
C++

// Step 1991: AST sidecar lifecycle hooks
// Verifies that saveBuffer auto-persists annotated AST sidecars, and that
// openFile auto-restores those annotations into the newly-parsed live AST.
#include "HeadlessEditorState.h"
#include "MCPServer.h"
#include "SidecarPersistence.h"
#include <cassert>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
namespace fs = std::filesystem;
static json rpc(HeadlessEditorState& state, const std::string& method,
json params = json::object()) {
json req = {{"jsonrpc", "2.0"}, {"id", 1},
{"method", method}, {"params", std::move(params)}};
return state.processAgentRequest(req, "test-session");
}
static std::string makeTempWorkspace(const std::string& name) {
fs::path tmp = fs::temp_directory_path() / name;
fs::create_directories(tmp / "src");
std::ofstream f(tmp / "src" / "Foo.h");
f << "namespace foo {\nstruct Foo {\n int x = 0;\n};\n}\n";
return tmp.string();
}
int main() {
int passed = 0;
// Test 1: saveBuffer creates .ast.json sidecar
{
std::string ws = makeTempWorkspace("step1991_t1");
std::string filePath = ws + "/src/Foo.h";
HeadlessEditorState state;
state.workspaceRoot = ws;
state.defaultLanguage = "cpp";
rpc(state, "openFile", {{"path", filePath}, {"language", "cpp"}});
auto saveResp = rpc(state, "saveBuffer", {{"path", filePath}});
std::string sc = sidecarPath(ws, filePath);
assert(fs::exists(sc));
assert(saveResp["result"].contains("annotationsSaved"));
assert(saveResp["result"].contains("sidecarPath"));
std::cout << "Test 1 PASSED: saveBuffer creates .ast.json sidecar\n";
++passed;
}
// Test 2: saveBuffer response includes semannoPath
{
std::string ws = makeTempWorkspace("step1991_t2");
std::string filePath = ws + "/src/Foo.h";
HeadlessEditorState state;
state.workspaceRoot = ws;
state.defaultLanguage = "cpp";
rpc(state, "openFile", {{"path", filePath}, {"language", "cpp"}});
auto saveResp = rpc(state, "saveBuffer", {{"path", filePath}});
assert(saveResp["result"].contains("semannoPath"));
assert(saveResp["result"]["success"] == true);
std::cout << "Test 2 PASSED: saveBuffer response includes semannoPath\n";
++passed;
}
// Test 3: openFile after save returns annotationsRestored in response
{
std::string ws = makeTempWorkspace("step1991_t3");
std::string filePath = ws + "/src/Foo.h";
HeadlessEditorState state;
state.workspaceRoot = ws;
state.defaultLanguage = "cpp";
rpc(state, "openFile", {{"path", filePath}, {"language", "cpp"}});
rpc(state, "saveBuffer", {{"path", filePath}});
rpc(state, "closeFile", {{"path", filePath}});
auto reopenResp = rpc(state, "openFile",
{{"path", filePath}, {"language", "cpp"}});
assert(!reopenResp.contains("error"));
assert(reopenResp["result"].contains("annotationsRestored"));
assert(reopenResp["result"].contains("staleAnnotations"));
std::cout << "Test 3 PASSED: openFile response includes annotationsRestored\n";
++passed;
}
// Test 4: stale annotations (file content replaced) are skipped without error
{
std::string ws = makeTempWorkspace("step1991_t4");
std::string altPath = ws + "/src/Alt.h";
{
std::ofstream f(altPath);
f << "namespace alt {\nstruct Alt {\n bool flag = false;\n};\n}\n";
}
HeadlessEditorState state;
state.workspaceRoot = ws;
state.defaultLanguage = "cpp";
rpc(state, "openFile", {{"path", altPath}});
rpc(state, "saveBuffer", {{"path", altPath}});
rpc(state, "closeFile", {{"path", altPath}});
// Replace file content — existing sidecar nodes become stale
{ std::ofstream f(altPath); f << "// empty\n"; }
auto reopenResp = rpc(state, "openFile", {{"path", altPath}});
assert(!reopenResp.contains("error"));
assert(reopenResp["result"].contains("annotationsRestored"));
std::cout << "Test 4 PASSED: stale annotations skipped without error\n";
++passed;
}
// Test 5: openFile with no sidecar returns annotationsRestored=0 silently
{
std::string ws = makeTempWorkspace("step1991_t5");
std::string freshPath = ws + "/src/Fresh.h";
{ std::ofstream f(freshPath); f << "struct Fresh { int v = 0; };\n"; }
// Ensure no sidecar exists
std::string sc = sidecarPath(ws, freshPath);
if (fs::exists(sc)) fs::remove(sc);
HeadlessEditorState state;
state.workspaceRoot = ws;
state.defaultLanguage = "cpp";
auto openResp = rpc(state, "openFile", {{"path", freshPath}});
assert(!openResp.contains("error"));
assert(openResp["result"].contains("annotationsRestored"));
assert(openResp["result"]["annotationsRestored"] == 0);
std::cout << "Test 5 PASSED: openFile with no sidecar returns annotationsRestored=0\n";
++passed;
}
std::cout << "\n" << passed << "/5 passed\n";
return passed == 5 ? 0 : 1;
}