Skip to content

Commit 04f2a4a

Browse files
add unit tests for fingerprint
1 parent 7207603 commit 04f2a4a

File tree

1 file changed

+128
-1
lines changed

1 file changed

+128
-1
lines changed

pkg/alertmanager/multitenant_test.go

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828

2929
"github.com/go-kit/log"
3030
"github.com/gogo/status"
31+
"github.com/google/go-cmp/cmp"
32+
"github.com/google/uuid"
3133
"github.com/grafana/alerting/definition"
3234
alertingReceivers "github.com/grafana/alerting/receivers"
3335
"github.com/grafana/dskit/clusterutil"
@@ -3632,8 +3634,8 @@ func TestComputeConfig(t *testing.T) {
36323634
}
36333635

36343636
func Test_amConfigFingerprint(t *testing.T) {
3637+
const expectedTotalFields = 23 // Total fields: 3 (PostableApiTemplate) + 14 (EmailSenderConfig) + 6 (amConfig)
36353638
t.Run("ensure all fields in the fingerprint", func(t *testing.T) {
3636-
const expectedTotalFields = 23 // Total fields: 3 (PostableApiTemplate) + 14 (EmailSenderConfig) + 6 (amConfig)
36373639
// Helper function to get field count of a struct
36383640
getFieldCount := func(v interface{}) int {
36393641
t := reflect.TypeOf(v)
@@ -3651,6 +3653,131 @@ func Test_amConfigFingerprint(t *testing.T) {
36513653

36523654
require.Equalf(t, expectedTotalFields, totalFields, "Total fields across structs is %d, expected %d; new fields may require updating fingerprint method", totalFields, expectedTotalFields)
36533655
})
3656+
3657+
url, err := url.Parse("http://localhost")
3658+
require.NoError(t, err)
3659+
3660+
fullConfig := amConfig{
3661+
User: "user-grafana",
3662+
RawConfig: simpleConfigOne,
3663+
Templates: []definition.PostableApiTemplate{
3664+
{
3665+
Name: "test",
3666+
Content: "test",
3667+
Kind: definition.MimirTemplateKind,
3668+
},
3669+
{
3670+
Name: "test2",
3671+
Content: "test2",
3672+
Kind: definition.GrafanaTemplateKind,
3673+
},
3674+
{
3675+
Name: "test3",
3676+
Content: "test3",
3677+
Kind: definition.GrafanaTemplateKind,
3678+
},
3679+
},
3680+
TmplExternalURL: url,
3681+
EmailConfig: alertingReceivers.EmailSenderConfig{
3682+
AuthPassword: "custom-password",
3683+
AuthUser: "custom-user",
3684+
ContentTypes: []string{"text/html", "text/plain"},
3685+
EhloIdentity: "custom-identity",
3686+
ExternalURL: "http://custom-url",
3687+
FromAddress: "custom@address.com",
3688+
FromName: "Custom From Name",
3689+
Host: "custom-host",
3690+
SentBy: "Mimir vunknown",
3691+
SkipVerify: true,
3692+
StartTLSPolicy: "custom-policy",
3693+
StaticHeaders: map[string]string{"test": "test", "test2": "test2", "test3": "test3"},
3694+
},
3695+
}
3696+
3697+
jsonCfg, err := json.Marshal(fullConfig)
3698+
require.NoError(t, err)
3699+
3700+
t.Run("fingerprint should be stable", func(t *testing.T) {
3701+
expected := fullConfig.fingerprint()
3702+
3703+
// do it many times to make sure order of elements in the map does not affect fingerprint
3704+
for i := 0; i < 100; i++ {
3705+
cfg2 := amConfig{}
3706+
require.NoError(t, json.Unmarshal(jsonCfg, &cfg2)) // copy structure
3707+
assert.Empty(t, cmp.Diff(fullConfig, cfg2, cmp.AllowUnexported(amConfig{})))
3708+
rand.Shuffle(len(cfg2.Templates), func(i, j int) {
3709+
cfg2.Templates[i], cfg2.Templates[j] = cfg2.Templates[j], cfg2.Templates[i]
3710+
})
3711+
require.Equal(t, expected, cfg2.fingerprint())
3712+
}
3713+
})
3714+
3715+
t.Run("fingerprint should change", func(t *testing.T) {
3716+
cfg := amConfig{}
3717+
require.NoError(t, json.Unmarshal(jsonCfg, &cfg)) // copy structure
3718+
notChecked := expectedTotalFields
3719+
setStringFieldsWithRandomValue := func(val reflect.Value, callback func(fieldName string)) {
3720+
t := val.Type()
3721+
for i := 0; i < t.NumField(); i++ {
3722+
field := val.Field(i)
3723+
// Skip unexported fields (cannot be set via reflection)
3724+
if !field.CanSet() {
3725+
continue
3726+
}
3727+
switch field.Kind() {
3728+
case reflect.String:
3729+
field.SetString(uuid.NewString())
3730+
case reflect.Bool:
3731+
field.SetBool(!field.Bool())
3732+
default:
3733+
continue
3734+
}
3735+
callback(t.Field(i).Name)
3736+
notChecked--
3737+
}
3738+
}
3739+
3740+
lastFingerprint := cfg.fingerprint()
3741+
assertField := func(prefix string) func(fieldName string) {
3742+
return func(fieldName string) {
3743+
newFP := cfg.fingerprint()
3744+
assert.NotEqualf(t, lastFingerprint, newFP, "Changes in fields [%s%s] did not cause fingerprint to change", prefix, fieldName)
3745+
lastFingerprint = newFP
3746+
}
3747+
}
3748+
3749+
setStringFieldsWithRandomValue(reflect.ValueOf(&cfg).Elem(), assertField(""))
3750+
setStringFieldsWithRandomValue(reflect.ValueOf(&cfg.EmailConfig).Elem(), assertField("EmailConfig."))
3751+
setStringFieldsWithRandomValue(reflect.ValueOf(&cfg.Templates[1]).Elem(), assertField("Templates[1]."))
3752+
cfg.Templates = append(cfg.Templates, definition.PostableApiTemplate{
3753+
Name: "test3",
3754+
Content: "test3",
3755+
Kind: definition.GrafanaTemplateKind,
3756+
})
3757+
assertField("")("Templates")
3758+
notChecked--
3759+
3760+
cfg.TmplExternalURL = nil
3761+
assertField("")("TmplExternalURL")
3762+
cfg.TmplExternalURL, err = url.Parse("http://new-url")
3763+
require.NoError(t, err)
3764+
assertField("")("TmplExternalURL")
3765+
notChecked--
3766+
3767+
cfg.EmailConfig.ContentTypes = []string{"text/plain", "text/html"}
3768+
assertField("EmailConfig.")("ContentTypes")
3769+
notChecked--
3770+
3771+
cfg.EmailConfig.StaticHeaders = map[string]string{"test2": "test", "test": "test2", "test3": "test3"}
3772+
assertField("EmailConfig.")("StaticHeaders")
3773+
notChecked--
3774+
3775+
cfg.EmailConfig = alertingReceivers.EmailSenderConfig{}
3776+
assertField("")("EmailConfig")
3777+
notChecked--
3778+
3779+
require.Equal(t, 0, notChecked)
3780+
})
36543781
}
36553782

36563783
func TestSyncStates(t *testing.T) {

0 commit comments

Comments
 (0)