Files
ImaGen/internal/config/config_test.go
mAi 237270b204 mAi: #211 - bootstrap ImaGen framework skeleton
First step of the model-agnostic image-generation framework. Lands the
plumbing other components (skill, ComfyUI/Replicate adapters, agents)
will plug into:

- internal/backend: Backend interface (Request/Result), thread-safe
  Registry with init-time Register, plus a Mock reference adapter that
  emits a deterministic gradient PNG for smoke tests.
- internal/config: YAML loader for ~/.config/imagen.yaml. Framework owns
  default_backend + output settings + a per-backend block; each adapter
  owns the schema below its own block via BackendSpec.Raw.
- internal/output: filename templating ({date}/{time}/{slug}/{seed}/
  {backend}/{ext}), JSON metadata sidecar, --output override path.
- internal/prompt: embedded styles.yaml, style-preset suffix application.
- internal/server: 501 stub — HTTP surface lands in a follow-up issue.
- cmd/imagen: generate / backends / config (init|validate|path) / serve
  / version subcommands. Stdlib-only flag parsing with a small helper to
  honour positional prompt args ahead of flags (matches the issue spec).
- Tests for output (slug, naming template, sidecar), backend (mock PNG
  validity + determinism, registry build + duplicate panic), config
  (round-trip + validation), prompt (style apply + unknown-style error).
- CLAUDE.md, README.md, docs/architecture.md, docs/usage.md, Makefile.

Acceptance criteria from #211:
1. go build ./... — clean
2. imagen backends — lists registered backends, exits 0
3. imagen generate "test prompt" --backend mock --output /tmp/x.png —
   writes a 1024x1024 PNG plus an x.png.json sidecar
4. imagen config init | imagen config validate — round-trips cleanly
5. CLAUDE.md "Adding a new adapter" — six-step recipe
2026-05-08 14:37:05 +02:00

71 lines
1.6 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
)
func TestLoadAndValidate(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "imagen.yaml")
if err := os.WriteFile(path, []byte(Sample), 0o644); err != nil {
t.Fatalf("write sample: %v", err)
}
cfg, err := Load(path)
if err != nil {
t.Fatalf("Load: %v", err)
}
if cfg.DefaultBackend != "mock" {
t.Errorf("default = %q", cfg.DefaultBackend)
}
mock, ok := cfg.Backends["mock"]
if !ok {
t.Fatalf("mock backend missing")
}
if mock.Type != "mock" {
t.Errorf("mock type = %q", mock.Type)
}
flux, ok := cfg.Backends["flux-schnell-local"]
if !ok {
t.Fatalf("flux backend missing")
}
if flux.Raw["base_url"] != "http://mrock:8188" {
t.Errorf("flux base_url = %v", flux.Raw["base_url"])
}
}
func TestValidateRejectsUnknownDefault(t *testing.T) {
c := &Config{
DefaultBackend: "ghost",
Backends: map[string]BackendSpec{"real": {Type: "mock"}},
}
if err := c.Validate(); err == nil {
t.Errorf("expected error for unknown default_backend")
}
}
func TestValidateRejectsMissingType(t *testing.T) {
c := &Config{
Backends: map[string]BackendSpec{"x": {}},
}
if err := c.Validate(); err == nil {
t.Errorf("expected error for missing type")
}
}
func TestExpandPath(t *testing.T) {
home, _ := os.UserHomeDir()
cases := map[string]string{
"": "",
"/abs/path": "/abs/path",
"~": home,
"~/foo/bar": filepath.Join(home, "foo/bar"),
}
for in, want := range cases {
if got := ExpandPath(in); got != want {
t.Errorf("ExpandPath(%q) = %q, want %q", in, got, want)
}
}
}