feat: sitemap settings singleton with content types and additional addresses
- one settings tile opening the golem15.sitemap.settings singleton - Files tab (site address, file size, S1 files partial), Additional addresses repeater, Content types tab built from signpost at boot - golem15_sitemap_types; a type switched off leaves the files (reason type) - a save validates (errors.*) and dispatches a rebuild after commit
This commit is contained in:
@@ -10,9 +10,6 @@ import (
|
||||
"git.golem15.com/golem15/summercms/modules/lagoon"
|
||||
)
|
||||
|
||||
// cabana calls the hook on a settings save through this interface.
|
||||
var _ lagoon.HasBeforeValidate = (*models.Settings)(nil)
|
||||
|
||||
// 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
|
||||
@@ -67,31 +64,59 @@ func TestAllowedFrontendURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSettingsBeforeValidate pins the save check of T-17-15: the hook cabana
|
||||
// calls on a settings save passes a null, empty or accepted frontend_url and
|
||||
// answers a validation error keyed frontend_url for any other value.
|
||||
func TestSettingsBeforeValidate(t *testing.T) {
|
||||
// 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 }
|
||||
for _, url := range []*string{nil, text(""), text("https://www.ex.pl/")} {
|
||||
if err := (&models.Settings{FrontendURL: url}).BeforeValidate(nil); err != nil {
|
||||
t.Errorf("BeforeValidate(%q) = %v, want nil", models.Text(url), err)
|
||||
ok := func(s models.Settings) models.Settings {
|
||||
if s.FileSize == 0 {
|
||||
s.FileSize = 1000
|
||||
}
|
||||
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": {FileSize: 1},
|
||||
"file size 50 000": {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)
|
||||
}
|
||||
}
|
||||
want := map[string]any{"frontend_url": []string{"golem15.sitemap::lang.errors.frontend_url"}}
|
||||
for _, url := range []string{
|
||||
"javascript:alert(document.domain)//",
|
||||
`https://x"/><y a="`,
|
||||
"0",
|
||||
" https://www.ex.pl",
|
||||
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{}, map[string]any{"file_size": []string{"golem15.sitemap::lang.errors.file_size"}}},
|
||||
"file size 50 001": {models.Settings{FileSize: 50001}, 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 := (&models.Settings{FrontendURL: &url}).BeforeValidate(nil)
|
||||
err := c.s.Validate()
|
||||
var invalid *cabana.ValidationError
|
||||
if !errors.As(err, &invalid) {
|
||||
t.Errorf("BeforeValidate(%q) = %v, want a *cabana.ValidationError", url, err)
|
||||
t.Errorf("%s: Validate = %v, want a *cabana.ValidationError", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(invalid.Details, want) {
|
||||
t.Errorf("BeforeValidate(%q) details = %v, want %v", url, invalid.Details, want)
|
||||
if !reflect.DeepEqual(invalid.Details, c.want) {
|
||||
t.Errorf("%s: details = %v, want %v", name, invalid.Details, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user