Files
Megalodon/internal/config/config_test.go
T
VeillaxandClaude Sonnet 5.5 0d2cb31abf
CI / test (push) Successful in 2m26s
Release / release (push) Successful in 2m36s
Megalodon: manage ARK servers over RCON from a web dashboard
Watches any number of ARK:SE servers (RCON for players and chat, the query
port for map, version and player cap), records joins, leaves, chat and an
audit trail, and keeps a player database across servers.

Control: save, broadcast, chat, whisper, kick, time of day, MOTD, wild dino
wipe, a raw console with history and macros, restarts with timed warnings and
a world save, optional start command and auto-start. Bans are kept by
Megalodon: timed, cluster-wide and re-enforced when the player returns.

Automation: schedules (every N or at times of day), Discord/ntfy/webhook
notifications, welcome message, chat word filter.

Access: viewer, moderator and admin roles, API tokens, Prometheus metrics.
cmd/fakeark simulates a server for trying it out.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 23:36:13 -06:00

92 lines
3.1 KiB
Go

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")
}
}