Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.22

require (
github.com/fsnotify/fsnotify v1.7.0
github.com/spf13/cast v1.6.0
github.com/stretchr/testify v1.8.4
)

Expand Down
58 changes: 0 additions & 58 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,72 +1,14 @@
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kyoh86/scopelint v0.2.0 h1:suOCh1T05nIY8srcI266aqwf3RLtO8kniZOTaAnzRyg=
github.com/kyoh86/scopelint v0.2.0/go.mod h1:veFgnmDG8sPR5nFaXGX2mEIOXKHjWpGo79v/NaiTcRE=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/alecthomas/kingpin.v2 v2.2.5 h1:qskSCq465uEvC3oGocwvZNsO3RF3SpLVLumOAhL0bXo=
gopkg.in/alecthomas/kingpin.v2 v2.2.5/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
272 changes: 272 additions & 0 deletions src/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,275 @@ func TestWatchConfig(t *testing.T) {
// We can't easily test the file watching functionality in a unit test
// but we can at least verify the watcher is set up without errors
}

func TestLoadConfig_Defaults(t *testing.T) {
tempDir, err := os.MkdirTemp("", "defaults-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

// Config with no port, logLevel, or logFormat
configPath := filepath.Join(tempDir, "config.json")
configContent := `{
"endpoints": [
{
"method": "GET",
"status": 200,
"path": "/test",
"jsonPath": "` + jsonFile + `"
}
]
}`
err = os.WriteFile(configPath, []byte(configContent), 0644)
assert.NoError(t, err)

cfg, err := LoadConfig(configPath)
assert.NoError(t, err)
assert.Equal(t, 3000, cfg.Port, "default port should be 3000")
assert.Equal(t, "info", cfg.LogLevel, "default logLevel should be info")
assert.Equal(t, "text", cfg.LogFormat, "default logFormat should be text")
}

func TestLoadConfig_InvalidJSON(t *testing.T) {
tempDir, err := os.MkdirTemp("", "invalid-json-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(`{invalid json}`), 0644)
assert.NoError(t, err)

_, err = LoadConfig(configPath)
assert.Error(t, err)
assert.Contains(t, err.Error(), "error parsing config file")
}

func TestLoadConfig_FileNotFound(t *testing.T) {
_, err := LoadConfig("/nonexistent/path/config.json")
assert.Error(t, err)
assert.Contains(t, err.Error(), "error reading config file")
}

func TestLoadConfig_WithHost(t *testing.T) {
tempDir, err := os.MkdirTemp("", "host-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

configPath := filepath.Join(tempDir, "config.json")
configContent := `{
"host": "localhost",
"port": 9090,
"endpoints": [
{
"method": "GET",
"status": 200,
"path": "/test",
"jsonPath": "` + jsonFile + `"
}
]
}`
err = os.WriteFile(configPath, []byte(configContent), 0644)
assert.NoError(t, err)

cfg, err := LoadConfig(configPath)
assert.NoError(t, err)
assert.Equal(t, "localhost", cfg.Host)
assert.Equal(t, 9090, cfg.Port)
}

func TestConfig_GetEndpoints(t *testing.T) {
cfg := &Config{
Endpoints: []Endpoint{
{Method: "GET", Path: "/a"},
{Method: "POST", Path: "/b"},
},
}

endpoints := cfg.GetEndpoints()
assert.Len(t, endpoints, 2)
assert.Equal(t, "/a", endpoints[0].Path)
assert.Equal(t, "/b", endpoints[1].Path)

// Modifying returned slice should not affect original
endpoints[0].Path = "/modified"
assert.Equal(t, "/a", cfg.Endpoints[0].Path)
}

func TestConfig_GetPort(t *testing.T) {
cfg := &Config{Port: 4000}
assert.Equal(t, 4000, cfg.GetPort())
}

func TestConfig_GetHost(t *testing.T) {
cfg := &Config{Host: "example.com"}
assert.Equal(t, "example.com", cfg.GetHost())
}

func TestConfig_GetLogConfig(t *testing.T) {
cfg := &Config{
LogLevel: "debug",
LogFormat: "json",
LogPath: "/var/log/server.log",
}

level, format, path := cfg.GetLogConfig()
assert.Equal(t, "debug", level)
assert.Equal(t, "json", format)
assert.Equal(t, "/var/log/server.log", path)
}

func TestConfig_Validate_DifferentMethodsSamePath(t *testing.T) {
tempDir, err := os.MkdirTemp("", "methods-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

cfg := Config{
Endpoints: []Endpoint{
{Method: "GET", Path: "/users", JsonPath: jsonFile, Status: 200},
{Method: "POST", Path: "/users", JsonPath: jsonFile, Status: 201},
{Method: "PUT", Path: "/users", JsonPath: jsonFile, Status: 200},
{Method: "DELETE", Path: "/users", JsonPath: jsonFile, Status: 204},
},
}

err = cfg.Validate()
assert.NoError(t, err, "different HTTP methods on the same path should be allowed")
}

func TestConfig_Validate_MultipleEndpoints(t *testing.T) {
tempDir, err := os.MkdirTemp("", "multi-ep-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

cfg := Config{
Endpoints: []Endpoint{
{Method: "GET", Path: "/users", JsonPath: jsonFile, Status: 200},
{Method: "GET", Path: "/posts", JsonPath: jsonFile, Status: 200},
{Method: "GET", Path: "/comments", JsonPath: jsonFile, Status: 200},
},
}

err = cfg.Validate()
assert.NoError(t, err)
}

func TestConfig_Validate_EndpointWithType(t *testing.T) {
tempDir, err := os.MkdirTemp("", "type-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

cfg := Config{
Endpoints: []Endpoint{
{Type: "api", Method: "GET", Path: "/test", JsonPath: jsonFile, Status: 200},
},
}

err = cfg.Validate()
assert.NoError(t, err)
}

func TestConfig_Reload_InvalidConfig(t *testing.T) {
tempDir, err := os.MkdirTemp("", "reload-invalid-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

// Create initial valid config
configPath := filepath.Join(tempDir, "config.json")
initialConfig := `{
"port": 8080,
"endpoints": [
{
"method": "GET",
"status": 200,
"path": "/test",
"jsonPath": "` + jsonFile + `"
}
]
}`
err = os.WriteFile(configPath, []byte(initialConfig), 0644)
assert.NoError(t, err)

cfg, err := LoadConfig(configPath)
assert.NoError(t, err)
assert.Equal(t, 8080, cfg.Port)

// Overwrite with invalid JSON
err = os.WriteFile(configPath, []byte(`{invalid}`), 0644)
assert.NoError(t, err)

// Reload should fail
err = cfg.Reload(configPath)
assert.Error(t, err)
// Original config should remain unchanged
assert.Equal(t, 8080, cfg.Port)
}

func TestLoadConfig_WithLogPath(t *testing.T) {
tempDir, err := os.MkdirTemp("", "logpath-test")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)

jsonFile := filepath.Join(tempDir, "test.json")
err = os.WriteFile(jsonFile, []byte(`{}`), 0644)
assert.NoError(t, err)

configPath := filepath.Join(tempDir, "config.json")
configContent := `{
"port": 3000,
"logLevel": "warn",
"logFormat": "json",
"logPath": "/tmp/test-server.log",
"endpoints": [
{
"method": "GET",
"status": 200,
"path": "/test",
"jsonPath": "` + jsonFile + `"
}
]
}`
err = os.WriteFile(configPath, []byte(configContent), 0644)
assert.NoError(t, err)

cfg, err := LoadConfig(configPath)
assert.NoError(t, err)
assert.Equal(t, "warn", cfg.LogLevel)
assert.Equal(t, "json", cfg.LogFormat)
assert.Equal(t, "/tmp/test-server.log", cfg.LogPath)
}

func TestConfig_Validate_EmptyJsonPath(t *testing.T) {
// Endpoint with no jsonPath and no folder should still pass validation
// (jsonPath check is only for non-empty jsonPath)
cfg := Config{
Endpoints: []Endpoint{
{Method: "POST", Path: "/webhook", Status: 200},
},
}

err := cfg.Validate()
assert.NoError(t, err)
}
Loading
Loading