docs: rewrite PLAN.md and update CODEBASE.md, README.md
- Full rewrite of PLAN.md to reflect current architecture (27 REST + 3 WS endpoints, embedded frontend, automation, scheduler, health check, GoReleaser CI) - Added health.go, mods.go, scheduler.go, robfig/cron dep to CODEBASE.md - Added Gitea Actions CI/CD section to CODEBASE.md - Added conventional commits to code style section - Added missing API routes and embed/ to README.md
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigManager_ListEmpty(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
configs, err := cm.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
if len(configs) != 0 {
|
||||
t.Errorf("List() returned %d configs, want 0", len(configs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_CreateAndGet(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
if err := cm.Create("server", "host = 0.0.0.0"); err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
}
|
||||
|
||||
content, err := cm.Get("server")
|
||||
if err != nil {
|
||||
t.Fatalf("Get() error = %v", err)
|
||||
}
|
||||
if content != "host = 0.0.0.0" {
|
||||
t.Errorf("Get() = %q, want %q", content, "host = 0.0.0.0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_CreateDuplicate(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
cm.Create("server", "content")
|
||||
err := cm.Create("server", "content2")
|
||||
if err == nil {
|
||||
t.Error("Create() should fail for existing config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_Update(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
cm.Create("server", "old")
|
||||
if err := cm.Update("server", "new"); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
content, _ := cm.Get("server")
|
||||
if content != "new" {
|
||||
t.Errorf("Get() after Update() = %q, want %q", content, "new")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_Delete(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
cm.Create("server", "content")
|
||||
if err := cm.Delete("server"); err != nil {
|
||||
t.Fatalf("Delete() error = %v", err)
|
||||
}
|
||||
|
||||
_, err := cm.Get("server")
|
||||
if err == nil {
|
||||
t.Error("Get() should fail after Delete()")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_Duplicate(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
cm.Create("server", "host = 0.0.0.0")
|
||||
if err := cm.Duplicate("server", "backup"); err != nil {
|
||||
t.Fatalf("Duplicate() error = %v", err)
|
||||
}
|
||||
|
||||
content, _ := cm.Get("backup")
|
||||
if content != "host = 0.0.0.0" {
|
||||
t.Errorf("Get(backup) = %q, want %q", content, "host = 0.0.0.0")
|
||||
}
|
||||
|
||||
configs, _ := cm.List()
|
||||
if len(configs) != 2 {
|
||||
t.Errorf("List() returned %d configs, want 2", len(configs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_ListWithMultipleFiles(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
cm.Create("server", "s")
|
||||
cm.Create("difficulty", "d")
|
||||
os.WriteFile(filepath.Join(dir, "notacfg.txt"), []byte("txt"), 0644)
|
||||
|
||||
configs, err := cm.List()
|
||||
if err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
if len(configs) != 2 {
|
||||
t.Errorf("List() returned %d configs, want 2 (should exclude non-.cfg)", len(configs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_GetNotFound(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
_, err := cm.Get("nonexistent")
|
||||
if err == nil {
|
||||
t.Error("Get() should fail for nonexistent config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManager_CreateWithPathTraversal(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cm := NewConfigManager(dir)
|
||||
|
||||
// Attempt to create with path traversal
|
||||
err := cm.Create("../../etc/passwd", "content")
|
||||
if err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify it was sanitized to just "passwd.cfg" in the cfgDir
|
||||
path := filepath.Join(dir, "passwd.cfg")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
t.Error("File should be created with sanitized name in cfgDir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeConfigName(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"server", "server"},
|
||||
{"../etc/passwd", "passwd"},
|
||||
{"../../secret.cfg", "secret"},
|
||||
{"server.cfg", "server"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := sanitizeConfigName(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("sanitizeConfigName(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user