package classes import ( "context" "os" "path/filepath" "strings" "testing" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/compass" "git.golem15.com/golem15/summercms/modules/pact" "git.golem15.com/golem15/summercms/modules/signpost" ) // testService is a service over an application config without a database: // env is SUMMER_ENV, site the golem15.seo.site_url ("" leaves it unset), // environ more SUMMER_ variables. func testService(t *testing.T, env, site string, environ ...string) *Service { t.Helper() svc, _ := testApp(t, env, site, environ...) return svc } func testApp(t *testing.T, env, site string, environ ...string) (*Service, *backpack.App) { t.Helper() dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "app.yaml"), []byte("name: seo-unit\nlocale: en\nfallback_locale: en\n"), 0o644); err != nil { t.Fatal(err) } vars := append([]string{"SUMMER_ENV=" + env}, environ...) if site != "" { vars = append(vars, "SUMMER_GOLEM15__SEO__SITE_URL="+site) } cfg, err := compass.Open(compass.Options{Dir: dir, Environ: vars}) if err != nil { t.Fatal(err) } app := backpack.New(cfg) svc, err := NewService(app) if err != nil { t.Fatal(err) } return svc, app } // testPage is a record of the pages content type (/pages/:slug): a path // may carry a /pl prefix; a published record is matched exactly on its own // slug in the path's locale and inexactly on another of its slugs; a draft // is never matched. type testPage struct { id string slugs map[string]string // locale -> slug published bool } type typesPlugin struct{ types []pact.MenuItemType } func (typesPlugin) ID() string { return "acme.pages" } func (p typesPlugin) MenuItemTypes() []pact.MenuItemType { return p.types } func testPagesType(pages ...testPage) pact.MenuItemType { find := func(slug string) (testPage, string, bool) { for _, p := range pages { for locale, s := range p.slugs { if s == slug { return p, locale, true } } } return testPage{}, "", false } return pact.MenuItemType{ Key: "acme-page", DefaultPattern: "/pages/:slug", Resolve: func(context.Context, pact.MenuItemResolve) (*pact.ResolvedMenuItem, error) { return nil, nil }, Content: &pact.ContentDescriptor{ Model: "acme_pages", Controller: "acme.pages.pages", Describe: func(_ context.Context, in pact.ContentDescribe) (*pact.ContentRecord, error) { for _, p := range pages { if p.id == in.ID { rec := &pact.ContentRecord{ID: p.id, Published: p.published} for locale, slug := range p.slugs { rec.Locales = append(rec.Locales, pact.ContentLocale{Locale: locale, URL: "/pages/" + slug, Title: "Page " + p.id}) } return rec, nil } } return nil, nil }, Match: func(_ context.Context, in pact.ContentMatch) (*pact.ContentMatchResult, error) { locale, path := "en", in.Path if rest, ok := strings.CutPrefix(path, "/pl/"); ok { locale, path = "pl", "/"+rest } values, ok := signpost.MatchPattern(in.Pattern, path) if !ok { return nil, nil } p, own, ok := find(values["slug"]) if !ok || !p.published { return nil, nil } return &pact.ContentMatchResult{ID: p.id, Locale: locale, Exact: own == locale}, nil }, }, } } // testServiceWithPages is testService with the pages content type // registered in signpost. func testServiceWithPages(t *testing.T, site string, pages ...testPage) *Service { t.Helper() svc, app := testApp(t, "production", site) if err := signpost.Activate(app, []typesPlugin{{types: []pact.MenuItemType{testPagesType(pages...)}}}); err != nil { t.Fatal(err) } return svc } // dryDB is a Postgres-dialect GORM handle that never connects: every query // is built and not run, so reads find no rows. func dryDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(postgres.New(postgres.Config{DSN: "host=127.0.0.1 port=1 user=seo dbname=seo sslmode=disable"}), &gorm.Config{DryRun: true, DisableAutomaticPing: true, Logger: logger.Discard}) if err != nil { t.Fatal(err) } return db }