Add step 610 service dependency risk map

This commit is contained in:
Bill
2026-02-17 12:06:07 -07:00
parent 7091176103
commit 4c30332d7d
4 changed files with 271 additions and 0 deletions

View File

@@ -4369,4 +4369,13 @@ target_link_libraries(step609_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step610_test tests/step610_test.cpp)
target_include_directories(step610_test PRIVATE src)
target_link_libraries(step610_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -0,0 +1,78 @@
#pragma once
// Step 610: Service Dependency Risk Map
#include <map>
#include <string>
#include <vector>
#include "ValidationErrorUtil.h"
enum class DependencyRisk {
Low,
Medium,
High
};
struct DependencyEdge {
std::string edgeId;
std::string sourceService;
std::string targetService;
DependencyRisk risk = DependencyRisk::Low;
};
class ServiceDependencyRiskMap {
public:
bool addEdge(const DependencyEdge& edge, std::string* error) {
if (!error) return false;
error->clear();
if (edge.edgeId.empty()) return failWith(error, "edge_id_missing");
if (edge.sourceService.empty()) return failWith(error, "source_service_missing");
if (edge.targetService.empty()) return failWith(error, "target_service_missing");
if (edges_.count(edge.edgeId) != 0) return failWith(error, "edge_duplicate");
edges_[edge.edgeId] = edge;
order_.push_back(edge.edgeId);
return true;
}
bool updateRisk(const std::string& edgeId, DependencyRisk risk, std::string* error) {
if (!error) return false;
error->clear();
auto it = edges_.find(edgeId);
if (it == edges_.end()) return failWith(error, "edge_missing");
it->second.risk = risk;
return true;
}
int highRiskInboundCount(const std::string& targetService) const {
int count = 0;
for (const auto& id : order_) {
const auto& edge = edges_.at(id);
if (edge.targetService == targetService && edge.risk == DependencyRisk::High) ++count;
}
return count;
}
int highRiskOutboundCount(const std::string& sourceService) const {
int count = 0;
for (const auto& id : order_) {
const auto& edge = edges_.at(id);
if (edge.sourceService == sourceService && edge.risk == DependencyRisk::High) ++count;
}
return count;
}
std::vector<DependencyEdge> byService(const std::string& serviceId) const {
std::vector<DependencyEdge> out;
for (const auto& id : order_) {
const auto& edge = edges_.at(id);
if (serviceId.empty() || edge.sourceService == serviceId || edge.targetService == serviceId) {
out.push_back(edge);
}
}
return out;
}
private:
std::map<std::string, DependencyEdge> edges_;
std::vector<std::string> order_;
};

View File

@@ -0,0 +1,154 @@
// Step 610: Service Dependency Risk Map (12 tests)
#include "ServiceDependencyRiskMap.h"
#include <iostream>
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 DependencyEdge edge(const std::string& id,
const std::string& src,
const std::string& dst,
DependencyRisk risk = DependencyRisk::Low) {
return {id, src, dst, risk};
}
void test_add_edge_success() {
TEST(add_edge_success);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b"), &error), "add should succeed");
CHECK(map.byService("svc-a").size() == 1, "service count mismatch");
PASS();
}
void test_add_edge_rejects_missing_edge_id() {
TEST(add_edge_rejects_missing_edge_id);
ServiceDependencyRiskMap map;
std::string error;
CHECK(!map.addEdge(edge("", "svc-a", "svc-b"), &error), "add should fail");
CHECK(error == "edge_id_missing", "wrong error");
PASS();
}
void test_add_edge_rejects_missing_source() {
TEST(add_edge_rejects_missing_source);
ServiceDependencyRiskMap map;
std::string error;
CHECK(!map.addEdge(edge("e1", "", "svc-b"), &error), "add should fail");
CHECK(error == "source_service_missing", "wrong error");
PASS();
}
void test_add_edge_rejects_missing_target() {
TEST(add_edge_rejects_missing_target);
ServiceDependencyRiskMap map;
std::string error;
CHECK(!map.addEdge(edge("e1", "svc-a", ""), &error), "add should fail");
CHECK(error == "target_service_missing", "wrong error");
PASS();
}
void test_add_edge_rejects_duplicate() {
TEST(add_edge_rejects_duplicate);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b"), &error), "first add failed");
CHECK(!map.addEdge(edge("e1", "svc-a", "svc-c"), &error), "duplicate should fail");
CHECK(error == "edge_duplicate", "wrong error");
PASS();
}
void test_update_risk_success() {
TEST(update_risk_success);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b"), &error), "add failed");
CHECK(map.updateRisk("e1", DependencyRisk::High, &error), "update should succeed");
CHECK(map.highRiskOutboundCount("svc-a") == 1, "outbound count mismatch");
PASS();
}
void test_update_risk_rejects_missing_edge() {
TEST(update_risk_rejects_missing_edge);
ServiceDependencyRiskMap map;
std::string error;
CHECK(!map.updateRisk("missing", DependencyRisk::High, &error), "update should fail");
CHECK(error == "edge_missing", "wrong error");
PASS();
}
void test_high_risk_inbound_count() {
TEST(high_risk_inbound_count);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b", DependencyRisk::High), &error), "add e1 failed");
CHECK(map.addEdge(edge("e2", "svc-c", "svc-b", DependencyRisk::Medium), &error), "add e2 failed");
CHECK(map.addEdge(edge("e3", "svc-d", "svc-b", DependencyRisk::High), &error), "add e3 failed");
CHECK(map.highRiskInboundCount("svc-b") == 2, "inbound high-risk count mismatch");
PASS();
}
void test_high_risk_outbound_count() {
TEST(high_risk_outbound_count);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b", DependencyRisk::High), &error), "add e1 failed");
CHECK(map.addEdge(edge("e2", "svc-a", "svc-c", DependencyRisk::Low), &error), "add e2 failed");
CHECK(map.addEdge(edge("e3", "svc-a", "svc-d", DependencyRisk::High), &error), "add e3 failed");
CHECK(map.highRiskOutboundCount("svc-a") == 2, "outbound high-risk count mismatch");
PASS();
}
void test_by_service_filters_source_or_target() {
TEST(by_service_filters_source_or_target);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b"), &error), "add e1 failed");
CHECK(map.addEdge(edge("e2", "svc-c", "svc-a"), &error), "add e2 failed");
CHECK(map.addEdge(edge("e3", "svc-x", "svc-y"), &error), "add e3 failed");
CHECK(map.byService("svc-a").size() == 2, "service filter mismatch");
PASS();
}
void test_by_service_empty_returns_all() {
TEST(by_service_empty_returns_all);
ServiceDependencyRiskMap map;
std::string error;
CHECK(map.addEdge(edge("e1", "svc-a", "svc-b"), &error), "add e1 failed");
CHECK(map.addEdge(edge("e2", "svc-c", "svc-d"), &error), "add e2 failed");
CHECK(map.byService("").size() == 2, "empty filter should return all");
PASS();
}
void test_counts_zero_for_unknown_service() {
TEST(counts_zero_for_unknown_service);
ServiceDependencyRiskMap map;
CHECK(map.highRiskInboundCount("missing") == 0, "inbound unknown should be zero");
CHECK(map.highRiskOutboundCount("missing") == 0, "outbound unknown should be zero");
PASS();
}
int main() {
std::cout << "Step 610: Service Dependency Risk Map\n";
test_add_edge_success(); // 1
test_add_edge_rejects_missing_edge_id(); // 2
test_add_edge_rejects_missing_source(); // 3
test_add_edge_rejects_missing_target(); // 4
test_add_edge_rejects_duplicate(); // 5
test_update_risk_success(); // 6
test_update_risk_rejects_missing_edge(); // 7
test_high_risk_inbound_count(); // 8
test_high_risk_outbound_count(); // 9
test_by_service_filters_source_or_target(); // 10
test_by_service_empty_returns_all(); // 11
test_counts_zero_for_unknown_service(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -11890,3 +11890,33 @@ and active-window listing.
- `editor/src/ChangeFreezeCalendar.h` within header-size limit (`67` <= `600`)
- `editor/tests/step609_test.cpp` within test-file size guidance (`149` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
### Step 610: Service Dependency Risk Map
**Status:** PASS (12/12 tests)
Implements service dependency edge tracking with risk updates and high-risk
inbound/outbound rollups.
**Files added:**
- `editor/src/ServiceDependencyRiskMap.h` - dependency risk module:
- dependency edge registration with validation/duplicate guards
- per-edge risk updates and missing-edge guards
- high-risk inbound/outbound counts and service filtering
- `editor/tests/step610_test.cpp` - 12 tests covering:
- edge add success/failure behavior
- risk update success/failure behavior
- high-risk rollup and service filter behavior
**Files modified:**
- `editor/CMakeLists.txt` - `step610_test` target
**Verification run:**
- `cmake -S editor -B editor/build-native` - PASS
- `cmake --build editor/build-native --target step610_test step609_test` - PASS
- `./editor/build-native/step610_test` - PASS (12/12)
- `./editor/build-native/step609_test` - PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/ServiceDependencyRiskMap.h` within header-size limit (`78` <= `600`)
- `editor/tests/step610_test.cpp` within test-file size guidance (`154` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`