//go:build windows

package winpwn

import (
	"fmt"
	"testing"
	"time"
)

// TestNamedPipeServerClient exercises ServePipe/DialPipe end to end: a
// goroutine plays the server (CreateNamedPipe + ConnectNamedPipe, both
// overlapped), the test goroutine plays the client (CreateFile), and both
// sides talk over the resulting *Tube exactly like any other transport.
func TestNamedPipeServerClient(t *testing.T) {
	pipeName := fmt.Sprintf("winpwn_test_%d", time.Now().UnixNano())

	serverErr := make(chan error, 1)
	serverMsg := make(chan []byte, 1)
	go func() {
		tube, err := ServePipe(pipeName)
		if err != nil {
			serverErr <- err
			return
		}
		defer tube.Close()

		got, err := tube.RecvLine()
		if err != nil {
			serverErr <- err
			return
		}
		serverMsg <- got

		if err := tube.SendLine([]byte("pong")); err != nil {
			serverErr <- err
			return
		}
		serverErr <- nil
	}()

	// ServePipe's CreateNamedPipe call may not have run yet; retry the
	// dial briefly rather than racing it with a fixed sleep.
	var client *Tube
	var err error
	for i := 0; i < 100; i++ {
		client, err = DialPipe(pipeName)
		if err == nil {
			break
		}
		time.Sleep(20 * time.Millisecond)
	}
	if err != nil {
		t.Fatalf("DialPipe: %v", err)
	}
	defer client.Close()

	if err := client.SendLine([]byte("ping")); err != nil {
		t.Fatalf("client SendLine: %v", err)
	}

	select {
	case got := <-serverMsg:
		if string(got) != "ping\n" {
			t.Errorf("server received %q, want %q", got, "ping\n")
		}
	case err := <-serverErr:
		t.Fatalf("server error: %v", err)
	case <-time.After(5 * time.Second):
		t.Fatal("timed out waiting for the server to receive the client's message")
	}

	got, err := client.RecvLine()
	if err != nil {
		t.Fatalf("client RecvLine: %v", err)
	}
	if string(got) != "pong\n" {
		t.Errorf("client received %q, want %q", got, "pong\n")
	}

	if err := <-serverErr; err != nil {
		t.Fatalf("server goroutine error: %v", err)
	}
}
