feat(types): semilla con fields para 11 tipos + CLI --test-types-yaml

- examples/types.yaml: principal_field + fields para Person, Email,
  Domain, Phone, Org, IBAN, Account, Document, Address, Url, Table.
  44 fields totales. Documentacion del formato en cabecera.
- project_manager.cpp: seed con fields para los tipos basicos (fallback
  cuando no se encuentra examples/types.yaml).
- main.cpp:
  - Log de carga incluye conteo de schemas y total de fields.
  - --test-types-yaml <path>: smoke test que carga, serializa a temp y
    recarga. Compara entidades/relaciones/fields field-a-field. Salida
    PASS/FAIL con exit code 0/1. Permite verificar round-trip sin
    framework de tests.

Verificado: examples/types.yaml round-trip estable (11 entities, 44
fields, 6 relations).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-01 00:00:53 +02:00
parent 8a36ad068a
commit fb7538088b
3 changed files with 170 additions and 16 deletions
+81 -3
View File
@@ -189,9 +189,17 @@ static bool load_input() {
g_atlas_bound = false;
if (g_atlas) { graph_icons_destroy(g_atlas); g_atlas = nullptr; }
g_atlas = ge::build_icon_atlas(codepoints);
int total_fields = 0;
int with_schema = 0;
for (const auto& e : pt.entities) {
total_fields += (int)e.fields.size();
if (!e.fields.empty()) ++with_schema;
}
std::fprintf(stdout,
"[graph_explorer] types.yaml: %zu entities, %zu relations, %zu icons\n",
pt.entities.size(), pt.relations.size(), codepoints.size());
"[graph_explorer] types.yaml: %zu entities (%d con schema, %d fields totales),"
" %zu relations, %zu icons\n",
pt.entities.size(), with_schema, total_fields,
pt.relations.size(), codepoints.size());
}
}
@@ -765,7 +773,75 @@ static void usage() {
" graph_explorer --input operations <path>\n"
" graph_explorer --types <types.yaml>\n"
" graph_explorer --layout force|grid|circular|radial|hierarchical|fixed\n"
" graph_explorer --project <slug>\n");
" graph_explorer --project <slug>\n"
" graph_explorer --test-types-yaml <path> (load+save+reload smoke test)\n");
}
// Smoke test del parser+writer (issue 0005 round-trip): carga `path`,
// serializa a un temporal y vuelve a cargar. Compara campos clave de
// ParsedTypes. Devuelve exit code 0 si OK, 1 si discrepancia, 2 si error.
static int test_types_yaml_roundtrip(const char* path) {
ge::ParsedTypes pt1;
std::string err;
if (!ge::types_load_yaml(path, &pt1, &err)) {
std::fprintf(stderr, "[test] load1 fail: %s\n", err.c_str());
return 2;
}
std::string tmp = std::string(path) + ".roundtrip.yaml";
if (!ge::types_save_yaml(tmp.c_str(), pt1, &err)) {
std::fprintf(stderr, "[test] save fail: %s\n", err.c_str());
return 2;
}
ge::ParsedTypes pt2;
if (!ge::types_load_yaml(tmp.c_str(), &pt2, &err)) {
std::fprintf(stderr, "[test] load2 fail: %s\n", err.c_str());
return 2;
}
auto cmp = [&]() -> bool {
if (pt1.entities.size() != pt2.entities.size()) return false;
if (pt1.relations.size() != pt2.relations.size()) return false;
for (size_t i = 0; i < pt1.entities.size(); ++i) {
const auto& a = pt1.entities[i];
const auto& b = pt2.entities[i];
if (a.name != b.name) return false;
if (a.color != b.color) return false;
if (a.icon_name != b.icon_name) return false;
if (a.principal_field != b.principal_field) return false;
if (a.fields.size() != b.fields.size()) return false;
for (size_t j = 0; j < a.fields.size(); ++j) {
const auto& fa = a.fields[j];
const auto& fb = b.fields[j];
if (fa.name != fb.name) return false;
if (fa.kind != fb.kind) return false;
if (fa.required != fb.required) return false;
if (fa.enum_values != fb.enum_values) return false;
}
}
for (size_t i = 0; i < pt1.relations.size(); ++i) {
const auto& a = pt1.relations[i];
const auto& b = pt2.relations[i];
if (a.name != b.name) return false;
if (a.color != b.color) return false;
if (a.style != b.style) return false;
}
return true;
};
int total_fields = 0;
for (const auto& e : pt1.entities) total_fields += (int)e.fields.size();
if (cmp()) {
std::fprintf(stdout,
"[test] PASS — %zu entities, %d fields, %zu relations (round-trip estable)\n",
pt1.entities.size(), total_fields, pt1.relations.size());
std::remove(tmp.c_str());
return 0;
}
std::fprintf(stderr,
"[test] FAIL — discrepancia tras round-trip. dump preservado en %s\n",
tmp.c_str());
return 1;
}
int main(int argc, char** argv) {
@@ -790,6 +866,8 @@ int main(int argc, char** argv) {
g_layout_initial = argv[++i];
} else if (std::strcmp(a, "--project") == 0 && i + 1 < argc) {
project_arg = argv[++i];
} else if (std::strcmp(a, "--test-types-yaml") == 0 && i + 1 < argc) {
return test_types_yaml_roundtrip(argv[++i]);
} else if (std::strcmp(a, "--help") == 0 || std::strcmp(a, "-h") == 0) {
usage();
return 0;