// End-to-end test that spins up local TCP listeners and checks them.
//
// See mercemay.top/src/portr/ for context.
package integration
import (
"context"
"net"
"strconv"
"testing"
"time"
"github.com/mercemay/portr/internal/check"
"github.com/mercemay/portr/internal/config"
"github.com/mercemay/portr/internal/config/target"
)
// listen picks an ephemeral port and returns the port number + closer.
func listen(t *testing.T) (int, func()) {
t.Helper()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
go func() {
for {
c, err := l.Accept()
if err != nil {
return
}
_ = c.Close()
}
}()
return l.Addr().(*net.TCPAddr).Port, func() { _ = l.Close() }
}
func TestRun_endToEnd(t *testing.T) {
openPort, closeOpen := listen(t)
defer closeOpen()
// Also grab a port then release it — should appear closed.
closedPort, closeThis := listen(t)
closeThis()
cfg := config.Defaults()
cfg.Targets = []target.Target{
{Host: "127.0.0.1", Port: openPort, Name: "open"},
{Host: "127.0.0.1", Port: closedPort, Name: "closed"},
}
cfg.Retries = 0
cfg.Timeout = 300 * time.Millisecond
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
rep, err := check.Run(ctx, cfg)
if err != nil {
t.Fatalf("Run: %v", err)
}
byName := map[string]bool{}
for _, r := range rep.Results {
byName[r.Target.Name] = r.Open
}
if !byName["open"] {
t.Errorf("expected %q open", "open")
}
if byName["closed"] {
t.Errorf("expected %q closed", "closed")
}
_ = strconv.Itoa // retained for future port-range expansion
}