vitrine

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

config_test.go · 3.8 KiB · bd3b610b14