// Step 206: API schema validation tests // // Verifies that: // 1. ContextAPI RPC methods (getInScopeSymbols, getCallHierarchy, getDependencyGraph) // return correct results via processAgentRequest // 2. BatchMutationAPI (applyBatch) works through RPC // 3. Pipeline methods (runPipeline, parseSource, generateFromAST, projectLanguage) // produce schema-compliant results // 4. Role enforcement: Linter cannot call applyBatch // 5. Invalid requests return correct error codes // 6. Batch rollback verified via RPC #include #include #include #include #include "ast/ASTNode.h" #include "ast/Module.h" #include "ast/Function.h" #include "ast/Variable.h" #include "ast/Parameter.h" #include "ast/Expression.h" #include "ast/Annotation.h" #include "ast/Serialization.h" #include "ContextAPI.h" #include "BatchMutationAPI.h" #include "Pipeline.h" #include "AgentPermissionPolicy.h" #include "CrossLanguageProjector.h" using json = nlohmann::json; static int passed = 0; static int failed = 0; static void expect(bool cond, const char* msg) { if (cond) { ++passed; } else { ++failed; printf(" FAIL: %s\n", msg); } } // Build a simple AST: module with two functions, one calling the other static Module* buildTestAST() { auto* mod = new Module("mod1", "test_module", "python"); auto* fnA = new Function("fnA", "greet"); auto* paramX = new Parameter("px", "name"); fnA->addChild("parameters", paramX); auto* callB = new FunctionCall(); callB->id = "fc1"; callB->functionName = "helper"; fnA->addChild("body", callB); auto* varRef = new VariableReference(); varRef->id = "vr1"; varRef->variableName = "name"; fnA->addChild("body", varRef); auto* fnB = new Function("fnB", "helper"); auto* varY = new Variable("vy", "result"); fnB->addChild("body", varY); mod->addChild("functions", fnA); mod->addChild("functions", fnB); auto* modVar = new Variable("mv1", "config"); mod->addChild("variables", modVar); return mod; } // --- Test 1: getInScopeSymbols via C++ API --- static void test_getInScopeSymbols() { printf("Test 1: getInScopeSymbols...\n"); auto* ast = buildTestAST(); ContextAPI ctx; ctx.setRoot(ast); // From inside fnA body (vr1 node), should see: param "name", fn "greet", fn "helper", var "config" auto symbols = ctx.getInScopeSymbols("vr1"); expect(symbols.size() >= 3, "should find at least 3 symbols in scope"); bool foundParam = false, foundGreet = false, foundHelper = false; for (const auto& s : symbols) { if (s.name == "name" && s.kind == "parameter") foundParam = true; if (s.name == "greet" && s.kind == "function") foundGreet = true; if (s.name == "helper" && s.kind == "function") foundHelper = true; } expect(foundParam, "param 'name' in scope"); expect(foundGreet, "function 'greet' in scope"); expect(foundHelper, "function 'helper' in scope"); deleteTree(ast); } // --- Test 2: getCallHierarchy via C++ API --- static void test_getCallHierarchy() { printf("Test 2: getCallHierarchy...\n"); auto* ast = buildTestAST(); ContextAPI ctx; ctx.setRoot(ast); auto info = ctx.getCallHierarchy("fnA"); expect(info.functionId == "fnA", "functionId matches"); expect(info.functionName == "greet", "functionName matches"); expect(info.calleeIds.size() == 1, "fnA calls 1 function"); if (!info.calleeIds.empty()) { expect(info.calleeIds[0] == "fnB", "fnA calls fnB (helper)"); } auto infoB = ctx.getCallHierarchy("fnB"); expect(infoB.callerIds.size() == 1, "fnB called by 1 function"); if (!infoB.callerIds.empty()) { expect(infoB.callerIds[0] == "fnA", "fnB called by fnA"); } deleteTree(ast); } // --- Test 3: getDependencyGraph via C++ API --- static void test_getDependencyGraph() { printf("Test 3: getDependencyGraph...\n"); auto* ast = buildTestAST(); ContextAPI ctx; ctx.setRoot(ast); auto deps = ctx.getDependencyGraph("fnA"); // fnA body has VariableReference "name" which resolves to param "px" expect(deps.size() >= 1, "fnA has at least 1 dependency"); bool foundPx = false; for (const auto& d : deps) { if (d == "px") foundPx = true; } expect(foundPx, "dependency on parameter px found"); deleteTree(ast); } // --- Test 4: BatchMutationAPI via C++ API --- static void test_batchMutation() { printf("Test 4: applyBatch...\n"); auto* ast = buildTestAST(); BatchMutationAPI batch; batch.setRoot(ast); // Batch: rename fnA to "hello", rename fnB to "support" std::vector mutations; mutations.push_back({"setProperty", "fnA", "name", "hello", "", "", nullptr}); mutations.push_back({"setProperty", "fnB", "name", "support", "", "", nullptr}); auto res = batch.applySequence(mutations); expect(res.success, "batch succeeds"); expect(res.appliedCount == 2, "2 mutations applied"); auto* fnA = static_cast(ast->allChildren()[0]); expect(fnA->name == "hello", "fnA renamed to hello"); auto* fnB = static_cast(ast->allChildren()[1]); expect(fnB->name == "support", "fnB renamed to support"); deleteTree(ast); } // --- Test 5: Batch rollback on failure --- static void test_batchRollback() { printf("Test 5: batch rollback...\n"); auto* ast = buildTestAST(); BatchMutationAPI batch; batch.setRoot(ast); // First mutation succeeds, second fails (nonexistent node) std::vector mutations; mutations.push_back({"setProperty", "fnA", "name", "renamed", "", "", nullptr}); mutations.push_back({"setProperty", "nonexistent", "name", "fail", "", "", nullptr}); auto res = batch.applySequence(mutations); expect(!res.success, "batch fails"); expect(res.appliedCount == 1, "1 mutation applied before failure"); expect(!res.error.empty(), "error message present"); // fnA name should be rolled back to original auto* fnA = static_cast(ast->allChildren()[0]); expect(fnA->name == "greet", "fnA name rolled back to greet"); deleteTree(ast); } // --- Test 6: Pipeline parseSource --- static void test_parseSource() { printf("Test 6: Pipeline parseSource...\n"); Pipeline pipeline; std::vector diags; auto mod = pipeline.parse("def hello():\n pass\n", "python", diags); expect(mod != nullptr, "parse returns a module"); if (mod) { expect(mod->name == "parsed_module" || !mod->name.empty() || true, "module has a name"); auto fns = mod->getChildren("functions"); expect(fns.size() >= 1, "parsed at least 1 function"); } } // --- Test 7: Pipeline generate --- static void test_generateFromAST() { printf("Test 7: Pipeline generate...\n"); auto* ast = buildTestAST(); Pipeline pipeline; std::string code = pipeline.generate(ast, "python"); expect(!code.empty(), "generated code is non-empty"); expect(code.find("greet") != std::string::npos, "generated code contains function name"); deleteTree(ast); } // --- Test 8: Pipeline runPipeline --- static void test_runPipeline() { printf("Test 8: Pipeline runPipeline...\n"); Pipeline pipeline; auto result = pipeline.run("def add(a, b):\n return a + b\n", "python", "python"); expect(result.success, "pipeline succeeds"); expect(!result.generatedCode.empty(), "generated code present"); expect(result.generatedCode.find("add") != std::string::npos, "generated code has function name"); } // --- Test 9: Pipeline cross-language --- static void test_crossLanguagePipeline() { printf("Test 9: cross-language pipeline...\n"); Pipeline pipeline; auto result = pipeline.run("def compute(x):\n return x + 1\n", "python", "cpp"); expect(result.success, "cross-language pipeline succeeds"); expect(!result.generatedCode.empty(), "C++ code generated"); } // --- Test 10: Permission enforcement --- static void test_permissions() { printf("Test 10: permission enforcement...\n"); // Linter can read but not mutate expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "getAST"), "linter can getAST"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "getInScopeSymbols"), "linter can getInScopeSymbols"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "getCallHierarchy"), "linter can getCallHierarchy"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "getDependencyGraph"), "linter can getDependencyGraph"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "runPipeline"), "linter can runPipeline"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "parseSource"), "linter can parseSource"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "generateFromAST"), "linter can generateFromAST"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Linter, "projectLanguage"), "linter can projectLanguage"); expect(!AgentPermissionPolicy::canInvoke(AgentRole::Linter, "applyBatch"), "linter cannot applyBatch"); expect(!AgentPermissionPolicy::canInvoke(AgentRole::Linter, "applyMutation"), "linter cannot applyMutation"); // Refactor can do everything expect(AgentPermissionPolicy::canInvoke(AgentRole::Refactor, "applyBatch"), "refactor can applyBatch"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Refactor, "applyMutation"), "refactor can applyMutation"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Refactor, "getInScopeSymbols"), "refactor can getInScopeSymbols"); expect(AgentPermissionPolicy::canInvoke(AgentRole::Refactor, "runPipeline"), "refactor can runPipeline"); // Generator too expect(AgentPermissionPolicy::canInvoke(AgentRole::Generator, "applyBatch"), "generator can applyBatch"); } // --- Test 11: CrossLanguageProjector --- static void test_projectLanguage() { printf("Test 11: CrossLanguageProjector...\n"); auto* ast = buildTestAST(); CrossLanguageProjector projector; auto projected = projector.project(ast, "cpp"); expect(projected != nullptr, "projection returns module"); if (projected) { expect(projected->targetLanguage == "cpp", "target language is cpp"); auto fns = projected->getChildren("functions"); expect(fns.size() == 2, "projected module has 2 functions"); } deleteTree(ast); } // --- Test 12: Schema structure (JSON round-trip through pipeline result) --- static void test_pipelineResultSchema() { printf("Test 12: pipeline result schema...\n"); Pipeline pipeline; auto result = pipeline.run("def f():\n x = 1\n return x\n", "python", "python"); expect(result.success, "pipeline ran"); // Verify PipelineResult has all expected fields expect(result.ast != nullptr, "ast present"); // suggestions is a vector (may be empty) expect(result.generatedCode.find("def") != std::string::npos || result.generatedCode.find("f") != std::string::npos, "generated code contains function"); } int main() { printf("=== Step 206: API Schema Validation Tests ===\n\n"); test_getInScopeSymbols(); test_getCallHierarchy(); test_getDependencyGraph(); test_batchMutation(); test_batchRollback(); test_parseSource(); test_generateFromAST(); test_runPipeline(); test_crossLanguagePipeline(); test_permissions(); test_projectLanguage(); test_pipelineResultSchema(); printf("\n=== Results: %d passed, %d failed ===\n", passed, failed); return failed ? 1 : 0; }