// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

//go:build darwin && !ios

package portlist

import (
	"bufio"
	"encoding/json"
	"fmt"
	"strings"
	"testing"

	"go4.org/mem"
)

func TestParsePort(t *testing.T) {
	type InOut struct {
		in     string
		expect int
	}
	tests := []InOut{
		{"1.2.3.4:5678", 5678},
		{"0.0.0.0.999", 999},
		{"1.2.3.4:*", 0},
		{"5.5.5.5:0", 0},
		{"[1::2]:5", 5},
		{"[1::2].5", 5},
		{"gibberish", -1},
	}

	for _, io := range tests {
		got := parsePort(mem.S(io.in))
		if got != io.expect {
			t.Fatalf("input:%#v expect:%v got:%v\n", io.in, io.expect, got)
		}
	}
}

const netstatOutput = `
// macOS
tcp4       0      0  *.23                   *.*                    LISTEN     
tcp6       0      0  *.24                   *.*                    LISTEN
tcp4      0      0  *.8185                 *.*                    LISTEN
tcp4       0      0  127.0.0.1.8186         *.*                    LISTEN
tcp6       0      0  ::1.8187               *.*                    LISTEN
tcp4       0      0  127.1.2.3.8188         *.*                    LISTEN

udp6       0      0  *.106                 *.*                      
udp4       0      0  *.104                 *.*                      
udp46      0      0  *.146                 *.*                      
`

func TestParsePortsNetstat(t *testing.T) {
	for _, loopBack := range [...]bool{false, true} {
		t.Run(fmt.Sprintf("loopback_%v", loopBack), func(t *testing.T) {
			want := List{
				{"tcp", 23, "", 0},
				{"tcp", 24, "", 0},
				{"udp", 104, "", 0},
				{"udp", 106, "", 0},
				{"udp", 146, "", 0},
				{"tcp", 8185, "", 0}, // but not 8186, 8187, 8188 on localhost, when loopback is false
			}
			if loopBack {
				want = append(want,
					Port{"tcp", 8186, "", 0},
					Port{"tcp", 8187, "", 0},
					Port{"tcp", 8188, "", 0},
				)
			}
			pl, err := appendParsePortsNetstat(nil, bufio.NewReader(strings.NewReader(netstatOutput)), loopBack)
			if err != nil {
				t.Fatal(err)
			}
			pl = sortAndDedup(pl)
			jgot, _ := json.MarshalIndent(pl, "", "\t")
			jwant, _ := json.MarshalIndent(want, "", "\t")
			if len(pl) != len(want) {
				t.Fatalf("Got:\n%s\n\nWant:\n%s\n", jgot, jwant)
			}
			for i := range pl {
				if pl[i] != want[i] {
					t.Errorf("row#%d\n got: %+v\n\nwant: %+v\n",
						i, pl[i], want[i])
					t.Fatalf("Got:\n%s\n\nWant:\n%s\n", jgot, jwant)
				}
			}
		})
	}
}
