Files
whetstone_DSL/editor/tests/step378_test.cpp

250 lines
9.1 KiB
C++

// Step 378: WorkflowOrchestrator main loop (12 tests)
#include <cassert>
#include <iostream>
#include <string>
#include "WorkflowOrchestrator.h"
static WorkItem makeItem(const std::string& id,
const std::string& name,
const std::string& workerType,
bool reviewRequired = false) {
WorkItem w;
w.id = id;
w.nodeId = id + "_node";
w.nodeName = name;
w.nodeType = "Function";
w.bufferId = "buf";
w.contextWidth = "local";
w.workerType = workerType;
w.reviewRequired = reviewRequired;
w.priority = "medium";
w.status = WI_PENDING;
w.createdAt = workItemTimestamp();
return w;
}
int main() {
int passed = 0;
// Test 1: single deterministic/template task completes in one step
{
WorkflowState ws("p1");
ws.queue.enqueue(makeItem("wi1", "getName", "template"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
OrchestratorEvent ev = orch.step();
assert(ev.type == "completed");
assert(ws.queue.getByStatus(WI_COMPLETE).size() == 1);
std::cout << "Test 1 PASSED: single template task completes in one step\n";
passed++;
}
// Test 2: dependency cascade after completion
{
WorkflowState ws("p2");
WorkItem a = makeItem("a", "getA", "template");
WorkItem b = makeItem("b", "getB", "template");
b.dependencies.push_back("a");
ws.queue.enqueue(a);
ws.queue.enqueue(b);
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step();
assert(ws.queue.getByStatus(WI_COMPLETE).size() == 1);
assert(ws.queue.getByStatus(WI_READY).size() == 1); // b promoted
std::cout << "Test 2 PASSED: dependency cascade promotion\n";
passed++;
}
// Test 3: human task blocks as needs-human
{
WorkflowState ws("p3");
ws.queue.enqueue(makeItem("wi3", "hardTask", "human"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step();
auto blockers = orch.getBlockers();
bool hasHuman = false;
for (const auto& b : blockers) if (b.type == "needs-human") hasHuman = true;
assert(hasHuman);
std::cout << "Test 3 PASSED: human task blocks\n";
passed++;
}
// Test 4: SLM task blocks as needs-external-model
{
WorkflowState ws("p4");
ws.queue.enqueue(makeItem("wi4", "fileTask", "slm"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step();
auto blockers = orch.getBlockers();
bool hasExternal = false;
for (const auto& b : blockers) if (b.type == "needs-external-model") hasExternal = true;
assert(hasExternal);
std::cout << "Test 4 PASSED: agent task blocks for external model\n";
passed++;
}
// Test 5: runToCompletion processes deterministic tasks
{
WorkflowState ws("p5");
ws.queue.enqueue(makeItem("w1", "getOne", "template"));
ws.queue.enqueue(makeItem("w2", "getTwo", "template"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
auto stats = orch.runToCompletion();
assert(stats.complete == 2);
std::cout << "Test 5 PASSED: runToCompletion completes deterministic tasks\n";
passed++;
}
// Test 6: advance emits stage events
{
WorkflowState ws("p6");
ws.queue.enqueue(makeItem("wi6", "getX", "template"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
auto events = orch.advance();
bool routed = false, context = false, executed = false, completed = false;
for (const auto& e : events) {
if (e.type == "routed") routed = true;
if (e.type == "context-assembled") context = true;
if (e.type == "executed") executed = true;
if (e.type == "completed") completed = true;
}
assert(routed && context && executed && completed);
std::cout << "Test 6 PASSED: stage events emitted\n";
passed++;
}
// Test 7: blockers report review items
{
WorkflowState ws("p7");
ws.queue.enqueue(makeItem("wi7", "getY", "template", true)); // force review
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step();
auto blockers = orch.getBlockers();
bool hasReview = false;
for (const auto& b : blockers) if (b.type == "needs-review") hasReview = true;
assert(hasReview);
std::cout << "Test 7 PASSED: review blockers reported\n";
passed++;
}
// Test 8: empty workflow returns immediately
{
WorkflowState ws("p8");
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
auto stats = orch.runToCompletion();
assert(stats.total == 0);
auto ev = orch.step();
assert(ev.type == "blocked");
std::cout << "Test 8 PASSED: empty workflow no-op\n";
passed++;
}
// Test 9: mixed workflow advances what it can
{
WorkflowState ws("p9");
ws.queue.enqueue(makeItem("d1", "getFast", "template"));
ws.queue.enqueue(makeItem("h1", "manual", "human"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
auto stats = orch.runToCompletion();
assert(stats.complete == 1);
assert(!orch.getBlockers().empty());
std::cout << "Test 9 PASSED: mixed workflow partially advances\n";
passed++;
}
// Test 10: runUntil predicate can stop early
{
WorkflowState ws("p10");
ws.queue.enqueue(makeItem("a1", "getA", "template"));
ws.queue.enqueue(makeItem("a2", "getB", "template"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
auto stats = orch.runUntil([](const WorkflowState& s) {
return s.getStats().complete >= 1;
});
assert(stats.complete >= 1);
std::cout << "Test 10 PASSED: runUntil predicate stop\n";
passed++;
}
// Test 11: blocked event includes blocker detail when applicable
{
WorkflowState ws("p11");
ws.queue.enqueue(makeItem("h2", "manual2", "human"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step(); // process human task first
OrchestratorEvent ev = orch.step(); // now no ready, returns blocked
assert(ev.type == "blocked");
assert(ev.detail.contains("blockers"));
std::cout << "Test 11 PASSED: blocked event includes blocker detail\n";
passed++;
}
// Test 12: agent task stores context payload in result
{
WorkflowState ws("p12");
ws.queue.enqueue(makeItem("ll1", "llTask", "llm"));
RoutingEngine routing;
auto workers = WorkerRegistry::getDefaultRegistry();
ContextAssembler assembler;
ReviewGate review;
WorkflowOrchestrator orch(ws, routing, workers, assembler, review);
orch.step();
auto item = ws.queue.getItem("ll1");
assert(item.has_value());
assert(item->result.astJson.is_object());
assert(item->result.astJson.contains("workerType"));
std::cout << "Test 12 PASSED: agent context payload preserved\n";
passed++;
}
std::cout << "\nResults: " << passed << "/12\n";
assert(passed == 12);
return 0;
}