Files
panel/pkg/module/render_test.go
T
2026-07-14 23:37:25 -07:00

154 lines
4.6 KiB
Go

package module
import (
"os"
"path/filepath"
"strings"
"testing"
)
// TestRenderWritesTemplate verifies the Go text/template pipeline:
// - template file read from manifest.Dir/<cf.Template>
// - values flow via .Values.key
// - output written under dataPath/<cf.Path>, with sub-dirs created
// - missing keys render as empty (missingkey=zero)
func TestRenderWritesTemplate(t *testing.T) {
dir := t.TempDir()
// Simulate a module directory with a template subdir.
if err := os.MkdirAll(filepath.Join(dir, "templates"), 0o755); err != nil {
t.Fatal(err)
}
tmplPath := filepath.Join(dir, "templates", "demo.conf.tmpl")
tmplBody := "name={{ .Values.name }}\npassword={{ .Values.password }}\nmissing={{ .Values.nope }}END\n"
if err := os.WriteFile(tmplPath, []byte(tmplBody), 0o644); err != nil {
t.Fatal(err)
}
m := &Manifest{
ID: "demo",
Name: "Demo",
Dir: dir,
ConfigFiles: []ConfigFile{
{Path: "conf/demo.conf", Format: "kv", Template: "templates/demo.conf.tmpl"},
},
}
dataPath := filepath.Join(t.TempDir(), "instance-data")
values := map[string]string{
"name": "hello",
"password": "s3cret",
}
if err := Render(m, dataPath, values); err != nil {
t.Fatalf("Render: %v", err)
}
out, err := os.ReadFile(filepath.Join(dataPath, "conf", "demo.conf"))
if err != nil {
t.Fatalf("read rendered: %v", err)
}
got := string(out)
if !strings.Contains(got, "name=hello") {
t.Errorf("missing 'name=hello' in %q", got)
}
if !strings.Contains(got, "password=s3cret") {
t.Errorf("missing 'password=s3cret' in %q", got)
}
if !strings.Contains(got, "missing=END") {
t.Errorf("missing-key didn't render as empty in %q", got)
}
}
// TestRenderSkipsConfigFilesWithoutTemplate verifies a ConfigFile entry
// without a Template is left alone (operator edits it directly, for example
// via SFTP/file manager).
func TestRenderSkipsConfigFilesWithoutTemplate(t *testing.T) {
m := &Manifest{
ID: "demo",
Name: "Demo",
Dir: t.TempDir(),
ConfigFiles: []ConfigFile{
{Path: "manual.cfg", Format: "kv"}, // no Template
},
}
dataPath := filepath.Join(t.TempDir(), "instance-data")
if err := Render(m, dataPath, nil); err != nil {
t.Errorf("Render with no-template ConfigFile should not error: %v", err)
}
if _, err := os.Stat(filepath.Join(dataPath, "manual.cfg")); err == nil {
t.Error("should not have written manual.cfg")
}
}
func TestGenerateSecretsUnique(t *testing.T) {
m := &Manifest{
Secrets: []Secret{
{Name: "telnet_password", Generated: true},
{Name: "admin_token", Generated: true},
{Name: "unused_static", Generated: false},
},
}
secrets, err := GenerateSecrets(m)
if err != nil {
t.Fatalf("GenerateSecrets: %v", err)
}
if len(secrets) != 2 {
t.Errorf("got %d secrets, want 2 (only Generated=true)", len(secrets))
}
if secrets["telnet_password"] == "" || secrets["admin_token"] == "" {
t.Errorf("empty secret values: %+v", secrets)
}
if secrets["telnet_password"] == secrets["admin_token"] {
t.Errorf("two secrets collided: %q", secrets["telnet_password"])
}
if _, ok := secrets["unused_static"]; ok {
t.Errorf("non-generated secret should not be in output")
}
}
// TestRenderAgainst7DTDTemplate is an integration smoke test that renders
// the shipped 7 Days to Die template with a plausible value set and asserts
// the resulting XML contains the expected property values.
func TestRenderAgainst7DTDTemplate(t *testing.T) {
m, err := LoadFile(filepath.Join("..", "..", "modules", "7dtd", "module.yaml"))
if err != nil {
t.Skipf("7dtd manifest not loadable: %v", err)
}
// The template path under the manifest may not exist yet during early
// development. Skip cleanly if so.
var tmplRef, outRel string
for _, cf := range m.ConfigFiles {
if cf.Template != "" {
tmplRef = filepath.Join(m.Dir, cf.Template)
outRel = cf.Path
break
}
}
if tmplRef == "" {
t.Skip("7dtd manifest has no config_files with templates")
}
if _, err := os.Stat(tmplRef); err != nil {
t.Skipf("7dtd template not present: %v", err)
}
dataPath := t.TempDir()
values := map[string]string{
"server_name": "Refuge Test",
"telnet_password": "test-pw-123",
"max_players": "16",
"world_seed": "Navezgane",
}
if err := Render(m, dataPath, values); err != nil {
t.Fatalf("Render 7dtd: %v", err)
}
out, err := os.ReadFile(filepath.Join(dataPath, outRel))
if err != nil {
t.Fatalf("read: %v", err)
}
got := string(out)
for _, needle := range []string{"Refuge Test", "test-pw-123", `value="16"`} {
if !strings.Contains(got, needle) {
t.Errorf("rendered serverconfig.xml missing %q; got:\n%s", needle, got)
}
}
}