Repository: prometheus/alertmanager
File: silence/silence_test.go (Go)

Code before:
				{s: newSilence("2", now.Add(-time.Second)), expectPresentAfterGc: false},
				{s: newSilence("3", now.Add(time.Second)), expectPresentAfterGc: true},
			},
		},
		"silences added with Set are handled correctly": {
			initialState: []silenceEntry{
				{s: newSilence("1", now), expectPresentAfterGc: false},
			},
			updates: []silenceEntry{
				{s: newSilence("", now.Add(time.Second)), expectPresentAfterGc: true},
				{s: newSilence("", now.Add(-time.Second)), expectPresentAfterGc: false},
			},
		},
		"silence update does not leak state": {
			initialState: []silenceEntry{
				{s: newSilence("1", now), expectPresentAfterGc: false},
			},
			updates: []silenceEntry{
				{s: newSilence("1", now.Add(time.Second)), expectPresentAfterGc: true},
			},
		},
	}

	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			silences, err := New(Options{})
			silClock := clock.NewMock()
			silences.clock = silClock
			require.NoError(t, err)

			expectedRemaining := []string{}
			expectedGCCount := 0
			for _, sil := range tc.initialState {
				silences.st[sil.s.Id] = &pb.MeshSilence{
					Silence:   sil.s,
					ExpiresAt: sil.s.EndsAt,
				}
				if sil.expectPresentAfterGc {
					expectedRemaining = append(expectedRemaining, sil.s.Id)
				} else {
					expectedGCCount += 1
				}
				// simulate this silences being seen in a query
				silences.mc.Get(silences.st[sil.s.Id].Silence)
			}
			silClock.Add(-time.Second)
			for _, sil := range tc.updates {
				if sil.s.Id != "" {
					// we're replacing a silence which now will not get GC'd
					expectedGCCount -= 1
				}

Review comment (suggestion):
Please just move the `require` after the `New` call:

```suggestion
			silences, err := New(Options{})
			require.NoError(t, err)
```

Code after:
		s.clock = clock
		sil1 := &pb.Silence{
			Matchers: []*pb.Matcher{{
				Type:    pb.Matcher_EQUAL,
				Name:    "foo",
				Pattern: "bar",
			}},
			StartsAt: clock.Now(),
			EndsAt:   clock.Now().Add(time.Minute),
		}
		require.NoError(t, s.Set(sil1))
		// Need to query the silence to populate the matcher cache.
		s.Query(QMatches(model.LabelSet{"foo": "bar"}))
		require.Len(t, s.st, 1)
		require.Len(t, s.mc, 1)
		// must clone sil1 before replacing it.
		sil2 := cloneSilence(sil1)
		sil2.Matchers = []*pb.Matcher{{
			Type:    pb.Matcher_EQUAL,
			Name:    "bar",
			Pattern: "baz",
		}}
		require.NoError(t, s.Set(sil2))
		// Need to query the silence to populate the matcher cache.
		s.Query(QMatches(model.LabelSet{"bar": "baz"}))
		require.Len(t, s.st, 2)
		require.Len(t, s.mc, 2)
		// Move time forward and both silence and cache entry should be garbage
		// collected.
		clock.Add(time.Minute)
		n, err := s.GC()
		require.NoError(t, err)
		require.Equal(t, 2, n)
		require.Empty(t, s.st)
		require.Empty(t, s.mc)
	})

	// This test checks for a memory leak that occurred in the matcher cache when
	// updating an existing silence.
	t.Run("updating a silences does not leak cache entries", func(t *testing.T) {
		s, err := New(Options{})
		require.NoError(t, err)
		clock := clock.NewMock()
		s.clock = clock
		sil1 := &pb.Silence{
			Id: "1",
			Matchers: []*pb.Matcher{{
				Type:    pb.Matcher_EQUAL,
				Name:    "foo",
				Pattern: "bar",
			}},