package seo_test import ( "context" "encoding/json" "net/http" "testing" "git.golem15.com/golem15/sm-seo-plugin/classes" "git.golem15.com/golem15/sm-seo-plugin/models" "git.golem15.com/golem15/summercms/modules/lagoon" "gorm.io/gorm" ) // autolinksOf reads the resolve answer's autolinks as phrase → url, and // its autolink_max (-1 when absent). func autolinksOf(t *testing.T, got map[string]any) (map[string]string, int) { t.Helper() out := map[string]string{} if list, ok := got["autolinks"].([]any); ok { for _, item := range list { m, _ := item.(map[string]any) phrase, _ := m["phrase"].(string) url, _ := m["url"].(string) out[phrase] = url } } most := -1 if n, ok := got["autolink_max"].(float64); ok { most = int(n) } return out, most } // saveAutolink writes an auto-link through the service, as the screen and // the API do. func (e *testEnv) saveAutolink(t *testing.T, a models.Autolink) models.Autolink { t.Helper() svc := e.seoPlugin(t).Service() err := lagoon.Transaction(t.Context(), e.db, func(ctx context.Context, tx *gorm.DB) error { return svc.SaveAutolink(classes.WithTx(ctx, tx), tx, &a) }) if err != nil { t.Fatalf("save auto-link %q: %v", a.Phrase, err) } return a } // TestAutolinksTracer is the tracer of D-82's auto-links: an editor adds // the phrase "lwia grzywa" for every locale, and the next page that // resolves carries it with autolink_max; the page it links to does not, // nor does a disabled phrase or a phrase of another locale; autolink_max 0 // answers none. func TestAutolinksTracer(t *testing.T) { env := bootEnv(t) token := env.admin(t, true) env.expect(t, http.StatusCreated, http.MethodPost, "/_seo/api/v1/pages", token, map[string]any{"url": "/grzyby/lwia-grzywa", "title": "Lwia grzywa"}) env.createPage(t, token, map[string]any{"title": "Other", "slug": "other", "published": true}) env.saveAutolink(t, models.Autolink{Phrase: " lwia grzywa ", URL: "/grzyby/lwia-grzywa", Enabled: true}) env.saveAutolink(t, models.Autolink{Phrase: "boczniak", URL: "/grzyby/boczniak", Enabled: false}) env.saveAutolink(t, models.Autolink{Phrase: "lion's mane", URL: "/lions-mane", Locale: "en", Enabled: true}) links, most := autolinksOf(t, env.resolve(t, "/pl/pages/other")) if most != 3 || len(links) != 1 || links["lwia grzywa"] != "/grzyby/lwia-grzywa" { t.Fatalf("pl page autolinks = %v, autolink_max %d; want only lwia grzywa and 3", links, most) } links, _ = autolinksOf(t, env.resolve(t, "/pages/other")) if len(links) != 2 || links["lion's mane"] != "/lions-mane" || links["lwia grzywa"] == "" { t.Fatalf("en page autolinks = %v, want lwia grzywa and the en phrase", links) } got := env.resolve(t, "/grzyby/lwia-grzywa") if got["action"] != "ok" { t.Fatalf("registry page resolves %v", got) } if links, _ = autolinksOf(t, got); links["lwia grzywa"] != "" { t.Fatalf("the target page carries its own phrase: %v", links) } if got := env.resolve(t, "/pages/missing"); got["autolinks"] != nil || got["autolink_max"] != nil { t.Fatalf("a not_found answer carries auto-links: %v", got) } var revisions int64 env.db.Raw(`SELECT COUNT(*) FROM golem15_seo_revisions WHERE subject_kind = 'autolink'`).Scan(&revisions) if revisions != 3 { t.Fatalf("auto-link history rows = %d, want 3", revisions) } env.expect(t, http.StatusOK, http.MethodPut, "/_seo/api/v1/settings", token, map[string]any{"autolink_max": 0}) got = env.resolve(t, "/pl/pages/other") if links, most = autolinksOf(t, got); most != 0 || len(links) != 0 { raw, _ := json.Marshal(got) t.Fatalf("autolink_max 0 answers %s", raw) } rec := env.call(t, http.MethodPut, "/_seo/api/v1/settings", token, map[string]any{"autolink_max": 21}) if rec.Code != http.StatusUnprocessableEntity { t.Fatalf("autolink_max 21 = %d %s, want 422", rec.Code, rec.Body.String()) } }