// A peer that meshes BEFORE its overlay attaches must still get a channel.
//
// Build: ./build_tests.sh && ./test_overlay_late_attach
//
// WHY THIS EXISTS. Layer 13's third peer joins the topic and only then opens and
// attaches its overlay — which is the ordinary case in the game, where the swarm
// is up long before anyone asks for an asset. The already-attached peers open
// their overlay channel towards it the moment the connection forms, the newcomer
// has no handler for the protocol yet, and protomux rejects. Nothing re-opened
// afterwards: the newcomer's own Open arrived at a peer whose OverlayTransport
// still held a pointer to the channel that had just been rejected, so its pair()
// callback — the one mechanism built for exactly this race — declined to act:
//
// if (!channel_) open_channel(); // channel_ non-null, though the channel is closed
//
// The JS reference has no such state to go stale: hyper-overlay's peer entry is
// keyed by mux and DELETED in onclose (lib/protocol.js), so the pair callback's
// re-entry into attach(mux) always creates a fresh channel. The port kept the
// entry and the pointer.
//
// Two muxes over a loopback, no sockets, no threads, no DHT — the ordering is the
// whole bug, so it is provoked at the layer the ordering lives in. A real
// Protocol is not needed and is deliberately absent: what is asserted here is
// that a channel exists and that the owner is told about it. That the bytes then
// move is layer 13's claim, not this one's.
#include "overlay_transport.h"
#include <cstdio>
#include <string>
#include <vector>
using hyperswarm::OverlayTransport;
static int tests_run = 0;
static int tests_passed = 0;
static void check(const char *name, bool ok) {
tests_run++;
if (ok) {
tests_passed++;
printf(" PASS %s\n", name);
} else {
printf(" FAIL %s\n", name);
}
}
int main() {
printf("=== Overlay late attach ===\n");
Protomux a, b;
a.set_write_callback([&b](const uint8_t *d, size_t n) { b.recv(d, n); return true; });
b.set_write_callback([&a](const uint8_t *d, size_t n) { a.recv(d, n); return true; });
// ---- A is meshed with its overlay already attached. B is not. ----
// This is the holder in layer 13's Test C, and B is the third peer, which has
// joined the swarm but has not opened its overlay yet.
bool a_joined_pool = false;
OverlayTransport ta(&a, nullptr, "A",
[&a_joined_pool](OverlayTransport *) { a_joined_pool = true; });
// Read from the mux, because the transport no longer answers it: a channel WAS
// created (the slot exists) and is now closed (the slot is empty), which is the
// reject, and has_channel() reporting false for it is the fix itself.
check("A opened an overlay channel on attach", a.local_channels().size() == 1);
check("B has no overlay yet, so A's channel is rejected",
a.local_channels()[0] == nullptr && !ta.ready());
check("A forgot the rejected channel", !ta.has_channel());
check("A did not join the pool on a rejected channel", !a_joined_pool);
// ---- B attaches. THE LINE UNDER TEST IS EVERYTHING BELOW THIS ONE. ----
bool b_joined_pool = false;
OverlayTransport tb(&b, nullptr, "B",
[&b_joined_pool](OverlayTransport *) { b_joined_pool = true; });
check("the late overlay's channel opens", tb.ready());
check("the late overlay joins its own pool", b_joined_pool);
check("the peer that rejected it re-opens its side", ta.ready());
check("and that peer rejoins its pool, so a fetch can reach it", a_joined_pool);
// A channel both sides call open on is a channel both sides can write on. send()
// refuses while `opened` is false, which is how a re-paired channel that never
// actually opened would still look fine above.
const std::vector<uint8_t> frame(8, 0x7f);
check("A can send on the re-opened channel", ta.send(hyperoverlay::MsgId::SyncState, frame));
check("B can send on it too", tb.send(hyperoverlay::MsgId::SyncState, frame));
printf("\n=== %d/%d passed ===\n", tests_passed, tests_run);
return tests_passed == tests_run ? 0 : 1;
}