//go:build linux package peer import ( "net" "strings" "sync" "testing" "omnisocketgo/cmd/internal/latencylog" "omnisocketgo/cmd/internal/protocol" "omnisocketgo/cmd/internal/server" ) func TestClientsExchangeMessagesWithLinuxTimestamps(t *testing.T) { hub := server.NewHub() serverAddr, cleanup := startRealHubServer(t, hub) defer cleanup() peerALogger := &recordingLogger{} peerA, err := Dial(serverAddr, "peer-a", WithLogger(peerALogger)) if err != nil { t.Fatalf("Dial(peer-a) error = %v", err) } defer func() { _ = peerA.Close() }() peerBLogger := &recordingLogger{} peerB, err := Dial(serverAddr, "peer-b", WithLogger(peerBLogger)) if err != nil { t.Fatalf("Dial(peer-b) error = %v", err) } defer func() { _ = peerB.Close() }() inboxDir := t.TempDir() waitFor(t, func() bool { return hub.HasPeer("peer-a") && hub.HasPeer("peer-b") }, "both peers to be registered") if err := peerA.SendText("peer-b", "hello"); err != nil { t.Fatalf("SendText() error = %v", err) } textMsg, err := peerB.Receive() if err != nil { t.Fatalf("peerB.Receive(text) error = %v", err) } if _, err := peerB.PersistMessage(textMsg, inboxDir); err != nil { t.Fatalf("peerB.PersistMessage(text) error = %v", err) } if err := peerA.SendFile("peer-b", "payload.bin", []byte{0x01, 0x02, 0x03}); err != nil { t.Fatalf("SendFile() error = %v", err) } fileMsg, err := peerB.Receive() if err != nil { t.Fatalf("peerB.Receive(file) error = %v", err) } if _, err := peerB.PersistMessage(fileMsg, inboxDir); err != nil { t.Fatalf("peerB.PersistMessage(file) error = %v", err) } waitFor(t, func() bool { return hasMessageEvents(peerALogger.Events(), 1, latencylog.EventAAppPrepBegin, latencylog.EventATXSched, latencylog.EventATXSoftware) }, "peer-a text kernel timestamps") waitFor(t, func() bool { return hasMessageEvents(peerALogger.Events(), 2, latencylog.EventAAppPrepBegin, latencylog.EventATXSched, latencylog.EventATXSoftware) }, "peer-a file kernel timestamps") waitFor(t, func() bool { return hasMessageEvents(peerBLogger.Events(), 1, latencylog.EventBRXSoftware, latencylog.EventBAppRecv, latencylog.EventBPersistBegin, latencylog.EventBPersistEnd) }, "peer-b text receive timestamps") waitFor(t, func() bool { return hasMessageEvents(peerBLogger.Events(), 2, latencylog.EventBRXSoftware, latencylog.EventBAppRecv, latencylog.EventBPersistBegin, latencylog.EventBPersistEnd) }, "peer-b file receive timestamps") } func startRealHubServer(t *testing.T, hub *server.Hub) (string, func()) { t.Helper() listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen() error = %v", err) } var ( wg sync.WaitGroup stop = make(chan struct{}) errOnce sync.Once ) wg.Add(1) go func() { defer wg.Done() for { conn, acceptErr := listener.Accept() if acceptErr != nil { select { case <-stop: return default: } if strings.Contains(acceptErr.Error(), "closed") { return } t.Errorf("listener.Accept() error = %v", acceptErr) return } wg.Add(1) go func(rawConn net.Conn) { defer wg.Done() if serveErr := hub.ServeConn(rawConn); serveErr != nil && !isExpectedHubServeExit(serveErr) { errOnce.Do(func() { t.Logf("hub.ServeConn() ended with %v", serveErr) }) } }(conn) } }() cleanup := func() { close(stop) _ = listener.Close() wg.Wait() } return listener.Addr().String(), cleanup } func hasMessageEvents(events []latencylog.Event, messageID uint64, wantEvents ...string) bool { seen := make(map[string]bool, len(wantEvents)) for _, event := range events { if event.MessageID != messageID { continue } if event.TsUnixNano <= 0 { return false } seen[event.Event] = true } for _, wantEvent := range wantEvents { if !seen[wantEvent] { return false } } return true } func isExpectedHubServeExit(err error) bool { if err == nil { return true } message := err.Error() return strings.Contains(message, "closed") || strings.Contains(message, "protocol: read frame: EOF") } func TestLinuxTimestampedReceivePreservesBusinessMessageShape(t *testing.T) { hub := server.NewHub() serverAddr, cleanup := startRealHubServer(t, hub) defer cleanup() peerA, err := Dial(serverAddr, "peer-a") if err != nil { t.Fatalf("Dial(peer-a) error = %v", err) } defer func() { _ = peerA.Close() }() peerB, err := Dial(serverAddr, "peer-b") if err != nil { t.Fatalf("Dial(peer-b) error = %v", err) } defer func() { _ = peerB.Close() }() waitFor(t, func() bool { return hub.HasPeer("peer-a") && hub.HasPeer("peer-b") }, "both peers to be registered") want := protocol.Message{ Type: protocol.MessageTypeFile, ID: 1, From: "peer-a", To: "peer-b", FileName: "payload.bin", Body: []byte{0xde, 0xad, 0xbe, 0xef}, } if err := peerA.SendFile(want.To, want.FileName, want.Body); err != nil { t.Fatalf("SendFile() error = %v", err) } got, err := peerB.Receive() if err != nil { t.Fatalf("peerB.Receive() error = %v", err) } if got.Type != want.Type || got.ID != want.ID || got.From != want.From || got.To != want.To || got.FileName != want.FileName || string(got.Body) != string(want.Body) { t.Fatalf("received message mismatch: got %+v want %+v", got, want) } }