...

Source file src/github.com/prometheus/alertmanager/template/template_test.go

Documentation: github.com/prometheus/alertmanager/template

     1  // Copyright 2018 Prometheus Team
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package template
    15  
    16  import (
    17  	"net/url"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/prometheus/common/model"
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/prometheus/alertmanager/types"
    25  )
    26  
    27  func TestPairNames(t *testing.T) {
    28  	pairs := Pairs{
    29  		{"name1", "value1"},
    30  		{"name2", "value2"},
    31  		{"name3", "value3"},
    32  	}
    33  
    34  	expected := []string{"name1", "name2", "name3"}
    35  	require.EqualValues(t, expected, pairs.Names())
    36  }
    37  
    38  func TestPairValues(t *testing.T) {
    39  	pairs := Pairs{
    40  		{"name1", "value1"},
    41  		{"name2", "value2"},
    42  		{"name3", "value3"},
    43  	}
    44  
    45  	expected := []string{"value1", "value2", "value3"}
    46  	require.EqualValues(t, expected, pairs.Values())
    47  }
    48  
    49  func TestKVSortedPairs(t *testing.T) {
    50  	kv := KV{"d": "dVal", "b": "bVal", "c": "cVal"}
    51  
    52  	expectedPairs := Pairs{
    53  		{"b", "bVal"},
    54  		{"c", "cVal"},
    55  		{"d", "dVal"},
    56  	}
    57  
    58  	for i, p := range kv.SortedPairs() {
    59  		require.EqualValues(t, p.Name, expectedPairs[i].Name)
    60  		require.EqualValues(t, p.Value, expectedPairs[i].Value)
    61  	}
    62  
    63  	// validates alertname always comes first
    64  	kv = KV{"d": "dVal", "b": "bVal", "c": "cVal", "alertname": "alert", "a": "aVal"}
    65  
    66  	expectedPairs = Pairs{
    67  		{"alertname", "alert"},
    68  		{"a", "aVal"},
    69  		{"b", "bVal"},
    70  		{"c", "cVal"},
    71  		{"d", "dVal"},
    72  	}
    73  
    74  	for i, p := range kv.SortedPairs() {
    75  		require.EqualValues(t, p.Name, expectedPairs[i].Name)
    76  		require.EqualValues(t, p.Value, expectedPairs[i].Value)
    77  	}
    78  }
    79  
    80  func TestKVRemove(t *testing.T) {
    81  	kv := KV{
    82  		"key1": "val1",
    83  		"key2": "val2",
    84  		"key3": "val3",
    85  		"key4": "val4",
    86  	}
    87  
    88  	kv = kv.Remove([]string{"key2", "key4"})
    89  
    90  	expected := []string{"key1", "key3"}
    91  	require.EqualValues(t, expected, kv.Names())
    92  }
    93  
    94  func TestAlertsFiring(t *testing.T) {
    95  	alerts := Alerts{
    96  		{Status: string(model.AlertFiring)},
    97  		{Status: string(model.AlertResolved)},
    98  		{Status: string(model.AlertFiring)},
    99  		{Status: string(model.AlertResolved)},
   100  		{Status: string(model.AlertResolved)},
   101  	}
   102  
   103  	for _, alert := range alerts.Firing() {
   104  		if alert.Status != string(model.AlertFiring) {
   105  			t.Errorf("unexpected status %q", alert.Status)
   106  		}
   107  	}
   108  }
   109  
   110  func TestAlertsResolved(t *testing.T) {
   111  	alerts := Alerts{
   112  		{Status: string(model.AlertFiring)},
   113  		{Status: string(model.AlertResolved)},
   114  		{Status: string(model.AlertFiring)},
   115  		{Status: string(model.AlertResolved)},
   116  		{Status: string(model.AlertResolved)},
   117  	}
   118  
   119  	for _, alert := range alerts.Resolved() {
   120  		if alert.Status != string(model.AlertResolved) {
   121  			t.Errorf("unexpected status %q", alert.Status)
   122  		}
   123  	}
   124  }
   125  
   126  func TestData(t *testing.T) {
   127  	u, err := url.Parse("http://example.com/")
   128  	require.NoError(t, err)
   129  	tmpl := &Template{ExternalURL: u}
   130  	startTime := time.Time{}.Add(1 * time.Second)
   131  	endTime := time.Time{}.Add(2 * time.Second)
   132  
   133  	for _, tc := range []struct {
   134  		receiver    string
   135  		groupLabels model.LabelSet
   136  		alerts      []*types.Alert
   137  
   138  		exp *Data
   139  	}{
   140  		{
   141  			receiver: "webhook",
   142  			exp: &Data{
   143  				Receiver:          "webhook",
   144  				Status:            "resolved",
   145  				Alerts:            Alerts{},
   146  				GroupLabels:       KV{},
   147  				CommonLabels:      KV{},
   148  				CommonAnnotations: KV{},
   149  				ExternalURL:       u.String(),
   150  			},
   151  		},
   152  		{
   153  			receiver: "webhook",
   154  			groupLabels: model.LabelSet{
   155  				model.LabelName("job"): model.LabelValue("foo"),
   156  			},
   157  			alerts: []*types.Alert{
   158  				{
   159  					Alert: model.Alert{
   160  						StartsAt: startTime,
   161  						Labels: model.LabelSet{
   162  							model.LabelName("severity"): model.LabelValue("warning"),
   163  							model.LabelName("job"):      model.LabelValue("foo"),
   164  						},
   165  						Annotations: model.LabelSet{
   166  							model.LabelName("description"): model.LabelValue("something happened"),
   167  							model.LabelName("runbook"):     model.LabelValue("foo"),
   168  						},
   169  					},
   170  				},
   171  				{
   172  					Alert: model.Alert{
   173  						StartsAt: startTime,
   174  						EndsAt:   endTime,
   175  						Labels: model.LabelSet{
   176  							model.LabelName("severity"): model.LabelValue("critical"),
   177  							model.LabelName("job"):      model.LabelValue("foo"),
   178  						},
   179  						Annotations: model.LabelSet{
   180  							model.LabelName("description"): model.LabelValue("something else happened"),
   181  							model.LabelName("runbook"):     model.LabelValue("foo"),
   182  						},
   183  					},
   184  				},
   185  			},
   186  			exp: &Data{
   187  				Receiver: "webhook",
   188  				Status:   "firing",
   189  				Alerts: Alerts{
   190  					{
   191  						Status:      "firing",
   192  						Labels:      KV{"severity": "warning", "job": "foo"},
   193  						Annotations: KV{"description": "something happened", "runbook": "foo"},
   194  						StartsAt:    startTime,
   195  						Fingerprint: "9266ef3da838ad95",
   196  					},
   197  					{
   198  						Status:      "resolved",
   199  						Labels:      KV{"severity": "critical", "job": "foo"},
   200  						Annotations: KV{"description": "something else happened", "runbook": "foo"},
   201  						StartsAt:    startTime,
   202  						EndsAt:      endTime,
   203  						Fingerprint: "3b15fd163d36582e",
   204  					},
   205  				},
   206  				GroupLabels:       KV{"job": "foo"},
   207  				CommonLabels:      KV{"job": "foo"},
   208  				CommonAnnotations: KV{"runbook": "foo"},
   209  				ExternalURL:       u.String(),
   210  			},
   211  		},
   212  		{
   213  			receiver:    "webhook",
   214  			groupLabels: model.LabelSet{},
   215  			alerts: []*types.Alert{
   216  				{
   217  					Alert: model.Alert{
   218  						StartsAt: startTime,
   219  						Labels: model.LabelSet{
   220  							model.LabelName("severity"): model.LabelValue("warning"),
   221  							model.LabelName("job"):      model.LabelValue("foo"),
   222  						},
   223  						Annotations: model.LabelSet{
   224  							model.LabelName("description"): model.LabelValue("something happened"),
   225  							model.LabelName("runbook"):     model.LabelValue("foo"),
   226  						},
   227  					},
   228  				},
   229  				{
   230  					Alert: model.Alert{
   231  						StartsAt: startTime,
   232  						EndsAt:   endTime,
   233  						Labels: model.LabelSet{
   234  							model.LabelName("severity"): model.LabelValue("critical"),
   235  							model.LabelName("job"):      model.LabelValue("bar"),
   236  						},
   237  						Annotations: model.LabelSet{
   238  							model.LabelName("description"): model.LabelValue("something else happened"),
   239  							model.LabelName("runbook"):     model.LabelValue("bar"),
   240  						},
   241  					},
   242  				},
   243  			},
   244  			exp: &Data{
   245  				Receiver: "webhook",
   246  				Status:   "firing",
   247  				Alerts: Alerts{
   248  					{
   249  						Status:      "firing",
   250  						Labels:      KV{"severity": "warning", "job": "foo"},
   251  						Annotations: KV{"description": "something happened", "runbook": "foo"},
   252  						StartsAt:    startTime,
   253  						Fingerprint: "9266ef3da838ad95",
   254  					},
   255  					{
   256  						Status:      "resolved",
   257  						Labels:      KV{"severity": "critical", "job": "bar"},
   258  						Annotations: KV{"description": "something else happened", "runbook": "bar"},
   259  						StartsAt:    startTime,
   260  						EndsAt:      endTime,
   261  						Fingerprint: "c7e68cb08e3e67f9",
   262  					},
   263  				},
   264  				GroupLabels:       KV{},
   265  				CommonLabels:      KV{},
   266  				CommonAnnotations: KV{},
   267  				ExternalURL:       u.String(),
   268  			},
   269  		},
   270  	} {
   271  		tc := tc
   272  		t.Run("", func(t *testing.T) {
   273  			got := tmpl.Data(tc.receiver, tc.groupLabels, tc.alerts...)
   274  			require.Equal(t, tc.exp, got)
   275  		})
   276  	}
   277  }
   278  
   279  func TestTemplateExpansion(t *testing.T) {
   280  	tmpl, err := FromGlobs()
   281  	require.NoError(t, err)
   282  
   283  	for _, tc := range []struct {
   284  		title string
   285  		in    string
   286  		data  interface{}
   287  		html  bool
   288  
   289  		exp  string
   290  		fail bool
   291  	}{
   292  		{
   293  			title: "Template without action",
   294  			in:    `abc`,
   295  			exp:   "abc",
   296  		},
   297  		{
   298  			title: "Template with simple action",
   299  			in:    `{{ "abc" }}`,
   300  			exp:   "abc",
   301  		},
   302  		{
   303  			title: "Template with invalid syntax",
   304  			in:    `{{ `,
   305  			fail:  true,
   306  		},
   307  		{
   308  			title: "Template using toUpper",
   309  			in:    `{{ "abc" | toUpper }}`,
   310  			exp:   "ABC",
   311  		},
   312  		{
   313  			title: "Template using toLower",
   314  			in:    `{{ "ABC" | toLower }}`,
   315  			exp:   "abc",
   316  		},
   317  		{
   318  			title: "Template using title",
   319  			in:    `{{ "abc" | title }}`,
   320  			exp:   "Abc",
   321  		},
   322  		{
   323  			title: "Template using positive match",
   324  			in:    `{{ if match "^a" "abc"}}abc{{ end }}`,
   325  			exp:   "abc",
   326  		},
   327  		{
   328  			title: "Template using negative match",
   329  			in:    `{{ if match "abcd" "abc" }}abc{{ end }}`,
   330  			exp:   "",
   331  		},
   332  		{
   333  			title: "Template using join",
   334  			in:    `{{ . | join "," }}`,
   335  			data:  []string{"a", "b", "c"},
   336  			exp:   "a,b,c",
   337  		},
   338  		{
   339  			title: "Text template without HTML escaping",
   340  			in:    `{{ "<b>" }}`,
   341  			exp:   "<b>",
   342  		},
   343  		{
   344  			title: "HTML template with escaping",
   345  			in:    `{{ "<b>" }}`,
   346  			html:  true,
   347  			exp:   "&lt;b&gt;",
   348  		},
   349  		{
   350  			title: "HTML template using safeHTML",
   351  			in:    `{{ "<b>" | safeHtml }}`,
   352  			html:  true,
   353  			exp:   "<b>",
   354  		},
   355  		{
   356  			title: "Template using reReplaceAll",
   357  			in:    `{{ reReplaceAll "ab" "AB" "abcdabcda"}}`,
   358  			exp:   "ABcdABcda",
   359  		},
   360  		{
   361  			title: "Template using stringSlice",
   362  			in:    `{{ with .GroupLabels }}{{ with .Remove (stringSlice "key1" "key3") }}{{ .SortedPairs.Values }}{{ end }}{{ end }}`,
   363  			data: Data{
   364  				GroupLabels: KV{
   365  					"key1": "key1",
   366  					"key2": "key2",
   367  					"key3": "key3",
   368  					"key4": "key4",
   369  				},
   370  			},
   371  			exp: "[key2 key4]",
   372  		},
   373  	} {
   374  		tc := tc
   375  		t.Run(tc.title, func(t *testing.T) {
   376  			f := tmpl.ExecuteTextString
   377  			if tc.html {
   378  				f = tmpl.ExecuteHTMLString
   379  			}
   380  			got, err := f(tc.in, tc.data)
   381  			if tc.fail {
   382  				require.NotNil(t, err)
   383  				return
   384  			}
   385  			require.NoError(t, err)
   386  			require.Equal(t, tc.exp, got)
   387  		})
   388  	}
   389  }
   390  

View as plain text