Extract connect into core (app::connect_parts); thin the command + screen.

Move the wiring orchestration — transform lookup, identity-compatibility
check, identity NC fill, transform apply, Connection creation/add — out of
the `connect` command and the interactive connect screen into
core/app/connect.{hpp,cpp}: app::connect_parts(System*, m1,p1, m2,p2) returns
a ConnectResult (ok/refused/error, connection_name, transform_name, wires,
identity_info, nc_added) with no Print/dialog/FTXUI. Name/pattern resolution
and ambiguity reporting stay in the command — that is arg-parsing, not the op.

Both frontends now call the one core op, removing the duplicated logic. This
also unifies a divergence: the interactive screen previously called
CheckIdentityCompatible without the info pointer and so never filled identity
NC pins (unlike the command); routing it through app::connect_parts makes the
screen fill NCs and surface the same warning, matching the scriptable path.

Command output is unchanged. Prune the now-dead transform.hpp / domain
connect.hpp includes from the frontends (commands.cpp keeps transform_vpx.hpp
only for ValidatePartForKind).

Add tests/test_connect.cpp (core, no UI): identity-compatible pair wires,
unknown type pairing is refused with nothing created, subset side gets NC
pins filled and the warning reported.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 20:12:11 +02:00
parent 25939998ab
commit a040cc1957
5 changed files with 223 additions and 73 deletions

79
tests/test_connect.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include <doctest/doctest.h>
#include "core/app/connect.hpp"
#include "core/domain/connect.hpp"
#include "core/domain/modules.hpp"
#include "core/domain/parts.hpp"
#include "core/domain/pins.hpp"
#include "core/domain/system.hpp"
// app::connect_parts is pure core: given two already-resolved parts it looks up
// the transform, fills identity NC pins, creates the Connection and returns a
// ConnectResult. No Print/dialog/FTXUI. These tests drive it directly.
namespace {
// A part with the given pin names, attached to a fresh module.
Part *make_part(System &sys, const std::string &mod, const std::string &part,
std::initializer_list<const char *> pins,
const std::string &type = "")
{
Module *m = sys.modules()->merge(mod);
Part *p = new Part(part);
p->connector_type = type;
m->add(p);
for (const char *pn : pins) p->add(new Pin(pn));
return p;
}
} // namespace
TEST_CASE("connect_parts wires an identity-compatible pair") {
System sys;
Module *a = sys.modules()->merge("A");
Module *b = sys.modules()->merge("B");
Part *p1 = make_part(sys, "A", "J1", {"1", "2"});
Part *p2 = make_part(sys, "B", "P1", {"1", "2"});
app::ConnectResult r = app::connect_parts(&sys, a, p1, b, p2);
CHECK(r.ok);
CHECK_FALSE(r.refused);
CHECK(r.transform_name == "identity");
CHECK(r.wires == 2);
CHECK(r.identity_info.empty()); // identical sets → no NC fill, no warning
CHECK(r.nc_added == 0);
CHECK(r.connection_name == "A/J1 <-> B/P1");
CHECK(sys.connections()->size() == 1);
}
TEST_CASE("connect_parts refuses an unknown connector-type pairing") {
System sys;
Module *a = sys.modules()->merge("A");
Module *b = sys.modules()->merge("B");
Part *p1 = make_part(sys, "A", "J1", {"1"}, "foo");
Part *p2 = make_part(sys, "B", "P1", {"1"}, "bar");
app::ConnectResult r = app::connect_parts(&sys, a, p1, b, p2);
CHECK_FALSE(r.ok);
CHECK(r.refused);
CHECK(r.error.find("no transform") != std::string::npos);
CHECK(sys.connections()->size() == 0); // nothing created
}
TEST_CASE("connect_parts fills NC pins on the subset side and reports it") {
System sys;
Module *a = sys.modules()->merge("A");
Module *b = sys.modules()->merge("B");
Part *p1 = make_part(sys, "A", "J1", {"1", "2", "3"}); // larger side
Part *p2 = make_part(sys, "B", "P1", {"1", "2"}); // missing "3"
app::ConnectResult r = app::connect_parts(&sys, a, p1, b, p2);
CHECK(r.ok);
CHECK_FALSE(r.identity_info.empty()); // subset path surfaces a warning
CHECK(r.nc_added == 1); // pin "3" materialised on B
CHECK(r.wires == 3); // all three now wired
CHECK(p2->size() == 3); // the NC pin really got added
}