config_test.go
package main
import (
"os"
"path/filepath"
"strings"
"testing"
)
// writeConfig drops config text into a temp dir and returns the file path.
func writeConfig(t *testing.T, text string) string {
t.Helper()
p := filepath.Join(t.TempDir(), "vitrine.cfg")
if err := os.WriteFile(p, []byte(text), 0o644); err != nil {
t.Fatal(err)
}
return p
}
func TestConfigValid(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /srv/demo
meta/research/thing:
path: /srv/thing
expose-git: true
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
if got := cfg.Exhibits["demo"]; got.Path != "/srv/demo" || got.ExposeGit {
t.Errorf("demo = %+v", got)
}
if got := cfg.Exhibits["meta/research/thing"]; got.Path != "/srv/thing" || !got.ExposeGit {
t.Errorf("meta/research/thing = %+v", got)
}
}
func TestConfigRawField(t *testing.T) {
p := writeConfig(t, `
exhibits:
assets:
path: /srv/assets
raw: true
docs:
path: /srv/docs
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
if !cfg.Exhibits["assets"].Raw {
t.Errorf("assets: raw = false, want true")
}
if cfg.Exhibits["docs"].Raw {
t.Errorf("docs: raw = true, want default false")
}
}
func TestConfigRelativePathResolvesAgainstConfigDir(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: sub/tree
updemo:
path: ../elsewhere
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
dir := filepath.Dir(p)
if want := filepath.Join(dir, "sub/tree"); cfg.Exhibits["demo"].Path != want {
t.Errorf("demo path = %q, want %q", cfg.Exhibits["demo"].Path, want)
}
if want := filepath.Join(filepath.Dir(dir), "elsewhere"); cfg.Exhibits["updemo"].Path != want {
t.Errorf("updemo path = %q, want %q", cfg.Exhibits["updemo"].Path, want)
}
}
func TestConfigUnknownFieldRejected(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /srv/demo
expose-gti: true
`)
if _, err := loadConfig(p); err == nil {
t.Fatal("want error for unknown field, got nil")
}
}
func TestConfigPercentPathMeansExhibitName(t *testing.T) {
p := writeConfig(t, `
exhibits:
meta/research/thing:
path: "%"
expose-git: true
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
want := filepath.Join(filepath.Dir(p), "meta/research/thing")
if got := cfg.Exhibits["meta/research/thing"]; got.Path != want || !got.ExposeGit {
t.Errorf("exhibit = %+v, want path %q", got, want)
}
}
func TestConfigMissingPathRejected(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
expose-git: true
bare-key:
`)
_, err := loadConfig(p)
if err == nil || !strings.Contains(err.Error(), "path") {
t.Fatalf("want missing-path error, got %v", err)
}
}
func TestConfigTrailingSlashesNormalized(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo/:
path: /srv/demo/
shorthand/:
path: "%"
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
if got, ok := cfg.Exhibits["demo"]; !ok || got.Path != "/srv/demo" {
t.Errorf("demo = %+v, ok=%v; want slash-trimmed name and path", got, ok)
}
want := filepath.Join(filepath.Dir(p), "shorthand")
if got, ok := cfg.Exhibits["shorthand"]; !ok || got.Path != want {
t.Errorf("shorthand = %+v, ok=%v; want path %q", got, ok, want)
}
}
func TestConfigSlashCollisionsRejected(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /srv/demo
demo/:
path: /srv/other
`)
_, err := loadConfig(p)
if err == nil || !strings.Contains(err.Error(), "duplicate") {
t.Fatalf("want duplicate error, got %v", err)
}
}
func TestConfigRootPathRejected(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /
`)
_, err := loadConfig(p)
if err == nil || !strings.Contains(err.Error(), "root") {
t.Fatalf("want filesystem-root error, got %v", err)
}
}
func TestConfigEmptyExhibitsRejected(t *testing.T) {
for name, text := range map[string]string{
"empty-map": "exhibits: {}\n",
"absent-key": "# nothing here\n",
} {
p := writeConfig(t, text)
if _, err := loadConfig(p); err == nil {
t.Errorf("%s: want error for empty exhibits, got nil", name)
}
}
}
func TestConfigMissingFileRejected(t *testing.T) {
if _, err := loadConfig(filepath.Join(t.TempDir(), "nope.cfg")); err == nil {
t.Fatal("want error for missing file, got nil")
}
}
func TestConfigForwardValid(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /srv/demo
forwards:
game:
to: http://localhost:8866
/:
to: https://localhost:9999/subpath
`)
cfg, err := loadConfig(p)
if err != nil {
t.Fatal(err)
}
if got := cfg.Forwards["game"].To; got != "http://localhost:8866" {
t.Errorf("game to = %q", got)
}
// The root forward normalizes to the empty name: the same
// no-leading-slash, no-trailing-slash form every other mount name uses.
if got := cfg.Forwards[""].To; got != "https://localhost:9999/subpath" {
t.Errorf("root to = %q; forwards = %+v", got, cfg.Forwards)
}
}
func TestConfigForwardsOnlyIsValid(t *testing.T) {
p := writeConfig(t, `
forwards:
/:
to: http://localhost:8866
`)
if _, err := loadConfig(p); err != nil {
t.Fatalf("a forwards-only config should load: %v", err)
}
}
func TestConfigForwardBadTargetsRejected(t *testing.T) {
for name, tc := range map[string]struct{ to, wantErr string }{
"missing": {"", "to"},
"schemeless": {"localhost:8866", "scheme"},
"relative": {"/some/path", "scheme"},
"ws": {"ws://localhost:8866", "Upgrade"},
"wss": {"wss://localhost:8866", "Upgrade"},
"ftp": {"ftp://localhost:21", "scheme"},
"no-host": {"http://", "host"},
} {
p := writeConfig(t, "forwards:\n fwd:\n to: \""+tc.to+"\"\n")
_, err := loadConfig(p)
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("%s (to=%q): want error containing %q, got %v", name, tc.to, tc.wantErr, err)
}
}
}
func TestConfigForwardExhibitCollisionRejected(t *testing.T) {
p := writeConfig(t, `
exhibits:
demo:
path: /srv/demo
forwards:
demo/:
to: http://localhost:8866
`)
_, err := loadConfig(p)
if err == nil || !strings.Contains(err.Error(), "collides") {
t.Fatalf("want collision error, got %v", err)
}
}
func TestConfigRootExhibitRejected(t *testing.T) {
// The root name is meaningful only for forwards; a root *exhibit* stays
// a misconfiguration, same as it always was (empty-after-trimming name).
p := writeConfig(t, `
exhibits:
/:
path: /srv/demo
`)
if _, err := loadConfig(p); err == nil {
t.Fatal("want error for root exhibit name, got nil")
}
}
config_test.go · 6.4 KiB · 1f54fea43d