package services // Pure-Go tests for RenderSpec. import ( "errors" "testing" ) func TestRenderSpec_HappyPath(t *testing.T) { s := DefaultRenderSpec() if err := s.Validate(); err != nil { t.Fatalf("default render spec must validate: %v", err) } } func TestRenderSpec_ShapeMustBeKnown(t *testing.T) { cases := []RenderShape{ShapeList, ShapeCards, ShapeCalendar} for _, sh := range cases { t.Run(string(sh), func(t *testing.T) { s := RenderSpec{Shape: sh} if err := s.Validate(); err != nil { t.Fatalf("shape %q must validate: %v", sh, err) } }) } } func TestRenderSpec_UnknownShapeRejects(t *testing.T) { s := RenderSpec{Shape: "kanban"} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown shape must reject, got %v", err) } } func TestRenderSpec_ListColumnEnum(t *testing.T) { s := RenderSpec{Shape: ShapeList, List: &ListConfig{Columns: []string{"date", "bogus"}}} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown list column must reject, got %v", err) } } func TestRenderSpec_KnownListColumnsAccepted(t *testing.T) { s := RenderSpec{Shape: ShapeList, List: &ListConfig{Columns: KnownListColumns}} if err := s.Validate(); err != nil { t.Fatalf("known columns must validate: %v", err) } } func TestRenderSpec_ListSortEnum(t *testing.T) { s := RenderSpec{Shape: ShapeList, List: &ListConfig{Sort: "weird"}} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown sort must reject, got %v", err) } } func TestRenderSpec_ListDensityEnum(t *testing.T) { s := RenderSpec{Shape: ShapeList, List: &ListConfig{Density: "huge"}} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown density must reject, got %v", err) } } func TestRenderSpec_CardsGroupByEnum(t *testing.T) { s := RenderSpec{Shape: ShapeCards, Cards: &CardsConfig{GroupBy: "month"}} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown group_by must reject, got %v", err) } } func TestRenderSpec_CalendarViewEnum(t *testing.T) { s := RenderSpec{Shape: ShapeCalendar, Calendar: &CalendarConfig{DefaultView: "year"}} if err := s.Validate(); !errors.Is(err, ErrInvalidInput) { t.Fatalf("unknown default_view must reject, got %v", err) } } func TestRenderSpec_RoundTrip(t *testing.T) { original := RenderSpec{ Shape: ShapeList, List: &ListConfig{ Columns: []string{"time", "actor", "title", "project"}, Sort: SortDateDesc, Density: DensityCompact, }, Cards: &CardsConfig{GroupBy: CardsGroupByDay, Sort: SortDateAsc}, Calendar: &CalendarConfig{DefaultView: CalendarMonth}, } b, err := MarshalRenderSpec(original) if err != nil { t.Fatalf("marshal: %v", err) } parsed, err := UnmarshalRenderSpec(b) if err != nil { t.Fatalf("unmarshal: %v", err) } if parsed.Shape != original.Shape { t.Errorf("shape mismatch: %v vs %v", parsed.Shape, original.Shape) } if parsed.List == nil || parsed.List.Density != DensityCompact { t.Errorf("list config not preserved: %+v", parsed.List) } }