mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-09-21 18:39:05 +02:00
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
co-authored by
Elias Schneider
parent
7c55bdf115
commit
1934efa84c
@@ -6,12 +6,16 @@ package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/italypaleale/francis/components/standalone"
|
||||
"github.com/italypaleale/francis/host/local"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -26,8 +30,9 @@ const testActorHostPSK = "pocket-id-test-actor-host-psk-32bytes"
|
||||
func NewActorHostForTest(t *testing.T, register func(t *testing.T, h *local.Host)) *local.Host {
|
||||
t.Helper()
|
||||
|
||||
address := freeLoopbackUDPAddr(t)
|
||||
hostOpts := []local.HostOption{
|
||||
local.WithAddress(freeLoopbackAddr(t)),
|
||||
local.WithAddress(address),
|
||||
local.WithRuntimePSKs([]byte(testActorHostPSK)),
|
||||
local.WithStandaloneMemoryProvider(standalone.StandaloneMemoryOptions{}),
|
||||
local.WithShutdownGracePeriod(time.Second),
|
||||
@@ -62,19 +67,58 @@ func NewActorHostForTest(t *testing.T, register func(t *testing.T, h *local.Host
|
||||
t.Fatal("timed out waiting for the actor host to become ready")
|
||||
}
|
||||
|
||||
// Francis signals host readiness before starting the peer server, so wait for a remote TLS response before a fast test can trigger cleanup
|
||||
waitForActorHostPeerServer(t, address, errCh)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// freeLoopbackAddr reserves a free loopback port and returns its address
|
||||
// waitForActorHostPeerServer waits until the WebTransport listener has passed the startup point that races with shutdown
|
||||
func waitForActorHostPeerServer(t *testing.T, address string, errCh <-chan error) {
|
||||
t.Helper()
|
||||
|
||||
// The probe intentionally omits the Francis client certificate because a remote TLS rejection is enough to prove the peer server is accepting connections
|
||||
//nolint:gosec
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{http3.NextProtoH3},
|
||||
}
|
||||
deadline := time.Now().Add(10 * time.Second)
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
probeCtx, probeCancel := context.WithTimeout(t.Context(), 200*time.Millisecond)
|
||||
conn, err := quic.DialAddr(probeCtx, address, tlsConfig, &quic.Config{})
|
||||
probeCancel()
|
||||
if conn != nil {
|
||||
_ = conn.CloseWithError(0, "readiness probe complete")
|
||||
return
|
||||
}
|
||||
|
||||
var transportErr *quic.TransportError
|
||||
if errors.As(err, &transportErr) && transportErr.Remote {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case runErr := <-errCh:
|
||||
t.Fatalf("actor host stopped before its peer server became ready: %v", runErr)
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf("timed out waiting for actor host peer server %s", address)
|
||||
}
|
||||
|
||||
// freeLoopbackUDPAddr reserves a free loopback UDP port and returns its address
|
||||
// The port is released before returning, so the actor host can bind it
|
||||
func freeLoopbackAddr(t *testing.T) string {
|
||||
func freeLoopbackUDPAddr(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
var lc net.ListenConfig
|
||||
lis, err := lc.Listen(t.Context(), "tcp", "127.0.0.1:0")
|
||||
lis, err := lc.ListenPacket(t.Context(), "udp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
addr := lis.Addr().String()
|
||||
addr := lis.LocalAddr().String()
|
||||
err = lis.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user