package signpost_test import ( "context" "reflect" "testing" "time" "git.golem15.com/golem15/summercms/modules/pact" ) // fullRecord is a record that sets every optional descriptor fact: first // publication, two candidate parents, an author with a page of their own, // two images (one decorative with an empty alt), a video and a heading // outline under a level-2 top heading. func fullRecord(id string) *pact.ContentRecord { published := time.Date(2026, 10, 1, 9, 30, 0, 0, time.UTC) changed := time.Date(2026, 10, 3, 12, 0, 0, 0, time.UTC) words := 420 return &pact.ContentRecord{ ID: id, Published: true, LastMod: &changed, PublishedAt: &published, Parents: []pact.ContentRef{ {Type: "acme-category", ID: "3"}, {Type: "acme-category", ID: "8"}, }, Authors: []pact.ContentPerson{{Name: "Ada Lovelace", Ref: pact.ContentRef{Type: "acme-author", ID: "12"}}}, TopHeading: 2, Locales: []pact.ContentLocale{{ Locale: "en", URL: "/articles/" + id, Title: "Engines", Words: &words, Images: []pact.ContentImage{ {URL: "/media/engine.jpg", Alt: "The analytical engine", Width: 1200, Height: 800}, {URL: "/media/rule.png", Decorative: true}, }, Videos: []pact.ContentVideo{{Provider: "youtube", ID: "abc123"}}, Headings: []pact.ContentHeading{ {Level: 2, Text: "Origins"}, {Level: 3, Text: "The mill"}, {Level: 2, Text: "Legacy"}, }, }}, } } // TestContentDescriptorFields: a type that sets the optional descriptor // facts (Kind, Tools and the record and locale fields) hands every value // through signpost.Describe and signpost.Records unchanged, and ContentType // exposes Kind and Tools; a type that sets none of them describes exactly // as before, with nil and empty slices treated alike. func TestContentDescriptorFields(t *testing.T) { full := pact.MenuItemType{ Key: "acme-article", DefaultPattern: "/articles/:slug", Resolve: echo, Content: &pact.ContentDescriptor{ Model: "acme_articles", Kind: pact.ContentKindArticle, Tools: map[string]string{pact.ContentToolEdit: "acme_update_page"}, Describe: func(_ context.Context, in pact.ContentDescribe) (*pact.ContentRecord, error) { return fullRecord(in.ID), nil }, Records: func(_ context.Context, in pact.ContentList) (pact.ContentPage, error) { return pact.ContentPage{Records: []pact.ContentRecord{*fullRecord("1")}}, nil }, }, } plainRecord := func(id string) *pact.ContentRecord { return &pact.ContentRecord{ID: id, Published: true, Locales: []pact.ContentLocale{{Locale: "en", URL: "/pages/" + id, Title: "About"}}} } plain := pact.MenuItemType{ Key: "acme-page", DefaultPattern: "/pages/:slug", Resolve: echo, Content: &pact.ContentDescriptor{ Model: "acme_pages", Describe: func(_ context.Context, in pact.ContentDescribe) (*pact.ContentRecord, error) { return plainRecord(in.ID), nil }, Records: func(_ context.Context, in pact.ContentList) (pact.ContentPage, error) { return pact.ContentPage{Records: []pact.ContentRecord{*plainRecord("1")}}, nil }, }, } reg := contentRegistry(t, "", full, plain) ctx := context.Background() t.Run("full type hands every value through Describe", func(t *testing.T) { got, err := reg.Describe(ctx, "acme-article", "5", "") if err != nil { t.Fatalf("Describe: %v", err) } if want := fullRecord("5"); !reflect.DeepEqual(got, want) { t.Fatalf("Describe = %+v, want %+v", got, want) } if got.TopHeading != 2 || len(got.Parents) != 2 || got.Parents[0].ID != "3" { t.Fatalf("record facts lost: TopHeading %d, Parents %+v", got.TopHeading, got.Parents) } if a := got.Authors[0]; a.Name != "Ada Lovelace" || a.Ref != (pact.ContentRef{Type: "acme-author", ID: "12"}) { t.Fatalf("author = %+v", a) } loc := got.Locales[0] if len(loc.Images) != 2 || !loc.Images[1].Decorative || loc.Images[1].Alt != "" || loc.Images[0].Width != 1200 { t.Fatalf("images = %+v", loc.Images) } if len(loc.Videos) != 1 || loc.Videos[0] != (pact.ContentVideo{Provider: "youtube", ID: "abc123"}) { t.Fatalf("videos = %+v", loc.Videos) } if len(loc.Headings) != 3 || loc.Headings[1] != (pact.ContentHeading{Level: 3, Text: "The mill"}) { t.Fatalf("headings = %+v", loc.Headings) } }) t.Run("full type hands every value through Records", func(t *testing.T) { page, err := reg.Records(ctx, "acme-article", "", 10, "") if err != nil { t.Fatalf("Records: %v", err) } if len(page.Records) != 1 || !reflect.DeepEqual(page.Records[0], *fullRecord("1")) { t.Fatalf("Records = %+v", page.Records) } }) t.Run("ContentType exposes Kind and Tools", func(t *testing.T) { typ, ok := reg.ContentType("acme-article") if !ok { t.Fatal("ContentType(acme-article) not found") } if typ.Content.Kind != "article" { t.Fatalf("Kind = %q", typ.Content.Kind) } if !reflect.DeepEqual(typ.Content.Tools, map[string]string{"edit": "acme_update_page"}) { t.Fatalf("Tools = %v", typ.Content.Tools) } }) t.Run("type without the facts describes as before", func(t *testing.T) { got, err := reg.Describe(ctx, "acme-page", "4", "") if err != nil { t.Fatalf("Describe: %v", err) } if !reflect.DeepEqual(got, plainRecord("4")) { t.Fatalf("Describe = %+v", got) } if got.PublishedAt != nil || got.Parents != nil || got.Authors != nil || got.TopHeading != 0 { t.Fatalf("plain record gained facts: %+v", got) } loc := got.Locales[0] if loc.Images != nil || loc.Videos != nil || loc.Headings != nil { t.Fatalf("plain locale gained facts: %+v", loc) } page, err := reg.Records(ctx, "acme-page", "", 10, "") if err != nil || len(page.Records) != 1 || !reflect.DeepEqual(page.Records[0], *plainRecord("1")) { t.Fatalf("Records = %+v, %v", page, err) } typ, _ := reg.ContentType("acme-page") if typ.Content.Kind != "" || typ.Content.Tools != nil { t.Fatalf("plain type: Kind %q, Tools %v", typ.Content.Kind, typ.Content.Tools) } if k, _ := reg.ContentTypeForModel("acme_pages"); k.Key != "acme-page" { t.Fatalf("ContentTypeForModel = %q", k.Key) } }) t.Run("nil and empty slices are handed through alike", func(t *testing.T) { empty := pact.MenuItemType{ Key: "acme-note", DefaultPattern: "/notes/:slug", Resolve: echo, Content: &pact.ContentDescriptor{ Model: "acme_notes", Tools: map[string]string{}, Describe: func(_ context.Context, in pact.ContentDescribe) (*pact.ContentRecord, error) { return &pact.ContentRecord{ ID: in.ID, Parents: []pact.ContentRef{}, Authors: []pact.ContentPerson{}, Locales: []pact.ContentLocale{{Locale: "en", Images: []pact.ContentImage{}, Videos: []pact.ContentVideo{}, Headings: []pact.ContentHeading{}}}, }, nil }, }, } r := contentRegistry(t, "", empty) got, err := r.Describe(ctx, "acme-note", "1", "") if err != nil { t.Fatalf("Describe: %v", err) } if len(got.Parents) != 0 || len(got.Authors) != 0 || len(got.Locales[0].Images) != 0 || len(got.Locales[0].Videos) != 0 || len(got.Locales[0].Headings) != 0 { t.Fatalf("empty slices grew: %+v", got) } typ, _ := r.ContentType("acme-note") if len(typ.Content.Tools) != 0 { t.Fatalf("Tools = %v", typ.Content.Tools) } }) }