package config import ( "path/filepath" "strings" "testing" ) func TestPasswordRoundTrip(t *testing.T) { h, err := HashPassword("correct horse") if err != nil { t.Fatal(err) } if !CheckPassword(h, "correct horse") || CheckPassword(h, "wrong") || CheckPassword("garbage", "x") { t.Fatal("password check is wrong") } } func TestRoles(t *testing.T) { if !Admin.Allows(Moderator) || !Moderator.Allows(Viewer) || Viewer.Allows(Moderator) || Role("").Allows(Viewer) || Role("root").Allows(Viewer) { t.Fatal("role ordering is wrong") } } func TestValidate(t *testing.T) { good := Config{Servers: []Server{{ID: "island", Host: "10.0.0.2", RCONPort: 27020}}} good.Defaults() if err := good.Validate(); err != nil { t.Fatal(err) } cases := map[string]func(c *Config){ "bad id": func(c *Config) { c.Servers[0].ID = "The Island" }, "duplicate id": func(c *Config) { c.Servers = append(c.Servers, c.Servers[0]) }, "no host": func(c *Config) { c.Servers[0].Host = "" }, "bad port": func(c *Config) { c.Servers[0].RCONPort = 70000 }, "bad user role": func(c *Config) { c.Users = []User{{Name: "a", Role: "root"}} }, "job no timing": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "save"}} }, "job both": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "save", Every: "1h", At: []string{"04:00"}}} }, "job too often": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "save", Every: "5s"}} }, "job bad time": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "save", At: []string{"4am"}}} }, "job bad server": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "save", Every: "1h", Servers: []string{"nope"}}} }, "job no message": func(c *Config) { c.Jobs = []Job{{ID: "j", Name: "j", Action: "broadcast", Every: "1h"}} }, "notify bad url": func(c *Config) { c.Notify = []Notify{{Name: "n", Kind: "discord", URL: "javascript:alert(1)"}} }, "filter action": func(c *Config) { c.ChatFilter.Action = "nuke" }, } for name, mutate := range cases { c := clone(good) mutate(&c) if err := c.Validate(); err == nil { t.Errorf("%s: expected an error", name) } } } func TestUpdatePersistsAndRejects(t *testing.T) { path := filepath.Join(t.TempDir(), "megalodon.json") h, err := Load(path) if err != nil { t.Fatal(err) } if h.Exists() { t.Fatal("file should not exist before the first update") } err = h.Update(func(c *Config) error { c.Servers = append(c.Servers, Server{ID: "island", Host: "ark.lan", Password: "secret"}) return nil }) if err != nil { t.Fatal(err) } if err := h.Update(func(c *Config) error { c.Servers[0].ID = "BAD ID"; return nil }); err == nil || !strings.Contains(err.Error(), "server id") { t.Fatalf("invalid update accepted: %v", err) } h2, err := Load(path) if err != nil { t.Fatal(err) } c := h2.Get() if len(c.Servers) != 1 || c.Servers[0].ID != "island" || c.Servers[0].Password != "secret" || c.Servers[0].RCONPort != 27020 { t.Fatalf("reloaded config is wrong: %+v", c.Servers) } // Get returns a copy. c.Servers[0].Host = "changed" if h2.Get().Servers[0].Host != "ark.lan" { t.Fatal("Get leaked internal state") } }