diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index 1214f223..9dd8f9b5 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ Usage of /usr/local/bin/argus: Put the name of the Notify service to send a test message. -test.service string Put the name of the Service to test the version query. + -web.basic-auth.password string + Password for basic auth + -web.basic-auth.username string + Username for basic auth -web.cert-file string HTTPS certificate file path. -web.listen-host string diff --git a/cmd/argus/main_test.go b/cmd/argus/main_test.go index e9ba03c6..09518304 100644 --- a/cmd/argus/main_test.go +++ b/cmd/argus/main_test.go @@ -78,6 +78,7 @@ func TestTheMain(t *testing.T) { t.Run(name, func(t *testing.T) { file := fmt.Sprintf("%s.yml", name) + os.Remove(tc.db) tc.file(file, t) defer os.Remove(tc.db) resetFlags() diff --git a/commands/announce_test.go b/commands/announce_test.go index 1f050f34..486f2228 100644 --- a/commands/announce_test.go +++ b/commands/announce_test.go @@ -39,7 +39,6 @@ func TestController_AnnounceCommand(t *testing.T) { "not tried does delay by 15s": { index: 2, timeDifference: 15 * time.Second, - failed: nil, }, "failed does delay by 15s": { index: 0, @@ -137,7 +136,6 @@ func TestController_Find(t *testing.T) { want: nil}, "nil controller": { command: "ls -lah /root", - want: nil, nilController: true}, } diff --git a/commands/commands_test.go b/commands/commands_test.go index 25b711df..da00c4f0 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -83,7 +83,6 @@ func TestCommand_Exec(t *testing.T) { }{ "command that will pass": { cmd: Command{"date", "+%m-%d-%Y"}, - err: nil, outputRegex: `[0-9]{2}-[0-9]{2}-[0-9]{4}\s+$`}, "command that will fail": { cmd: Command{"false"}, @@ -144,12 +143,10 @@ func TestController_ExecIndex(t *testing.T) { }{ "command index out of range": { index: 2, - err: nil, outputRegex: `^$`, noAnnounce: true}, "command index that will pass": { index: 0, - err: nil, outputRegex: `[0-9]{2}-[0-9]{2}-[0-9]{4}\s+$`}, "command index that will fail": { index: 1, @@ -208,15 +205,12 @@ func TestController_Exec(t *testing.T) { }{ "nil Controller": { nilController: true, - err: nil, outputRegex: `^$`, noAnnounce: true}, "nil Command": { - err: nil, outputRegex: `^$`, noAnnounce: true}, "single Command": { - err: nil, outputRegex: `[0-9]{2}-[0-9]{2}-[0-9]{4}\s+$`, commands: &Slice{ {"date", "+%m-%d-%Y"}}}, diff --git a/commands/help_test.go b/commands/help_test.go index a0d70009..0361f042 100644 --- a/commands/help_test.go +++ b/commands/help_test.go @@ -19,8 +19,10 @@ package command import ( "fmt" "os" + "strings" "testing" + "github.com/release-argus/Argus/notifiers/shoutrrr" svcstatus "github.com/release-argus/Argus/service/status" "github.com/release-argus/Argus/util" ) @@ -69,3 +71,38 @@ func TestMain(m *testing.M) { // exit os.Exit(exitCode) } + +func testShoutrrr(failing bool, selfSignedCert bool) *shoutrrr.Shoutrrr { + url := "valid.release-argus.io" + if selfSignedCert { + url = strings.Replace(url, "valid", "invalid", 1) + } + shoutrrr := shoutrrr.New( + nil, "", + &map[string]string{"max_tries": "1"}, + &map[string]string{}, + "gotify", + // trunk-ignore(gitleaks/generic-api-key) + &map[string]string{"host": url, "path": "/gotify", "token": "AGE-LlHU89Q56uQ"}, + shoutrrr.NewDefaults( + "", nil, nil, nil), + shoutrrr.NewDefaults( + "", nil, nil, nil), + shoutrrr.NewDefaults( + "", nil, nil, nil)) + shoutrrr.Main.InitMaps() + shoutrrr.Defaults.InitMaps() + shoutrrr.HardDefaults.InitMaps() + + shoutrrr.ID = "test" + shoutrrr.ServiceStatus = &svcstatus.Status{ + ServiceID: stringPtr("service"), + } + shoutrrr.ServiceStatus.Fails.Shoutrrr.Init(1) + shoutrrr.Failed = &shoutrrr.ServiceStatus.Fails.Shoutrrr + + if failing { + shoutrrr.URLFields["token"] = "invalid" + } + return shoutrrr +} diff --git a/commands/init_test.go b/commands/init_test.go index e749a44b..3be989fd 100644 --- a/commands/init_test.go +++ b/commands/init_test.go @@ -228,7 +228,6 @@ func TestCommand_String(t *testing.T) { cmd: &Command{}, want: ""}, "nil command": { - cmd: nil, want: ""}, "command with no args": { cmd: &Command{"ls"}, @@ -370,17 +369,16 @@ func TestCommand_Init(t *testing.T) { {"false"}}, }, "nil Notifiers": { - shoutrrrNotifiers: nil, command: &Slice{ {"date", "+%m-%d-%Y"}}, }, "non-nil Notifiers": { - shoutrrrNotifiers: nil, command: &Slice{ {"date", "+%m-%d-%Y"}}, + shoutrrrNotifiers: &shoutrrr.Slice{ + "test": testShoutrrr(false, false)}, }, "nil parentInterval": { - parentInterval: nil, command: &Slice{ {"date", "+%m-%d-%Y"}}, }, diff --git a/config/settings.go b/config/settings.go index 9cecb346..96d89724 100644 --- a/config/settings.go +++ b/config/settings.go @@ -15,8 +15,6 @@ package config import ( - "crypto/sha256" - "encoding/hex" "flag" "fmt" "os" @@ -28,14 +26,16 @@ import ( // Export the flags. var ( - LogLevel = flag.String("log.level", "INFO", "ERROR, WARN, INFO, VERBOSE or DEBUG") - LogTimestamps = flag.Bool("log.timestamps", false, "Enable timestamps in CLI output.") - DataDatabaseFile = flag.String("data.database-file", "data/argus.db", "Database file path.") - WebListenHost = flag.String("web.listen-host", "0.0.0.0", "IP address to listen on for UI, API, and telemetry.") - WebListenPort = flag.String("web.listen-port", "8080", "Port to listen on for UI, API, and telemetry.") - WebCertFile = flag.String("web.cert-file", "", "HTTPS certificate file path.") - WebPKeyFile = flag.String("web.pkey-file", "", "HTTPS private key file path.") - WebRoutePrefix = flag.String("web.route-prefix", "/", "Prefix for web endpoints") + LogLevel = flag.String("log.level", "INFO", "ERROR, WARN, INFO, VERBOSE or DEBUG") + LogTimestamps = flag.Bool("log.timestamps", false, "Enable timestamps in CLI output.") + DataDatabaseFile = flag.String("data.database-file", "data/argus.db", "Database file path.") + WebListenHost = flag.String("web.listen-host", "0.0.0.0", "IP address to listen on for UI, API, and telemetry.") + WebListenPort = flag.String("web.listen-port", "8080", "Port to listen on for UI, API, and telemetry.") + WebCertFile = flag.String("web.cert-file", "", "HTTPS certificate file path.") + WebPKeyFile = flag.String("web.pkey-file", "", "HTTPS private key file path.") + WebRoutePrefix = flag.String("web.route-prefix", "/", "Prefix for web endpoints") + WebBasicAuthUsername = flag.String("web.basic-auth.username", "", "Username for basic auth") + WebBasicAuthPassword = flag.String("web.basic-auth.password", "", "Password for basic auth") ) // Settings for the binary. @@ -64,6 +64,12 @@ type SettingsBase struct { Web WebSettings `yaml:"web,omitempty"` // Web settings } +// CheckValues of the SettingsBase. +func (s *SettingsBase) CheckValues() { + // Web + s.Web.CheckValues() +} + // MapEnvToStruct maps environment variables to this struct. func (s *SettingsBase) MapEnvToStruct() { err := mapEnvToStruct(s, "", nil) @@ -73,6 +79,7 @@ func (s *SettingsBase) MapEnvToStruct() { strings.ReplaceAll(util.ErrorToString(err), "\\", "\n"), util.LogFrom{}, true) } + s.CheckValues() // Set hash values and remove empty structs. } // LogSettings for the binary. @@ -143,24 +150,11 @@ func (s *WebSettingsBasicAuth) String(prefix string) (str string) { // CheckValues will ensure that the values are SHA256 hashed. func (ba *WebSettingsBasicAuth) CheckValues() { - // Ensure it's hashed. - sha256Regex := "^h__[a-f0-9]{64}$" - // Username - Hash if not already hashed. - if !util.RegexCheck(sha256Regex, ba.Username) { - ba.UsernameHash = sha256.Sum256([]byte(ba.Username)) - ba.Username = fmt.Sprintf("h__%x", ba.UsernameHash) - } else { - hash, _ := hex.DecodeString(ba.Username[3:]) - copy(ba.UsernameHash[:], hash) - } - // Password - Hash if not already hashed. - if !util.RegexCheck(sha256Regex, ba.Password) { - ba.PasswordHash = sha256.Sum256([]byte(ba.Password)) - ba.Password = fmt.Sprintf("h__%x", ba.PasswordHash) - } else { - hash, _ := hex.DecodeString(ba.Password[3:]) - copy(ba.PasswordHash[:], hash) - } + // Username + ba.UsernameHash = util.GetHash(ba.Username) + // Password + ba.PasswordHash = util.GetHash(ba.Password) + ba.Password = util.FmtHash(ba.PasswordHash) } // FaviconSettings contains the favicon override settings. @@ -170,29 +164,26 @@ type FaviconSettings struct { } func (s *Settings) NilUndefinedFlags(flagset *map[string]bool) { - if !(*flagset)["log.level"] { - LogLevel = nil - } - if !(*flagset)["log.timestamps"] { - LogTimestamps = nil - } - if !(*flagset)["data.database-file"] { - DataDatabaseFile = nil - } - if !(*flagset)["web.listen-host"] { - WebListenHost = nil - } - if !(*flagset)["web.listen-port"] { - WebListenPort = nil - } - if !(*flagset)["web.cert-file"] { - WebCertFile = nil - } - if !(*flagset)["web.pkey-file"] { - WebPKeyFile = nil - } - if !(*flagset)["web.route-prefix"] { - WebRoutePrefix = nil + for _, f := range []struct { + Flag string + Variable interface{} + }{ + {"log.level", &LogLevel}, + {"log.timestamps", &LogTimestamps}, + {"data.database-file", &DataDatabaseFile}, + {"web.listen-host", &WebListenHost}, + {"web.listen-port", &WebListenPort}, + {"web.cert-file", &WebCertFile}, + {"web.pkey-file", &WebPKeyFile}, + {"web.route-prefix", &WebRoutePrefix}, + {"web.basic-auth.username", &WebBasicAuthUsername}, + {"web.basic-auth.password", &WebBasicAuthPassword}, + } { + if !(*flagset)[f.Flag] { + if strPtr, ok := f.Variable.(**string); ok { + *strPtr = nil + } + } } } @@ -238,17 +229,25 @@ func (s *Settings) SetDefaults() { webListenPort := "8080" s.HardDefaults.Web.ListenPort = &webListenPort - // RoutePrefix - s.FromFlags.Web.RoutePrefix = WebRoutePrefix - webRoutePrefix := "/" - s.HardDefaults.Web.RoutePrefix = &webRoutePrefix - // CertFile s.FromFlags.Web.CertFile = WebCertFile // KeyFile s.FromFlags.Web.KeyFile = WebPKeyFile + // RoutePrefix + s.FromFlags.Web.RoutePrefix = WebRoutePrefix + webRoutePrefix := "/" + s.HardDefaults.Web.RoutePrefix = &webRoutePrefix + + // BasicAuth + if WebBasicAuthUsername != nil || WebBasicAuthPassword != nil { + s.FromFlags.Web.BasicAuth = &WebSettingsBasicAuth{} + s.FromFlags.Web.BasicAuth.Username = util.StringWithEnv(util.DefaultIfNil(WebBasicAuthUsername)) + s.FromFlags.Web.BasicAuth.Password = util.StringWithEnv(util.DefaultIfNil(WebBasicAuthPassword)) + s.FromFlags.Web.BasicAuth.CheckValues() + } + // Overwrite defaults with environment variables. s.HardDefaults.MapEnvToStruct() } @@ -352,8 +351,26 @@ func (s *Settings) WebKeyFile() *string { return keyFile } -// CheckValues of the Settings. -func (s *Settings) CheckValues() { - // Web - s.Web.CheckValues() +// WebBasicAuthUsername. +func (s *Settings) WebBasicAuthUsernameHash() [32]byte { + // Username set through flag. + if s.FromFlags.Web.BasicAuth != nil && s.FromFlags.Web.BasicAuth.Username != "" { + return s.FromFlags.Web.BasicAuth.UsernameHash + } + // Username set through config. + if s.Web.BasicAuth != nil && s.Web.BasicAuth.Username != "" { + return s.Web.BasicAuth.UsernameHash + } + return s.HardDefaults.Web.BasicAuth.UsernameHash +} + +// WebBasicAuthPassword. +func (s *Settings) WebBasicAuthPasswordHash() [32]byte { + if s.FromFlags.Web.BasicAuth != nil && s.FromFlags.Web.BasicAuth.Password != "" { + return s.FromFlags.Web.BasicAuth.PasswordHash + } + if s.Web.BasicAuth != nil { + return s.Web.BasicAuth.PasswordHash + } + return s.HardDefaults.Web.BasicAuth.PasswordHash } diff --git a/config/settings_test.go b/config/settings_test.go index a5f002b7..63e4e212 100644 --- a/config/settings_test.go +++ b/config/settings_test.go @@ -17,17 +17,104 @@ package config import ( - "crypto/sha256" "fmt" "os" "regexp" "strings" - "sync" "testing" "github.com/release-argus/Argus/util" ) +func TestSettingsBase_CheckValues(t *testing.T) { + // GIVEN a Settings struct with some values set + tests := map[string]struct { + had Settings + want Settings + wantUsernameHash string + wantPasswordHash string + }{ + "BasicAuth - empty": { + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{}}}}, + want: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: nil}}}, + }, + "BasicAuth - hashed Username and str Password": { + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Username: util.FmtHash(util.GetHash("user")), + Password: "pass"}}}}, + want: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Username: util.FmtHash(util.GetHash("user")), + Password: util.FmtHash(util.GetHash("pass"))}}}}, + }, + "Favicon - empty": { + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + Favicon: &FaviconSettings{}}}}, + want: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + Favicon: nil}}}, + }, + "Favicon - full": { + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + Favicon: &FaviconSettings{ + SVG: "https://example.com/favicon.svg", + PNG: "https://example.com/favicon.png"}}}}, + want: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + Favicon: &FaviconSettings{ + SVG: "https://example.com/favicon.svg", + PNG: "https://example.com/favicon.png"}}}}, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + // WHEN CheckValues is called on it + tc.had.CheckValues() + + // THEN the Settings are converted/removed where necessary + hadStr := tc.had.String("") + wantStr := tc.want.String("") + if hadStr != wantStr { + t.Errorf("want:\n%v\ngot:\n%v", + wantStr, hadStr) + } + // AND the BasicAuth username and password are hashed (if they exist) + if tc.want.Web.BasicAuth != nil { + wantUsernameHash := util.GetHash(tc.want.Web.BasicAuth.Username) + if tc.had.Web.BasicAuth.UsernameHash != wantUsernameHash { + t.Errorf("want: %x\ngot: %x", + wantUsernameHash, tc.had.Web.BasicAuth.UsernameHash) + } + wantPasswordHash := util.GetHash(tc.want.Web.BasicAuth.Password) + if tc.had.Web.BasicAuth.PasswordHash != wantPasswordHash { + t.Errorf("want: %x\ngot: %x", + wantPasswordHash, tc.had.Web.BasicAuth.PasswordHash) + } + } + }) + } +} + func TestSettings_NilUndefinedFlags(t *testing.T) { // GIVEN tests with flags set/unset var settings Settings @@ -44,21 +131,17 @@ func TestSettings_NilUndefinedFlags(t *testing.T) { "log.level": false, } flag := "log.level" - var flagLock sync.Mutex for name, tc := range tests { t.Run(name, func(t *testing.T) { - t.Parallel() // WHEN a flag is set/unset and NilUndefinedFlags is called - flagLock.Lock() flagset[flag] = tc.flagSet LogLevel = tc.setTo settings.NilUndefinedFlags(&flagset) // THEN the flag is defined/undefined correctly got := LogLevel - flagLock.Unlock() if (tc.flagSet && got == nil) || (!tc.flagSet && got != nil) { t.Errorf("%s %s:\nwant: %s\ngot: %v", @@ -296,6 +379,17 @@ func TestSettings_MapEnvToStruct(t *testing.T) { Web: WebSettings{ RoutePrefix: stringPtr("prefix")}}}, }, + "web.basic_auth": { + env: map[string]string{ + "ARGUS_WEB_BASIC_AUTH_USERNAME": "user", + "ARGUS_WEB_BASIC_AUTH_PASSWORD": "pass"}, + want: &Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}}}}, + }, } for name, tc := range tests { @@ -465,57 +559,95 @@ func TestSettings_GetWebFile_NotExist(t *testing.T) { } } -func TestWebSettingsBasicAuth_CheckValues(t *testing.T) { - // GIVEN a WebSettingsBasicAuth struct with some values set +func TestSettings_WebBasicAuthUsernameHash(t *testing.T) { + // GIVEN a Settings struct with some values set tests := map[string]struct { - had WebSettingsBasicAuth - want WebSettingsBasicAuth + want string // The string that was hashed + had Settings }{ - "str Username": { - had: WebSettingsBasicAuth{ - Username: "test"}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("test"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("")))}, + "empty": { + want: "", }, - "str Web.BasicAuth.Password": { - had: WebSettingsBasicAuth{ - Password: "just a password here"}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte(""))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("just a password here")))}, + "set in config": { + want: "user", + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Username: "user"}}}}, }, - "str Web.BasicAuth.Username and str Web.BasicAuth.Password": { - had: WebSettingsBasicAuth{ - Username: "user", - Password: "pass"}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, + "set in flag": { + want: "user", + had: Settings{ + FromFlags: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Username: "user"}}}}, }, - "str Web.BasicAuth.Username and Web.BasicAuth.Password already hashed": { - had: WebSettingsBasicAuth{ - Username: "user", - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + want := util.GetHash(tc.want) + tc.had.CheckValues() + tc.had.FromFlags.CheckValues() + // HardDefaults.Web.BasicAuth will never be nil if Basic Auth is in use + tc.had.HardDefaults = SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + UsernameHash: util.GetHash(""), + PasswordHash: util.GetHash("")}}} + + // WHEN WebBasicAuthUsernameHash is called on it + got := tc.had.WebBasicAuthUsernameHash() + + // THEN the hash is returned + if got != want { + t.Errorf("want: %s\ngot: %s", + want, got) + } + }) + } +} + +func TestSettings_WebBasicAuthPasswordHash(t *testing.T) { + // GIVEN a Settings struct with some values set + tests := map[string]struct { + want string // The string that was hashed + had Settings + }{ + "empty": { + want: "", }, - "hashed Web.BasicAuth.Username and str Web.BasicAuth.Password": { - had: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: "pass"}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, + "set in config": { + want: "pass", + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Password: "pass"}}}}, }, - "hashed Web.BasicAuth.Username and hashed Web.BasicAuth.Password": { - had: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, - want: WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}, + "set in flag": { + want: "pass", + had: Settings{ + FromFlags: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Password: "pass"}}}}, + }, + "set everywhere, use flag": { + want: "flag", + had: Settings{ + SettingsBase: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Password: "config"}}}, + FromFlags: SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + Password: "flag"}}}}, }, } @@ -523,15 +655,23 @@ func TestWebSettingsBasicAuth_CheckValues(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - // WHEN CheckValues is called on it + want := util.GetHash(tc.want) tc.had.CheckValues() - - // THEN the Settings are converted/removed where necessary - hadStr := tc.had.String("") - wantStr := tc.want.String("") - if hadStr != wantStr { - t.Errorf("want:\n%v\ngot:\n%v", - wantStr, hadStr) + tc.had.FromFlags.CheckValues() + // HardDefaults.Web.BasicAuth will never be nil if Basic Auth is in use + tc.had.HardDefaults = SettingsBase{ + Web: WebSettings{ + BasicAuth: &WebSettingsBasicAuth{ + UsernameHash: util.GetHash(""), + PasswordHash: util.GetHash("")}}} + + // WHEN WebBasicAuthPasswordHash is called on it + got := tc.had.WebBasicAuthPasswordHash() + + // THEN the hash is returned + if got != want { + t.Errorf("want: %s\ngot: %s", + want, got) } }) } @@ -553,21 +693,21 @@ func TestWebSettings_CheckValues(t *testing.T) { had: WebSettings{ BasicAuth: &WebSettingsBasicAuth{ Username: "user", - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}}, + Password: util.FmtHash(util.GetHash("pass"))}}, want: WebSettings{ BasicAuth: &WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}}, + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}}, }, "BasicAuth - hashed Username and str Password": { had: WebSettings{ BasicAuth: &WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), + Username: "user", Password: "pass"}}, want: WebSettings{ BasicAuth: &WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}}, + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}}, }, "Favicon - empty": { had: WebSettings{ @@ -621,59 +761,57 @@ func TestWebSettings_CheckValues(t *testing.T) { } } -func TestSettings_CheckValues(t *testing.T) { - // GIVEN a Settings struct with some values set +func TestWebSettingsBasicAuth_CheckValues(t *testing.T) { + // GIVEN a WebSettingsBasicAuth struct with some values set tests := map[string]struct { - had Settings - want Settings + had WebSettingsBasicAuth + want WebSettingsBasicAuth }{ - "BasicAuth - empty": { - had: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - BasicAuth: &WebSettingsBasicAuth{}}}}, - want: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - BasicAuth: nil}}}, + "str Username": { + had: WebSettingsBasicAuth{ + Username: "test"}, + want: WebSettingsBasicAuth{ + Username: "test", + Password: util.FmtHash(util.GetHash(""))}, }, - "BasicAuth - hashed Username and str Password": { - had: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - BasicAuth: &WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: "pass"}}}}, - want: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - BasicAuth: &WebSettingsBasicAuth{ - Username: fmt.Sprintf("h__%x", sha256.Sum256([]byte("user"))), - Password: fmt.Sprintf("h__%x", sha256.Sum256([]byte("pass")))}}}}, + "str Web.BasicAuth.Password": { + had: WebSettingsBasicAuth{ + Password: "just a password here"}, + want: WebSettingsBasicAuth{ + Username: "", + Password: util.FmtHash(util.GetHash("just a password here"))}, }, - "Favicon - empty": { - had: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - Favicon: &FaviconSettings{}}}}, - want: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - Favicon: nil}}}, + "str Web.BasicAuth.Username and str Web.BasicAuth.Password": { + had: WebSettingsBasicAuth{ + Username: "user", + Password: "pass"}, + want: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, }, - "Favicon - full": { - had: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - Favicon: &FaviconSettings{ - SVG: "https://example.com/favicon.svg", - PNG: "https://example.com/favicon.png"}}}}, - want: Settings{ - SettingsBase: SettingsBase{ - Web: WebSettings{ - Favicon: &FaviconSettings{ - SVG: "https://example.com/favicon.svg", - PNG: "https://example.com/favicon.png"}}}}, + "str Web.BasicAuth.Username and Web.BasicAuth.Password already hashed": { + had: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, + want: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, + }, + "hashed Web.BasicAuth.Username and str Web.BasicAuth.Password": { + had: WebSettingsBasicAuth{ + Username: "user", + Password: "pass"}, + want: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, + }, + "hashed Web.BasicAuth.Username and hashed Web.BasicAuth.Password": { + had: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, + want: WebSettingsBasicAuth{ + Username: "user", + Password: util.FmtHash(util.GetHash("pass"))}, }, } diff --git a/notifiers/shoutrrr/gets.go b/notifiers/shoutrrr/gets.go index 90ae3c40..0b11f8e2 100644 --- a/notifiers/shoutrrr/gets.go +++ b/notifiers/shoutrrr/gets.go @@ -23,7 +23,7 @@ import ( // GetOption from this/Main/Defaults/HardDefaults on FiFo. func (s *Shoutrrr) GetOption(key string) string { - return util.FirstNonDefault( + return util.FirstNonDefaultWithEnv( s.Options[key], s.Main.Options[key], s.Defaults.Options[key], @@ -42,7 +42,7 @@ func (s *ShoutrrrBase) SetOption(key string, value string) { // GetParam from this/Main/Defaults/HardDefaults on FiFo func (s *Shoutrrr) GetParam(key string) string { - return util.FirstNonDefault( + return util.FirstNonDefaultWithEnv( s.Params[key], s.Main.Params[key], s.Defaults.Params[key], @@ -61,7 +61,7 @@ func (s *ShoutrrrBase) SetParam(key string, value string) { // GetURLField from this/Main/Defaults/HardDefaults on FiFo func (s *Shoutrrr) GetURLField(key string) string { - return util.FirstNonDefault( + return util.FirstNonDefaultWithEnv( s.URLFields[key], s.Main.URLFields[key], s.Defaults.URLFields[key], diff --git a/notifiers/shoutrrr/gets_test.go b/notifiers/shoutrrr/gets_test.go index 47054b1f..5c3611a4 100644 --- a/notifiers/shoutrrr/gets_test.go +++ b/notifiers/shoutrrr/gets_test.go @@ -18,6 +18,7 @@ package shoutrrr import ( "fmt" + "os" "testing" "time" @@ -27,36 +28,43 @@ import ( func TestShoutrrr_GetOption(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - optionRoot *string - optionMain *string - optionDefault *string - optionHardDefault *string - wantString string + env map[string]string + root *string + main *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "this", - optionRoot: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + root: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "main overrides default and hardDefault": { - wantString: "this", - optionRoot: nil, - optionMain: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + main: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "default overrides hardDefault": { - wantString: "this", - optionRoot: nil, - optionDefault: stringPtr("this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + dfault: stringPtr("this"), + hardDefault: stringPtr("not_this"), }, "hardDefault is last resort": { - wantString: "this", - optionRoot: nil, - optionDefault: nil, - optionHardDefault: stringPtr("this"), + wantString: "this", + hardDefault: stringPtr("this"), + }, + "env var is used": { + wantString: "this", + env: map[string]string{"TESTSHOUTRRR_GETOPTION_ONE": "this"}, + root: stringPtr("${TESTSHOUTRRR_GETOPTION_ONE}"), + }, + "empty env var ignored": { + wantString: "that", + root: stringPtr("${TESTSHOUTRRR_GETOPTION_UNSET}"), + dfault: stringPtr("that"), }, } @@ -66,17 +74,21 @@ func TestShoutrrr_GetOption(t *testing.T) { key := "test" shoutrrr := testShoutrrr(false, false) - if tc.optionRoot != nil { - shoutrrr.Options[key] = *tc.optionRoot + if tc.root != nil { + shoutrrr.Options[key] = *tc.root + } + if tc.main != nil { + shoutrrr.Main.Options[key] = *tc.main } - if tc.optionMain != nil { - shoutrrr.Main.Options[key] = *tc.optionMain + if tc.dfault != nil { + shoutrrr.Defaults.Options[key] = *tc.dfault } - if tc.optionDefault != nil { - shoutrrr.Defaults.Options[key] = *tc.optionDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Options[key] = *tc.hardDefault } - if tc.optionHardDefault != nil { - shoutrrr.HardDefaults.Options[key] = *tc.optionHardDefault + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) } // WHEN GetOption is called @@ -105,36 +117,43 @@ func TestShoutrrr_GetOption(t *testing.T) { func TestShoutrrr_GetURLField(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - optionRoot *string - optionMain *string - optionDefault *string - optionHardDefault *string - wantString string + env map[string]string + root *string + main *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "this", - optionRoot: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + root: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "main overrides default and hardDefault": { - wantString: "this", - optionRoot: nil, - optionMain: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + main: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "default overrides hardDefault": { - wantString: "this", - optionRoot: nil, - optionDefault: stringPtr("this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + dfault: stringPtr("this"), + hardDefault: stringPtr("not_this"), }, "hardDefault is last resort": { - wantString: "this", - optionRoot: nil, - optionDefault: nil, - optionHardDefault: stringPtr("this"), + wantString: "this", + hardDefault: stringPtr("this"), + }, + "env var is used": { + wantString: "this", + env: map[string]string{"TESTSHOUTRRR_GETURLFIELD_ONE": "this"}, + root: stringPtr("${TESTSHOUTRRR_GETURLFIELD_ONE}"), + }, + "empty env var ignored": { + wantString: "that", + root: stringPtr("${TESTSHOUTRRR_GETURLFIELD_UNSET}"), + dfault: stringPtr("that"), }, } @@ -144,17 +163,21 @@ func TestShoutrrr_GetURLField(t *testing.T) { key := "test" shoutrrr := testShoutrrr(false, false) - if tc.optionRoot != nil { - shoutrrr.URLFields[key] = *tc.optionRoot + if tc.root != nil { + shoutrrr.URLFields[key] = *tc.root } - if tc.optionMain != nil { - shoutrrr.Main.URLFields[key] = *tc.optionMain + if tc.main != nil { + shoutrrr.Main.URLFields[key] = *tc.main } - if tc.optionDefault != nil { - shoutrrr.Defaults.URLFields[key] = *tc.optionDefault + if tc.dfault != nil { + shoutrrr.Defaults.URLFields[key] = *tc.dfault } - if tc.optionHardDefault != nil { - shoutrrr.HardDefaults.URLFields[key] = *tc.optionHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.URLFields[key] = *tc.hardDefault + } + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) } // WHEN GetURLField is called @@ -183,36 +206,43 @@ func TestShoutrrr_GetURLField(t *testing.T) { func TestShoutrrr_GetParam(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - optionRoot *string - optionMain *string - optionDefault *string - optionHardDefault *string - wantString string + env map[string]string + root *string + main *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "this", - optionRoot: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + root: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "main overrides default and hardDefault": { - wantString: "this", - optionRoot: nil, - optionMain: stringPtr("this"), - optionDefault: stringPtr("not_this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + main: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "default overrides hardDefault": { - wantString: "this", - optionRoot: nil, - optionDefault: stringPtr("this"), - optionHardDefault: stringPtr("not_this"), + wantString: "this", + dfault: stringPtr("this"), + hardDefault: stringPtr("not_this"), }, "hardDefault is last resort": { - wantString: "this", - optionRoot: nil, - optionDefault: nil, - optionHardDefault: stringPtr("this"), + wantString: "this", + hardDefault: stringPtr("this"), + }, + "env var is used": { + wantString: "this", + env: map[string]string{"TESTSHOUTRRR_GETPARAM_ONE": "this"}, + root: stringPtr("${TESTSHOUTRRR_GETPARAM_ONE}"), + }, + "empty env var ignored": { + wantString: "that", + root: stringPtr("${TESTSHOUTRRR_GETPARAM_UNSET}"), + dfault: stringPtr("that"), }, } @@ -222,17 +252,21 @@ func TestShoutrrr_GetParam(t *testing.T) { key := "test" shoutrrr := testShoutrrr(false, false) - if tc.optionRoot != nil { - shoutrrr.Params[key] = *tc.optionRoot + if tc.root != nil { + shoutrrr.Params[key] = *tc.root + } + if tc.main != nil { + shoutrrr.Main.Params[key] = *tc.main } - if tc.optionMain != nil { - shoutrrr.Main.Params[key] = *tc.optionMain + if tc.dfault != nil { + shoutrrr.Defaults.Params[key] = *tc.dfault } - if tc.optionDefault != nil { - shoutrrr.Defaults.Params[key] = *tc.optionDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Params[key] = *tc.hardDefault } - if tc.optionHardDefault != nil { - shoutrrr.HardDefaults.Params[key] = *tc.optionHardDefault + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) } // WHEN GetParam is called @@ -261,41 +295,38 @@ func TestShoutrrr_GetParam(t *testing.T) { func TestShoutrrr_GetDelay(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - delayRoot *string - delayMain *string - delayDefault *string - delayHardDefault *string - wantString string + root *string + main *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "1s", - delayRoot: stringPtr("1s"), - delayDefault: stringPtr("2s"), - delayHardDefault: stringPtr("2s"), + wantString: "1s", + root: stringPtr("1s"), + main: stringPtr("2s"), + dfault: stringPtr("2s"), + hardDefault: stringPtr("2s"), }, "main overrides default and hardDefault": { - wantString: "1s", - delayRoot: nil, - delayMain: stringPtr("1s"), - delayDefault: stringPtr("2s"), - delayHardDefault: stringPtr("2s"), + wantString: "1s", + main: stringPtr("1s"), + dfault: stringPtr("2s"), + hardDefault: stringPtr("2s"), }, "default overrides hardDefault": { - wantString: "1s", - delayRoot: nil, - delayDefault: stringPtr("1s"), - delayHardDefault: stringPtr("2s"), + wantString: "1s", + dfault: stringPtr("1s"), + hardDefault: stringPtr("2s"), }, "hardDefault is last resort": { - wantString: "1s", - delayRoot: nil, - delayDefault: nil, - delayHardDefault: stringPtr("1s"), + wantString: "1s", + hardDefault: stringPtr("1s"), }, "no delay anywhere defaults to 0s": {wantString: "0s", - delayRoot: nil, - delayDefault: nil, - delayHardDefault: nil}, + root: nil, + dfault: nil, + hardDefault: nil}, } for name, tc := range tests { @@ -304,17 +335,17 @@ func TestShoutrrr_GetDelay(t *testing.T) { key := "delay" shoutrrr := testShoutrrr(false, false) - if tc.delayRoot != nil { - shoutrrr.Options[key] = *tc.delayRoot + if tc.root != nil { + shoutrrr.Options[key] = *tc.root } - if tc.delayMain != nil { - shoutrrr.Main.Options[key] = *tc.delayMain + if tc.main != nil { + shoutrrr.Main.Options[key] = *tc.main } - if tc.delayDefault != nil { - shoutrrr.Defaults.Options[key] = *tc.delayDefault + if tc.dfault != nil { + shoutrrr.Defaults.Options[key] = *tc.dfault } - if tc.delayHardDefault != nil { - shoutrrr.HardDefaults.Options[key] = *tc.delayHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Options[key] = *tc.hardDefault } // WHEN GetDelay is called @@ -332,36 +363,33 @@ func TestShoutrrr_GetDelay(t *testing.T) { func TestShoutrrr_GetDelayDuration(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - delayRoot *string - delayMain *string - delayDefault *string - delayHardDefault *string - want time.Duration + root *string + main *string + dfault *string + hardDefault *string + want time.Duration }{ "root overrides all": { - want: 1 * time.Second, - delayRoot: stringPtr("1s"), - delayDefault: stringPtr("2s"), - delayHardDefault: stringPtr("2s"), + want: 1 * time.Second, + root: stringPtr("1s"), + main: stringPtr("2s"), + dfault: stringPtr("2s"), + hardDefault: stringPtr("2s"), }, "main overrides default and hardDefault": { - want: 1 * time.Second, - delayRoot: nil, - delayMain: stringPtr("1s"), - delayDefault: stringPtr("2s"), - delayHardDefault: stringPtr("2s"), + want: 1 * time.Second, + main: stringPtr("1s"), + dfault: stringPtr("2s"), + hardDefault: stringPtr("2s"), }, "default overrides hardDefault": { - want: 1 * time.Second, - delayRoot: nil, - delayDefault: stringPtr("1s"), - delayHardDefault: stringPtr("2s"), + want: 1 * time.Second, + dfault: stringPtr("1s"), + hardDefault: stringPtr("2s"), }, "hardDefault is last resort": { - want: 1 * time.Second, - delayRoot: nil, - delayDefault: nil, - delayHardDefault: stringPtr("1s"), + want: 1 * time.Second, + hardDefault: stringPtr("1s"), }, } @@ -371,17 +399,17 @@ func TestShoutrrr_GetDelayDuration(t *testing.T) { key := "delay" shoutrrr := testShoutrrr(false, false) - if tc.delayRoot != nil { - shoutrrr.Options[key] = *tc.delayRoot + if tc.root != nil { + shoutrrr.Options[key] = *tc.root } - if tc.delayMain != nil { - shoutrrr.Main.Options[key] = *tc.delayMain + if tc.main != nil { + shoutrrr.Main.Options[key] = *tc.main } - if tc.delayDefault != nil { - shoutrrr.Defaults.Options[key] = *tc.delayDefault + if tc.dfault != nil { + shoutrrr.Defaults.Options[key] = *tc.dfault } - if tc.delayHardDefault != nil { - shoutrrr.HardDefaults.Options[key] = *tc.delayHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Options[key] = *tc.hardDefault } // WHEN GetDelay is called @@ -408,26 +436,23 @@ func TestShoutrrr_GetMaxTries(t *testing.T) { "root overrides all": { want: 1, maxTriesRoot: stringPtr("1"), + maxTriesMain: stringPtr("2"), maxTriesDefault: stringPtr("2"), maxTriesHardDefault: stringPtr("2"), }, "main overrides default and hardDefault": { want: 1, - maxTriesRoot: nil, maxTriesMain: stringPtr("1"), maxTriesDefault: stringPtr("2"), maxTriesHardDefault: stringPtr("2"), }, "default overrides hardDefault": { want: 1, - maxTriesRoot: nil, maxTriesDefault: stringPtr("1"), maxTriesHardDefault: stringPtr("2"), }, "hardDefault is last resort": { want: 1, - maxTriesRoot: nil, - maxTriesDefault: nil, maxTriesHardDefault: stringPtr("1"), }, } @@ -472,49 +497,46 @@ func TestShoutrrr_Message(t *testing.T) { LatestVersion: "0.9.0", } tests := map[string]struct { - messageRoot *string - messageMain *string - messageDefault *string - messageHardDefault *string - want string + root *string + main *string + dfault *string + hardDefault *string + want string }{ "root overrides all": { - want: "New version!", - messageRoot: stringPtr("New version!"), - messageDefault: stringPtr("something"), - messageHardDefault: stringPtr("something"), + want: "New version!", + root: stringPtr("New version!"), + main: stringPtr("something"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "main overrides default and hardDefault": { - want: "New version!", - messageRoot: nil, - messageMain: stringPtr("New version!"), - messageDefault: stringPtr("something"), - messageHardDefault: stringPtr("something"), + want: "New version!", + main: stringPtr("New version!"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "default overrides hardDefault": { - want: "New version!", - messageRoot: nil, - messageDefault: stringPtr("New version!"), - messageHardDefault: stringPtr("something"), + want: "New version!", + dfault: stringPtr("New version!"), + hardDefault: stringPtr("something"), }, "hardDefault is last resort": { - want: "New version!", - messageRoot: nil, - messageDefault: nil, - messageHardDefault: stringPtr("New version!"), + want: "New version!", + hardDefault: stringPtr("New version!"), }, "jinja templating": { - want: "New version!", - messageRoot: stringPtr("{% if 'a' == 'a' %}New version!{% endif %}"), - messageDefault: stringPtr("something"), - messageHardDefault: stringPtr("something"), + want: "New version!", + root: stringPtr("{% if 'a' == 'a' %}New version!{% endif %}"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "jinja vars": { want: fmt.Sprintf("%s or %s/%s/releases/tag/%s", serviceInfo.WebURL, serviceInfo.URL, serviceInfo.ID, serviceInfo.LatestVersion), - messageRoot: stringPtr("{{ web_url }} or {{ service_url }}/{{ service_id }}/releases/tag/{{ version }}"), - messageDefault: stringPtr("something"), - messageHardDefault: stringPtr("something"), + root: stringPtr("{{ web_url }} or {{ service_url }}/{{ service_id }}/releases/tag/{{ version }}"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, } @@ -524,17 +546,17 @@ func TestShoutrrr_Message(t *testing.T) { key := "message" shoutrrr := testShoutrrr(false, false) - if tc.messageRoot != nil { - shoutrrr.Options[key] = *tc.messageRoot + if tc.root != nil { + shoutrrr.Options[key] = *tc.root } - if tc.messageMain != nil { - shoutrrr.Main.Options[key] = *tc.messageMain + if tc.main != nil { + shoutrrr.Main.Options[key] = *tc.main } - if tc.messageDefault != nil { - shoutrrr.Defaults.Options[key] = *tc.messageDefault + if tc.dfault != nil { + shoutrrr.Defaults.Options[key] = *tc.dfault } - if tc.messageHardDefault != nil { - shoutrrr.HardDefaults.Options[key] = *tc.messageHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Options[key] = *tc.hardDefault } // WHEN Message is called @@ -558,49 +580,46 @@ func TestShoutrrr_Title(t *testing.T) { LatestVersion: "0.9.0", } tests := map[string]struct { - titleRoot *string - titleMain *string - titleDefault *string - titleHardDefault *string - want string + root *string + main *string + dfault *string + hardDefault *string + want string }{ "root overrides all": { - want: "New version!", - titleRoot: stringPtr("New version!"), - titleDefault: stringPtr("something"), - titleHardDefault: stringPtr("something"), + want: "New version!", + root: stringPtr("New version!"), + main: stringPtr("something"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "main overrides default and hardDefault": { - want: "New version!", - titleRoot: nil, - titleMain: stringPtr("New version!"), - titleDefault: stringPtr("something"), - titleHardDefault: stringPtr("something"), + want: "New version!", + main: stringPtr("New version!"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "default overrides hardDefault": { - want: "New version!", - titleRoot: nil, - titleDefault: stringPtr("New version!"), - titleHardDefault: stringPtr("something"), + want: "New version!", + dfault: stringPtr("New version!"), + hardDefault: stringPtr("something"), }, "hardDefault is last resort": { - want: "New version!", - titleRoot: nil, - titleDefault: nil, - titleHardDefault: stringPtr("New version!"), + want: "New version!", + hardDefault: stringPtr("New version!"), }, "jinja templating": { - want: "New version!", - titleRoot: stringPtr("{% if 'a' == 'a' %}New version!{% endif %}"), - titleDefault: stringPtr("something"), - titleHardDefault: stringPtr("something"), + want: "New version!", + root: stringPtr("{% if 'a' == 'a' %}New version!{% endif %}"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, "jinja vars": { want: fmt.Sprintf("%s or %s/%s/releases/tag/%s", serviceInfo.WebURL, serviceInfo.URL, serviceInfo.ID, serviceInfo.LatestVersion), - titleRoot: stringPtr("{{ web_url }} or {{ service_url }}/{{ service_id }}/releases/tag/{{ version }}"), - titleDefault: stringPtr("something"), - titleHardDefault: stringPtr("something"), + root: stringPtr("{{ web_url }} or {{ service_url }}/{{ service_id }}/releases/tag/{{ version }}"), + dfault: stringPtr("something"), + hardDefault: stringPtr("something"), }, } @@ -610,17 +629,17 @@ func TestShoutrrr_Title(t *testing.T) { key := "title" shoutrrr := testShoutrrr(false, false) - if tc.titleRoot != nil { - shoutrrr.Params[key] = *tc.titleRoot + if tc.root != nil { + shoutrrr.Params[key] = *tc.root } - if tc.titleMain != nil { - shoutrrr.Main.Params[key] = *tc.titleMain + if tc.main != nil { + shoutrrr.Main.Params[key] = *tc.main } - if tc.titleDefault != nil { - shoutrrr.Defaults.Params[key] = *tc.titleDefault + if tc.dfault != nil { + shoutrrr.Defaults.Params[key] = *tc.dfault } - if tc.titleHardDefault != nil { - shoutrrr.HardDefaults.Params[key] = *tc.titleHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Params[key] = *tc.hardDefault } // WHEN Title is called @@ -638,36 +657,32 @@ func TestShoutrrr_Title(t *testing.T) { func TestShoutrrr_GetType(t *testing.T) { // GIVEN a Shoutrrr tests := map[string]struct { - typeRoot string - typeMain string - typeDefault string - typeHardDefault string - want string + root string + main string + dfault string + hardDefault string + want string }{ "root overrides all": { - want: "smtp", - typeRoot: "smtp", - typeDefault: "other", - typeHardDefault: "other", + want: "smtp", + root: "smtp", + main: "other", + dfault: "other", + hardDefault: "other", }, "main overrides default and hardDefault": { - want: "smtp", - typeRoot: "", - typeMain: "smtp", - typeDefault: "other", - typeHardDefault: "other", + want: "smtp", + main: "smtp", + dfault: "other", + hardDefault: "other", }, "default is ignored": { // uses ID - want: "test", - typeRoot: "", - typeDefault: "smtp", - typeHardDefault: "", + want: "test", + dfault: "smtp", }, "hardDefault is ignored": { // uses ID - want: "test", - typeRoot: "", - typeDefault: "", - typeHardDefault: "smtp", + want: "test", + hardDefault: "smtp", }, } @@ -676,8 +691,8 @@ func TestShoutrrr_GetType(t *testing.T) { t.Parallel() shoutrrr := testShoutrrr(false, false) - shoutrrr.Type = tc.typeRoot - shoutrrr.Main.Type = tc.typeMain + shoutrrr.Type = tc.root + shoutrrr.Main.Type = tc.main // WHEN GetType is called got := shoutrrr.GetType() diff --git a/notifiers/shoutrrr/shoutrrr_test.go b/notifiers/shoutrrr/shoutrrr_test.go index 25a1c96b..9043e583 100644 --- a/notifiers/shoutrrr/shoutrrr_test.go +++ b/notifiers/shoutrrr/shoutrrr_test.go @@ -533,48 +533,44 @@ func TestShoutrrr_BuildParams(t *testing.T) { LatestVersion: "1.2.3", } tests := map[string]struct { - paramsRoot *string - paramsMain *string - paramsDefault *string - paramsHardDefault *string - wantString string + root *string + main *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "this", - paramsRoot: stringPtr("this"), - paramsDefault: stringPtr("not_this"), - paramsHardDefault: stringPtr("not_this"), + wantString: "this", + root: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "main overrides default and hardDefault": { - wantString: "this", - paramsRoot: nil, - paramsMain: stringPtr("this"), - paramsDefault: stringPtr("not_this"), - paramsHardDefault: stringPtr("not_this"), + wantString: "this", + main: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "default overrides hardDefault": { - wantString: "this", - paramsRoot: nil, - paramsDefault: stringPtr("this"), - paramsHardDefault: stringPtr("not_this"), + wantString: "this", + dfault: stringPtr("this"), + hardDefault: stringPtr("not_this"), }, "hardDefault is last resort": { - wantString: "this", - paramsRoot: nil, - paramsDefault: nil, - paramsHardDefault: stringPtr("this"), + wantString: "this", + hardDefault: stringPtr("this"), }, "jinja templating": { - wantString: "this", - paramsRoot: stringPtr("{% if 'a' == 'a' %}this{% endif %}"), - paramsDefault: stringPtr("not_this"), - paramsHardDefault: stringPtr("not_this"), + wantString: "this", + root: stringPtr("{% if 'a' == 'a' %}this{% endif %}"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, "jinja vars": { - wantString: fmt.Sprintf("foo%s-%s", serviceInfo.ID, serviceInfo.LatestVersion), - paramsRoot: stringPtr("foo{{ service_id }}-{{ version }}"), - paramsDefault: stringPtr("not_this"), - paramsHardDefault: stringPtr("not_this"), + wantString: fmt.Sprintf("foo%s-%s", serviceInfo.ID, serviceInfo.LatestVersion), + root: stringPtr("foo{{ service_id }}-{{ version }}"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this"), }, } @@ -584,17 +580,17 @@ func TestShoutrrr_BuildParams(t *testing.T) { key := "test" shoutrrr := testShoutrrr(false, false) - if tc.paramsRoot != nil { - shoutrrr.Params[key] = *tc.paramsRoot + if tc.root != nil { + shoutrrr.Params[key] = *tc.root } - if tc.paramsMain != nil { - shoutrrr.Main.Params[key] = *tc.paramsMain + if tc.main != nil { + shoutrrr.Main.Params[key] = *tc.main } - if tc.paramsDefault != nil { - shoutrrr.Defaults.Params[key] = *tc.paramsDefault + if tc.dfault != nil { + shoutrrr.Defaults.Params[key] = *tc.dfault } - if tc.paramsHardDefault != nil { - shoutrrr.HardDefaults.Params[key] = *tc.paramsHardDefault + if tc.hardDefault != nil { + shoutrrr.HardDefaults.Params[key] = *tc.hardDefault } // WHEN BuildParams is called diff --git a/package-lock.json b/package-lock.json index a29034c9..bff7026c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "0.15.2", "license": "ISC", "dependencies": { - "@commitlint/cli": "^18.6.0", - "@commitlint/config-conventional": "^18.6.0" + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.2" }, "devDependencies": { - "husky": "^9.0.10", + "husky": "^9.0.11", "standard-version": "^9.5.0" } }, @@ -106,15 +106,15 @@ } }, "node_modules/@commitlint/cli": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.6.0.tgz", - "integrity": "sha512-FiH23cr9QG8VdfbmvJJZmdfHGVMCouOOAzoXZ3Cd7czGC52RbycwNt8YCI7SA69pAl+t30vh8LMaO/N+kcel6w==", - "dependencies": { - "@commitlint/format": "^18.6.0", - "@commitlint/lint": "^18.6.0", - "@commitlint/load": "^18.6.0", - "@commitlint/read": "^18.6.0", - "@commitlint/types": "^18.6.0", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.6.1.tgz", + "integrity": "sha512-5IDE0a+lWGdkOvKH892HHAZgbAjcj1mT5QrfA/SVbLJV/BbBMGyKN0W5mhgjekPJJwEQdVNvhl9PwUacY58Usw==", + "dependencies": { + "@commitlint/format": "^18.6.1", + "@commitlint/lint": "^18.6.1", + "@commitlint/load": "^18.6.1", + "@commitlint/read": "^18.6.1", + "@commitlint/types": "^18.6.1", "execa": "^5.0.0", "lodash.isfunction": "^3.0.9", "resolve-from": "5.0.0", @@ -129,10 +129,11 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.6.0.tgz", - "integrity": "sha512-CDCOf2eJz9D/TL44IBks0stM9TmdLCNE2B48owIU3YCadwzts/bobXPScagIgPQF6hhKYMEdj5zpUDlmbwuqwQ==", + "version": "18.6.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.6.2.tgz", + "integrity": "sha512-PcgSYg1AKGQIwDQKbaHtJsfqYy4uJTC7crLVZ83lfjcPaec4Pry2vLeaWej7ao2KsT20l9dWoMPpEGg8LWdUuA==", "dependencies": { + "@commitlint/types": "^18.6.1", "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { @@ -151,11 +152,11 @@ } }, "node_modules/@commitlint/config-validator": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.6.0.tgz", - "integrity": "sha512-Ptfa865arNozlkjxrYG3qt6wT9AlhNUHeuDyKEZiTL/l0ftncFhK/KN0t/EAMV2tec+0Mwxo0FmhbESj/bI+1g==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.6.1.tgz", + "integrity": "sha512-05uiToBVfPhepcQWE1ZQBR/Io3+tb3gEotZjnI4tTzzPk16NffN6YABgwFQCLmzZefbDcmwWqJWc2XT47q7Znw==", "dependencies": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "ajv": "^8.11.0" }, "engines": { @@ -163,11 +164,11 @@ } }, "node_modules/@commitlint/ensure": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.6.0.tgz", - "integrity": "sha512-xY07NmOBJ7JuhX3tic021PaeLepZARIQyqpAQoNQZoml1keBFfB6MbA7XlWZv0ebbarUFE4yhKxOPw+WFv7/qw==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.6.1.tgz", + "integrity": "sha512-BPm6+SspyxQ7ZTsZwXc7TRQL5kh5YWt3euKmEIBZnocMFkJevqs3fbLRb8+8I/cfbVcAo4mxRlpTPfz8zX7SnQ==", "dependencies": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -179,19 +180,19 @@ } }, "node_modules/@commitlint/execute-rule": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.4.4.tgz", - "integrity": "sha512-a37Nd3bDQydtg9PCLLWM9ZC+GO7X5i4zJvrggJv5jBhaHsXeQ9ZWdO6ODYR+f0LxBXXNYK3geYXJrCWUCP8JEg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.6.1.tgz", + "integrity": "sha512-7s37a+iWyJiGUeMFF6qBlyZciUkF8odSAnHijbD36YDctLhGKoYltdvuJ/AFfRm6cBLRtRk9cCVPdsEFtt/2rg==", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/format": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.6.0.tgz", - "integrity": "sha512-8UNWfs2slPPSQiiVpLGJTnPHv7Jkd5KYxfbNXbmLL583bjom4RrylvyrCVnmZReA8nNad7pPXq6mDH4FNVj6xg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.6.1.tgz", + "integrity": "sha512-K8mNcfU/JEFCharj2xVjxGSF+My+FbUHoqR+4GqPGrHNqXOGNio47ziiR4HQUPKtiNs05o8/WyLBoIpMVOP7wg==", "dependencies": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "chalk": "^4.1.0" }, "engines": { @@ -199,40 +200,40 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.6.0.tgz", - "integrity": "sha512-Xjx/ZyyJ4FdLuz0FcOvqiqSFgiO2yYj3QN9XlvyrxqbXTxPVC7QFEXJYBVPulUSN/gR7WXH1Udw+HYYfD17xog==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.6.1.tgz", + "integrity": "sha512-MOfJjkEJj/wOaPBw5jFjTtfnx72RGwqYIROABudOtJKW7isVjFe9j0t8xhceA02QebtYf4P/zea4HIwnXg8rvA==", "dependencies": { - "@commitlint/types": "^18.6.0", - "semver": "7.5.4" + "@commitlint/types": "^18.6.1", + "semver": "7.6.0" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/lint": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.6.0.tgz", - "integrity": "sha512-ycbuDWfyykPmslgiHzhz8dL6F0BJYltXLVfc+M49z0c+FNITM0v+r0Vd2+Tdtq06VTc894p2+YSmZhulY8Jn3Q==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.6.1.tgz", + "integrity": "sha512-8WwIFo3jAuU+h1PkYe5SfnIOzp+TtBHpFr4S8oJWhu44IWKuVx6GOPux3+9H1iHOan/rGBaiacicZkMZuluhfQ==", "dependencies": { - "@commitlint/is-ignored": "^18.6.0", - "@commitlint/parse": "^18.6.0", - "@commitlint/rules": "^18.6.0", - "@commitlint/types": "^18.6.0" + "@commitlint/is-ignored": "^18.6.1", + "@commitlint/parse": "^18.6.1", + "@commitlint/rules": "^18.6.1", + "@commitlint/types": "^18.6.1" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.6.0.tgz", - "integrity": "sha512-RRssj7TmzT0bowoEKlgwg8uQ7ORXWkw7lYLsZZBMi9aInsJuGNLNWcMxJxRZbwxG3jkCidGUg85WmqJvRjsaDA==", - "dependencies": { - "@commitlint/config-validator": "^18.6.0", - "@commitlint/execute-rule": "^18.4.4", - "@commitlint/resolve-extends": "^18.6.0", - "@commitlint/types": "^18.6.0", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.6.1.tgz", + "integrity": "sha512-p26x8734tSXUHoAw0ERIiHyW4RaI4Bj99D8YgUlVV9SedLf8hlWAfyIFhHRIhfPngLlCe0QYOdRKYFt8gy56TA==", + "dependencies": { + "@commitlint/config-validator": "^18.6.1", + "@commitlint/execute-rule": "^18.6.1", + "@commitlint/resolve-extends": "^18.6.1", + "@commitlint/types": "^18.6.1", "chalk": "^4.1.0", "cosmiconfig": "^8.3.6", "cosmiconfig-typescript-loader": "^5.0.0", @@ -246,19 +247,19 @@ } }, "node_modules/@commitlint/message": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.4.4.tgz", - "integrity": "sha512-lHF95mMDYgAI1LBXveJUyg4eLaMXyOqJccCK3v55ZOEUsMPrDi8upqDjd/NmzWmESYihaOMBTAnxm+6oD1WoDQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.6.1.tgz", + "integrity": "sha512-VKC10UTMLcpVjMIaHHsY1KwhuTQtdIKPkIdVEwWV+YuzKkzhlI3aNy6oo1eAN6b/D2LTtZkJe2enHmX0corYRw==", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/parse": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.6.0.tgz", - "integrity": "sha512-Y/G++GJpATFw54O0jikc/h2ibyGHgghtPnwsOk3O/aU092ydJ5XEHYcd7xGNQYuLweLzQis2uEwRNk9AVIPbQQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.6.1.tgz", + "integrity": "sha512-eS/3GREtvVJqGZrwAGRwR9Gdno3YcZ6Xvuaa+vUF8j++wsmxrA2En3n0ccfVO2qVOLJC41ni7jSZhQiJpMPGOQ==", "dependencies": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" }, @@ -336,12 +337,12 @@ } }, "node_modules/@commitlint/read": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.6.0.tgz", - "integrity": "sha512-w39ji8VfWhPKRquPhRHB3Yd8XIHwaNHgOh28YI1QEmZ59qVpuVUQo6h/NsVb+uoC6LbXZiofTZv2iFR084jKEA==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.6.1.tgz", + "integrity": "sha512-ia6ODaQFzXrVul07ffSgbZGFajpe8xhnDeLIprLeyfz3ivQU1dIoHp7yz0QIorZ6yuf4nlzg4ZUkluDrGN/J/w==", "dependencies": { - "@commitlint/top-level": "^18.4.4", - "@commitlint/types": "^18.6.0", + "@commitlint/top-level": "^18.6.1", + "@commitlint/types": "^18.6.1", "git-raw-commits": "^2.0.11", "minimist": "^1.2.6" }, @@ -350,12 +351,12 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.6.0.tgz", - "integrity": "sha512-k2Xp+Fxeggki2i90vGrbiLDMefPius3zGSTFFlRAPKce/SWLbZtI+uqE9Mne23mHO5lmcSV8z5m6ziiJwGpOcg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.6.1.tgz", + "integrity": "sha512-ifRAQtHwK+Gj3Bxj/5chhc4L2LIc3s30lpsyW67yyjsETR6ctHAHRu1FSpt0KqahK5xESqoJ92v6XxoDRtjwEQ==", "dependencies": { - "@commitlint/config-validator": "^18.6.0", - "@commitlint/types": "^18.6.0", + "@commitlint/config-validator": "^18.6.1", + "@commitlint/types": "^18.6.1", "import-fresh": "^3.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0", @@ -366,14 +367,14 @@ } }, "node_modules/@commitlint/rules": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.6.0.tgz", - "integrity": "sha512-pTalvCEvuCWrBWZA/YqO/3B3nZnY3Ncc+TmQsRajBdC1tkQIm5Iovdo4Ec7f2Dw1tVvpYMUUNAgcWqsY0WckWg==", - "dependencies": { - "@commitlint/ensure": "^18.6.0", - "@commitlint/message": "^18.4.4", - "@commitlint/to-lines": "^18.4.4", - "@commitlint/types": "^18.6.0", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.6.1.tgz", + "integrity": "sha512-kguM6HxZDtz60v/zQYOe0voAtTdGybWXefA1iidjWYmyUUspO1zBPQEmJZ05/plIAqCVyNUTAiRPWIBKLCrGew==", + "dependencies": { + "@commitlint/ensure": "^18.6.1", + "@commitlint/message": "^18.6.1", + "@commitlint/to-lines": "^18.6.1", + "@commitlint/types": "^18.6.1", "execa": "^5.0.0" }, "engines": { @@ -381,17 +382,17 @@ } }, "node_modules/@commitlint/to-lines": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.4.4.tgz", - "integrity": "sha512-mwe2Roa59NCz/krniAdCygFabg7+fQCkIhXqBHw00XQ8Y7lw4poZLLxeGI3p3bLpcEOXdqIDrEGLwHmG5lBdwQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.6.1.tgz", + "integrity": "sha512-Gl+orGBxYSNphx1+83GYeNy5N0dQsHBQ9PJMriaLQDB51UQHCVLBT/HBdOx5VaYksivSf5Os55TLePbRLlW50Q==", "engines": { "node": ">=v18" } }, "node_modules/@commitlint/top-level": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.4.4.tgz", - "integrity": "sha512-PBwW1drgeavl9CadB7IPRUk6rkUP/O8jEkxjlC+ofuh3pw0bzJdAT+Kw7M1Yc9KtTb9xTaqUB8uvRtaybHa/tQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.6.1.tgz", + "integrity": "sha512-HyiHQZUTf0+r0goTCDs/bbVv/LiiQ7AVtz6KIar+8ZrseB9+YJAIo8HQ2IC2QT1y3N1lbW6OqVEsTHjbT6hGSw==", "dependencies": { "find-up": "^5.0.0" }, @@ -400,9 +401,9 @@ } }, "node_modules/@commitlint/types": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.6.0.tgz", - "integrity": "sha512-oavoKLML/eJa2rJeyYSbyGAYzTxQ6voG5oeX3OrxpfrkRWhJfm4ACnhoRf5tgiybx2MZ+EVFqC1Lw3W8/uwpZA==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.6.1.tgz", + "integrity": "sha512-gwRLBLra/Dozj2OywopeuHj2ac26gjGkz2cZ+86cTJOdtWfiRRr4+e77ZDAGc6MDWxaWheI+mAV5TLWWRwqrFg==", "dependencies": { "chalk": "^4.1.0" }, @@ -425,9 +426,9 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" }, "node_modules/@types/node": { - "version": "20.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", - "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", "peer": true, "dependencies": { "undici-types": "~5.26.4" @@ -1571,9 +1572,9 @@ } }, "node_modules/husky": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", - "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true, "bin": { "husky": "bin.mjs" @@ -2393,9 +2394,9 @@ ] }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2958,15 +2959,15 @@ } }, "@commitlint/cli": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.6.0.tgz", - "integrity": "sha512-FiH23cr9QG8VdfbmvJJZmdfHGVMCouOOAzoXZ3Cd7czGC52RbycwNt8YCI7SA69pAl+t30vh8LMaO/N+kcel6w==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.6.1.tgz", + "integrity": "sha512-5IDE0a+lWGdkOvKH892HHAZgbAjcj1mT5QrfA/SVbLJV/BbBMGyKN0W5mhgjekPJJwEQdVNvhl9PwUacY58Usw==", "requires": { - "@commitlint/format": "^18.6.0", - "@commitlint/lint": "^18.6.0", - "@commitlint/load": "^18.6.0", - "@commitlint/read": "^18.6.0", - "@commitlint/types": "^18.6.0", + "@commitlint/format": "^18.6.1", + "@commitlint/lint": "^18.6.1", + "@commitlint/load": "^18.6.1", + "@commitlint/read": "^18.6.1", + "@commitlint/types": "^18.6.1", "execa": "^5.0.0", "lodash.isfunction": "^3.0.9", "resolve-from": "5.0.0", @@ -2975,10 +2976,11 @@ } }, "@commitlint/config-conventional": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.6.0.tgz", - "integrity": "sha512-CDCOf2eJz9D/TL44IBks0stM9TmdLCNE2B48owIU3YCadwzts/bobXPScagIgPQF6hhKYMEdj5zpUDlmbwuqwQ==", + "version": "18.6.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.6.2.tgz", + "integrity": "sha512-PcgSYg1AKGQIwDQKbaHtJsfqYy4uJTC7crLVZ83lfjcPaec4Pry2vLeaWej7ao2KsT20l9dWoMPpEGg8LWdUuA==", "requires": { + "@commitlint/types": "^18.6.1", "conventional-changelog-conventionalcommits": "^7.0.2" }, "dependencies": { @@ -2993,20 +2995,20 @@ } }, "@commitlint/config-validator": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.6.0.tgz", - "integrity": "sha512-Ptfa865arNozlkjxrYG3qt6wT9AlhNUHeuDyKEZiTL/l0ftncFhK/KN0t/EAMV2tec+0Mwxo0FmhbESj/bI+1g==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.6.1.tgz", + "integrity": "sha512-05uiToBVfPhepcQWE1ZQBR/Io3+tb3gEotZjnI4tTzzPk16NffN6YABgwFQCLmzZefbDcmwWqJWc2XT47q7Znw==", "requires": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "ajv": "^8.11.0" } }, "@commitlint/ensure": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.6.0.tgz", - "integrity": "sha512-xY07NmOBJ7JuhX3tic021PaeLepZARIQyqpAQoNQZoml1keBFfB6MbA7XlWZv0ebbarUFE4yhKxOPw+WFv7/qw==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.6.1.tgz", + "integrity": "sha512-BPm6+SspyxQ7ZTsZwXc7TRQL5kh5YWt3euKmEIBZnocMFkJevqs3fbLRb8+8I/cfbVcAo4mxRlpTPfz8zX7SnQ==", "requires": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -3015,48 +3017,48 @@ } }, "@commitlint/execute-rule": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.4.4.tgz", - "integrity": "sha512-a37Nd3bDQydtg9PCLLWM9ZC+GO7X5i4zJvrggJv5jBhaHsXeQ9ZWdO6ODYR+f0LxBXXNYK3geYXJrCWUCP8JEg==" + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.6.1.tgz", + "integrity": "sha512-7s37a+iWyJiGUeMFF6qBlyZciUkF8odSAnHijbD36YDctLhGKoYltdvuJ/AFfRm6cBLRtRk9cCVPdsEFtt/2rg==" }, "@commitlint/format": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.6.0.tgz", - "integrity": "sha512-8UNWfs2slPPSQiiVpLGJTnPHv7Jkd5KYxfbNXbmLL583bjom4RrylvyrCVnmZReA8nNad7pPXq6mDH4FNVj6xg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.6.1.tgz", + "integrity": "sha512-K8mNcfU/JEFCharj2xVjxGSF+My+FbUHoqR+4GqPGrHNqXOGNio47ziiR4HQUPKtiNs05o8/WyLBoIpMVOP7wg==", "requires": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "chalk": "^4.1.0" } }, "@commitlint/is-ignored": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.6.0.tgz", - "integrity": "sha512-Xjx/ZyyJ4FdLuz0FcOvqiqSFgiO2yYj3QN9XlvyrxqbXTxPVC7QFEXJYBVPulUSN/gR7WXH1Udw+HYYfD17xog==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.6.1.tgz", + "integrity": "sha512-MOfJjkEJj/wOaPBw5jFjTtfnx72RGwqYIROABudOtJKW7isVjFe9j0t8xhceA02QebtYf4P/zea4HIwnXg8rvA==", "requires": { - "@commitlint/types": "^18.6.0", - "semver": "7.5.4" + "@commitlint/types": "^18.6.1", + "semver": "7.6.0" } }, "@commitlint/lint": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.6.0.tgz", - "integrity": "sha512-ycbuDWfyykPmslgiHzhz8dL6F0BJYltXLVfc+M49z0c+FNITM0v+r0Vd2+Tdtq06VTc894p2+YSmZhulY8Jn3Q==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.6.1.tgz", + "integrity": "sha512-8WwIFo3jAuU+h1PkYe5SfnIOzp+TtBHpFr4S8oJWhu44IWKuVx6GOPux3+9H1iHOan/rGBaiacicZkMZuluhfQ==", "requires": { - "@commitlint/is-ignored": "^18.6.0", - "@commitlint/parse": "^18.6.0", - "@commitlint/rules": "^18.6.0", - "@commitlint/types": "^18.6.0" + "@commitlint/is-ignored": "^18.6.1", + "@commitlint/parse": "^18.6.1", + "@commitlint/rules": "^18.6.1", + "@commitlint/types": "^18.6.1" } }, "@commitlint/load": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.6.0.tgz", - "integrity": "sha512-RRssj7TmzT0bowoEKlgwg8uQ7ORXWkw7lYLsZZBMi9aInsJuGNLNWcMxJxRZbwxG3jkCidGUg85WmqJvRjsaDA==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.6.1.tgz", + "integrity": "sha512-p26x8734tSXUHoAw0ERIiHyW4RaI4Bj99D8YgUlVV9SedLf8hlWAfyIFhHRIhfPngLlCe0QYOdRKYFt8gy56TA==", "requires": { - "@commitlint/config-validator": "^18.6.0", - "@commitlint/execute-rule": "^18.4.4", - "@commitlint/resolve-extends": "^18.6.0", - "@commitlint/types": "^18.6.0", + "@commitlint/config-validator": "^18.6.1", + "@commitlint/execute-rule": "^18.6.1", + "@commitlint/resolve-extends": "^18.6.1", + "@commitlint/types": "^18.6.1", "chalk": "^4.1.0", "cosmiconfig": "^8.3.6", "cosmiconfig-typescript-loader": "^5.0.0", @@ -3067,16 +3069,16 @@ } }, "@commitlint/message": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.4.4.tgz", - "integrity": "sha512-lHF95mMDYgAI1LBXveJUyg4eLaMXyOqJccCK3v55ZOEUsMPrDi8upqDjd/NmzWmESYihaOMBTAnxm+6oD1WoDQ==" + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.6.1.tgz", + "integrity": "sha512-VKC10UTMLcpVjMIaHHsY1KwhuTQtdIKPkIdVEwWV+YuzKkzhlI3aNy6oo1eAN6b/D2LTtZkJe2enHmX0corYRw==" }, "@commitlint/parse": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.6.0.tgz", - "integrity": "sha512-Y/G++GJpATFw54O0jikc/h2ibyGHgghtPnwsOk3O/aU092ydJ5XEHYcd7xGNQYuLweLzQis2uEwRNk9AVIPbQQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.6.1.tgz", + "integrity": "sha512-eS/3GREtvVJqGZrwAGRwR9Gdno3YcZ6Xvuaa+vUF8j++wsmxrA2En3n0ccfVO2qVOLJC41ni7jSZhQiJpMPGOQ==", "requires": { - "@commitlint/types": "^18.6.0", + "@commitlint/types": "^18.6.1", "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" }, @@ -3126,23 +3128,23 @@ } }, "@commitlint/read": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.6.0.tgz", - "integrity": "sha512-w39ji8VfWhPKRquPhRHB3Yd8XIHwaNHgOh28YI1QEmZ59qVpuVUQo6h/NsVb+uoC6LbXZiofTZv2iFR084jKEA==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.6.1.tgz", + "integrity": "sha512-ia6ODaQFzXrVul07ffSgbZGFajpe8xhnDeLIprLeyfz3ivQU1dIoHp7yz0QIorZ6yuf4nlzg4ZUkluDrGN/J/w==", "requires": { - "@commitlint/top-level": "^18.4.4", - "@commitlint/types": "^18.6.0", + "@commitlint/top-level": "^18.6.1", + "@commitlint/types": "^18.6.1", "git-raw-commits": "^2.0.11", "minimist": "^1.2.6" } }, "@commitlint/resolve-extends": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.6.0.tgz", - "integrity": "sha512-k2Xp+Fxeggki2i90vGrbiLDMefPius3zGSTFFlRAPKce/SWLbZtI+uqE9Mne23mHO5lmcSV8z5m6ziiJwGpOcg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.6.1.tgz", + "integrity": "sha512-ifRAQtHwK+Gj3Bxj/5chhc4L2LIc3s30lpsyW67yyjsETR6ctHAHRu1FSpt0KqahK5xESqoJ92v6XxoDRtjwEQ==", "requires": { - "@commitlint/config-validator": "^18.6.0", - "@commitlint/types": "^18.6.0", + "@commitlint/config-validator": "^18.6.1", + "@commitlint/types": "^18.6.1", "import-fresh": "^3.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0", @@ -3150,34 +3152,34 @@ } }, "@commitlint/rules": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.6.0.tgz", - "integrity": "sha512-pTalvCEvuCWrBWZA/YqO/3B3nZnY3Ncc+TmQsRajBdC1tkQIm5Iovdo4Ec7f2Dw1tVvpYMUUNAgcWqsY0WckWg==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.6.1.tgz", + "integrity": "sha512-kguM6HxZDtz60v/zQYOe0voAtTdGybWXefA1iidjWYmyUUspO1zBPQEmJZ05/plIAqCVyNUTAiRPWIBKLCrGew==", "requires": { - "@commitlint/ensure": "^18.6.0", - "@commitlint/message": "^18.4.4", - "@commitlint/to-lines": "^18.4.4", - "@commitlint/types": "^18.6.0", + "@commitlint/ensure": "^18.6.1", + "@commitlint/message": "^18.6.1", + "@commitlint/to-lines": "^18.6.1", + "@commitlint/types": "^18.6.1", "execa": "^5.0.0" } }, "@commitlint/to-lines": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.4.4.tgz", - "integrity": "sha512-mwe2Roa59NCz/krniAdCygFabg7+fQCkIhXqBHw00XQ8Y7lw4poZLLxeGI3p3bLpcEOXdqIDrEGLwHmG5lBdwQ==" + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.6.1.tgz", + "integrity": "sha512-Gl+orGBxYSNphx1+83GYeNy5N0dQsHBQ9PJMriaLQDB51UQHCVLBT/HBdOx5VaYksivSf5Os55TLePbRLlW50Q==" }, "@commitlint/top-level": { - "version": "18.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.4.4.tgz", - "integrity": "sha512-PBwW1drgeavl9CadB7IPRUk6rkUP/O8jEkxjlC+ofuh3pw0bzJdAT+Kw7M1Yc9KtTb9xTaqUB8uvRtaybHa/tQ==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.6.1.tgz", + "integrity": "sha512-HyiHQZUTf0+r0goTCDs/bbVv/LiiQ7AVtz6KIar+8ZrseB9+YJAIo8HQ2IC2QT1y3N1lbW6OqVEsTHjbT6hGSw==", "requires": { "find-up": "^5.0.0" } }, "@commitlint/types": { - "version": "18.6.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.6.0.tgz", - "integrity": "sha512-oavoKLML/eJa2rJeyYSbyGAYzTxQ6voG5oeX3OrxpfrkRWhJfm4ACnhoRf5tgiybx2MZ+EVFqC1Lw3W8/uwpZA==", + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.6.1.tgz", + "integrity": "sha512-gwRLBLra/Dozj2OywopeuHj2ac26gjGkz2cZ+86cTJOdtWfiRRr4+e77ZDAGc6MDWxaWheI+mAV5TLWWRwqrFg==", "requires": { "chalk": "^4.1.0" } @@ -3194,9 +3196,9 @@ "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" }, "@types/node": { - "version": "20.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz", - "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", "peer": true, "requires": { "undici-types": "~5.26.4" @@ -4065,9 +4067,9 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, "husky": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", - "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true }, "import-fresh": { @@ -4661,9 +4663,9 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "requires": { "lru-cache": "^6.0.0" } diff --git a/package.json b/package.json index b5c05c8d..afa37816 100644 --- a/package.json +++ b/package.json @@ -5,17 +5,17 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "prepare": "husky install", + "prepare": "husky", "release": "standard-version --tag-prefix '' --sign" }, "author": "", "license": "ISC", "devDependencies": { - "husky": "^9.0.10", + "husky": "^9.0.11", "standard-version": "^9.5.0" }, "dependencies": { - "@commitlint/cli": "^18.6.0", - "@commitlint/config-conventional": "^18.6.0" + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.2" } } diff --git a/service/dashboard_test.go b/service/dashboard_test.go index 5e710c35..0e276368 100644 --- a/service/dashboard_test.go +++ b/service/dashboard_test.go @@ -27,45 +27,42 @@ import ( func TestDashboardOptions_GetAutoApprove(t *testing.T) { // GIVEN a DashboardOptions tests := map[string]struct { - autoApproveRoot *bool - autoApproveDefault *bool - autoApproveHardDefault *bool - wantBool bool + root *bool + dfault *bool + hardDefault *bool + want bool }{ "root overrides all": { - wantBool: true, - autoApproveRoot: boolPtr(true), - autoApproveDefault: boolPtr(false), - autoApproveHardDefault: boolPtr(false)}, + want: true, + root: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false)}, "default overrides hardDefault": { - wantBool: true, - autoApproveRoot: nil, - autoApproveDefault: boolPtr(true), - autoApproveHardDefault: boolPtr(false)}, + want: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false)}, "hardDefault is last resort": { - wantBool: true, - autoApproveRoot: nil, - autoApproveDefault: nil, - autoApproveHardDefault: boolPtr(true)}, + want: true, + hardDefault: boolPtr(true)}, } for name, tc := range tests { t.Run(name, func(t *testing.T) { dashboard := DashboardOptions{} - dashboard.AutoApprove = tc.autoApproveRoot - defaults := NewDashboardOptionsDefaults(tc.autoApproveDefault) + dashboard.AutoApprove = tc.root + defaults := NewDashboardOptionsDefaults(tc.dfault) dashboard.Defaults = &defaults - hardDefaults := NewDashboardOptionsDefaults(tc.autoApproveHardDefault) + hardDefaults := NewDashboardOptionsDefaults(tc.hardDefault) dashboard.HardDefaults = &hardDefaults // WHEN GetAutoApprove is called got := dashboard.GetAutoApprove() // THEN the function returns the correct result - if got != tc.wantBool { + if got != tc.want { t.Errorf("want: %t\ngot: %t", - tc.wantBool, got) + tc.want, got) } }) } diff --git a/service/deployed_version/gets.go b/service/deployed_version/gets.go index f30466f2..24cd766a 100644 --- a/service/deployed_version/gets.go +++ b/service/deployed_version/gets.go @@ -25,3 +25,8 @@ func (l *Lookup) GetAllowInvalidCerts() bool { l.Defaults.AllowInvalidCerts, l.HardDefaults.AllowInvalidCerts) } + +// GetURL will return the URL of the Lookup. +func (l *Lookup) GetURL() string { + return util.StringWithEnv(l.URL) +} diff --git a/service/deployed_version/gets_test.go b/service/deployed_version/gets_test.go index 54d11fe0..34c92eb0 100644 --- a/service/deployed_version/gets_test.go +++ b/service/deployed_version/gets_test.go @@ -17,32 +17,30 @@ package deployedver import ( + "os" "testing" ) func TestLookup_GetAllowInvalidCerts(t *testing.T) { // GIVEN a Lookup tests := map[string]struct { - allowInvalidCertsRoot *bool - allowInvalidCertsDefault *bool - allowInvalidCertsHardDefault *bool - wantBool bool + root *bool + dfault *bool + hardDefault *bool + wantBool bool }{ "root overrides all": { - wantBool: true, - allowInvalidCertsRoot: boolPtr(true), - allowInvalidCertsDefault: boolPtr(false), - allowInvalidCertsHardDefault: boolPtr(false)}, + wantBool: true, + root: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false)}, "default overrides hardDefault": { - wantBool: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsDefault: boolPtr(true), - allowInvalidCertsHardDefault: boolPtr(false)}, + wantBool: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false)}, "hardDefault is last resort": { - wantBool: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsDefault: nil, - allowInvalidCertsHardDefault: boolPtr(true)}, + wantBool: true, + hardDefault: boolPtr(true)}, } for name, tc := range tests { @@ -50,9 +48,9 @@ func TestLookup_GetAllowInvalidCerts(t *testing.T) { t.Parallel() lookup := testLookup() - lookup.AllowInvalidCerts = tc.allowInvalidCertsRoot - lookup.Defaults.AllowInvalidCerts = tc.allowInvalidCertsDefault - lookup.HardDefaults.AllowInvalidCerts = tc.allowInvalidCertsHardDefault + lookup.AllowInvalidCerts = tc.root + lookup.Defaults.AllowInvalidCerts = tc.dfault + lookup.HardDefaults.AllowInvalidCerts = tc.hardDefault // WHEN GetAllowInvalidCerts is called got := lookup.GetAllowInvalidCerts() @@ -65,3 +63,45 @@ func TestLookup_GetAllowInvalidCerts(t *testing.T) { }) } } + +func TestLookup_GetURL(t *testing.T) { + // GIVEN a Lookup + tests := map[string]struct { + env map[string]string + url string + want string + }{ + "returns URL": { + url: "https://example.com", + want: "https://example.com", + }, + "returns URL from env": { + env: map[string]string{"TESTLOOKUP_DV_GETURL_ONE": "https://example.com"}, + url: "${TESTLOOKUP_DV_GETURL_ONE}", + want: "https://example.com", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + + lookup := testLookup() + lookup.URL = tc.url + + // WHEN GetURL is called + got := lookup.GetURL() + + // THEN the function returns the correct result + if got != tc.want { + t.Errorf("want: %q\ngot: %q", + tc.want, got) + } + }) + } +} diff --git a/service/deployed_version/query.go b/service/deployed_version/query.go index f4b83650..6bb01b47 100644 --- a/service/deployed_version/query.go +++ b/service/deployed_version/query.go @@ -61,7 +61,7 @@ func (l *Lookup) query(logFrom *util.LogFrom) (string, error) { var version string // If JSON is provided, use it to extract the version. if l.JSON != "" { - version, err = util.GetValueByKey(rawBody, l.JSON, l.URL) + version, err = util.GetValueByKey(rawBody, l.JSON, l.GetURL()) if err != nil { jLog.Error(err, *logFrom, true) //nolint:wrapcheck @@ -189,7 +189,7 @@ func (l *Lookup) httpRequest(logFrom *util.LogFrom) (rawBody []byte, err error) customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } - req, err := http.NewRequest(http.MethodGet, l.URL, nil) + req, err := http.NewRequest(http.MethodGet, l.GetURL(), nil) if err != nil { jLog.Error(err, *logFrom, true) return @@ -198,12 +198,12 @@ func (l *Lookup) httpRequest(logFrom *util.LogFrom) (rawBody []byte, err error) // Set headers req.Header.Set("Connection", "close") for _, header := range l.Headers { - req.Header.Set(header.Key, header.Value) + req.Header.Set(util.StringWithEnv(header.Key), util.StringWithEnv(header.Value)) } // Basic auth if l.BasicAuth != nil { - req.SetBasicAuth(l.BasicAuth.Username, l.BasicAuth.Password) + req.SetBasicAuth(util.StringWithEnv(l.BasicAuth.Username), util.StringWithEnv(l.BasicAuth.Password)) } client := &http.Client{Transport: customTransport} diff --git a/service/deployed_version/query_test.go b/service/deployed_version/query_test.go index 662cfe57..a27b5a49 100644 --- a/service/deployed_version/query_test.go +++ b/service/deployed_version/query_test.go @@ -34,6 +34,7 @@ import ( func TestLookup_HTTPRequest(t *testing.T) { // GIVEN a Lookup() tests := map[string]struct { + env map[string]string url string errRegex string }{ @@ -46,6 +47,10 @@ func TestLookup_HTTPRequest(t *testing.T) { "valid url": { url: "https://release-argus.io", errRegex: "^$"}, + "url from env": { + env: map[string]string{"TESTLOOKUP_DV_HTTPREQUEST_ONE": "https://release-argus.io"}, + url: "${TESTLOOKUP_DV_HTTPREQUEST_ONE}", + errRegex: "^$"}, "404": { errRegex: "non-2XX response code: 404", url: "https://release-argus.io/foo/bar", @@ -56,6 +61,9 @@ func TestLookup_HTTPRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + } lookup := testLookup() lookup.URL = tc.url @@ -77,6 +85,7 @@ func TestLookup_HTTPRequest(t *testing.T) { func TestLookup_Query(t *testing.T) { // GIVEN a Lookup() tests := map[string]struct { + env map[string]string url string allowInvalidCerts bool noSemanticVersioning bool @@ -105,6 +114,14 @@ func TestLookup_Query(t *testing.T) { url: "https://release-argus.io", regex: "([0-9]+) The Argus Developers", }, + "url from env": { + noSemanticVersioning: true, + wantVersion: "[0-9]{4}", + errRegex: "^$", + env: map[string]string{"TESTLOOKUP_DV_QUERY_ONE": "https://release-argus.io"}, + url: "${TESTLOOKUP_DV_QUERY_ONE}", + regex: "([0-9]+) The Argus Developers", + }, "passing regex with no capture group": { noSemanticVersioning: true, wantVersion: "[0-9]{4}", @@ -165,6 +182,9 @@ func TestLookup_Query(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + } dvl := testLookup() dvl.URL = tc.url dvl.AllowInvalidCerts = &tc.allowInvalidCerts @@ -201,6 +221,7 @@ func TestLookup_Query(t *testing.T) { func TestLookup_Track(t *testing.T) { // GIVEN a Lookup() tests := map[string]struct { + env map[string]string lookup *Lookup allowInvalidCerts bool semanticVersioning bool @@ -292,6 +313,22 @@ func TestLookup_Track(t *testing.T) { wantDatabaseMesages: 1, wantAnnounces: 1, }, + "env vars in basic auth": { + env: map[string]string{ + "TESTLOOKUP_DV_TRACK_ONE": "test", + "TESTLOOKUP_DV_TRACK_TWO": "123"}, + startLatestVersion: "1.2.2", + wantDeployedVersion: "1.2.2", + basicAuth: &BasicAuth{ + Username: "${TESTLOOKUP_DV_TRACK_ONE}", + Password: "${TESTLOOKUP_DV_TRACK_TWO}"}, + lookup: &Lookup{ + URL: "https://valid.release-argus.io/basic-auth", + Regex: `non-semantic: "v([^"]+)`}, + semanticVersioning: true, + wantDatabaseMesages: 1, + wantAnnounces: 1, + }, "get version behind an invalid cert": { startLatestVersion: "1.2.2", wantDeployedVersion: "1.2.2", @@ -387,6 +424,10 @@ func TestLookup_Track(t *testing.T) { t.Run(name, func(t *testing.T) { // t.Parallel() - can't run in parallel because of stdout + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } stdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w diff --git a/service/latest_version/filter/docker.go b/service/latest_version/filter/docker.go index 7fe7e31b..53f9ebfb 100644 --- a/service/latest_version/filter/docker.go +++ b/service/latest_version/filter/docker.go @@ -361,11 +361,11 @@ func (d *DockerCheck) getToken() (token string) { // Have a Token, return it if d.Token != "" { - token = d.Token + token = util.StringWithEnv(d.Token) return } - token = d.Defaults.getToken(d.GetType()) + token = util.StringWithEnv(d.Defaults.getToken(d.GetType())) return } @@ -379,22 +379,22 @@ func (d *DockerCheckDefaults) getToken(dType string) (token string) { switch dType { case "ghcr": if d.RegistryGHCR != nil { - token = d.RegistryGHCR.Token + token = util.StringWithEnv(d.RegistryGHCR.Token) } case "hub": if d.RegistryHub != nil { - token = d.RegistryHub.Token + token = util.StringWithEnv(d.RegistryHub.Token) } case "quay": if d.RegistryQuay != nil { - token = d.RegistryQuay.Token + token = util.StringWithEnv(d.RegistryQuay.Token) } } if token != "" { return } - token = d.defaults.getToken(dType) + token = util.StringWithEnv(d.defaults.getToken(dType)) return } @@ -517,11 +517,13 @@ func (d *DockerCheckDefaults) getUsername() string { return "" } - if d.RegistryHub != nil && d.RegistryHub.Username != "" { - return d.RegistryHub.Username + if d.RegistryHub != nil { + if username := util.StringWithEnv(d.RegistryHub.Username); username != "" { + return username + } } - return d.defaults.getUsername() + return util.StringWithEnv(d.defaults.getUsername()) } // getUsername for the given type @@ -530,8 +532,8 @@ func (d *DockerCheck) getUsername() string { return "" } - if d.Username != "" { - return d.Username + if username := util.StringWithEnv(d.Username); username != "" { + return username } return d.Defaults.getUsername() } diff --git a/service/latest_version/filter/docker_test.go b/service/latest_version/filter/docker_test.go index b55258c8..1ebf10ba 100644 --- a/service/latest_version/filter/docker_test.go +++ b/service/latest_version/filter/docker_test.go @@ -1042,6 +1042,7 @@ func TestDockerCheck_CopyQueryToken(t *testing.T) { func TestDockerCheck_getUsername(t *testing.T) { // GIVEN a DockerCheck tests := map[string]struct { + env map[string]string dockerCheck *DockerCheck want string }{ @@ -1074,12 +1075,61 @@ func TestDockerCheck_getUsername(t *testing.T) { nil)), want: "anotherUser", }, + "env var is used": { + env: map[string]string{"TESTDOCKERCHECK_GETUSERNAME_ONE": "aUser"}, + dockerCheck: NewDockerCheck( + "", + "", "", + "${TESTDOCKERCHECK_GETUSERNAME_ONE}", "", + "", time.Time{}, + NewDockerCheckDefaults( + "", + "", + "", "anotherUser", + "", + nil)), + want: "aUser", + }, + "env var isn't used when empty": { + dockerCheck: NewDockerCheck( + "", + "", "", + "${TESTDOCKERCHECK_GETUSERNAME_UNSET}", "", + "", time.Time{}, + NewDockerCheckDefaults( + "", + "", + "", "anotherUser", + "", + nil)), + want: "anotherUser", + }, + "env var is used from default if main empty": { + env: map[string]string{"TESTDOCKERCHECK_GETUSERNAME_TWO": "cUser"}, + dockerCheck: NewDockerCheck( + "", + "", "", + "", "", + "", time.Time{}, + NewDockerCheckDefaults( + "", + "", + "", "${TESTDOCKERCHECK_GETUSERNAME_TWO}", + "", + nil)), + want: "cUser", + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + // WHEN getUsername is called on it got := tc.dockerCheck.getUsername() @@ -1095,6 +1145,7 @@ func TestDockerCheck_getUsername(t *testing.T) { func TestDockerCheckDefaults_getUsername(t *testing.T) { // GIVEN a DockerCheckDefaults tests := map[string]struct { + env map[string]string dockerCheckDefaults *DockerCheckDefaults want string }{ @@ -1141,12 +1192,61 @@ func TestDockerCheckDefaults_getUsername(t *testing.T) { nil)), want: "daUser", }, + "env var is used": { + env: map[string]string{"TESTDOCKERCHECK_GETUSERNAME_ONE": "aUser"}, + dockerCheckDefaults: NewDockerCheckDefaults( + "", + "", + "", "${TESTDOCKERCHECK_GETUSERNAME_ONE}", + "", + NewDockerCheckDefaults( + "", + "", + "", "anotherUser", + "", + nil)), + want: "aUser", + }, + "env var isn't used when empty": { + dockerCheckDefaults: NewDockerCheckDefaults( + "", + "", + "", "${TESTDOCKERCHECK_GETUSERNAME_UNSET}", + "", + NewDockerCheckDefaults( + "", + "", + "", "anotherUser", + "", + nil)), + want: "anotherUser", + }, + "env var is used from default if main empty": { + env: map[string]string{"TESTDOCKERCHECK_GETUSERNAME_TWO": "cUser"}, + dockerCheckDefaults: NewDockerCheckDefaults( + "", + "", + "", "", + "", + NewDockerCheckDefaults( + "", + "", + "", "${TESTDOCKERCHECK_GETUSERNAME_TWO}", + "", + nil)), + want: "cUser", + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + // WHEN getUsername is called on it got := tc.dockerCheckDefaults.getUsername() @@ -1798,6 +1898,7 @@ func TestDockerCheck_GetType(t *testing.T) { func TestDockerCheckDefaults_getToken(t *testing.T) { // GIVEN a DockerCheckDefaults tests := map[string]struct { + env map[string]string defaults *DockerCheckDefaults seekType string want string @@ -1877,12 +1978,38 @@ func TestDockerCheckDefaults_getToken(t *testing.T) { seekType: "quay", want: "quayToken", }, + "env var is used": { + env: map[string]string{"TESTDOCKERCHECKDEFAULTS_GETTOKEN_ONE": "fromEnv"}, + defaults: NewDockerCheckDefaults( + "", + "${TESTDOCKERCHECKDEFAULTS_GETTOKEN_ONE}", + "hubToken", "", + "quayToken", + nil), + seekType: "ghcr", + want: "fromEnv", + }, + "empty env var not ignored": { + defaults: NewDockerCheckDefaults( + "", + "${TESTDOCKERCHECKDEFAULTS_GETTOKEN_UNSET}", + "hubToken", "", + "quayToken", + nil), + seekType: "ghcr", + want: "", + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + // WHEN getToken is called on it got := tc.defaults.getToken(tc.seekType) @@ -1897,6 +2024,7 @@ func TestDockerCheckDefaults_getToken(t *testing.T) { func TestDockerCheck_getToken(t *testing.T) { // GIVEN a DockerCheck tests := map[string]struct { + env map[string]string dockerCheck *DockerCheck want string }{ @@ -1937,12 +2065,46 @@ func TestDockerCheck_getToken(t *testing.T) { nil)), want: "ghcrToken", }, + "env var is used": { + env: map[string]string{"TESTDOCKERCHECK_GETTOKEN_ONE": "fromEnv"}, + dockerCheck: NewDockerCheck( + "ghcr", + "", "", + "", "${TESTDOCKERCHECK_GETTOKEN_ONE}", + "", time.Now(), + NewDockerCheckDefaults( + "ghcr", + "ghcrToken", + "hubToken", "", + "quayToken", + nil)), + want: "fromEnv", + }, + "empty env var not ignored": { + dockerCheck: NewDockerCheck( + "ghcr", + "", "", + "", "${TESTDOCKERCHECK_GETTOKEN_UNSET}", + "", time.Now(), + NewDockerCheckDefaults( + "ghcr", + "ghcrToken", + "hubToken", "", + "quayToken", + nil)), + want: "", + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + // WHEN getToken is called on it got := tc.dockerCheck.getToken() diff --git a/service/latest_version/gets.go b/service/latest_version/gets.go index 4d1b6280..2503dd4a 100644 --- a/service/latest_version/gets.go +++ b/service/latest_version/gets.go @@ -22,7 +22,7 @@ import ( ) func (l *Lookup) GetAccessToken() *string { - return util.FirstNonNilPtr( + return util.FirstNonNilPtrWithEnv( l.AccessToken, l.Defaults.AccessToken, l.HardDefaults.AccessToken) @@ -70,7 +70,7 @@ func (l *Lookup) GetUsePreRelease() bool { // GetURL will ensure `url` is a valid GitHub API URL if `urlType` is 'github' func (l *Lookup) GetURL() string { - url := l.URL + url := util.StringWithEnv(l.URL) if l.Type == "github" { // Convert "owner/repo" to the API path. if strings.Count(url, "/") == 1 { diff --git a/service/latest_version/gets_test.go b/service/latest_version/gets_test.go index c68caec9..5dfcc9a8 100644 --- a/service/latest_version/gets_test.go +++ b/service/latest_version/gets_test.go @@ -17,6 +17,7 @@ package latestver import ( + "os" "testing" svcstatus "github.com/release-argus/Argus/service/status" @@ -25,36 +26,48 @@ import ( func TestLookup_GetAccessToken(t *testing.T) { // GIVEN a Lookup tests := map[string]struct { - accessTokenRoot *string - accessTokenDefault *string - accessTokenHardDefault *string - wantString string + env map[string]string + root *string + dfault *string + hardDefault *string + wantString string }{ "root overrides all": { - wantString: "this", - accessTokenRoot: stringPtr("this"), - accessTokenDefault: stringPtr("not_this"), - accessTokenHardDefault: stringPtr("not_this")}, + wantString: "this", + root: stringPtr("this"), + dfault: stringPtr("not_this"), + hardDefault: stringPtr("not_this")}, "default overrides hardDefault": { - wantString: "this", - accessTokenRoot: nil, - accessTokenDefault: stringPtr("this"), - accessTokenHardDefault: stringPtr("not_this")}, + wantString: "this", + dfault: stringPtr("this"), + hardDefault: stringPtr("not_this")}, "hardDefault is last resort": { - wantString: "this", - accessTokenRoot: nil, - accessTokenDefault: nil, - accessTokenHardDefault: stringPtr("this")}, + wantString: "this", + hardDefault: stringPtr("this")}, + "env var is used": { + wantString: "this", + env: map[string]string{"TESTLOOKUP_LV_GETACCESSTOKEN_ONE": "this"}, + root: stringPtr("${TESTLOOKUP_LV_GETACCESSTOKEN_ONE}"), + }, + "empty env var not ignored": { + wantString: "", + root: stringPtr("${TESTLOOKUP_LV_GETACCESSTOKEN_UNSET}"), + dfault: stringPtr("this"), + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } lookup := testLookup(false, false) - lookup.AccessToken = tc.accessTokenRoot - lookup.Defaults.AccessToken = tc.accessTokenDefault - lookup.HardDefaults.AccessToken = tc.accessTokenHardDefault + lookup.AccessToken = tc.root + lookup.Defaults.AccessToken = tc.dfault + lookup.HardDefaults.AccessToken = tc.hardDefault // WHEN GetAccessToken is called got := lookup.GetAccessToken() @@ -74,26 +87,23 @@ func TestLookup_GetAccessToken(t *testing.T) { func TestLookup_GetAllowInvalidCerts(t *testing.T) { // GIVEN a Lookup tests := map[string]struct { - allowInvalidCertsRoot *bool - allowInvalidCertsDefault *bool - allowInvalidCertsHardDefault *bool - wantBool bool + root *bool + dfault *bool + hardDefault *bool + wantBool bool }{ "root overrides all": { - wantBool: true, - allowInvalidCertsRoot: boolPtr(true), - allowInvalidCertsDefault: boolPtr(false), - allowInvalidCertsHardDefault: boolPtr(false)}, + wantBool: true, + root: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false)}, "default overrides hardDefault": { - wantBool: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsDefault: boolPtr(true), - allowInvalidCertsHardDefault: boolPtr(false)}, + wantBool: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false)}, "hardDefault is last resort": { - wantBool: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsDefault: nil, - allowInvalidCertsHardDefault: boolPtr(true)}, + wantBool: true, + hardDefault: boolPtr(true)}, } for name, tc := range tests { @@ -101,9 +111,9 @@ func TestLookup_GetAllowInvalidCerts(t *testing.T) { t.Parallel() lookup := testLookup(false, false) - lookup.AllowInvalidCerts = tc.allowInvalidCertsRoot - lookup.Defaults.AllowInvalidCerts = tc.allowInvalidCertsDefault - lookup.HardDefaults.AllowInvalidCerts = tc.allowInvalidCertsHardDefault + lookup.AllowInvalidCerts = tc.root + lookup.Defaults.AllowInvalidCerts = tc.dfault + lookup.HardDefaults.AllowInvalidCerts = tc.hardDefault // WHEN GetAllowInvalidCerts is called got := lookup.GetAllowInvalidCerts() @@ -216,26 +226,23 @@ func TestLookup_ServiceURL(t *testing.T) { func TestLookup_GetUsePreRelease(t *testing.T) { // GIVEN a Lookup tests := map[string]struct { - usePreReleaseRoot *bool - usePreReleaseDefault *bool - usePreReleaseHardDefault *bool - wantBool bool + root *bool + dfault *bool + hardDefault *bool + wantBool bool }{ "root overrides all": { - wantBool: true, - usePreReleaseRoot: boolPtr(true), - usePreReleaseDefault: boolPtr(false), - usePreReleaseHardDefault: boolPtr(false)}, + wantBool: true, + root: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false)}, "default overrides hardDefault": { - wantBool: true, - usePreReleaseRoot: nil, - usePreReleaseDefault: boolPtr(true), - usePreReleaseHardDefault: boolPtr(false)}, + wantBool: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false)}, "hardDefault is last resort": { - wantBool: true, - usePreReleaseRoot: nil, - usePreReleaseDefault: nil, - usePreReleaseHardDefault: boolPtr(true)}, + wantBool: true, + hardDefault: boolPtr(true)}, } for name, tc := range tests { @@ -243,9 +250,9 @@ func TestLookup_GetUsePreRelease(t *testing.T) { t.Parallel() lookup := testLookup(false, false) - lookup.UsePreRelease = tc.usePreReleaseRoot - lookup.Defaults.UsePreRelease = tc.usePreReleaseDefault - lookup.HardDefaults.UsePreRelease = tc.usePreReleaseHardDefault + lookup.UsePreRelease = tc.root + lookup.Defaults.UsePreRelease = tc.dfault + lookup.HardDefaults.UsePreRelease = tc.hardDefault // WHEN GetUsePreRelease is called got := lookup.GetUsePreRelease() @@ -262,6 +269,7 @@ func TestLookup_GetUsePreRelease(t *testing.T) { func TestLookup_GetURL(t *testing.T) { // GIVEN a Lookup tests := map[string]struct { + env map[string]string urlType bool url string tagFallback bool @@ -281,12 +289,27 @@ func TestLookup_GetURL(t *testing.T) { tagFallback: true, want: "https://api.github.com/repos/release-argus/Argus/tags", }, + "env var is used": { + env: map[string]string{"TESTLOOKUP_LV_GETURL_ONE": "https://release-argus.io"}, + urlType: true, + url: "${TESTLOOKUP_LV_GETURL_ONE}", + want: "https://release-argus.io", + }, + "empty env var not ignored": { + urlType: true, + url: "${TESTLOOKUP_LV_GETURL_UNSET}", + want: "", + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } lookup := testLookup(tc.urlType, false) lookup.URL = tc.url if !tc.urlType { diff --git a/service/options/options_test.go b/service/options/options_test.go index 163fe627..21e52094 100644 --- a/service/options/options_test.go +++ b/service/options/options_test.go @@ -64,28 +64,28 @@ func TestOptions_GetActive(t *testing.T) { func TestOptions_GetInterval(t *testing.T) { // GIVEN Options tests := map[string]struct { - intervalRoot string - intervalDefault string - intervalHardDefault string - wantString string + root string + dfault string + hardDefault string + want string }{ "root overrides all": { - wantString: "10s", - intervalRoot: "10s", - intervalDefault: "1m10s", - intervalHardDefault: "1m10s", + want: "10s", + root: "10s", + dfault: "1m10s", + hardDefault: "1m10s", }, "default overrides hardDefault": { - wantString: "10s", - intervalRoot: "", - intervalDefault: "10s", - intervalHardDefault: "1m10s", + want: "10s", + root: "", + dfault: "10s", + hardDefault: "1m10s", }, "hardDefault is last resort": { - wantString: "10s", - intervalRoot: "", - intervalDefault: "", - intervalHardDefault: "10s", + want: "10s", + root: "", + dfault: "", + hardDefault: "10s", }, } @@ -94,17 +94,17 @@ func TestOptions_GetInterval(t *testing.T) { t.Parallel() options := testOptions() - options.Interval = tc.intervalRoot - options.Defaults.Interval = tc.intervalDefault - options.HardDefaults.Interval = tc.intervalHardDefault + options.Interval = tc.root + options.Defaults.Interval = tc.dfault + options.HardDefaults.Interval = tc.hardDefault // WHEN GetInterval is called got := options.GetInterval() // THEN the function returns the correct result - if got != tc.wantString { + if got != tc.want { t.Errorf("want: %q\ngot: %q", - tc.wantString, got) + tc.want, got) } }) } @@ -113,28 +113,25 @@ func TestOptions_GetInterval(t *testing.T) { func TestOptions_GetSemanticVersioning(t *testing.T) { // GIVEN Options tests := map[string]struct { - semanticVersioningRoot *bool - semanticVersioningDefault *bool - semanticVersioningHardDefault *bool - wantBool bool + root *bool + dfault *bool + hardDefault *bool + wantBool bool }{ "root overrides all": { - wantBool: true, - semanticVersioningRoot: boolPtr(true), - semanticVersioningDefault: boolPtr(false), - semanticVersioningHardDefault: boolPtr(false), + wantBool: true, + root: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false), }, "default overrides hardDefault": { - wantBool: true, - semanticVersioningRoot: nil, - semanticVersioningDefault: boolPtr(true), - semanticVersioningHardDefault: boolPtr(false), + wantBool: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false), }, "hardDefault is last resort": { - wantBool: true, - semanticVersioningRoot: nil, - semanticVersioningDefault: nil, - semanticVersioningHardDefault: boolPtr(true), + wantBool: true, + hardDefault: boolPtr(true), }, } @@ -143,9 +140,9 @@ func TestOptions_GetSemanticVersioning(t *testing.T) { t.Parallel() options := testOptions() - options.SemanticVersioning = tc.semanticVersioningRoot - options.Defaults.SemanticVersioning = tc.semanticVersioningDefault - options.HardDefaults.SemanticVersioning = tc.semanticVersioningHardDefault + options.SemanticVersioning = tc.root + options.Defaults.SemanticVersioning = tc.dfault + options.HardDefaults.SemanticVersioning = tc.hardDefault // WHEN GetSemanticVersioning is called got := options.GetSemanticVersioning() @@ -162,23 +159,23 @@ func TestOptions_GetSemanticVersioning(t *testing.T) { func TestOptions_GetIntervalPointer(t *testing.T) { // GIVEN options tests := map[string]struct { - interval string - intervalD string - intervalHD string - want string + root string + dfault string + hardDefault string + want string }{ "root overrides all": { - interval: "10s", - intervalD: "20s", - intervalHD: "30s", - want: "10s"}, + root: "10s", + dfault: "20s", + hardDefault: "30s", + want: "10s"}, "default overrides hardDefault": { - intervalD: "20s", - intervalHD: "30s", - want: "20s"}, + dfault: "20s", + hardDefault: "30s", + want: "20s"}, "hardDefault is last resort": { - intervalHD: "30s", - want: "30s"}, + hardDefault: "30s", + want: "30s"}, } for name, tc := range tests { @@ -186,9 +183,9 @@ func TestOptions_GetIntervalPointer(t *testing.T) { t.Parallel() options := testOptions() - options.Interval = tc.interval - options.Defaults.Interval = tc.intervalD - options.HardDefaults.Interval = tc.intervalHD + options.Interval = tc.root + options.Defaults.Interval = tc.dfault + options.HardDefaults.Interval = tc.hardDefault // WHEN GetIntervalPointer is called got := options.GetIntervalPointer() diff --git a/testing/shoutrrr.go b/testing/shoutrrr.go index 7e932f65..6d2587e7 100644 --- a/testing/shoutrrr.go +++ b/testing/shoutrrr.go @@ -88,8 +88,8 @@ func findShoutrrr( if cfg.Notify != nil && cfg.Notify[name] != nil { hardDefaults := config.Defaults{} hardDefaults.SetDefaults() - emptyShoutrrs := shoutrrr.ShoutrrrDefaults{} - emptyShoutrrs.InitMaps() + emptyShoutrrrs := shoutrrr.ShoutrrrDefaults{} + emptyShoutrrrs.InitMaps() main := cfg.Notify[name] slice["test"] = shoutrrr.New( nil, // failed @@ -98,9 +98,9 @@ func findShoutrrr( &main.Params, main.Type, &main.URLFields, - &emptyShoutrrs, - &emptyShoutrrs, - &emptyShoutrrs) + &emptyShoutrrrs, + &emptyShoutrrrs, + &emptyShoutrrrs) slice["test"].InitMaps() slice["test"].Main.InitMaps() diff --git a/util/util.go b/util/util.go index 3162a916..23e03511 100644 --- a/util/util.go +++ b/util/util.go @@ -17,10 +17,14 @@ package util import ( "bytes" "crypto/rand" + "crypto/sha256" "encoding/base64" + "encoding/hex" "encoding/json" "fmt" "math/big" + "os" + "regexp" "sort" "strconv" "strings" @@ -119,6 +123,19 @@ func FirstNonNilPtr[T customComparable](pointers ...*T) *T { return nil } +// FirstNonNilPtrWithEnv will return the first pointer in `pointers` that is not nil after evaluating any environment variables. +func FirstNonNilPtrWithEnv(pointers ...*string) *string { + for _, pointer := range pointers { + if pointer != nil { + if val := StringWithEnv(*pointer); val != *pointer { + return &val + } + return pointer + } + } + return nil +} + // FirstNonDefault will return the first var in `vars` that is not the default. func FirstNonDefault[T comparable](vars ...T) T { var fresh T @@ -130,6 +147,16 @@ func FirstNonDefault[T comparable](vars ...T) T { return fresh } +// FirstNonDefaultWithEnv will return the first var in `vars` that is not an empty string after evaluating any environment variables. "" is returned if all are empty. +func FirstNonDefaultWithEnv(vars ...string) string { + for _, v := range vars { + if v = StringWithEnv(v); v != "" { + return v + } + } + return "" +} + // PrintlnIfNotDefault will print `msg` is `x` is not the default for that type. func PrintlnIfNotDefault[T comparable](x T, msg string) { var fresh T @@ -255,6 +282,51 @@ func BasicAuth(username string, password string) string { return base64.StdEncoding.EncodeToString([]byte(encode)) } +// isHashed will return whether the string is a hashed value. +func isHashed(s string) bool { + return RegexCheck("^h__[a-f0-9]{64}$", s) +} + +// Hash will return the SHA256 hash of the string. +func hash(s string) [32]byte { + return sha256.Sum256([]byte(s)) +} + +// HashFromString will return the byte slice of the hash string. +func hashFromString(s string) []byte { + hash, _ := hex.DecodeString(s) + return hash +} + +// GetHash will return the SHA256 hash of the string. If it's already hashed, the string hash is converted to a byte slice. +func GetHash(s string) [32]byte { + if isHashed(s) { + hash := hashFromString(s[3:]) + var hash32 [32]byte + copy(hash32[:], hash) + return hash32 + } + return hash(s) +} + +func FmtHash(h [32]byte) string { + return fmt.Sprintf("h__%x", h) +} + +// StringWithEnv will return the value at the environment variable, otherwise str. +func StringWithEnv(str string) string { + // If the envVar is a ref to an environment variable, return the value of that environment variable. + if str != "" && str[0] == '$' { + reg := regexp.MustCompile(`^\${(\w+)}$`) + if match := reg.FindStringSubmatch(str); len(match) > 1 { + val := os.Getenv(match[1]) + return val + } + } + // It's not, so return the string + return str +} + func GetKeysFromJSON(data string) []string { return getKeysFromJSONBytes([]byte(data), "") } diff --git a/util/util_test.go b/util/util_test.go index aff575c1..055a7f12 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -17,6 +17,7 @@ package util import ( + "crypto/sha256" "encoding/json" "fmt" "io" @@ -284,6 +285,107 @@ func TestFirstNonNilPtr(t *testing.T) { } } +func TestFirstNonNilPtrWithEnv(t *testing.T) { + // GIVEN a bunch of pointers + tests := map[string]struct { + env map[string]string + pointers []*string + allNil bool + wantIndex int + wantText string + diffAddress bool + }{ + "no pointers": { + pointers: []*string{}, + allNil: true, + }, + "all nil pointers": { + pointers: []*string{ + nil, + nil, + nil, + nil}, + allNil: true, + }, + "1 non-nil pointer": { + pointers: []*string{ + nil, + nil, + nil, + stringPtr("bar")}, + wantIndex: 3, + }, + "1 non-nil pointer (env var)": { + env: map[string]string{"TESTFIRSTNONNILPTRWITHENV_ONE": "bar"}, + pointers: []*string{ + nil, + nil, + nil, + stringPtr("${TESTFIRSTNONNILPTRWITHENV_ONE}")}, + wantIndex: 3, + diffAddress: true, + wantText: "bar", + }, + "1 non-nil pointer (unset env var)": { + pointers: []*string{ + nil, + nil, + nil, + stringPtr("${TESTFIRSTNONNILPTRWITHENV_UNSET}")}, + wantIndex: 3, + diffAddress: true, + wantText: "", + }, + "2 non-nil pointers": { + pointers: []*string{ + stringPtr("foo"), + nil, + nil, + stringPtr("bar")}, + wantIndex: 0, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + + // WHEN FirstNonNilPtrWithEnv is run on a slice of pointers + got := FirstNonNilPtrWithEnv(tc.pointers...) + + // THEN the correct pointer (or nil) is returned + if tc.allNil { + if got != nil { + t.Fatalf("got: %v\nfrom: %v", + got, tc.pointers) + } + return + } + // Addresses should be the same (unless we're using an env var) + if got != tc.pointers[tc.wantIndex] && !tc.diffAddress { + t.Errorf("want: %v\ngot: %v", + tc.pointers[tc.wantIndex], got) + // Addresses should only be the same + } else if got == tc.pointers[tc.wantIndex] { + // IF we're using an env var + if tc.diffAddress { + t.Errorf("addresses of pointers should differ (%v, %v)", + tc.pointers[tc.wantIndex], got) + } + // Should have what the env var is set to + } else if *got != tc.wantText { + t.Errorf("want: %v\ngot: %v", + tc.wantText, *got) + } + }) + } +} + func TestValueIfTrue(t *testing.T) { // GIVEN lists of strings tests := map[string]struct { @@ -376,6 +478,108 @@ func TestFirstNonDefault(t *testing.T) { } } +func TestFirstNonDefaultWithEnv(t *testing.T) { + // GIVEN a bunch of comparables + tests := map[string]struct { + env map[string]string + slice []string + allDefault bool + wantIndex int + wantText string + diffAddress bool + }{ + "no vars": { + slice: []string{}, + allDefault: true, + }, + "all default vars": { + slice: []string{ + "", + "", + "", + ""}, + allDefault: true, + }, + "1 non-default var": { + slice: []string{ + "", + "", + "", + "bar"}, + wantIndex: 3, + }, + "1 non-default var (env var)": { + env: map[string]string{"TESTFIRSTNONDEFAULTWITHENV_ONE": "bar"}, + slice: []string{ + "", + "", + "", + "${TESTFIRSTNONDEFAULTWITHENV_ONE}"}, + wantIndex: 3, + wantText: "bar", + diffAddress: true, + }, + "2 non-default vars": { + slice: []string{ + "foo", + "", + "", + "bar"}, + wantIndex: 0, + }, + "2 non-default vars (empty env vars ignored)": { + env: map[string]string{"TESTFIRSTNONDEFAULTWITHENV_TWO": "bar"}, + slice: []string{ + "${TESTFIRSTNONDEFAULTWITHENV_UNSET}", + "", + "", + "${TESTFIRSTNONDEFAULTWITHENV_TWO}"}, + wantIndex: 3, + wantText: "bar", + diffAddress: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } + + // WHEN FirstNonDefaultWithEnv is run on a slice of slice + got := FirstNonDefaultWithEnv(tc.slice...) + + // THEN the correct var (or "") is returned + if tc.allDefault { + if got != "" { + t.Fatalf("got: %v\nfrom: %v", + got, tc.slice) + } + return + } + // Addresses should be the same (unless we're using an env var) + if got != tc.slice[tc.wantIndex] && !tc.diffAddress { + t.Errorf("want: %v\ngot: %v", + tc.slice[tc.wantIndex], got) + // Addresses should only be the same + } else if got == tc.slice[tc.wantIndex] { + // IF we're using an env var + if tc.diffAddress { + t.Errorf("addresses of pointers should differ (%v, %v)", + tc.slice[tc.wantIndex], got) + } + // Should have what the env var is set to + } else if got != tc.wantText { + t.Errorf("want: %v\ngot: %v", + tc.wantText, got) + } + }) + } +} + func TestPrintlnIfNotDefault(t *testing.T) { // GIVEN a bunch of comparables tests := map[string]struct { @@ -1165,6 +1369,157 @@ func TestBasicAuth(t *testing.T) { } } +func TestIsHashed(t *testing.T) { + // GIVEN a string + tests := map[string]struct { + input string + want bool + }{ + "empty string": { + input: "", + want: false, + }, + "non-hashed string": { + input: "h__foo", + want: false, + }, + "hashed string": { + input: fmt.Sprintf("h__%x", sha256.Sum256([]byte("foo"))), + want: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + // WHEN isHashed is called on it + got := isHashed(tc.input) + + // THEN the hash is detected correctly + if got != tc.want { + t.Errorf("want: %v\ngot: %v", + tc.want, got) + } + }) + } +} + +func TestHash(t *testing.T) { + // GIVEN a string + tests := map[string]struct { + input string + want [32]byte + }{ + "empty string": { + input: "", + want: sha256.Sum256([]byte("")), + }, + "non-empty string": { + input: "foo", + want: sha256.Sum256([]byte("foo")), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + // WHEN hash is called on it + got := hash(tc.input) + + // THEN the string is hashed correctly + if got != tc.want { + t.Errorf("want: %q\ngot: %q", + tc.want, got) + } + }) + } +} + +func TestHashFromString(t *testing.T) { + // GIVEN a string that contains a hash + tests := map[string]string{ + "empty string": "", + "non-empty string": "foobar", + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + want := sha256.Sum256([]byte(tc)) + input := fmt.Sprintf("h__%x", want) + + // WHEN hashFromString is called on it + got := hashFromString(input[3:]) + + // THEN the string is hashed correctly + var got32 [32]byte + copy(got32[:], got[:]) + if got32 != want { + t.Errorf("want: %q\ngot: %q", + want, got32) + } + }) + } +} + +func TestGetHash(t *testing.T) { + // GIVEN a string that may or may not be hashed + tests := map[string]struct { + input string + alreadyHashed bool + }{ + "empty string": { + input: "", + }, + "non-empty string": { + input: "foo", + }, + "hashed string": { + input: fmt.Sprintf("h__%x", sha256.Sum256([]byte("foo"))), + alreadyHashed: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + want := tc.input + if !tc.alreadyHashed { + want = FmtHash(sha256.Sum256([]byte(tc.input))) + } + + // WHEN GetHash is called on it + got := GetHash(tc.input) + + // THEN the string is hashed correctly + gotHash := FmtHash(got) + if gotHash != want { + t.Errorf("want: %q\ngot: %q", + want, gotHash) + } + }) + } +} + +func TestFmtHash(t *testing.T) { + // GIVEN a hash + hash := sha256.Sum256([]byte("foo")) + + // WHEN FmtHash is called on it + got := FmtHash(hash) + + // THEN the hash is formatted correctly + want := fmt.Sprintf("h__%x", hash) + if got != want { + t.Errorf("want: %q\ngot: %q", + want, got) + } +} + func TestStringToPointer(t *testing.T) { // GIVEN a string tests := map[string]struct { diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 389ed5e0..99d4ccb3 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -54,7 +54,9 @@ func NewAPI(cfg *config.Config, log *util.JLog) *API { api.Router = baseRouter.PathPrefix(routePrefix).Subrouter().StrictSlash(true) baseRouter.Handle(routePrefix, http.RedirectHandler(routePrefix+"/", http.StatusPermanentRedirect)) - if api.Config.Settings.Web.BasicAuth != nil { + if api.Config.Settings.Web.BasicAuth != nil || + api.Config.Settings.FromFlags.Web.BasicAuth != nil || + api.Config.Settings.HardDefaults.Web.BasicAuth != nil { api.Router.Use(api.basicAuth()) } return api diff --git a/web/api/v1/http.go b/web/api/v1/http.go index d8bb5c5e..e46093ad 100644 --- a/web/api/v1/http.go +++ b/web/api/v1/http.go @@ -40,8 +40,8 @@ func (api *API) basicAuth() mux.MiddlewareFunc { passwordHash := sha256.Sum256([]byte(password)) // Protect from possible timing attacks - usernameMatch := ConstantTimeCompare(usernameHash, api.Config.Settings.Web.BasicAuth.UsernameHash) - passwordMatch := ConstantTimeCompare(passwordHash, api.Config.Settings.Web.BasicAuth.PasswordHash) + usernameMatch := ConstantTimeCompare(usernameHash, api.Config.Settings.WebBasicAuthUsernameHash()) + passwordMatch := ConstantTimeCompare(passwordHash, api.Config.Settings.WebBasicAuthPasswordHash()) if usernameMatch && passwordMatch { h.ServeHTTP(w, r) diff --git a/web/ui/package-lock.json b/web/ui/package-lock.json index 40ae5c33..c604244f 100644 --- a/web/ui/package-lock.json +++ b/web/ui/package-lock.json @@ -5233,9 +5233,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001581", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", - "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==", + "version": "1.0.30001587", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", + "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==", "funding": [ { "type": "opencollective", diff --git a/webhook/gets.go b/webhook/gets.go index 4c6970b7..75645934 100644 --- a/webhook/gets.go +++ b/webhook/gets.go @@ -158,7 +158,7 @@ func (w *WebHook) BuildRequest() (req *http.Request) { // GetSecret for the WebHook. func (w *WebHook) GetSecret() string { - return util.FirstNonDefault( + return util.FirstNonDefaultWithEnv( w.Secret, w.Main.Secret, w.Defaults.Secret, @@ -186,7 +186,7 @@ func (w *WebHook) GetType() string { // GetURL of the WebHook. func (w *WebHook) GetURL() (url string) { url = strings.Clone( - util.FirstNonDefault( + util.FirstNonDefaultWithEnv( w.URL, w.Main.URL, w.Defaults.URL, diff --git a/webhook/gets_test.go b/webhook/gets_test.go index 34f5e47a..8c83e039 100644 --- a/webhook/gets_test.go +++ b/webhook/gets_test.go @@ -19,6 +19,7 @@ package webhook import ( "encoding/json" "io" + "os" "testing" "time" ) @@ -26,39 +27,33 @@ import ( func TestWebHook_GetAllowInvalidCerts(t *testing.T) { // GIVEN a WebHook tests := map[string]struct { - allowInvalidCertsRoot *bool - allowInvalidCertsMain *bool - allowInvalidCertsDefault *bool - allowInvalidCertsHardDefault *bool - want bool + root *bool + main *bool + dfault *bool + hardDefault *bool + want bool }{ "root overrides all": { - want: true, - allowInvalidCertsRoot: boolPtr(true), - allowInvalidCertsMain: boolPtr(false), - allowInvalidCertsDefault: boolPtr(false), - allowInvalidCertsHardDefault: boolPtr(false), + want: true, + root: boolPtr(true), + main: boolPtr(false), + dfault: boolPtr(false), + hardDefault: boolPtr(false), }, "main overrides default+hardDefault": { - want: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsMain: boolPtr(true), - allowInvalidCertsDefault: boolPtr(false), - allowInvalidCertsHardDefault: boolPtr(false), + want: true, + main: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false), }, "default overrides hardDefault": { - want: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsMain: nil, - allowInvalidCertsDefault: boolPtr(true), - allowInvalidCertsHardDefault: boolPtr(false), + want: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false), }, "hardDefault is last resort": { - want: true, - allowInvalidCertsRoot: nil, - allowInvalidCertsMain: nil, - allowInvalidCertsDefault: nil, - allowInvalidCertsHardDefault: boolPtr(true), + want: true, + hardDefault: boolPtr(true), }, } @@ -67,10 +62,10 @@ func TestWebHook_GetAllowInvalidCerts(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.AllowInvalidCerts = tc.allowInvalidCertsRoot - webhook.Main.AllowInvalidCerts = tc.allowInvalidCertsMain - webhook.Defaults.AllowInvalidCerts = tc.allowInvalidCertsDefault - webhook.HardDefaults.AllowInvalidCerts = tc.allowInvalidCertsHardDefault + webhook.AllowInvalidCerts = tc.root + webhook.Main.AllowInvalidCerts = tc.main + webhook.Defaults.AllowInvalidCerts = tc.dfault + webhook.HardDefaults.AllowInvalidCerts = tc.hardDefault // WHEN GetAllowInvalidCerts is called got := webhook.GetAllowInvalidCerts() @@ -87,39 +82,33 @@ func TestWebHook_GetAllowInvalidCerts(t *testing.T) { func TestWebHook_GetDelay(t *testing.T) { // GIVEN a WebHook tests := map[string]struct { - delayRoot string - delayMain string - delayDefault string - delayHardDefault string - want string + root string + main string + dfault string + hardDefault string + want string }{ "root overrides all": { - want: "1s", - delayRoot: "1s", - delayMain: "2s", - delayDefault: "2s", - delayHardDefault: "2s", + want: "1s", + root: "1s", + main: "2s", + dfault: "2s", + hardDefault: "2s", }, "main overrides default+hardDefault": { - want: "1s", - delayRoot: "", - delayMain: "1s", - delayDefault: "2s", - delayHardDefault: "2s", + want: "1s", + main: "1s", + dfault: "2s", + hardDefault: "2s", }, "default overrides hardDefault": { - want: "1s", - delayRoot: "", - delayMain: "", - delayDefault: "1s", - delayHardDefault: "2s", + want: "1s", + dfault: "1s", + hardDefault: "2s", }, "hardDefault is last resort": { - want: "1s", - delayRoot: "", - delayMain: "", - delayDefault: "", - delayHardDefault: "1s", + want: "1s", + hardDefault: "1s", }, } @@ -128,10 +117,10 @@ func TestWebHook_GetDelay(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.Delay = tc.delayRoot - webhook.Main.Delay = tc.delayMain - webhook.Defaults.Delay = tc.delayDefault - webhook.HardDefaults.Delay = tc.delayHardDefault + webhook.Delay = tc.root + webhook.Main.Delay = tc.main + webhook.Defaults.Delay = tc.dfault + webhook.HardDefaults.Delay = tc.hardDefault // WHEN GetDelay is called got := webhook.GetDelay() @@ -148,39 +137,33 @@ func TestWebHook_GetDelay(t *testing.T) { func TestWebHook_GetDelayDuration(t *testing.T) { // GIVEN a WebHook tests := map[string]struct { - delayRoot string - delayMain string - delayDefault string - delayHardDefault string - want time.Duration + root string + main string + dfault string + hardDefault string + want time.Duration }{ "root overrides all": { - want: 1 * time.Second, - delayRoot: "1s", - delayMain: "2s", - delayDefault: "2s", - delayHardDefault: "2s", + want: 1 * time.Second, + root: "1s", + main: "2s", + dfault: "2s", + hardDefault: "2s", }, "main overrides default+hardDefault": { - want: 1 * time.Second, - delayRoot: "", - delayMain: "1s", - delayDefault: "2s", - delayHardDefault: "2s", + want: 1 * time.Second, + main: "1s", + dfault: "2s", + hardDefault: "2s", }, "default overrides hardDefault": { - want: 1 * time.Second, - delayRoot: "", - delayMain: "", - delayDefault: "1s", - delayHardDefault: "2s", + want: 1 * time.Second, + dfault: "1s", + hardDefault: "2s", }, "hardDefault is last resort": { - want: 1 * time.Second, - delayRoot: "", - delayMain: "", - delayDefault: "", - delayHardDefault: "1s", + want: 1 * time.Second, + hardDefault: "1s", }, } @@ -189,10 +172,10 @@ func TestWebHook_GetDelayDuration(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.Delay = tc.delayRoot - webhook.Main.Delay = tc.delayMain - webhook.Defaults.Delay = tc.delayDefault - webhook.HardDefaults.Delay = tc.delayHardDefault + webhook.Delay = tc.root + webhook.Main.Delay = tc.main + webhook.Defaults.Delay = tc.dfault + webhook.HardDefaults.Delay = tc.hardDefault // WHEN GetDelayDuration is called got := webhook.GetDelayDuration() @@ -209,39 +192,33 @@ func TestWebHook_GetDelayDuration(t *testing.T) { func TestWebHook_GetDesiredStatusCode(t *testing.T) { // GIVEN a WebHook tests := map[string]struct { - desiredStatusCodeRoot *int - desiredStatusCodeMain *int - desiredStatusCodeDefault *int - desiredStatusCodeHardDefault *int - want int + root *int + main *int + dfault *int + hardDefault *int + want int }{ "root overrides all": { - want: 1, - desiredStatusCodeRoot: intPtr(1), - desiredStatusCodeMain: intPtr(2), - desiredStatusCodeDefault: intPtr(2), - desiredStatusCodeHardDefault: intPtr(2), + want: 1, + root: intPtr(1), + main: intPtr(2), + dfault: intPtr(2), + hardDefault: intPtr(2), }, "main overrides default+hardDefault": { - want: 1, - desiredStatusCodeRoot: nil, - desiredStatusCodeMain: intPtr(1), - desiredStatusCodeDefault: intPtr(2), - desiredStatusCodeHardDefault: intPtr(2), + want: 1, + main: intPtr(1), + dfault: intPtr(2), + hardDefault: intPtr(2), }, "default overrides hardDefault": { - want: 1, - desiredStatusCodeRoot: nil, - desiredStatusCodeMain: nil, - desiredStatusCodeDefault: intPtr(1), - desiredStatusCodeHardDefault: intPtr(2), + want: 1, + dfault: intPtr(1), + hardDefault: intPtr(2), }, "hardDefault is last resort": { - want: 1, - desiredStatusCodeRoot: nil, - desiredStatusCodeMain: nil, - desiredStatusCodeDefault: nil, - desiredStatusCodeHardDefault: intPtr(1), + want: 1, + hardDefault: intPtr(1), }, } @@ -250,10 +227,10 @@ func TestWebHook_GetDesiredStatusCode(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.DesiredStatusCode = tc.desiredStatusCodeRoot - webhook.Main.DesiredStatusCode = tc.desiredStatusCodeMain - webhook.Defaults.DesiredStatusCode = tc.desiredStatusCodeDefault - webhook.HardDefaults.DesiredStatusCode = tc.desiredStatusCodeHardDefault + webhook.DesiredStatusCode = tc.root + webhook.Main.DesiredStatusCode = tc.main + webhook.Defaults.DesiredStatusCode = tc.dfault + webhook.HardDefaults.DesiredStatusCode = tc.hardDefault // WHEN GetDesiredStatusCode is called got := webhook.GetDesiredStatusCode() @@ -270,39 +247,33 @@ func TestWebHook_GetDesiredStatusCode(t *testing.T) { func TestWebHook_GetMaxTries(t *testing.T) { // GIVEN a WebHook tests := map[string]struct { - maxTriesRoot *uint - maxTriesMain *uint - maxTriesDefault *uint - maxTriesHardDefault *uint - want uint + root *uint + main *uint + dfault *uint + hardDefault *uint + want uint }{ "root overrides all": { - want: uint(1), - maxTriesRoot: uintPtr(1), - maxTriesMain: uintPtr(2), - maxTriesDefault: uintPtr(2), - maxTriesHardDefault: uintPtr(2), + want: uint(1), + root: uintPtr(1), + main: uintPtr(2), + dfault: uintPtr(2), + hardDefault: uintPtr(2), }, "main overrides default+hardDefault": { - want: uint(1), - maxTriesRoot: nil, - maxTriesMain: uintPtr(1), - maxTriesDefault: uintPtr(2), - maxTriesHardDefault: uintPtr(2), + want: uint(1), + main: uintPtr(1), + dfault: uintPtr(2), + hardDefault: uintPtr(2), }, "default overrides hardDefault": { - want: uint(1), - maxTriesRoot: nil, - maxTriesMain: nil, - maxTriesDefault: uintPtr(1), - maxTriesHardDefault: uintPtr(2), + want: uint(1), + dfault: uintPtr(1), + hardDefault: uintPtr(2), }, "hardDefault is last resort": { - want: uint(1), - maxTriesRoot: nil, - maxTriesMain: nil, - maxTriesDefault: nil, - maxTriesHardDefault: uintPtr(1), + want: uint(1), + hardDefault: uintPtr(1), }, } @@ -311,10 +282,10 @@ func TestWebHook_GetMaxTries(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.MaxTries = tc.maxTriesRoot - webhook.Main.MaxTries = tc.maxTriesMain - webhook.Defaults.MaxTries = tc.maxTriesDefault - webhook.HardDefaults.MaxTries = tc.maxTriesHardDefault + webhook.MaxTries = tc.root + webhook.Main.MaxTries = tc.main + webhook.Defaults.MaxTries = tc.dfault + webhook.HardDefaults.MaxTries = tc.hardDefault // WHEN GetMaxTries is called got := webhook.GetMaxTries() @@ -437,39 +408,33 @@ func TestWebHook_BuildRequest(t *testing.T) { func TestWebHook_GetType(t *testing.T) { // GIVEN a WebHook with Type in various locations tests := map[string]struct { - typeRoot string - typeMain string - typeDefault string - typeHardDefault string - want string + root string + main string + dfault string + hardDefault string + want string }{ "root overrides all": { - want: "github", - typeRoot: "github", - typeMain: "url", - typeDefault: "url", - typeHardDefault: "url", + want: "github", + root: "github", + main: "url", + dfault: "url", + hardDefault: "url", }, "main overrides default+hardDefault": { - want: "github", - typeRoot: "", - typeMain: "github", - typeDefault: "url", - typeHardDefault: "url", + want: "github", + main: "github", + dfault: "url", + hardDefault: "url", }, "default overrides hardDefault": { - want: "github", - typeRoot: "", - typeMain: "", - typeDefault: "github", - typeHardDefault: "url", + want: "github", + dfault: "github", + hardDefault: "url", }, "hardDefault is last resort": { - want: "github", - typeRoot: "", - typeMain: "", - typeDefault: "", - typeHardDefault: "github", + want: "github", + hardDefault: "github", }, } @@ -478,10 +443,10 @@ func TestWebHook_GetType(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.Type = tc.typeRoot - webhook.Main.Type = tc.typeMain - webhook.Defaults.Type = tc.typeDefault - webhook.HardDefaults.Type = tc.typeHardDefault + webhook.Type = tc.root + webhook.Main.Type = tc.main + webhook.Defaults.Type = tc.dfault + webhook.HardDefaults.Type = tc.hardDefault // WHEN GetType is called got := webhook.GetType() @@ -498,39 +463,44 @@ func TestWebHook_GetType(t *testing.T) { func TestWebHook_GetSecret(t *testing.T) { // GIVEN a WebHook with Secret in various locations tests := map[string]struct { - secretRoot string - secretMain string - secretDefault string - secretHardDefault string - want string + env map[string]string + root string + main string + dfault string + hardDefault string + want string }{ "root overrides all": { - want: "argus-secret", - secretRoot: "argus-secret", - secretMain: "unused", - secretDefault: "unused", - secretHardDefault: "unused", + want: "argus-secret", + root: "argus-secret", + main: "unused", + dfault: "unused", + hardDefault: "unused", }, "main overrides default+hardDefault": { - want: "argus-secret", - secretRoot: "", - secretMain: "argus-secret", - secretDefault: "unused", - secretHardDefault: "unused", + want: "argus-secret", + main: "argus-secret", + dfault: "unused", + hardDefault: "unused", }, "default overrides hardDefault": { - want: "argus-secret", - secretRoot: "", - secretMain: "", - secretDefault: "argus-secret", - secretHardDefault: "unused", + want: "argus-secret", + dfault: "argus-secret", + hardDefault: "unused", }, "hardDefault last resort": { - want: "argus-secret", - secretRoot: "", - secretMain: "", - secretDefault: "", - secretHardDefault: "argus-secret", + want: "argus-secret", + hardDefault: "argus-secret", + }, + "env var is used": { + env: map[string]string{"TESTWEBHOOK_GETSECRET_ONE": "argus-secret"}, + want: "argus-secret", + root: "${TESTWEBHOOK_GETSECRET_ONE}", + }, + "empty env var is ignored": { + want: "argus-secret", + root: "${TESTWEBHOOK_GETSECRET_UNSET}", + dfault: "argus-secret", }, } @@ -538,11 +508,15 @@ func TestWebHook_GetSecret(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } webhook := testWebHook(true, false, false) - webhook.Secret = tc.secretRoot - webhook.Main.Secret = tc.secretMain - webhook.Defaults.Secret = tc.secretDefault - webhook.HardDefaults.Secret = tc.secretHardDefault + webhook.Secret = tc.root + webhook.Main.Secret = tc.main + webhook.Defaults.Secret = tc.dfault + webhook.HardDefaults.Secret = tc.hardDefault // WHEN GetSecret is called got := webhook.GetSecret() @@ -559,39 +533,33 @@ func TestWebHook_GetSecret(t *testing.T) { func TestWebHook_GetSilentFails(t *testing.T) { // GIVEN a WebHook with SilentFails in various locations tests := map[string]struct { - silentFailsRoot *bool - silentFailsMain *bool - silentFailsDefault *bool - silentFailsHardDefault *bool - want bool + root *bool + main *bool + dfault *bool + hardDefault *bool + want bool }{ "root overrides all": { - want: true, - silentFailsRoot: boolPtr(true), - silentFailsMain: boolPtr(false), - silentFailsDefault: boolPtr(false), - silentFailsHardDefault: boolPtr(false), + want: true, + root: boolPtr(true), + main: boolPtr(false), + dfault: boolPtr(false), + hardDefault: boolPtr(false), }, "main overrides default+hardDefault": { - want: true, - silentFailsRoot: nil, - silentFailsMain: boolPtr(true), - silentFailsDefault: boolPtr(false), - silentFailsHardDefault: boolPtr(false), + want: true, + main: boolPtr(true), + dfault: boolPtr(false), + hardDefault: boolPtr(false), }, "default overrides hardDefault": { - want: true, - silentFailsRoot: nil, - silentFailsMain: nil, - silentFailsDefault: boolPtr(true), - silentFailsHardDefault: boolPtr(false), + want: true, + dfault: boolPtr(true), + hardDefault: boolPtr(false), }, "hardDefault is last resort": { - want: true, - silentFailsRoot: nil, - silentFailsMain: nil, - silentFailsDefault: nil, - silentFailsHardDefault: boolPtr(true), + want: true, + hardDefault: boolPtr(true), }, } @@ -600,10 +568,10 @@ func TestWebHook_GetSilentFails(t *testing.T) { t.Parallel() webhook := testWebHook(true, false, false) - webhook.SilentFails = tc.silentFailsRoot - webhook.Main.SilentFails = tc.silentFailsMain - webhook.Defaults.SilentFails = tc.silentFailsDefault - webhook.HardDefaults.SilentFails = tc.silentFailsHardDefault + webhook.SilentFails = tc.root + webhook.Main.SilentFails = tc.main + webhook.Defaults.SilentFails = tc.dfault + webhook.HardDefaults.SilentFails = tc.hardDefault // WHEN GetSilentFails is called got := webhook.GetSilentFails() @@ -620,55 +588,54 @@ func TestWebHook_GetSilentFails(t *testing.T) { func TestWebHook_GetURL(t *testing.T) { // GIVEN a WebHook with urls in various locations tests := map[string]struct { - urlRoot string - urlMain string - urlDefault string - urlHardDefault string - want string - latestVersion string + env map[string]string + root string + main string + dfault string + hardDefault string + want string + latestVersion string }{ "root overrides all": { - want: "https://release-argus.io", - urlRoot: "https://release-argus.io", - urlMain: "https://somewhere.com", - urlDefault: "https://somewhere.com", - urlHardDefault: "https://somewhere.com", + want: "https://release-argus.io", + root: "https://release-argus.io", + main: "https://somewhere.com", + dfault: "https://somewhere.com", + hardDefault: "https://somewhere.com", }, "main overrides default+hardDefault": { - want: "https://release-argus.io", - urlRoot: "", - urlMain: "https://release-argus.io", - urlDefault: "https://somewhere.com", - urlHardDefault: "https://somewhere.com", + want: "https://release-argus.io", + main: "https://release-argus.io", + dfault: "https://somewhere.com", + hardDefault: "https://somewhere.com", }, "default is last resort": { - want: "https://release-argus.io", - urlRoot: "", - urlMain: "", - urlDefault: "https://release-argus.io", - urlHardDefault: "https://somewhere.com", + want: "https://release-argus.io", + dfault: "https://release-argus.io", + hardDefault: "https://somewhere.com", }, "hardDefault last resort": { - want: "https://release-argus.io", - urlRoot: "", - urlMain: "", - urlDefault: "", - urlHardDefault: "https://release-argus.io", + want: "https://release-argus.io", + hardDefault: "https://release-argus.io", }, "uses latest_version": { - want: "https://release-argus.io/1.2.3", - urlRoot: "https://release-argus.io/{{ version }}", - urlMain: "", - urlDefault: "", - urlHardDefault: "", - latestVersion: "1.2.3", + want: "https://release-argus.io/1.2.3", + root: "https://release-argus.io/{{ version }}", + latestVersion: "1.2.3", }, "empty version when unfound": { - want: "https://release-argus.io/", - urlRoot: "https://release-argus.io/{{ version }}", - urlMain: "", - urlDefault: "", - urlHardDefault: "", + want: "https://release-argus.io/", + root: "https://release-argus.io/{{ version }}", + }, + "env var is used": { + env: map[string]string{"TESTWEBHOOK_GETURL_ONE": "https://release-argus.io"}, + want: "https://release-argus.io", + root: "${TESTWEBHOOK_GETURL_ONE}", + }, + "empty env var is ignored": { + want: "https://release-argus.io", + root: "${TESTWEBHOOK_GETURL_UNSET}", + dfault: "https://release-argus.io", }, } @@ -676,11 +643,15 @@ func TestWebHook_GetURL(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() + for k, v := range tc.env { + os.Setenv(k, v) + defer os.Unsetenv(k) + } webhook := testWebHook(true, false, false) - webhook.URL = tc.urlRoot - webhook.Main.URL = tc.urlMain - webhook.Defaults.URL = tc.urlDefault - webhook.HardDefaults.URL = tc.urlHardDefault + webhook.URL = tc.root + webhook.Main.URL = tc.main + webhook.Defaults.URL = tc.dfault + webhook.HardDefaults.URL = tc.hardDefault webhook.ServiceStatus.SetLatestVersion(tc.latestVersion, false) // WHEN GetURL is called diff --git a/webhook/headers_test.go b/webhook/headers_test.go index 28956a19..f7b3f58d 100644 --- a/webhook/headers_test.go +++ b/webhook/headers_test.go @@ -29,89 +29,89 @@ func TestWebHook_SetCustomHeaders(t *testing.T) { latestVersion := "1.2.3" serviceID := "service" tests := map[string]struct { - customHeaders *Headers - mainCustomHeaders *Headers - defaultCustomHeaders *Headers - wantHeaders map[string]string + root *Headers + main *Headers + dfault *Headers + want map[string]string }{ - "nil customHeaders": { - customHeaders: nil, + "nil root": { + root: nil, }, - "no customHeaders": { - customHeaders: &Headers{}, + "no root": { + root: &Headers{}, }, "standard headers": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Something", Value: "foo"}, {Key: "X-Service", Value: "test"}}, - wantHeaders: map[string]string{}, + want: map[string]string{}, }, "header with jinja expression": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Service", Value: "{% if 'a' == 'a' %}foo{% endif %}"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": "foo"}, }, "header with service id var": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID}, }, "header with version var": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, "header from .Main": { - mainCustomHeaders: &Headers{ + main: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, "header from .Defaults": { - defaultCustomHeaders: &Headers{ + dfault: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, "header from .CustomHeaders overrides those in .Main": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - mainCustomHeaders: &Headers{ + main: &Headers{ {Key: "X-Service", Value: "===="}, {Key: "X-Version", Value: "----"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, "header from .CustomHeaders overrides those in .Defaults": { - customHeaders: &Headers{ + root: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - defaultCustomHeaders: &Headers{ + dfault: &Headers{ {Key: "X-Service", Value: "===="}, {Key: "X-Version", Value: "----"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, "header from .Main overrides those in .Defaults": { - mainCustomHeaders: &Headers{ + main: &Headers{ {Key: "X-Service", Value: "{{ service_id }}"}, {Key: "X-Version", Value: "{{ version }}"}}, - defaultCustomHeaders: &Headers{ + dfault: &Headers{ {Key: "X-Service", Value: "===="}, {Key: "X-Version", Value: "----"}}, - wantHeaders: map[string]string{ + want: map[string]string{ "X-Service": serviceID, "X-Version": latestVersion}, }, @@ -133,27 +133,27 @@ func TestWebHook_SetCustomHeaders(t *testing.T) { &serviceID, &url) webhook.ServiceStatus.SetLatestVersion(latestVersion, false) - webhook.CustomHeaders = tc.customHeaders - webhook.Main.CustomHeaders = tc.mainCustomHeaders - webhook.Defaults.CustomHeaders = tc.defaultCustomHeaders + webhook.CustomHeaders = tc.root + webhook.Main.CustomHeaders = tc.main + webhook.Defaults.CustomHeaders = tc.dfault // WHEN setCustomHeaders is called on this request webhook.setCustomHeaders(req) // THEN the function returns the correct result - if tc.customHeaders == nil && tc.mainCustomHeaders == nil && tc.defaultCustomHeaders == nil { + if tc.root == nil && tc.main == nil && tc.dfault == nil { if len(req.Header) != 0 { t.Fatalf("custom headers was nil but Headers are %v", req.Header) } return } - if tc.wantHeaders == nil { - for _, header := range *tc.customHeaders { - tc.wantHeaders[header.Key] = header.Value + if tc.want == nil { + for _, header := range *tc.root { + tc.want[header.Key] = header.Value } } - for header, val := range tc.wantHeaders { + for header, val := range tc.want { if req.Header[header] == nil { t.Fatalf("%s: %s was not given to the request, got\n%v", header, val, req.Header)