Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 9 additions & 25 deletions grafana-alertcheck/cmd/grafana-alertcheck/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"bytes"
"errors"
"os"
"strings"
"testing"

"github.com/smartcontractkit/chainlink-testing-framework/grafana-alertcheck/internal/gate"
"github.com/stretchr/testify/require"
)

// The exit-code mapping, pinned directly against exitCode with no network
Expand All @@ -27,19 +27,15 @@ func TestExitCode(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := exitCode(tt.res, tt.err); got != tt.want {
t.Fatalf("exitCode(...) = %d, want %d", got, tt.want)
}
require.Equal(t, tt.want, exitCode(tt.res, tt.err))
})
}
}

func writeTempAlerts(t *testing.T) string {
t.Helper()
path := t.TempDir() + "/alerts.txt"
if err := os.WriteFile(path, []byte("Some Alert\n"), 0o644); err != nil {
t.Fatal(err)
}
require.NoError(t, os.WriteFile(path, []byte("Some Alert\n"), 0o644))
return path
}

Expand Down Expand Up @@ -100,12 +96,8 @@ func TestRunCheck_FlagValidation(t *testing.T) {
var stdout, stderr bytes.Buffer
args := append([]string{"check"}, tt.args(t)...)
code := run(args, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2; stderr = %q", code, stderr.String())
}
if !strings.Contains(stderr.String(), tt.wantErr) {
t.Fatalf("stderr = %q, want it to contain %q", stderr.String(), tt.wantErr)
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), tt.wantErr)
})
}
}
Expand All @@ -121,12 +113,8 @@ func TestRunCheck_ToInPastNoLog(t *testing.T) {
"--from", "1999-01-01T00:00:00Z", "--to", "2000-01-01T00:00:00Z",
"--alerts", writeTempAlerts(t),
}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2; stderr = %q", code, stderr.String())
}
if !strings.Contains(stderr.String(), "already passed") {
t.Fatalf("stderr = %q, want the past-`to` refusal", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), "already passed")
}

// --output json never writes to stdout when Check was never reached, because
Expand All @@ -137,10 +125,6 @@ func TestRunCheck_NoResultOnConfigError(t *testing.T) {
t.Setenv("GRAFANA_TOKEN", "")
var stdout, stderr bytes.Buffer
code := run([]string{"check", "--to", "2026-01-01T00:00:00Z", "--output", "json"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
if stdout.Len() != 0 {
t.Fatalf("stdout = %q, want empty", stdout.String())
}
require.Equal(t, 2, code)
require.Empty(t, stdout.String())
}
31 changes: 9 additions & 22 deletions grafana-alertcheck/cmd/grafana-alertcheck/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

const rulerBody = `{
Expand Down Expand Up @@ -60,19 +61,11 @@ func TestRunList_HappyPath(t *testing.T) {

var stdout, stderr bytes.Buffer
code := run([]string{"list"}, &stdout, &stderr)
if code != 0 {
t.Fatalf("code = %d, want 0; stderr = %q", code, stderr.String())
}
require.Equal(t, 0, code)
out := stdout.String()
if !strings.Contains(out, "rule0000006a") {
t.Errorf("stdout = %q, want it to list rule0000006a", out)
}
if !strings.Contains(out, "Example No Gateways Available") {
t.Errorf("stdout = %q, want it to list the rule title", out)
}
if !strings.Contains(out, "grafana-managed") {
t.Errorf("stdout = %q, want it to name the rule kind", out)
}
require.Contains(t, out, "rule0000006a")
require.Contains(t, out, "Example No Gateways Available")
require.Contains(t, out, "grafana-managed")
}

func TestRunList_UnsupportedVersion(t *testing.T) {
Expand All @@ -82,12 +75,8 @@ func TestRunList_UnsupportedVersion(t *testing.T) {

var stdout, stderr bytes.Buffer
code := run([]string{"list"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
if !strings.Contains(stderr.String(), "12.5.0") {
t.Fatalf("stderr = %q, want it to name the unsupported version", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), "12.5.0")
}

func TestRunList_RejectsArgs(t *testing.T) {
Expand All @@ -96,7 +85,5 @@ func TestRunList_RejectsArgs(t *testing.T) {

var stdout, stderr bytes.Buffer
code := run([]string{"list", "extra"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
require.Equal(t, 2, code)
}
39 changes: 11 additions & 28 deletions grafana-alertcheck/cmd/grafana-alertcheck/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,42 @@ package main

import (
"bytes"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestRun_NoArgs(t *testing.T) {
var stdout, stderr bytes.Buffer
code := run(nil, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
if !strings.Contains(stderr.String(), "usage") {
t.Fatalf("stderr = %q, want a usage message", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), "usage")
}

func TestRun_Help(t *testing.T) {
for _, flag := range []string{"-h", "-help", "--help"} {
t.Run(flag, func(t *testing.T) {
var stdout, stderr bytes.Buffer
code := run([]string{flag}, &stdout, &stderr)
if code != 0 {
t.Fatalf("code = %d, want 0 (requested help is not a could-not-check condition)", code)
}
if !strings.Contains(stdout.String(), "usage") {
t.Fatalf("stdout = %q, want a usage message", stdout.String())
}
if stderr.String() != "" {
t.Fatalf("stderr = %q, want empty — help goes to stdout", stderr.String())
}
require.Equal(t, 0, code, "requested help is not a could-not-check condition")
require.Contains(t, stdout.String(), "usage")
require.Empty(t, stderr.String(), "help goes to stdout")
})
}
}

func TestRun_UnknownSubcommand(t *testing.T) {
var stdout, stderr bytes.Buffer
code := run([]string{"bogus"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
if !strings.Contains(stderr.String(), `"bogus"`) {
t.Fatalf("stderr = %q, want it to name the unknown subcommand", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), `"bogus"`)
}

func TestRun_List_MissingEnv(t *testing.T) {
t.Setenv("GRAFANA_URL", "")
t.Setenv("GRAFANA_TOKEN", "")
var stdout, stderr bytes.Buffer
code := run([]string{"list"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2", code)
}
if !strings.Contains(stderr.String(), "GRAFANA_URL") {
t.Fatalf("stderr = %q, want it to name the missing env var", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), "GRAFANA_URL")
}
71 changes: 22 additions & 49 deletions grafana-alertcheck/cmd/grafana-alertcheck/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package main

import (
"bytes"
"strings"
"testing"
"time"

"github.com/smartcontractkit/chainlink-testing-framework/grafana-alertcheck/internal/gate"
"github.com/stretchr/testify/require"
)

// The golden table test: a fixed Result renders a deterministic, ordered rule
Expand Down Expand Up @@ -46,68 +46,41 @@ func TestRenderTable(t *testing.T) {
}

var buf bytes.Buffer
if err := renderTable(&buf, res); err != nil {
t.Fatalf("renderTable: %v", err)
}
require.NoError(t, renderTable(&buf, res))
out := buf.String()

// Rule table: Ape sorts before Zebra sorts before... Paused is skipped and
// carries no coverage entry, so it renders "-" for PROVED.
if !strings.Contains(out, "Ape Alert") || !strings.Contains(out, "unobservable") {
t.Fatalf("out = %q, want Ape's unobservable row", out)
}
if !strings.Contains(out, "heartbeat_gap") || !strings.Contains(out, "largest gap 5m0s") {
t.Fatalf("out = %q, want the coverage reason and largest gap", out)
}
if !strings.Contains(out, "Zebra Alert") || !strings.Contains(out, "clean") {
t.Fatalf("out = %q, want Zebra's clean row", out)
}
require.Contains(t, out, "Ape Alert")
require.Contains(t, out, "unobservable")
require.Contains(t, out, "heartbeat_gap")
require.Contains(t, out, "largest gap 5m0s")
require.Contains(t, out, "Zebra Alert")
require.Contains(t, out, "clean")

// The violations section must show up even without --output json, and must
// carry the --allow-paused hint text verbatim.
if !strings.Contains(out, "VIOLATIONS") {
t.Fatalf("out = %q, want a VIOLATIONS section", out)
}
if !strings.Contains(out, "--allow-paused") {
t.Fatalf("out = %q, want the --allow-paused hint in the human table", out)
}
if !strings.Contains(out, "STATE") || !strings.Contains(out, "HEALTH") {
t.Fatalf("out = %q, want the violations table to have STATE and HEALTH columns", out)
}
if !strings.Contains(out, string(gate.StateFiring)) || !strings.Contains(out, "error") {
t.Fatalf("out = %q, want Ape's violation State/Health", out)
}
require.Contains(t, out, "VIOLATIONS")
require.Contains(t, out, "--allow-paused")
require.Contains(t, out, "STATE")
require.Contains(t, out, "HEALTH")
require.Contains(t, out, string(gate.StateFiring))
require.Contains(t, out, "error")

// The footer: per-rule thresholds, global thresholds, and skew with its own
// bound rather than the fixed hard limit.
if !strings.Contains(out, "Ape Alert: maxGap=1m0s healthGrace=2m0s evalStaleAfter=1m0s") {
t.Fatalf("out = %q, want Ape's per-rule thresholds", out)
}
if !strings.Contains(out, "Zebra Alert: maxGap=1m0s healthGrace=1m0s evalStaleAfter=1m0s") {
t.Fatalf("out = %q, want Zebra's per-rule thresholds", out)
}
if strings.Contains(out, "Paused Alert: maxGap") {
t.Fatalf("out = %q, a skipped rule must not report thresholds it never had", out)
}
if !strings.Contains(out, "global: transitionGrace=5m0s (source: Ape Alert (for=5m)) drainTimeout=2m0s") {
t.Fatalf("out = %q, want the global thresholds line", out)
}
if !strings.Contains(out, "largest measured clock skew: 1.5s (bound ±250ms, hard limit 1m0s)") {
t.Fatalf("out = %q, want the skew and its own bound, not the hard limit misused as one", out)
}
if !strings.Contains(out, "violations: 2") {
t.Fatalf("out = %q, want the violation count", out)
}
if !strings.Contains(out, "13.1.0") {
t.Fatalf("out = %q, want the grafana version", out)
}
require.Contains(t, out, "Ape Alert: maxGap=1m0s healthGrace=2m0s evalStaleAfter=1m0s")
require.Contains(t, out, "Zebra Alert: maxGap=1m0s healthGrace=1m0s evalStaleAfter=1m0s")
require.NotContains(t, out, "Paused Alert: maxGap")
require.Contains(t, out, "global: transitionGrace=5m0s (source: Ape Alert (for=5m)) drainTimeout=2m0s")
require.Contains(t, out, "largest measured clock skew: 1.5s (bound ±250ms, hard limit 1m0s)")
require.Contains(t, out, "violations: 2")
require.Contains(t, out, "13.1.0")
}

// The "-" case: a rule decide never asked proveCoverage about (paused before
// the window opened) has an empty CoverageResult and must not be reported as
// either proved or unobservable.
func TestProvedLabel_Skipped(t *testing.T) {
if got := provedLabel(gate.CoverageResult{}); got != "-" {
t.Fatalf("provedLabel(zero value) = %q, want \"-\"", got)
}
require.Equal(t, "-", provedLabel(gate.CoverageResult{}))
}
35 changes: 10 additions & 25 deletions grafana-alertcheck/cmd/grafana-alertcheck/watch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package main
import (
"bytes"
"os"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

// The record step's flag-validation matrix. Every case fails inside
Expand Down Expand Up @@ -47,12 +48,8 @@ func TestRunWatch_FlagValidation(t *testing.T) {
var stdout, stderr bytes.Buffer
args := append([]string{"watch"}, tt.args(t)...)
code := run(args, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2; stderr = %q", code, stderr.String())
}
if !strings.Contains(stderr.String(), tt.wantErr) {
t.Fatalf("stderr = %q, want it to contain %q", stderr.String(), tt.wantErr)
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), tt.wantErr)
})
}
}
Expand All @@ -68,18 +65,10 @@ func TestRunWatch_DaemonChildDispatch(t *testing.T) {
// enough to prove dispatch happened without needing a real recording.
missing := os.DevNull + ".missing"
code := run([]string{"watch", "--daemon-child", "--out", missing, "--ready-fd", "0"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2; stderr = %q", code, stderr.String())
}
if !strings.Contains(stderr.String(), missing) {
t.Fatalf("stderr = %q, want RunDaemonChild's read failure naming %q", stderr.String(), missing)
}
if strings.Contains(watchUsage, "daemon-child") {
t.Fatalf("watchUsage = %q, must never name --daemon-child", watchUsage)
}
if strings.Contains(watchUsage, "ready-fd") {
t.Fatalf("watchUsage = %q, must never name --ready-fd", watchUsage)
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), missing)
require.NotContains(t, watchUsage, "daemon-child")
require.NotContains(t, watchUsage, "ready-fd")
}

func TestRunWatch_DaemonChild_MissingEnv(t *testing.T) {
Expand All @@ -88,10 +77,6 @@ func TestRunWatch_DaemonChild_MissingEnv(t *testing.T) {

var stdout, stderr bytes.Buffer
code := run([]string{"watch", "--daemon-child", "--out", "log.jsonl"}, &stdout, &stderr)
if code != 2 {
t.Fatalf("code = %d, want 2; stderr = %q", code, stderr.String())
}
if !strings.Contains(stderr.String(), "GRAFANA_URL") {
t.Fatalf("stderr = %q, want it to name the missing env var", stderr.String())
}
require.Equal(t, 2, code)
require.Contains(t, stderr.String(), "GRAFANA_URL")
}
4 changes: 4 additions & 0 deletions grafana-alertcheck/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module github.com/smartcontractkit/chainlink-testing-framework/grafana-alertcheck

go 1.26.6

require github.com/stretchr/testify v1.12.1

require go.yaml.in/yaml/v3 v3.0.5 // indirect
4 changes: 4 additions & 0 deletions grafana-alertcheck/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading