Веб-морда: поиск, фильтры, сортировка и пагинация клиентов; раскладка настроек
- список клиентов: поиск по домену и названию, фильтр по статусу (чипы со счётчиками), сортировка по любой колонке, пагинация 10/25/50/100; состояние в URL — отфильтрованный вид можно сохранить ссылкой - размеры последних бэкапов одним запросом вместо запроса на клиента - шапка выровнена по колонке контента; настройки на всю ширину: ntfy и Telegram двумя карточками, на узком экране — друг под другом Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c6942605c1
commit
83832fc734
8 files changed
+626
-179
No files matched your search
@@ -0,0 +1,87 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func testClients() ([]*Client, map[int64]int64) {
|
||||
now := time.Now().Unix()
|
||||
var cs []*Client
|
||||
sizes := map[int64]int64{}
|
||||
for i := 1; i <= 30; i++ {
|
||||
c := &Client{ID: int64(i), Domain: fmt.Sprintf("srv%02d.example.com", i), Name: fmt.Sprintf("srv%02d", i),
|
||||
StaleHours: 26, LastBackupAt: now - int64(i)*60, CreatedAt: now}
|
||||
sizes[c.ID] = int64(i) * 1000
|
||||
cs = append(cs, c)
|
||||
}
|
||||
cs[0].Name = "Нидерланды" // srv01
|
||||
cs[1].LastBackupAt = 0 // srv02 — never
|
||||
cs[2].LastErrorAt = now // srv03 — fail
|
||||
cs[3].Disabled = true // srv04 — off
|
||||
cs[4].LastBackupAt = now - 100*3600 // srv05 — stale
|
||||
return cs, sizes
|
||||
}
|
||||
|
||||
func view(t *testing.T, rawQuery string) *clientsView {
|
||||
t.Helper()
|
||||
cs, sizes := testClients()
|
||||
r := httptest.NewRequest("GET", "/?"+rawQuery, nil)
|
||||
return buildClientsView(parseClientsQuery(r), cs, sizes)
|
||||
}
|
||||
|
||||
func TestClientsSearchFilter(t *testing.T) {
|
||||
v := view(t, "q=нидер")
|
||||
if v.Found != 1 || v.Rows[0].Domain != "srv01.example.com" {
|
||||
t.Fatalf("поиск по названию: %d", v.Found)
|
||||
}
|
||||
if v := view(t, "q=SRV1"); v.Found != 10 { // srv10..srv19
|
||||
t.Fatalf("поиск без учёта регистра: %d", v.Found)
|
||||
}
|
||||
v = view(t, "status=fail")
|
||||
if v.Found != 1 || v.Rows[0].Domain != "srv03.example.com" {
|
||||
t.Fatalf("фильтр fail: %+v", v.Found)
|
||||
}
|
||||
chips := map[string]int{}
|
||||
for _, c := range v.Chips {
|
||||
chips[c.Key] = c.Count
|
||||
}
|
||||
if chips[""] != 30 || chips["never"] != 1 || chips["off"] != 1 || chips["stale"] != 1 || chips["ok"] != 26 {
|
||||
t.Fatalf("счётчики: %v", chips)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientsSortAndPaging(t *testing.T) {
|
||||
v := view(t, "")
|
||||
if v.Found != 30 || len(v.Rows) != 25 || v.Rows[0].Domain != "srv01.example.com" || v.Next == "" {
|
||||
t.Fatalf("первая страница: found=%d rows=%d", v.Found, len(v.Rows))
|
||||
}
|
||||
v = view(t, "page=2")
|
||||
if len(v.Rows) != 5 || v.From != 26 || v.To != 30 || v.Prev == "" || v.Next != "" {
|
||||
t.Fatalf("вторая страница: %+v", v)
|
||||
}
|
||||
if v := view(t, "page=99"); v.Query.Page != 2 {
|
||||
t.Fatalf("страница за пределами: %d", v.Query.Page)
|
||||
}
|
||||
v = view(t, "sort=size&per=10")
|
||||
if v.Rows[0].Domain != "srv30.example.com" || len(v.Rows) != 10 {
|
||||
t.Fatalf("размер по убыванию по умолчанию: %s", v.Rows[0].Domain)
|
||||
}
|
||||
if v := view(t, "sort=size&dir=asc"); v.Rows[0].Domain != "srv01.example.com" {
|
||||
t.Fatalf("размер по возрастанию: %s", v.Rows[0].Domain)
|
||||
}
|
||||
if v := view(t, "sort=status"); v.Rows[0].StatusClass != "fail" {
|
||||
t.Fatalf("сортировка по статусу: %s", v.Rows[0].StatusClass)
|
||||
}
|
||||
// повторный клик по текущей колонке меняет направление
|
||||
if u := view(t, "sort=size").Query.SortURL("size"); !strings.Contains(u, "dir=asc") {
|
||||
t.Fatalf("SortURL: %s", u)
|
||||
}
|
||||
// фильтр сохраняется в ссылках пагинации
|
||||
if u := view(t, "q=srv&status=ok").Query.PageURL(2); !strings.Contains(u, "q=srv") || !strings.Contains(u, "status=ok") {
|
||||
t.Fatalf("PageURL: %s", u)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user