Files
sm-sitemap-plugin/models/settings_test.go
Jakub Zych 8f4ff2a97d feat: HTML sitemap groups API
- GET /_sitemap/api/v1/html?locale= answers the overview: the listed
  addresses of the XML sitemaps by group, in the order pages, offers,
  services, categories (as a tree), posts (newest first), tags, authors,
  other content types, each with its title in the locale, total, the
  first html_page_size links and more
- GET /_sitemap/api/v1/html/{group}?locale=&page= answers one page of a
  group; past the last page, an unknown group or the HTML sitemap off
  answer 404; every answer is noindex; per-IP bucket sitemap-html
- the HTML sitemap tab: html_enabled, the address per locale (html_paths)
  and html_page_size 50 to 500, with errors.html_path and errors.page_size
- contract.Listing.Settings answers HTMLEnabled, HTMLPaths, HTMLPageSize
  and KeyFile
2026-11-04 12:32:47 +01:00

126 lines
4.7 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package models_test
import (
"errors"
"reflect"
"testing"
"git.golem15.com/golem15/sm-sitemap-plugin/models"
"git.golem15.com/golem15/summercms/modules/cabana"
"git.golem15.com/golem15/summercms/modules/lagoon"
)
// TestAllowedFrontendURL pins the one definition of a frontend_url that may
// be saved, imported and applied (T-17-15): empty, or an absolute http(s)
// URL with a host and without whitespace, control characters, a double
// quote, <, > or &.
func TestAllowedFrontendURL(t *testing.T) {
for _, u := range []string{
"",
"https://www.ex.pl",
"https://www.ex.pl/",
"https://www.ex.pl///",
"http://localhost:3000",
"HTTPS://WWW.EX.PL",
"https://www.ex.pl/app",
"https://www.ex.pl:8443/base/",
"https://[::1]:3000",
// The frontend URLs of the golden inputs.
"https://api.example.test",
"https://www.example.test/",
} {
if !models.AllowedFrontendURL(u) {
t.Errorf("AllowedFrontendURL(%q) = false, want true", u)
}
}
for _, u := range []string{
"0",
"javascript:alert(document.domain)//",
"JavaScript://www.ex.pl/%0aalert(1)",
`https://x"/><y a="`,
"https://www.ex.pl/?a=1&b=2",
"https://www.ex.pl <x>",
" https://www.ex.pl",
"https://www.ex.pl\n",
"https://www.ex.pl\t",
"https://www.ex.pl ",
"https://www.ex.pl\x7f",
"https://www.ex.pl\xff",
"//www.ex.pl",
"www.ex.pl",
"/path",
"https:///x",
"https://:8080",
"https:www.ex.pl",
"http://",
"https://",
"https://%zz",
"data:text/html,x",
"ftp://www.ex.pl",
} {
if models.AllowedFrontendURL(u) {
t.Errorf("AllowedFrontendURL(%q) = true, want false", u)
}
}
}
// TestSettingsValidate pins the save checks of the settings singleton
// (UI-SPEC C26, T-33-12): the check the controller runs before a save passes
// a null, empty or accepted site_url, a file size from 1 to 50 000 and
// additional addresses that are paths or http(s) URLs, and answers a
// validation error keyed by the field for any other value.
func TestSettingsValidate(t *testing.T) {
text := func(s string) *string { return &s }
ok := func(s models.Settings) models.Settings {
if s.FileSize == 0 {
s.FileSize = 1000
}
if s.HTMLPageSize == 0 {
s.HTMLPageSize = models.DefaultHTMLPageSize
}
return s
}
extra := func(urls ...string) lagoon.Jsonable[[]models.ExtraURL] {
rows := []models.ExtraURL{}
for _, u := range urls {
rows = append(rows, models.ExtraURL{URL: u})
}
return lagoon.Jsonable[[]models.ExtraURL]{Data: rows, Valid: true}
}
for name, s := range map[string]models.Settings{
"null site_url": ok(models.Settings{}),
"empty site_url": ok(models.Settings{SiteURL: text("")}),
"site_url": ok(models.Settings{SiteURL: text("https://www.ex.pl/")}),
"file size 1": ok(models.Settings{FileSize: 1}),
"file size 50 000": ok(models.Settings{FileSize: 50000}),
"extra addresses": ok(models.Settings{ExtraURLs: extra("/", "/o-nas?x=1&y=2", "https://www.ex.pl/kontakt")}),
} {
if err := s.Validate(); err != nil {
t.Errorf("%s: Validate = %v, want nil", name, err)
}
}
for name, c := range map[string]struct {
s models.Settings
want map[string]any
}{
"javascript site_url": {ok(models.Settings{SiteURL: text("javascript:alert(document.domain)//")}), map[string]any{"site_url": []string{"golem15.sitemap::lang.errors.frontend_url"}}},
"attribute site_url": {ok(models.Settings{SiteURL: text(`https://x"/><y a="`)}), map[string]any{"site_url": []string{"golem15.sitemap::lang.errors.frontend_url"}}},
"path site_url": {ok(models.Settings{SiteURL: text("/x")}), map[string]any{"site_url": []string{"golem15.sitemap::lang.errors.frontend_url"}}},
"file size 0": {models.Settings{HTMLPageSize: 150}, map[string]any{"file_size": []string{"golem15.sitemap::lang.errors.file_size"}}},
"file size 50 001": {models.Settings{FileSize: 50001, HTMLPageSize: 150}, map[string]any{"file_size": []string{"golem15.sitemap::lang.errors.file_size"}}},
"javascript address": {ok(models.Settings{ExtraURLs: extra("/", "javascript:alert(1)")}), map[string]any{"extra_urls.1.url": []string{"golem15.sitemap::lang.errors.url_scheme"}}},
"protocol-relative": {ok(models.Settings{ExtraURLs: extra("//evil.test/x")}), map[string]any{"extra_urls.0.url": []string{"golem15.sitemap::lang.errors.url_scheme"}}},
"address with a space": {ok(models.Settings{ExtraURLs: extra("/a b")}), map[string]any{"extra_urls.0.url": []string{"golem15.sitemap::lang.errors.url_scheme"}}},
} {
err := c.s.Validate()
var invalid *cabana.ValidationError
if !errors.As(err, &invalid) {
t.Errorf("%s: Validate = %v, want a *cabana.ValidationError", name, err)
continue
}
if !reflect.DeepEqual(invalid.Details, c.want) {
t.Errorf("%s: details = %v, want %v", name, invalid.Details, c.want)
}
}
}