// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package hookstats import ( "errors" "testing" "time" metrics "github.com/hashicorp/go-metrics/compat" "github.com/hashicorp/nomad/ci" "github.com/shoenig/test/must" ) func TestHandler(t *testing.T) { ci.Parallel(t) // Generate base labels that represent what an operator would see and then // create out new handler to interact with. baseLabels := []metrics.Label{ {Name: "datacenter", Value: "dc1"}, {Name: "node_class", Value: "none"}, {Name: "node_pool", Value: "default"}, {Name: "namespace", Value: "default"}, {Name: "host", Value: "client-5d3c"}, {Name: "node_id", Value: "35db24e7-0a55-80d2-2279-e022c37cc591"}, } newHandler := NewHandler(baseLabels, "test_hook") // The data stored is within the in-memory sink as map entries, so we need // to know the key names to pull this out correctly. Build those now. var metricKeySuffix, sampleName, counterSuccessName, counterFailureName string for _, label := range baseLabels { metricKeySuffix += ";" + label.Name + "=" + label.Value } metricKeySuffix += ";" + "hook_name=test_hook_name" sampleName = "nomad_test.client.test_hook.prerun.elapsed" + metricKeySuffix counterSuccessName = "nomad_test.client.test_hook.prerun.success" + metricKeySuffix counterFailureName = "nomad_test.client.test_hook.prerun.failed" + metricKeySuffix // Create an in-memory sink and global, so we can actually look at and test // the metrics we emit. inMemorySink := metrics.NewInmemSink(10*time.Millisecond, 50*time.Millisecond) _, err := metrics.NewGlobal(metrics.DefaultConfig("nomad_test"), inMemorySink) must.NoError(t, err) // Emit hook related metrics where the supplied error is nil and check that // the data is as expected. newHandler.Emit(time.Now(), "test_hook_name", "prerun", nil) sinkData := inMemorySink.Data() must.Len(t, 1, sinkData) must.MapContainsKey(t, sinkData[0].Counters, counterSuccessName) must.MapContainsKey(t, sinkData[0].Samples, sampleName) successCounter := sinkData[0].Counters[counterSuccessName] must.Eq(t, 1, successCounter.Count) must.Eq(t, 1, successCounter.Sum) sample1 := sinkData[0].Samples[sampleName] must.Eq(t, 1, sample1.Count) must.True(t, sample1.Sum > 0) // Create a new in-memory sink and global collector to ensure we don't have // leftovers from the previous test. inMemorySink = metrics.NewInmemSink(10*time.Millisecond, 50*time.Millisecond) _, err = metrics.NewGlobal(metrics.DefaultConfig("nomad_test"), inMemorySink) must.NoError(t, err) // Emit a hook related metrics where the supplied error is non-nil and // check that the data is as expected. newHandler.Emit(time.Now(), "test_hook_name", "prerun", errors.New("test error")) sinkData = inMemorySink.Data() must.Len(t, 1, sinkData) must.MapContainsKey(t, sinkData[0].Counters, counterFailureName) must.MapContainsKey(t, sinkData[0].Samples, sampleName) failureCounter := sinkData[0].Counters[counterFailureName] must.Eq(t, 1, failureCounter.Count) must.Eq(t, 1, failureCounter.Sum) sample2 := sinkData[0].Samples[sampleName] must.Eq(t, 1, sample2.Count) must.True(t, sample2.Sum > 0) } func TestNoOpHandler(t *testing.T) { ci.Parallel(t) newHandler := NewNoOpHandler() // Create a new in-memory sink and global collector, so we can test that no // metrics are emitted. inMemorySink := metrics.NewInmemSink(10*time.Millisecond, 50*time.Millisecond) _, err := metrics.NewGlobal(metrics.DefaultConfig("nomad_test"), inMemorySink) must.NoError(t, err) // Call the function with a non-nil error and check the results of the // in-memory sink. newHandler.Emit(time.Now(), "test_hook_name", "prerun", errors.New("test error")) sinkData := inMemorySink.Data() must.Len(t, 1, sinkData) must.MapLen(t, 0, sinkData[0].Counters) must.MapLen(t, 0, sinkData[0].Samples) }