package controller import ( "strings" "testing" "hy2xs-admin/credential" "hy2xs-admin/service" ) // Правила валидатора обязаны спрашивать КОНТРАКТ, а не собственную копию. // // Прежние версии этих проверок обращались к регекспу внутри слоя контроллеров и // потому не увидели бы расхождения между копией и правилом — а именно так этот // класс дефектов и появлялся: набор символов логина существовал в четырёх // местах, и все четыре разошлись. Сам контракт проверяется в пакете credential; // здесь проверяется ПРОВОДКА: тег валидатора и функция контракта отвечают // одинаково на одних и тех же значениях. func TestCredentialStrTagDelegatesToContract(t *testing.T) { for _, candidate := range []string{ "hy2xsadmin", "admin.ops", "admin+1", "admi", "админ12", "admin пробел", strings.Repeat("a", credential.AdminUsernameMaxLength+1), } { expected := credential.IsValidAdminUsername(candidate) if got := validate.Var(candidate, "credentialStr") == nil; got != expected { t.Errorf("тег credentialStr на %q дал %v, контракт — %v", candidate, got, expected) } } } // Тот же вопрос про пароль, и он важнее. // // Границы пароля стояли прямо в теге (`min=6,max=64`), и тег НЕ МОГ выразить // предел bcrypt в байтах — правила длины в байтах у go-playground/validator // нет. Поэтому пароль из 64 кириллических букв проходил валидацию формы и // падал в хешировании, возвращаясь оператору системной ошибкой. Проверка // закрепляет, что правило теперь одно и что оно то же самое, которым // пользуются bootstrap-установка и `reset-admin`. func TestAdminPasswordTagDelegatesToContract(t *testing.T) { for _, candidate := range []string{ "bootstrap-password", "abcde ", strings.Repeat("a", credential.AdminPasswordMinLength-1), strings.Repeat("a", credential.AdminPasswordMaxLength), strings.Repeat("a", credential.AdminPasswordMaxLength+1), strings.Repeat("я", 36), strings.Repeat("я", 37), strings.Repeat("😀", 18), strings.Repeat("😀", 19), strings.Repeat("я", credential.AdminPasswordMaxLength), "abcde\n", } { expected := credential.IsValidAdminPassword(candidate) if got := validate.Var(candidate, "adminPassword") == nil; got != expected { t.Errorf( "тег adminPassword на значении из %d символов / %d байт дал %v, контракт — %v", len([]rune(candidate)), len(candidate), got, expected, ) } } } // Имя пира проверяется ОДНИМ правилом на весь продукт: панель и импорт ведут в // одну таблицу и не имеют права требовать разного. func TestPeerNameRuleIsSharedWithImport(t *testing.T) { accepted := []string{ "client-01", "alpha1", "bootstrap-admin-peer", strings.Repeat("a", 6), strings.Repeat("a", 32), } for _, name := range accepted { if !service.IsValidPeerName(name) { t.Errorf("имя %q отклонено, ожидался приём", name) } } rejected := []string{ "", " ", "pc1", strings.Repeat("a", 33), "peer name", "peer\nname", "peer/name", "peer:name", "peer.name", "пир-01", } for _, name := range rejected { if service.IsValidPeerName(name) { t.Errorf("имя %q принято, ожидался отказ", name) } } }