Surface -u/-d's captured stdout via INFO() on test failure

CapturedStdout swallows compose_up_command()/compose_down_command()'s own
fmt::print()/spdlog output (spdlog's default sink is stdout) so it doesn't
pollute Catch2's console reporting -- but that also meant a failure had no
diagnostic trail at all. Both captures are now kept and attached via
INFO(), which Catch2 only actually prints alongside a failing assertion in
the same scope, staying silent on a normal passing run.

Also softens the port-forward reply REQUIRE to CHECK: a flaky reply on one
run must never skip the -d/--down cleanup below it, which is exactly what
a REQUIRE there would do -- found while investigating a real failure on
the actual target device (managed networks not torn down by -d), where
losing that diagnostic trail (and, if this were interactive, the -d skip
compounding into a real leaked uplink relay) would have made root-causing
much harder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
2026-09-07 13:34:07 +00:00
parent 73b9c58b8b
commit ac4194c96b
@@ -254,10 +254,18 @@ TEST_CASE("compose lifecycle: test-compose/compose.yaml up, verify, down", "[int
up_args.compose_file_name = compose_path.string();
AppConfig up_config = reload_config();
int up_result = 1;
std::string up_output;
{
CapturedStdout capture;
up_result = dispatch_command(up_args, "/nonexistent/unused-config.yaml", up_config);
up_output = capture.contents();
}
// Only surfaced if something below actually fails -- compose_up_command()'s
// own fmt::print()/spdlog output (spdlog's default sink is stdout, same
// as slocker-lite's own plain status lines) would otherwise be silently
// discarded by CapturedStdout, leaving no way to diagnose *why* a step
// failed on a run this test itself can't rerun interactively.
INFO("up output:\n" << up_output);
CHECK(up_result == 0);
auto containers = list_compose_containers();
@@ -298,8 +306,16 @@ TEST_CASE("compose lifecycle: test-compose/compose.yaml up, verify, down", "[int
reply = tcp_request(*host_ip, 18080, 2000);
return reply.has_value() && !reply->empty();
}));
REQUIRE(reply.has_value());
CHECK(reply->find("Hello from test-worker") != std::string::npos);
CHECK(reply.has_value());
// CHECK, not REQUIRE: a flaky port-forward reply on this specific
// run must never skip the -d/--down cleanup below -- an extern
// network's uplink relay left running would otherwise leak real
// host state (and, if this were run interactively rather than with
// output redirected to a file, hang the invoking shell -- see
// reference_device_ssh_access.md).
if (reply) {
CHECK(reply->find("Hello from test-worker") != std::string::npos);
}
}
ParsedArgs down_args;
@@ -307,10 +323,13 @@ TEST_CASE("compose lifecycle: test-compose/compose.yaml up, verify, down", "[int
down_args.compose_file_name = compose_path.string();
AppConfig down_config = reload_config();
int down_result = 1;
std::string down_output;
{
CapturedStdout capture;
down_result = dispatch_command(down_args, "/nonexistent/unused-config.yaml", down_config);
down_output = capture.contents();
}
INFO("down output:\n" << down_output);
CHECK(down_result == 0);
AppConfig after_down = reload_config();