<commit_before>package smtpd

import (
	"crypto/tls"
	"fmt"
	"github.com/HouzuoGuo/laitos/daemon/common"
	"github.com/HouzuoGuo/laitos/daemon/smtpd/mailcmd"
	"github.com/HouzuoGuo/laitos/inet"
	"net"
	"net/mail"
	"net/smtp"
	"strings"
	"testing"
)

func TestSMTPD_StartAndBlock(t *testing.T) {
	daemon := Daemon{
		ForwardMailClient: inet.MailClient{
			MailFrom: "howard@localhost",
			MTAHost:  "smtp.example.com",
			MTAPort:  25,
		},
	}
	// Test missing mandatory parameters
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward address") {
		t.Fatal(err)
	}
	daemon.ForwardTo = []string{"<EMAIL>", "<EMAIL>"}

	daemon.ForwardMailClient.MTAHost = ""
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward mail client") {
		t.Fatal(err)
	}
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "howard@abc",
		MTAHost:  "a.b.c.d",
		MTAPort:  61358,
	}

	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "my domain names") {
		t.Fatal(err)
	}
	daemon.MyDomains = []string{"example.com", "howard.name"}

	// Must not initialise if forward mailer is myself
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "howard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  daemon.Port,
	}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward MTA") {
		t.Fatal(err)
	}
	// One of the forward addresses loops back to server domain
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "howard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25252, // avoid triggering the init error of looping mails back to daemon itself
	}
	daemon.ForwardTo = []string{"<EMAIL>", "<EMAIL>"}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "loop back") {
		t.Fatal(err)
	}
	// One of the forward addresses does not have at sign
	daemon.ForwardTo = []string{"<EMAIL>", "howard"}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "at sign") {
		t.Fatal(err)
	}
	daemon.ForwardTo = []string{"howard@localhost"}

	// Test default settings
	if err := daemon.Initialise(); err != nil || daemon.Address != "0.0.0.0" || daemon.Port != 25 || daemon.PerIPLimit != 2 {
		t.Fatalf("%+v %+v", err, daemon)
	}

	// Prepare settings for tests
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "howard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25, // allow developer to inspect result by running a postfix
	}
	daemon.Address = "127.0.0.1"
	daemon.Port = 61358   // do not loop back to myself
	daemon.PerIPLimit = 5 // limit must be high enough to tolerate consecutive mail tests
	// If parameter values are satisfactory, daemon should properly and successfully initialise even without the presence of a mail command runner
	if err := daemon.Initialise(); err != nil {
		t.Fatal(err)
	}

	// SMTPD is also responsible for initialising its mail command runner if it is present
	daemon.CommandRunner = &mailcmd.CommandRunner{
		Processor:       nil,
		ReplyMailClient: inet.MailClient{},
	}
	// Even though command runner is assigned, SMTPD should continue to initialise without command runner if command runner does not have config.
	if err := daemon.Initialise(); err != nil {
		t.Fatal(err)
	}
	// SMTPD should not receive init error from command processor
	daemon.CommandRunner.Processor = common.GetInsaneCommandProcessor()
	if err := daemon.Initialise(); !strings.Contains(err.Error(), common.ErrBadProcessorConfig) {
		t.Fatal(err)
	}
	// Give it a good command processor
	daemon.CommandRunner.Processor = common.GetTestCommandProcessor()

	// Must not initialise if mail processor reply mailer is myself
	daemon.CommandRunner.ReplyMailClient = inet.MailClient{
		MailFrom: "howard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  daemon.Port,
	}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "reply MTA") {
		t.Fatal(err)
	}
	// Must not initialise if mail processor reply mailer is not there
	daemon.CommandRunner.ReplyMailClient.MTAHost = ""
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "reply mailer") {
		t.Fatal(err)
	}
	daemon.CommandRunner.ReplyMailClient = inet.MailClient{
		MailFrom: "howard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25,
	}

	TestSMTPD(&daemon, t)
}

func TestTLS(t *testing.T) {
	t.Skip("This test case is prepared for developer to run manually")

	from := mail.Address{"", "<EMAIL>"}
	to := mail.Address{"", "<EMAIL>"}
	headers := make(map[string]string)
	headers["From"] = from.String()
	headers["To"] = to.String()
	headers["Subject"] = "Example subject"<commit_msg>fix go vet style issue in TestSMTPD_StartAndBlock<commit_after>package smtpd

import (
	"crypto/tls"
	"fmt"
	"github.com/HouzuoGuo/laitos/daemon/common"
	"github.com/HouzuoGuo/laitos/daemon/smtpd/mailcmd"
	"github.com/HouzuoGuo/laitos/inet"
	"net"
	"net/mail"
	"net/smtp"
	"strings"
	"testing"
)

func TestSMTPD_StartAndBlock(t *testing.T) {
	daemon := Daemon{
		ForwardMailClient: inet.MailClient{
			MailFrom: "howard@localhost",
			MTAHost:  "smtp.example.com",
			MTAPort:  25,
		},
	}
	// Test missing mandatory parameters
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward address") {
		t.Fatal(err)
	}
	daemon.ForwardTo = []string{"<EMAIL>", "<EMAIL>"}

	daemon.ForwardMailClient.MTAHost = ""
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward mail client") {
		t.Fatal(err)
	}
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "howard@abc",
		MTAHost:  "a.b.c.d",
		MTAPort:  61358,
	}

	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "my domain names") {
		t.Fatal(err)
	}
	daemon.MyDomains = []string{"example.com", "howard.name"}

	// Must not initialise if forward mailer is myself
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "<EMAIL>ard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  daemon.Port,
	}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "forward MTA") {
		t.Fatal(err)
	}
	// One of the forward addresses loops back to server domain
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "<EMAIL>ard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25252, // avoid triggering the init error of looping mails back to daemon itself
	}
	daemon.ForwardTo = []string{"<EMAIL>", "<EMAIL>"}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "loop back") {
		t.Fatal(err)
	}
	// One of the forward addresses does not have at sign
	daemon.ForwardTo = []string{"<EMAIL>", "howard"}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "at sign") {
		t.Fatal(err)
	}
	daemon.ForwardTo = []string{"howard@localhost"}

	// Test default settings
	if err := daemon.Initialise(); err != nil || daemon.Address != "0.0.0.0" || daemon.Port != 25 || daemon.PerIPLimit != 2 {
		t.Fatalf("%+v %+v", err, daemon)
	}

	// Prepare settings for tests
	daemon.ForwardMailClient = inet.MailClient{
		MailFrom: "<EMAIL>ard@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25, // allow developer to inspect result by running a postfix
	}
	daemon.Address = "127.0.0.1"
	daemon.Port = 61358   // do not loop back to myself
	daemon.PerIPLimit = 5 // limit must be high enough to tolerate consecutive mail tests
	// If parameter values are satisfactory, daemon should properly and successfully initialise even without the presence of a mail command runner
	if err := daemon.Initialise(); err != nil {
		t.Fatal(err)
	}

	// SMTPD is also responsible for initialising its mail command runner if it is present
	daemon.CommandRunner = &mailcmd.CommandRunner{
		Processor:       nil,
		ReplyMailClient: inet.MailClient{},
	}
	// Even though command runner is assigned, SMTPD should continue to initialise without command runner if command runner does not have config.
	if err := daemon.Initialise(); err != nil {
		t.Fatal(err)
	}
	// SMTPD should not receive init error from command processor
	daemon.CommandRunner.Processor = common.GetInsaneCommandProcessor()
	if err := daemon.Initialise(); !strings.Contains(err.Error(), common.ErrBadProcessorConfig) {
		t.Fatal(err)
	}
	// Give it a good command processor
	daemon.CommandRunner.Processor = common.GetTestCommandProcessor()

	// Must not initialise if mail processor reply mailer is myself
	daemon.CommandRunner.ReplyMailClient = inet.MailClient{
		MailFrom: "<EMAIL>",
		MTAHost:  "127.0.0.1",
		MTAPort:  daemon.Port,
	}
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "reply MTA") {
		t.Fatal(err)
	}
	// Must not initialise if mail processor reply mailer is not there
	daemon.CommandRunner.ReplyMailClient.MTAHost = ""
	if err := daemon.Initialise(); err == nil || !strings.Contains(err.Error(), "reply mailer") {
		t.Fatal(err)
	}
	daemon.CommandRunner.ReplyMailClient = inet.MailClient{
		MailFrom: "<EMAIL>@localhost",
		MTAHost:  "127.0.0.1",
		MTAPort:  25,
	}

	TestSMTPD(&daemon, t)
}

func TestTLS(t *testing.T) {
	t.Skip("This test case is prepared for developer to run manually")

	from := mail.Address{Name: "", Address: "<EMAIL>"}
	to := mail.Address{Name: "", Address: "<EMAIL>"}
	headers := make(map[string]string)
	headers["From"] = from.String()
	headers["To"] = to.String()
	headers["Subject"] = "Example subject"
{'fraction_non_alphanumeric': 0.14947571153434625, 'fraction_numerical': 0.013909693986732292, 'mean_word_length': 4.184137548530227, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 14, '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': '7628416', 'n_tokens_mistral': 3450, 'n_tokens_neox': 3264, 'n_words': 827}