<commit_before>package gircclient

import (
	"bufio"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"runtime"
	"testing"
	"time"

	"github.com/goshuirc/irc-go/ircmap"
	"github.com/goshuirc/irc-go/ircmsg"
)

func TestPlainConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	initialiseServerConnection(client)

	// we mock up a server connection to test the client
	listener, _ := net.Listen("tcp", ":0")

	client.Connect(listener.Addr().String(), false, nil)
	go client.ReceiveLoop()

	testServerConnection(t, reactor, client, listener)
}

func TestFailingConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	// we mock up a server connection to test the client
	listener, _ := net.Listen("tcp", ":0")

	// Try to connect before setting InitialNick and InitialUser
	err := client.Connect(listener.Addr().String(), false, nil)

	if err == nil {
		t.Error(
			"ServerConnection allowed connection before InitialNick and InitialUser were set",
		)
	}

	// Actually set attributes and fail properly this time
	client.InitialNick = "test"
	client.InitialUser = "t"
	client.Connect("here is a malformed address:6667", false, nil)

	if err == nil {
		t.Error(
			"ServerConnection allowed connection with a blatently malformed address",
		)
	}
}

func TestTLSConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	initialiseServerConnection(client)

	// generate a test certificate to use
	priv, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)

	notBefore := time.Now().Add(-1 * time.Hour * 30) // valid 30 hours ago
	notAfter := notBefore.Add(time.Hour * 90)        // for 90 hours

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"gIRC-Go Co"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	template.IPAddresses = append(template.IPAddresses, net.ParseIP("127.0.0.1"))
	template.IPAddresses = append(template.IPAddresses, net.ParseIP("::"))
	template.DNSNames = append(template.DNSNames, "localhost")

	derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)

	c := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	b, _ := x509.MarshalECPrivateKey(priv)
	k := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})

	// we mock up a server connection to test the client
	listenerKeyPair, _ := tls.X509KeyPair(c, k)

	var listenerTLSConfig tls.Config
	listenerTLSConfig.Certificates = make([]tls.Certificate, 0)
	listenerTLSConfig.Certificates = append(listenerTLSConfig.Certificates, listenerKeyPair)
	listener, _ := tls.Listen("tcp", ":0", &listenerTLSConfig)

	// mock up the client side too
	clientTLSCertPool := x509.NewCertPool()
	clientTLSCertPool.AppendCertsFromPEM(c)

	var clientTLSConfig tls.Config
	clientTLSConfig.RootCAs = clientTLSCertPool
	clientTLSConfig.ServerName = "localhost"
	go client.Connect(listener.Addr().String(), true, &clientTLSConfig)
	go client.ReceiveLoop()

	testServerConnection(t, reactor, client, listener)
}

func sendMessage(conn net.Conn, tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) {
	ircmsg := ircmsg.MakeMessage(tags, prefix, command, params...)
	line, err := ircmsg.Line()
	if err != nil {
		return
	}
	fmt.Fprintf(conn, line)

	// need to wait for a quick moment here for TLS to process any changes this
	// message has caused
	runtime.Gosched()
	time.Sleep(10 * time.Millisecond)
}

func initialiseServerConnection(client *ServerConnection) {
	client.InitialNick = "coolguy"
	client.InitialUser = "c"
	client.InitialRealName = "girc-go Test Client  "
}

func testServerConnection(t *testing.T, reactor Reactor, client *ServerConnection, listener net.Listener) {
	// start our reader
	conn, _ := listener.Accept()
	reader := bufio.NewReader(conn)

	var message string

	// CAP<commit_msg>fix broken param in reactor_test<commit_after>package gircclient

import (
	"bufio"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"net"
	"runtime"
	"testing"
	"time"

	"github.com/goshuirc/irc-go/ircmap"
	"github.com/goshuirc/irc-go/ircmsg"
)

func TestPlainConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	initialiseServerConnection(client)

	// we mock up a server connection to test the client
	listener, _ := net.Listen("tcp", ":0")

	client.Connect(listener.Addr().String(), false, nil)
	go client.ReceiveLoop()

	testServerConnection(t, reactor, client, listener)
}

func TestFailingConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	// we mock up a server connection to test the client
	listener, _ := net.Listen("tcp", ":0")

	// Try to connect before setting InitialNick and InitialUser
	err := client.Connect(listener.Addr().String(), false, nil)

	if err == nil {
		t.Error(
			"ServerConnection allowed connection before InitialNick and InitialUser were set",
		)
	}

	// Actually set attributes and fail properly this time
	client.InitialNick = "test"
	client.InitialUser = "t"
	client.Connect("here is a malformed address:6667", false, nil)

	if err == nil {
		t.Error(
			"ServerConnection allowed connection with a blatently malformed address",
		)
	}
}

func TestTLSConnection(t *testing.T) {
	reactor := NewReactor()
	client := reactor.CreateServer("local")

	initialiseServerConnection(client)

	// generate a test certificate to use
	priv, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)

	notBefore := time.Now().Add(-1 * time.Hour * 30) // valid 30 hours ago
	notAfter := notBefore.Add(time.Hour * 90)        // for 90 hours

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"gIRC-Go Co"},
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA:                  true,
	}

	template.IPAddresses = append(template.IPAddresses, net.ParseIP("127.0.0.1"))
	template.IPAddresses = append(template.IPAddresses, net.ParseIP("::"))
	template.DNSNames = append(template.DNSNames, "localhost")

	derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)

	c := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	b, _ := x509.MarshalECPrivateKey(priv)
	k := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})

	// we mock up a server connection to test the client
	listenerKeyPair, _ := tls.X509KeyPair(c, k)

	var listenerTLSConfig tls.Config
	listenerTLSConfig.Certificates = make([]tls.Certificate, 0)
	listenerTLSConfig.Certificates = append(listenerTLSConfig.Certificates, listenerKeyPair)
	listener, _ := tls.Listen("tcp", ":0", &listenerTLSConfig)

	// mock up the client side too
	clientTLSCertPool := x509.NewCertPool()
	clientTLSCertPool.AppendCertsFromPEM(c)

	var clientTLSConfig tls.Config
	clientTLSConfig.RootCAs = clientTLSCertPool
	clientTLSConfig.ServerName = "localhost"
	go client.Connect(listener.Addr().String(), true, &clientTLSConfig)
	go client.ReceiveLoop()

	testServerConnection(t, reactor, client, listener)
}

func sendMessage(conn net.Conn, tags map[string]string, prefix string, command string, params ...string) {
	ircmsg := ircmsg.MakeMessage(tags, prefix, command, params...)
	line, err := ircmsg.Line()
	if err != nil {
		return
	}
	fmt.Fprintf(conn, line)

	// need to wait for a quick moment here for TLS to process any changes this
	// message has caused
	runtime.Gosched()
	time.Sleep(10 * time.Millisecond)
}

func initialiseServerConnection(client *ServerConnection) {
	client.InitialNick = "coolguy"
	client.InitialUser = "c"
	client.InitialRealName = "girc-go Test Client  "
}

func testServerConnection(t *testing.T, reactor Reactor, client *ServerConnection, listener net.Listener) {
	// start our reader
	conn, _ := listener.Accept()
	reader := bufio.NewReader(conn)

	var message string

	// CAP
{'fraction_non_alphanumeric': 0.12758270294682172, 'fraction_numerical': 0.015355086372360844, 'mean_word_length': 4.454433497536946, 'pattern_counts': {'":': 8, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1421473', 'n_tokens_mistral': 3128, 'n_tokens_neox': 2889, 'n_words': 689}