Skip to content

Commit

Permalink
chore: Add Alert Method e2e tests (#481)
Browse files Browse the repository at this point in the history
Extends end-to-end tests with `v1alpha.AlertMethod` coverage.

It utilizes generated examples to easily test application of different
Alert Method variants.
  • Loading branch information
nieomylnieja authored Jul 8, 2024
1 parent 9732f54 commit 75d8472
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 62 deletions.
3 changes: 1 addition & 2 deletions manifest/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ type ProjectScopedObject interface {
func FilterByKind[T Object](objects []Object) []T {
var filtered []T
for i := range objects {
v, ok := objects[i].(T)
if ok {
if v, ok := objects[i].(T); ok {
filtered = append(filtered, v)
}
}
Expand Down
76 changes: 76 additions & 0 deletions tests/examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//go:build e2e_test

package tests

import (
"encoding/json"
"log"
"testing"

"github.com/stretchr/testify/require"

v1alphaExamples "github.com/nobl9/nobl9-go/internal/manifest/v1alpha/examples"
"github.com/nobl9/nobl9-go/manifest"
)

type exampleWrapper struct {
v1alphaExamples.Example
rawObject []byte
}

var examplesRegistry = func() map[manifest.Kind][]exampleWrapper {
kindToExamples := map[manifest.Kind][]v1alphaExamples.Example{
manifest.KindProject: v1alphaExamples.Project(),
manifest.KindService: v1alphaExamples.Service(),
manifest.KindAlertMethod: v1alphaExamples.AlertMethod(),
manifest.KindSLO: v1alphaExamples.SLO(),
manifest.KindAgent: v1alphaExamples.Agent(),
manifest.KindDirect: v1alphaExamples.Direct(),
manifest.KindAlertPolicy: v1alphaExamples.AlertPolicy(),
manifest.KindAlertSilence: v1alphaExamples.AlertSilence(),
manifest.KindAnnotation: v1alphaExamples.Annotation(),
manifest.KindBudgetAdjustment: v1alphaExamples.BudgetAdjustment(),
manifest.KindDataExport: v1alphaExamples.DataExport(),
manifest.KindRoleBinding: v1alphaExamples.RoleBinding(),
}
wrapped := make(map[manifest.Kind][]exampleWrapper, len(kindToExamples))
for kind, examples := range kindToExamples {
wrapped[kind] = make([]exampleWrapper, 0, len(examples))
for _, example := range examples {
object := example.GetObject()
rawObject, err := json.Marshal(object)
if err != nil {
log.Panicf("failed to marshal example %T object: %v", object, err)
}
wrapped[kind] = append(wrapped[kind], exampleWrapper{
Example: example,
rawObject: rawObject,
})
}
}
return wrapped
}()

func getExample[T any](t *testing.T, kind manifest.Kind, variant, subVariant string) *T {
t.Helper()
examples, ok := examplesRegistry[kind]
if !ok {
require.True(t, ok, "%s kind not found in registry", kind)
}
decode := func(rawObject []byte) *T {
var object T
if err := json.Unmarshal(rawObject, &object); err != nil {
log.Panicf("failed to unmarshal example %T object: %v", object, err)
}
return &object
}
if variant == "" && subVariant == "" {
return decode(examples[0].rawObject)
}
for _, example := range examples {
if example.GetVariant() == variant && (subVariant == "" || example.GetSubVariant() == subVariant) {
return decode(example.rawObject)
}
}
return nil
}
31 changes: 15 additions & 16 deletions tests/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package tests

import (
"context"
"encoding/json"
"fmt"
"regexp"
"strconv"
Expand All @@ -15,7 +16,6 @@ import (

"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
v1alphaProject "github.com/nobl9/nobl9-go/manifest/v1alpha/project"
)

const objectDescription = "Object generated by e2e SDK tests"
Expand Down Expand Up @@ -52,7 +52,7 @@ func assertSubset[T manifest.Object](t *testing.T, actual, expected []T, f objec
}
}
if !found {
t.Errorf("expected object %s not found in the actual list", expected[i].GetName())
t.Errorf("expected %T %s not found in the actual list", expected[i], expected[i].GetName())
}
}
}
Expand All @@ -77,20 +77,6 @@ func v1Delete[T manifest.Object](t *testing.T, ctx context.Context, inputs []T)
require.NoError(t, err)
}

func generateV1alphaProject(t *testing.T) v1alphaProject.Project {
t.Helper()
return v1alphaProject.New(
v1alphaProject.Metadata{
Name: generateName(),
Labels: annotateLabels(t, v1alpha.Labels{}),
Annotations: commonAnnotations,
},
v1alphaProject.Spec{
Description: objectDescription,
},
)
}

// generateName generates a unique name for the test object.
func generateName() string {
return fmt.Sprintf("sdk-e2e-%d-%d", objectsCounter.Add(1), testStartTime.UnixNano())
Expand All @@ -101,8 +87,21 @@ func generateName() string {
// It also adds unique test identifier label to the provided labels so that we can reliably retrieve objects created withing a given test without .
func annotateLabels(t *testing.T, labels v1alpha.Labels) v1alpha.Labels {
t.Helper()
if labels == nil {
labels = make(v1alpha.Labels, 3)
}
labels["origin"] = []string{"sdk-e2e-test"}
labels[uniqueTestIdentifierLabel.Key] = []string{uniqueTestIdentifierLabel.Value}
labels["sdk-test-name"] = []string{t.Name()}
return labels
}

// deepCopyObject creates a deep copy of the provided object using JSON encoding and decoding.
func deepCopyObject[T any](t *testing.T, object T) T {
t.Helper()
data, err := json.Marshal(object)
require.NoError(t, err)
var copied T
require.NoError(t, json.Unmarshal(data, &copied))
return copied
}
130 changes: 130 additions & 0 deletions tests/v1alpha_alertmethod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//go:build e2e_test

package tests

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
v1alphaAlertMethod "github.com/nobl9/nobl9-go/manifest/v1alpha/alertmethod"
"github.com/nobl9/nobl9-go/sdk"
objectsV1 "github.com/nobl9/nobl9-go/sdk/endpoints/objects/v1"
)

func Test_Objects_V1_V1alpha_AlertMethod(t *testing.T) {
t.Parallel()
ctx := context.Background()
project := generateV1alphaProject(t)
alertMethodTypes := v1alpha.AlertMethodTypeValues()
allObjects := make([]manifest.Object, 0, len(alertMethodTypes)+1)
allObjects = append(allObjects, project)

for i, typ := range v1alpha.AlertMethodTypeValues() {
method := newV1alphaAlertMethod(t,
typ,
v1alphaAlertMethod.Metadata{
Name: generateName(),
DisplayName: fmt.Sprintf("Alert Method %d", i),
Project: project.GetName(),
},
)
if i == 0 {
method.Metadata.Project = defaultProject
}
allObjects = append(allObjects, method)
}

v1Apply(t, ctx, allObjects)
t.Cleanup(func() { v1Delete(t, ctx, allObjects) })
inputs := manifest.FilterByKind[v1alphaAlertMethod.AlertMethod](allObjects)

filterTests := map[string]struct {
request objectsV1.GetAlertMethodsRequest
expected []v1alphaAlertMethod.AlertMethod
returnsAll bool
}{
"all": {
request: objectsV1.GetAlertMethodsRequest{Project: sdk.ProjectsWildcard},
expected: manifest.FilterByKind[v1alphaAlertMethod.AlertMethod](allObjects),
returnsAll: true,
},
"default project": {
request: objectsV1.GetAlertMethodsRequest{},
expected: []v1alphaAlertMethod.AlertMethod{inputs[0]},
returnsAll: true,
},
"filter by project": {
request: objectsV1.GetAlertMethodsRequest{
Project: project.GetName(),
},
expected: inputs[1:],
},
"filter by name": {
request: objectsV1.GetAlertMethodsRequest{
Project: project.GetName(),
Names: []string{inputs[3].Metadata.Name},
},
expected: []v1alphaAlertMethod.AlertMethod{inputs[3]},
},
}
for name, test := range filterTests {
t.Run(name, func(t *testing.T) {
t.Parallel()
actual, err := client.Objects().V1().GetV1alphaAlertMethods(ctx, test.request)
require.NoError(t, err)
if !test.returnsAll {
require.Len(t, actual, len(test.expected))
}
assertSubset(t, actual, test.expected, assertV1alphaAlertMethodsAreEqual)
})
}
}

func newV1alphaAlertMethod(
t *testing.T,
typ v1alpha.AlertMethodType,
metadata v1alphaAlertMethod.Metadata,
) v1alphaAlertMethod.AlertMethod {
t.Helper()
variant := getExample[v1alphaAlertMethod.AlertMethod](t, manifest.KindAlertMethod, typ.String(), "")
variant.Spec.Description = objectDescription
return v1alphaAlertMethod.New(metadata, variant.Spec)
}

func assertV1alphaAlertMethodsAreEqual(t *testing.T, expected, actual v1alphaAlertMethod.AlertMethod) {
t.Helper()
expected = deepCopyObject(t, expected)
actual.Status = nil
typ, err := expected.Spec.GetType()
require.NoError(t, err)
switch typ {
case v1alpha.AlertMethodTypeDiscord:
expected.Spec.Discord.URL = "[hidden]"
case v1alpha.AlertMethodTypeJira:
expected.Spec.Jira.APIToken = "[hidden]"
case v1alpha.AlertMethodTypeOpsgenie:
expected.Spec.Opsgenie.Auth = "[hidden]"
case v1alpha.AlertMethodTypePagerDuty:
expected.Spec.PagerDuty.IntegrationKey = "[hidden]"
case v1alpha.AlertMethodTypeServiceNow:
expected.Spec.ServiceNow.Password = "[hidden]"
case v1alpha.AlertMethodTypeSlack:
expected.Spec.Slack.URL = "[hidden]"
case v1alpha.AlertMethodTypeTeams:
expected.Spec.Teams.URL = "[hidden]"
case v1alpha.AlertMethodTypeWebhook:
expected.Spec.Webhook.URL = "[hidden]"
for i, header := range expected.Spec.Webhook.Headers {
if header.IsSecret {
expected.Spec.Webhook.Headers[i].Value = "[hidden]"
}
}
}
assert.Equal(t, expected, actual)
}
47 changes: 27 additions & 20 deletions tests/v1alpha_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,25 @@ func Test_Objects_V1_V1alpha_Project(t *testing.T) {
t.Parallel()
ctx := context.Background()
inputs := []v1alphaProject.Project{
v1alphaProject.New(
newV1alphaProject(t,
v1alphaProject.Metadata{
Name: generateName(),
Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"green"}}),
Annotations: commonAnnotations,
},
v1alphaProject.Spec{
Description: objectDescription,
DisplayName: "Project 1",
Labels: v1alpha.Labels{"team": []string{"green"}},
},
),
v1alphaProject.New(
newV1alphaProject(t,
v1alphaProject.Metadata{
Name: generateName(),
Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}),
Annotations: commonAnnotations,
},
v1alphaProject.Spec{
Description: objectDescription,
DisplayName: "Project 2",
Labels: v1alpha.Labels{"team": []string{"orange"}},
},
),
v1alphaProject.New(
newV1alphaProject(t,
v1alphaProject.Metadata{
Name: generateName(),
Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}),
Annotations: commonAnnotations,
},
v1alphaProject.Spec{
Description: objectDescription,
DisplayName: "Project 3",
Labels: v1alpha.Labels{"team": []string{"orange"}},
},
),
}
Expand Down Expand Up @@ -91,13 +82,29 @@ func Test_Objects_V1_V1alpha_Project(t *testing.T) {
if !test.returnsAll {
require.Len(t, actual, len(test.expected))
}
assertSubset(t, actual, test.expected, assertProjectsAreEqual)
assertSubset(t, actual, test.expected, assertV1alphaProjectsAreEqual)
})
}
}

func assertProjectsAreEqual(t *testing.T, expected, actual v1alphaProject.Project) {
func newV1alphaProject(
t *testing.T,
metadata v1alphaProject.Metadata,
) v1alphaProject.Project {
t.Helper()
annotateLabels(t, metadata.Labels)
metadata.Annotations = commonAnnotations
return v1alphaProject.New(metadata, v1alphaProject.Spec{Description: objectDescription})
}

func generateV1alphaProject(t *testing.T) v1alphaProject.Project {
t.Helper()
return newV1alphaProject(t, v1alphaProject.Metadata{Name: generateName()})
}

func assertV1alphaProjectsAreEqual(t *testing.T, expected, actual v1alphaProject.Project) {
t.Helper()
expected = deepCopyObject(t, expected)
assert.Regexp(t, timeRFC3339Regexp, actual.Spec.CreatedAt)
assert.Regexp(t, userIDRegexp, actual.Spec.CreatedBy)
actual.Spec.CreatedAt = ""
Expand Down
Loading

0 comments on commit 75d8472

Please sign in to comment.