Files
phishingclub/backend/config/config_test.go
2025-08-21 16:14:09 +02:00

456 lines
9.9 KiB
Go

package config
import (
"encoding/json"
"fmt"
"io/fs"
"net"
"os"
"reflect"
"testing"
"testing/fstest"
"github.com/go-errors/errors"
"github.com/phishingclub/phishingclub/data"
"github.com/phishingclub/phishingclub/file"
"github.com/phishingclub/phishingclub/file/filemock"
)
const (
DEFAULT_ADMIN_ADDR = "127.0.0.1:8002"
DEFAULT_PHISHING_HTTP_ADDR = "127.0.0.1:8000"
DEFAULT_PHISHING_HTTPS_ADDR = "127.0.0.1:8001"
DEFAULT_ACME_EMAIL = ""
)
var (
adminHost = "phish.test"
adminTLS = false
adminPublicCertPath = fmt.Sprintf(
"%s/%s",
data.DefaultAdminCertDir,
data.DefaultAdminPublicCertFileName,
)
adminPrivateCertPath = fmt.Sprintf(
"%s/%s",
data.DefaultAdminCertDir,
data.DefaultAdminPrivateCertFileName,
)
configFileOK = []byte(`{
"administration": {
"address": "127.0.0.1:4000"
}
}`)
configFileEmpty = []byte("{")
databaseOK = Database{
Engine: DefaultAdministrationUseSqlite,
DSN: DefaultAdministrationDSN,
}
)
func newTestConfig() *Config {
return &Config{
acme: ACME{
Email: DEFAULT_ACME_EMAIL,
},
tlsCertPath: adminPublicCertPath,
tlsKeyPath: adminPrivateCertPath,
adminNetAddress: net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: DefaultDevAdministrationPort,
},
phishingHTTPNetAddress: net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: DefaultDevHTTPPhishingPort,
},
phishingHTTPSNetAddress: net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: DefaultDevHTTPSPhishingPort,
},
database: databaseOK,
fileWriter: &filemock.Writer{},
}
}
func TestNewConfig(t *testing.T) {
t.Run("happy path", testNewConfigHappyPath)
t.Run("invalid administration address and port split", testNewConfigInvalidAdministrationAddress)
t.Run("invalid administration ip", testNewConfigInvalidAdministrationIP)
t.Run("invalid administration port", testNewConfigInvalidAdministrationPort)
t.Run("invalid administration port string", testNewConfigInvalidAdministrationPortString)
t.Run("invalid database", testNewConfigInvalidDatabase)
t.Run("writer with nil", testNewConfigWithNilWriter)
}
func testNewConfigWithNilWriter(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"127.0.0.1:8080",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
nil,
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err == nil {
if !errors.Is(err, ErrWriterIsNil) {
t.Error("expected ErrWriterIsNil error from nil writer")
}
t.Error("expected error from nil writer")
return
}
}
func testNewConfigInvalidAdministrationAddress(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"foobar",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err == nil {
t.Error("expected error from invalid address")
return
}
}
func testNewConfigInvalidAdministrationIP(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"999.00.999.999:1234",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if !errors.Is(err, ErrInvalidIP) {
t.Error(err)
return
}
}
func testNewConfigHappyPath(t *testing.T) {
addr := "127.0.0.1:1234"
c, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
addr,
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err != nil {
t.Error(err)
return
}
if c.AdminNetAddress() != addr {
t.Errorf("expected %s but got %s", addr, c.AdminNetAddress())
return
}
if c.database.DSN != databaseOK.DSN {
t.Errorf("expected %s but got %s", databaseOK.DSN, c.database.DSN)
return
}
if c.database.Engine != databaseOK.Engine {
t.Errorf("expected %s but got %s", databaseOK.Engine, c.database.Engine)
return
}
}
func testNewConfigInvalidAdministrationPort(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"127.0.0.1:-1",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if !errors.Is(err, ErrInvalidPort) {
t.Error(err)
return
}
}
func testNewConfigInvalidAdministrationPortString(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"127.0.0.1:999999999999999999999999999999999999999999",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err == nil {
t.Error("expected error from invalid string port")
return
}
}
func testNewConfigInvalidDatabase(t *testing.T) {
_, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
"127.0.0.1:1234",
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
Database{
Engine: "foobar",
DSN: "file:./data.db?cache=shared&mode=rwc&_fk=1",
}, &filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err == nil {
t.Errorf("expected %s but got %s", ErrInvalidDatabase, err)
return
}
}
func TestSetFileWriter(t *testing.T) {
t.Run("happypath", func(t *testing.T) {
c := newTestConfig()
err := c.SetFileWriter(&filemock.Writer{})
if err != nil {
t.Error(err)
return
}
})
t.Run("nil writer", func(t *testing.T) {
c := newTestConfig()
err := c.SetFileWriter(nil)
if err == nil {
if !errors.Is(err, ErrWriterIsNil) {
t.Error("expected ErrWriterIsNil error from nil writer")
}
t.Error("expected error from nil writer")
return
}
})
}
func TestWriteToFile(t *testing.T) {
filepath := "./testFile"
c := newTestConfig()
m := filemock.Writer{}
dto := c.ToDTO()
conf, err := json.MarshalIndent(dto, "", " ")
if err != nil {
t.Error(err)
return
}
m.
On("Write", filepath, conf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0644)).
Return(0, nil)
err = c.SetFileWriter(&m)
if err != nil {
t.Error(err)
return
}
err = c.WriteToFile(filepath)
if err != nil {
t.Error(err)
return
}
}
func TestToDTO(t *testing.T) {
addr := "127.0.0.1:1234"
c, err := NewConfig(
DEFAULT_ACME_EMAIL,
adminHost,
adminTLS,
adminPublicCertPath,
adminPrivateCertPath,
addr,
DEFAULT_PHISHING_HTTP_ADDR,
DEFAULT_PHISHING_HTTPS_ADDR,
databaseOK,
&filemock.Writer{},
"",
"",
IPSecurityConfig{
AdminAllowed: defaultAdminAllowed,
TrustedProxies: defaultTrustedProxies,
TrustedIPHeader: DefaultTrustedIPHeader,
},
)
if err != nil {
t.Error(err)
return
}
dto := c.ToDTO()
if dto.AdministrationServer.Address != addr {
t.Errorf("expected %s but got %s", addr, dto.AdministrationServer.Address)
return
}
}
func TestNewDTOFromFile(t *testing.T) {
t.Run("happypath", testNewDTOFromFileHappyPath)
t.Run("file error", testNewDTOFromFileFileError)
t.Run("bad content", testNewDTOFromFileBadContent)
}
func testNewDTOFromFileHappyPath(t *testing.T) {
filesystem := fstest.MapFS{}
path := "config.json"
filesystem[path] = &fstest.MapFile{
Data: configFileOK,
}
dto, err := NewDTOFromFile(filesystem, path)
if err != nil {
t.Error(err)
return
}
if dto.AdministrationServer.Address != "127.0.0.1:4000" {
t.Errorf("Expected %s Got %s", "127.0.0.1:4000", dto.AdministrationServer.Address)
return
}
}
func testNewDTOFromFileFileError(t *testing.T) {
filesystem := fstest.MapFS{}
path := "config.json"
_, err := NewDTOFromFile(filesystem, path)
if !errors.Is(err, fs.ErrNotExist) {
t.Errorf("expected %s but got %s", fs.ErrNotExist, err)
return
}
}
func testNewDTOFromFileBadContent(t *testing.T) {
filesystem := fstest.MapFS{}
path := "config.json"
filesystem[path] = &fstest.MapFile{
Data: configFileEmpty,
}
_, err := NewDTOFromFile(filesystem, path)
if err == nil {
t.Error("expected error from invalid file contents")
return
}
}
func TestNewDefaultConfig(t *testing.T) {
tests := []struct {
name string
want *Config
}{
{
name: "happypath",
want: &Config{
tlsCertPath: adminPublicCertPath,
tlsKeyPath: adminPrivateCertPath,
adminNetAddress: net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: DefaultDevAdministrationPort,
},
database: Database{
Engine: DefaultAdministrationUseSqlite,
DSN: DefaultAdministrationDSN,
},
fileWriter: &file.FileWriter{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewDevDefaultConfig(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewDefaultConfig() = %v, want %v", got, tt.want)
}
})
}
}
func TestConfig_Database(t *testing.T) {
t.Run("happypath", func(t *testing.T) {
c := newTestConfig()
if !reflect.DeepEqual(c.Database(), databaseOK) {
t.Errorf("expected %v but got %v", databaseOK, c.Database())
return
}
})
}