//go:build !windows

package ipcauth

import (
	"errors"
	"os"
	"path/filepath"
	"syscall"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

// A symlink is the shape the arbitrary-read attempt takes: the caller owns the
// link, the file it points at belongs to someone else.
func TestOpenOwnedFileRefusesSymlink(t *testing.T) {
	dir := t.TempDir()
	target := filepath.Join(dir, "target.log")
	require.NoError(t, os.WriteFile(target, []byte("secret"), 0600))

	link := filepath.Join(dir, "gui-client.log")
	require.NoError(t, os.Symlink(target, link))

	id, err := CurrentProcessIdentity()
	require.NoError(t, err)

	_, err = OpenOwnedFile(id, link)
	// O_NOFOLLOW on a symlink reports ELOOP on Linux/Darwin and EMLINK on FreeBSD.
	if !errors.Is(err, syscall.ELOOP) && !errors.Is(err, syscall.EMLINK) {
		t.Fatalf("symlink open: got %v, want ELOOP or EMLINK", err)
	}
}

// A fifo would block the open until a writer showed up, stalling the daemon
// while it holds its lock.
func TestOpenOwnedFileRefusesFifoWithoutBlocking(t *testing.T) {
	path := filepath.Join(t.TempDir(), "gui-client.log")
	require.NoError(t, syscall.Mkfifo(path, 0600))

	id, err := CurrentProcessIdentity()
	require.NoError(t, err)

	done := make(chan error, 1)
	go func() {
		_, err := OpenOwnedFile(id, path)
		done <- err
	}()

	select {
	case err := <-done:
		require.ErrorContains(t, err, "not a regular file")
	case <-time.After(5 * time.Second):
		t.Fatal("opening a fifo blocked")
	}
}
