mirror of
https://github.com/aziis98/statuspage.git
synced 2026-10-07 07:05:07 +00:00
- ui: add uptime% chip (green >=99, red below) after ssh; remove "up" word - ui: all shadows use #0004 shadow-color; modal shadow-lg derives from it - ui: status modal draws a datetime tick mark every 20 history entries from the newest - server: drop max_points/downsampling/bucketing; metrics served raw, plots share a ~24h window - docs: fix ps --no-headers in top example; document raw metrics API - server: inject <meta name=title/description> into index.html from config title + per-group host patterns (shared with --check recap)
213 lines
4.6 KiB
Go
213 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestExpandHostPattern(t *testing.T) {
|
|
tests := []struct {
|
|
pattern string
|
|
want []string
|
|
err string
|
|
}{
|
|
{
|
|
pattern: "server{1..3}.example.org",
|
|
want: []string{"server1.example.org", "server2.example.org", "server3.example.org"},
|
|
},
|
|
{
|
|
pattern: "server{1,2,3}.example.org",
|
|
want: []string{"server1.example.org", "server2.example.org", "server3.example.org"},
|
|
},
|
|
{
|
|
pattern: "server{2..5,8..10}.example.org",
|
|
want: []string{
|
|
"server2.example.org", "server3.example.org", "server4.example.org",
|
|
"server5.example.org", "server8.example.org", "server9.example.org", "server10.example.org",
|
|
},
|
|
},
|
|
{
|
|
pattern: "server-{a3,a4}-{1..2}.example.org",
|
|
want: []string{
|
|
"server-a3-1.example.org",
|
|
"server-a3-2.example.org",
|
|
"server-a4-1.example.org",
|
|
"server-a4-2.example.org",
|
|
},
|
|
},
|
|
{
|
|
pattern: "node{01..03}",
|
|
want: []string{"node01", "node02", "node03"},
|
|
},
|
|
{
|
|
pattern: "router.example.net",
|
|
want: []string{"router.example.net"},
|
|
},
|
|
{
|
|
pattern: "host-{1..2}-{a,b}",
|
|
want: []string{"host-1-a", "host-1-b", "host-2-a", "host-2-b"},
|
|
},
|
|
{
|
|
pattern: "{1..10..2}.example.org",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
{
|
|
pattern: "host-{5..2}.example.org",
|
|
err: "5 > 2",
|
|
},
|
|
{
|
|
pattern: "host-{x..y}.example.org",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
{
|
|
pattern: "host-{1,2.example.org",
|
|
err: "unmatched '{'",
|
|
},
|
|
{
|
|
pattern: "host-{1..2-{3..4}}.example.org",
|
|
err: "nested",
|
|
},
|
|
{
|
|
pattern: "{{",
|
|
err: "unmatched '{'",
|
|
},
|
|
{
|
|
pattern: "{{1,2}}",
|
|
err: "nested",
|
|
},
|
|
{
|
|
pattern: "{a}{{b}}",
|
|
err: "nested",
|
|
},
|
|
{
|
|
pattern: "{1..5..10}",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
{
|
|
pattern: "{1...5}",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
{
|
|
pattern: "{1..2..3}",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
{
|
|
pattern: "{1..2.5}",
|
|
err: "endpoints must be numbers",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got, err := expandHostPattern(tt.pattern)
|
|
if tt.err != "" {
|
|
if err == nil {
|
|
t.Errorf("%q: expected error containing %q, got nil", tt.pattern, tt.err)
|
|
} else if !strings.Contains(err.Error(), tt.err) {
|
|
t.Errorf("%q: expected error containing %q, got %q", tt.pattern, tt.err, err)
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
t.Errorf("%q: unexpected error: %v", tt.pattern, err)
|
|
continue
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("%q:\n got %v\nwant %v", tt.pattern, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStatusPayloadExpandsGlobs(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.yaml")
|
|
cfg := `
|
|
groups:
|
|
- name: Aula 3
|
|
machines:
|
|
- host: a3-dott{1..3}.cs.dm.unipi.it
|
|
machines:
|
|
- host: router.example.net
|
|
`
|
|
if err := os.WriteFile(path, []byte(cfg), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conf, err := LoadConfig(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mon := NewMonitor(conf, nil)
|
|
p := mon.StatusPayload()
|
|
|
|
if len(p.Groups) != 1 {
|
|
t.Fatalf("want 1 group, got %d", len(p.Groups))
|
|
}
|
|
g := p.Groups[0]
|
|
want := []string{
|
|
"a3-dott1.cs.dm.unipi.it",
|
|
"a3-dott2.cs.dm.unipi.it",
|
|
"a3-dott3.cs.dm.unipi.it",
|
|
}
|
|
if len(g.Machines) != len(want) {
|
|
t.Fatalf("group machines: want %d, got %d", len(want), len(g.Machines))
|
|
}
|
|
for i, m := range g.Machines {
|
|
if m.Host != want[i] {
|
|
t.Errorf("machine %d: host %q, want %q", i, m.Host, want[i])
|
|
}
|
|
}
|
|
if len(p.Machines) != 1 {
|
|
t.Fatalf("top-level machines: want 1, got %d", len(p.Machines))
|
|
}
|
|
if p.Machines[0].Host != "router.example.net" {
|
|
t.Errorf("top-level host: %q", p.Machines[0].Host)
|
|
}
|
|
}
|
|
|
|
func TestStatusPayloadUptime(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfgPath := filepath.Join(dir, "config.yaml")
|
|
cfg := `
|
|
machines:
|
|
- host: a3-dott1.cs.dm.unipi.it
|
|
`
|
|
if err := os.WriteFile(cfgPath, []byte(cfg), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conf, err := LoadConfig(cfgPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dbPath := filepath.Join(dir, "h.db")
|
|
hist, err := NewHistory(dbPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer hist.Close()
|
|
id := conf.Machines[0].ID
|
|
for i := 0; i < 10; i++ {
|
|
hist.Record(id, statusUp, "")
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
hist.Record(id, statusDown, "")
|
|
}
|
|
hist.Record(id, statusDegraded, "")
|
|
|
|
mon := NewMonitor(conf, hist)
|
|
p := mon.StatusPayload()
|
|
if len(p.Machines) != 1 {
|
|
t.Fatalf("want 1 machine, got %d", len(p.Machines))
|
|
}
|
|
ms := p.Machines[0]
|
|
if ms.Uptime == nil {
|
|
t.Fatal("expected uptime")
|
|
}
|
|
want := float64(10) / float64(15) * 100
|
|
if math.Abs(*ms.Uptime-want) > 0.001 {
|
|
t.Errorf("uptime %v, want %v", *ms.Uptime, want)
|
|
}
|
|
}
|