func HasExactlyAttributesEqualTo(want event.EventContext) EventMatcher {
	return func(have event.Event) error {
		if want.GetSpecVersion() != have.SpecVersion() {
			return fmt.Errorf("not matching specversion: want = '%s', got = '%s'", want.GetSpecVersion(), have.SpecVersion())
		}
		vs := spec.VS.Version(want.GetSpecVersion())
		for _, a := range vs.Attributes() {
			if !reflect.DeepEqual(a.Get(want), a.Get(have.Context)) {
				return fmt.Errorf("expecting attribute '%s' equal to '%s', got '%s'", a.PrefixedName(), a.Get(want), a.Get(have.Context))
			}
		}
		return nil
	}
}
HasExactlyAttributesEqualTo checks if the event has exactly the provided spec attributes (excluding extension attributes)
package test
import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"
	"github.com/google/go-cmp/cmp"
	"github.com/cloudevents/sdk-go/v2/binding/spec"
	"github.com/cloudevents/sdk-go/v2/event"
)
type EventMatcher func(have event.Event) error
func AllOf(matchers ...EventMatcher) EventMatcher {
	return func(have event.Event) error {
		for _, m := range matchers {
			if err := m(have); err != nil {
				return err
			}
		}
		return nil
	}
}
func AnyOf(matchers ...EventMatcher) EventMatcher {
	return func(have event.Event) error {
		var errs []error
		for _, m := range matchers {
			if err := m(have); err == nil {
				return nil
			} else {
				errs = append(errs, err)
			}
		}
		var sb strings.Builder
		sb.WriteString("Cannot match any of the provided matchers\n")
		for i, err := range errs {
			sb.WriteString(fmt.Sprintf("%d: %s\n", i+1, err))
		}
		return errors.New(sb.String())
	}
}
func HasId(id string) EventMatcher {
	return HasAttributeKind(spec.ID, id)
}
func HasType(ty string) EventMatcher {
	return HasAttributeKind(spec.Type, ty)
}
func HasSpecVersion(specVersion string) EventMatcher {
	return HasAttributeKind(spec.SpecVersion, specVersion)
}
func HasSource(source string) EventMatcher {
	return HasAttributeKind(spec.Source, source)
}
func HasDataContentType(dataContentType string) EventMatcher {
	return HasAttributeKind(spec.DataContentType, dataContentType)
}
func HasDataSchema(schema string) EventMatcher {
	return HasAttributeKind(spec.DataSchema, schema)
}
func HasSubject(subject string) EventMatcher {
	return HasAttributeKind(spec.Subject, subject)
}
func HasTime(t time.Time) EventMatcher {
	return HasAttributeKind(spec.Time, t)
}
func ContainsAttributes(attrs ...spec.Kind) EventMatcher {
	return func(have event.Event) error {
		haveVersion := spec.VS.Version(have.SpecVersion())
		for _, k := range attrs {
			attr := haveVersion.AttributeFromKind(k)
			if isEmpty(attr) {
				return fmt.Errorf("attribute name '%s' unrecognized", k.String())
			}
			if isEmpty(attr.Get(have.Context)) {
				return fmt.Errorf("missing or nil/empty attribute '%s'", k.String())
			}
		}
		return nil
	}
}
func ContainsExtensions(exts ...string) EventMatcher {
	return func(have event.Event) error {
		for _, ext := range exts {
			if _, ok := have.Extensions()[ext]; !ok {
				return fmt.Errorf("expecting extension '%s'", ext)
			}
		}
		return nil
	}
}
func ContainsExactlyExtensions(exts ...string) EventMatcher {
	return func(have event.Event) error {
		extsInEvent := map[string]struct{}{}
		for k := range have.Extensions() {
			extsInEvent[k] = struct{}{}
		}
		for _, ext := range exts {
			if _, ok := have.Extensions()[ext]; !ok {
				return fmt.Errorf("expecting extension '%s'", ext)
			} else {
				delete(extsInEvent, ext)
			}
		}
		if len(extsInEvent) != 0 {
			var unexpectedKeys []string
			for k := range extsInEvent {
				unexpectedKeys = append(unexpectedKeys, k)
			}
			return fmt.Errorf("not expecting extensions '%v'", unexpectedKeys)
		}
		return nil
	}
}