carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "log"
9 "strings"
10 "testing"
11
12 "github.com/carlosframework/platform/internal/alerting"
13)
14
15// TestPageOperatorUsesTheArmedSinks: the paging primitive goes out through
16// the same webhook (and SNS topic) the agent's alerts use, its failures are
17// counted where the alert failures are, and the package function pages on
18// whatever the metrics loop armed. With no sinks it returns nil and says so
19// once.
20func TestPageOperatorUsesTheArmedSinks(t *testing.T) {
21 type page struct{ Kind, Level, Subject, Body string }
22 var pages []page
23 var fail error
24 post := func(url string, body []byte) error {
25 var p page
26 if err := json.Unmarshal(body, &p); err != nil {
27 t.Fatalf("webhook body %s: %v", body, err)
28 }
29 if url != "http://example.test/hook" {
30 t.Fatalf("posted to %s", url)
31 }
32 pages = append(pages, p)
33 return fail
34 }
35 counts := &alertCounters{}
36 pub := &fakeSNS{}
37 sinks := newAlertSinks("http://example.test/hook", post, testTopicARN, pub, counts)
38 ctx := context.Background()
39
40 if err := sinks.PageOperator(ctx, alerting.LevelIncident, "carlos move stuck: shop.example", "the body"); err != nil {
41 t.Fatal(err)
42 }
43 want := page{"page", "incident", "carlos move stuck: shop.example", "the body"}
44 if len(pages) != 1 || pages[0] != want {
45 t.Fatalf("pages %+v, want %+v", pages, want)
46 }
47 if len(pub.in) != 1 || deref(pub.in[0].Subject) != want.Subject || deref(pub.in[0].Message) != want.Body {
48 t.Fatalf("sns: %+v", pub.in)
49 }
50
51 // The package function pages on the armed sinks.
52 armPaging(sinks)
53 t.Cleanup(func() { armPaging(nil) })
54 if err := PageOperator(ctx, alerting.LevelWarning, "second", "body two"); err != nil {
55 t.Fatal(err)
56 }
57 if len(pages) != 2 || pages[1].Subject != "second" || pages[1].Level != "warning" {
58 t.Fatalf("pages %+v", pages)
59 }
60
61 // A failed send is returned and counted with the alert failures.
62 fail = errors.New("boom")
63 if err := PageOperator(ctx, alerting.LevelIncident, "third", "body"); err == nil {
64 t.Fatal("a failed webhook post returned nil")
65 }
66 if counts.webhook.Load() != 1 || counts.sns.Load() != 0 {
67 t.Fatalf("failures webhook=%d sns=%d", counts.webhook.Load(), counts.sns.Load())
68 }
69
70 // No sinks: nil, and one line in the journal however often it is asked.
71 var buf bytes.Buffer
72 prev := log.Writer()
73 log.SetOutput(&buf)
74 t.Cleanup(func() { log.SetOutput(prev) })
75 armPaging(nil)
76 var none *alertSinks
77 for range 2 {
78 if err := PageOperator(ctx, alerting.LevelIncident, "s", "b"); err != nil {
79 t.Fatal(err)
80 }
81 if err := none.PageOperator(ctx, alerting.LevelIncident, "s", "b"); err != nil {
82 t.Fatal(err)
83 }
84 }
85 if n := strings.Count(buf.String(), "no sink configured"); n != 1 {
86 t.Fatalf("no-sink notice logged %d times, want once:\n%s", n, buf.String())
87 }
88 if len(pages) != 3 {
89 t.Fatalf("an unarmed page reached the old sinks: %+v", pages)
90 }
91}
92