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>
This commit is contained in:
146
editor/tests/step1991_test.cpp
Normal file
146
editor/tests/step1991_test.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user