panel public release
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user