mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:47:33 +00:00
chore: move the go code into a subfolder
This commit is contained in:
178
apps/api/internal/integration/assert.go
Normal file
178
apps/api/internal/integration/assert.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
resources_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
|
||||
)
|
||||
|
||||
// Details is the interface that covers both v1 and v2 proto generated object details.
|
||||
type Details interface {
|
||||
comparable
|
||||
GetSequence() uint64
|
||||
GetCreationDate() *timestamppb.Timestamp
|
||||
GetChangeDate() *timestamppb.Timestamp
|
||||
GetResourceOwner() string
|
||||
}
|
||||
|
||||
// DetailsMsg is the interface that covers all proto messages which contain v1 or v2 object details.
|
||||
type DetailsMsg[D Details] interface {
|
||||
GetDetails() D
|
||||
}
|
||||
|
||||
type ListDetails interface {
|
||||
comparable
|
||||
GetTotalResult() uint64
|
||||
GetTimestamp() *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
type ListDetailsMsg[L ListDetails] interface {
|
||||
GetDetails() L
|
||||
}
|
||||
|
||||
type ResourceListDetailsMsg interface {
|
||||
GetDetails() *resources_object.ListDetails
|
||||
}
|
||||
|
||||
// AssertDetails asserts values in a message's object Details,
|
||||
// if the object Details in expected is a non-nil value.
|
||||
// It targets API v2 messages that have the `GetDetails()` method.
|
||||
//
|
||||
// Dynamically generated values are not compared with expected.
|
||||
// Instead a sanity check is performed.
|
||||
// For the sequence a non-zero value is expected.
|
||||
// If the change date is populated, it is checked with a tolerance of 1 minute around Now.
|
||||
//
|
||||
// The resource owner is compared with expected.
|
||||
func AssertDetails[D Details, M DetailsMsg[D]](t assert.TestingT, expected, actual M) {
|
||||
wantDetails, gotDetails := expected.GetDetails(), actual.GetDetails()
|
||||
var nilDetails D
|
||||
if wantDetails == nilDetails {
|
||||
assert.Nil(t, gotDetails)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NotZero(t, gotDetails.GetSequence())
|
||||
|
||||
if wantDetails.GetCreationDate() != nil {
|
||||
wantCreationDate := time.Now()
|
||||
gotCreationDate := gotDetails.GetCreationDate().AsTime()
|
||||
assert.WithinRange(t, gotCreationDate, wantCreationDate.Add(-time.Minute), wantCreationDate.Add(time.Minute))
|
||||
}
|
||||
|
||||
if wantDetails.GetChangeDate() != nil {
|
||||
wantChangeDate := time.Now()
|
||||
gotChangeDate := gotDetails.GetChangeDate().AsTime()
|
||||
assert.WithinRange(t, gotChangeDate, wantChangeDate.Add(-time.Minute), wantChangeDate.Add(time.Minute))
|
||||
}
|
||||
|
||||
assert.Equal(t, wantDetails.GetResourceOwner(), gotDetails.GetResourceOwner())
|
||||
}
|
||||
|
||||
func AssertResourceDetails(t assert.TestingT, expected *resources_object.Details, actual *resources_object.Details) {
|
||||
if expected.GetChanged() != nil {
|
||||
wantChangeDate := time.Now()
|
||||
gotChangeDate := actual.GetChanged().AsTime()
|
||||
assert.WithinRange(t, gotChangeDate, wantChangeDate.Add(-time.Minute), wantChangeDate.Add(time.Minute))
|
||||
}
|
||||
if expected.GetCreated() != nil {
|
||||
wantCreatedDate := time.Now()
|
||||
gotCreatedDate := actual.GetCreated().AsTime()
|
||||
assert.WithinRange(t, gotCreatedDate, wantCreatedDate.Add(-time.Minute), wantCreatedDate.Add(time.Minute))
|
||||
}
|
||||
if expected.GetOwner() != nil {
|
||||
expectedOwner := expected.GetOwner()
|
||||
actualOwner := actual.GetOwner()
|
||||
if !assert.NotNil(t, actualOwner) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, expectedOwner.GetId(), actualOwner.GetId())
|
||||
assert.Equal(t, expectedOwner.GetType(), actualOwner.GetType())
|
||||
}
|
||||
assert.NotEmpty(t, actual.GetId())
|
||||
if expected.GetId() != "" {
|
||||
assert.Equal(t, expected.GetId(), actual.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
func AssertListDetails[L ListDetails, D ListDetailsMsg[L]](t assert.TestingT, expected, actual D) {
|
||||
wantDetails, gotDetails := expected.GetDetails(), actual.GetDetails()
|
||||
var nilDetails L
|
||||
if wantDetails == nilDetails {
|
||||
assert.Nil(t, gotDetails)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, wantDetails.GetTotalResult(), gotDetails.GetTotalResult())
|
||||
|
||||
if wantDetails.GetTimestamp() != nil {
|
||||
gotCD := gotDetails.GetTimestamp().AsTime()
|
||||
wantCD := time.Now()
|
||||
assert.WithinRange(t, gotCD, wantCD.Add(-10*time.Minute), wantCD.Add(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
func AssertResourceListDetails[D ResourceListDetailsMsg](t assert.TestingT, expected, actual D) {
|
||||
wantDetails, gotDetails := expected.GetDetails(), actual.GetDetails()
|
||||
if wantDetails == nil {
|
||||
assert.Nil(t, gotDetails)
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, wantDetails.GetTotalResult(), gotDetails.GetTotalResult())
|
||||
assert.Equal(t, wantDetails.GetAppliedLimit(), gotDetails.GetAppliedLimit())
|
||||
|
||||
if wantDetails.GetTimestamp() != nil {
|
||||
gotCD := gotDetails.GetTimestamp().AsTime()
|
||||
wantCD := time.Now()
|
||||
assert.WithinRange(t, gotCD, wantCD.Add(-10*time.Minute), wantCD.Add(time.Minute))
|
||||
}
|
||||
}
|
||||
|
||||
func AssertGrpcStatus(t assert.TestingT, expected codes.Code, err error) {
|
||||
assert.Error(t, err)
|
||||
statusErr, ok := status.FromError(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, expected, statusErr.Code())
|
||||
}
|
||||
|
||||
// EqualProto is inspired by [assert.Equal], only that it tests equality of a proto message.
|
||||
// A message diff is printed on the error test log if the messages are not equal.
|
||||
//
|
||||
// As [assert.Equal] is based on reflection, comparing 2 proto messages sometimes fails,
|
||||
// due to their internal state.
|
||||
// Expected messages are usually with a vanilla state, eg only exported fields contain data.
|
||||
// Actual messages obtained from the gRPC client had unexported fields with data.
|
||||
// This makes them hard to compare.
|
||||
func EqualProto(t testing.TB, expected, actual proto.Message) bool {
|
||||
t.Helper()
|
||||
if proto.Equal(expected, actual) {
|
||||
return true
|
||||
}
|
||||
t.Errorf("Proto messages not equal: %s", diffProto(expected, actual))
|
||||
return false
|
||||
}
|
||||
|
||||
func diffProto(expected, actual proto.Message) string {
|
||||
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(protojson.Format(expected)),
|
||||
B: difflib.SplitLines(protojson.Format(actual)),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "\n\nDiff:\n" + diff
|
||||
}
|
Reference in New Issue
Block a user