summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-20 22:31:47 +0300
committerPaul Buetow <paul@buetow.org>2026-07-20 22:31:47 +0300
commiteec5d1c0c024dde767667b1c6f066af8975a51aa (patch)
treed03bee567bd5e279753d02ac74c4ad499749e2c9 /internal
parentfaed27641eb18ff8267cb2757d3447675555b39d (diff)
Show recently reporting hosts with separate markerHEAD0.5.5master
Diffstat (limited to 'internal')
-rw-r--r--internal/goprecords/report.go19
-rw-r--r--internal/goprecords/report_test.go51
-rw-r--r--internal/version/version.go2
3 files changed, 66 insertions, 6 deletions
diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go
index 531cfc3..bc752f5 100644
--- a/internal/goprecords/report.go
+++ b/internal/goprecords/report.go
@@ -5,6 +5,7 @@ import (
"io"
"sort"
"strings"
+ "time"
)
type metricExtractor struct {
@@ -145,6 +146,7 @@ func wrapHTMLDocument(body string) string {
b.WriteString("<title>goprecords uptime report</title>\n")
b.WriteString(htmlStyle)
b.WriteString("</head>\n<body>\n")
+ b.WriteString("<p><strong>Host markers:</strong> * current uptime record; + recent upload with stale or not-yet-recorded uptime.</p>\n")
b.WriteString(body)
b.WriteString("</body>\n</html>\n")
return b.String()
@@ -322,10 +324,6 @@ func (r reportBuilder) buildHostTable() ([]tableRow, bool, bool) {
break
}
h := kv.agg
- active := " "
- if h.IsActive(90) {
- active = "*"
- }
lastUpdated := ""
if !h.LastUpdated.IsZero() {
lastUpdated = h.LastUpdated.UTC().Format("2006-01-02 15:04")
@@ -333,7 +331,7 @@ func (r reportBuilder) buildHostTable() ([]tableRow, bool, bool) {
}
rows = append(rows, tableRow{
Pos: fmt.Sprintf("%d.", i+1),
- Name: active + h.Stats.Name,
+ Name: hostStatusMarker(h) + h.Stats.Name,
Value: r.humanStrHost(h),
LastKernel: h.LastKernel,
LastUpdated: lastUpdated,
@@ -345,6 +343,17 @@ func (r reportBuilder) buildHostTable() ([]tableRow, bool, bool) {
return rows, true, hasLastUpdated
}
+func hostStatusMarker(h *HostAggregate) string {
+ if h.IsActive(90) {
+ return "*"
+ }
+ age := time.Since(h.LastUpdated)
+ if !h.LastUpdated.IsZero() && age >= 0 && age < 24*time.Hour {
+ return "+"
+ }
+ return " "
+}
+
func (r reportBuilder) buildCategoryTable() ([]tableRow, bool) {
m := r.aggregates.Kernel
switch r.category {
diff --git a/internal/goprecords/report_test.go b/internal/goprecords/report_test.go
index 68371ee..51e5917 100644
--- a/internal/goprecords/report_test.go
+++ b/internal/goprecords/report_test.go
@@ -93,6 +93,50 @@ func TestReportWithData(t *testing.T) {
}
}
+func TestHostStatusMarker(t *testing.T) {
+ now := time.Now()
+ tests := []struct {
+ name string
+ lastSeen uint64
+ lastUpdated time.Time
+ want string
+ }{
+ {name: "current uptime record", lastSeen: uint64(now.Unix()), lastUpdated: now.Add(-time.Hour), want: "*"},
+ {name: "recent upload with stale record", lastSeen: uint64(now.Add(-100 * 24 * time.Hour).Unix()), lastUpdated: now.Add(-time.Hour), want: "+"},
+ {name: "stale upload and record", lastSeen: uint64(now.Add(-100 * 24 * time.Hour).Unix()), lastUpdated: now.Add(-25 * time.Hour), want: " "},
+ {name: "future upload timestamp", lastSeen: uint64(now.Add(-100 * 24 * time.Hour).Unix()), lastUpdated: now.Add(time.Hour), want: " "},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ host := NewHostAggregate("host", "Linux")
+ host.Stats.LastSeen = tt.lastSeen
+ host.LastUpdated = tt.lastUpdated
+ if got := hostStatusMarker(host); got != tt.want {
+ t.Fatalf("hostStatusMarker() = %q, want %q", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestReportMarksRecentUploadWithStaleRecord(t *testing.T) {
+ aggs := &Aggregates{
+ Host: make(map[string]*HostAggregate),
+ Kernel: make(map[string]*Aggregate),
+ KernelMajor: make(map[string]*Aggregate),
+ KernelName: make(map[string]*Aggregate),
+ }
+ host := NewHostAggregate("reporting", "FreeBSD 14.2")
+ host.Stats.LastSeen = uint64(time.Now().Add(-100 * 24 * time.Hour).Unix())
+ host.LastUpdated = time.Now().Add(-time.Hour)
+ aggs.Host[host.Stats.Name] = host
+
+ report := NewHostReporter(aggs, 20, MetricUptime, FormatPlaintext, 1).Report()
+ if !strings.Contains(report, "+reporting") {
+ t.Fatalf("expected recent-upload marker in report, got %q", report)
+ }
+}
+
func TestReportHTML(t *testing.T) {
aggs := &Aggregates{
Host: make(map[string]*HostAggregate),
@@ -122,6 +166,13 @@ func TestReportHTML(t *testing.T) {
}
}
+func TestHTMLDocumentExplainsHostMarkers(t *testing.T) {
+ report := wrapHTMLDocument("<p>report</p>")
+ if !strings.Contains(report, "* current uptime record; + recent upload") {
+ t.Fatalf("expected host marker legend, got %q", report)
+ }
+}
+
func TestReportLastUpdated(t *testing.T) {
aggs := &Aggregates{
Host: make(map[string]*HostAggregate),
diff --git a/internal/version/version.go b/internal/version/version.go
index 9a3f8a1..9fbb409 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -1,4 +1,4 @@
package version
// Tag is the application release version.
-const Tag = "0.5.4"
+const Tag = "0.5.5"