blob: 6151969606fc13765566e9b69d12da2f2469f482 [file] [log] [blame]
package config_validator
import (
"config"
"config_parser"
"testing"
"time"
)
// Allows generating a list of MetricTypes for which we can run tests.
func metricTypesExcept(remove ...config.MetricDefinition_MetricType) (s []config.MetricDefinition_MetricType) {
types := map[config.MetricDefinition_MetricType]bool{}
for t := range config.MetricDefinition_MetricType_name {
types[config.MetricDefinition_MetricType(t)] = true
}
for _, r := range remove {
delete(types, r)
}
delete(types, config.MetricDefinition_UNSET)
for t, _ := range types {
s = append(s, t)
}
return
}
func makeValidMetadata() config.MetricDefinition_Metadata {
return config.MetricDefinition_Metadata{
ExpirationDate: time.Now().AddDate(1, 0, 0).Format(dateFormat),
Owner: []string{"google@example.com"},
MaxReleaseStage: config.ReleaseStage_DEBUG,
}
}
func makeValidMetric() config.MetricDefinition {
return makeValidMetricWithName("the_metric_name")
}
// makeValidMetric returns a valid instance of config.MetricDefinition which
// can be modified to fail various validation checks for testing purposes.
func makeValidMetricWithName(name string) config.MetricDefinition {
metadata := makeValidMetadata()
return config.MetricDefinition{
Id: config_parser.IdFromName(name),
MetricName: name,
MetricType: config.MetricDefinition_EVENT_COUNT,
MetaData: &metadata,
MetricDimensions: []*config.MetricDefinition_MetricDimension{
&config.MetricDefinition_MetricDimension{
EventCodes: map[uint32]string{1: "hello_world"},
},
},
}
}
// Test that makeValidMetric returns a valid metric.
func TestValidateMakeValidMetric(t *testing.T) {
m := makeValidMetric()
if err := validateMetricDefinition(m); err != nil {
t.Errorf("Rejected valid metric: %v", err)
}
}
func TestValidateMakeValidMetadata(t *testing.T) {
m := makeValidMetadata()
if err := validateMetadata(m); err != nil {
t.Errorf("Rejected valid metadata: %v", err)
}
}
// Test that repeated ids are rejected.
func TestValidateUniqueMetricId(t *testing.T) {
m1Name := "TpzQweXFfRXpQrDWvplhfXFbJptlKmkIlHBAzjPnADtJWVAawVrbPGg"
m2Name := "zDnlfjrXpwuQYpBrNTeCbtsRBydKuKdCvEjlGwdRJlxMjbYOSGPjhif"
m1 := makeValidMetricWithName(m1Name)
m2 := makeValidMetricWithName(m2Name)
metrics := []*config.MetricDefinition{&m1, &m2}
if err := validateConfiguredMetricDefinitions(metrics); err == nil {
t.Error("Accepted metric definitions with identical ids.")
}
}
// Test that invalid names are rejected.
func TestValidateMetricInvalidMetricName(t *testing.T) {
m := makeValidMetricWithName("_invalid_name")
if err := validateMetricDefinition(m); err == nil {
t.Error("Accepted metric definition with invalid name.")
}
}
// Test that metric id 0 is not accepted.
func TestValidateZeroMetricId(t *testing.T) {
m := makeValidMetricWithName("NRaMinLNcqiYmgEypLLVGnXymNpxJzqabtbbjLycCMEohvVzZtAYpah")
if err := validateMetricDefinition(m); err == nil {
t.Error("Accepted metric definition with 0 id.")
}
}
// Test that we do not accept a metric with type UNSET.
func TestValidateUnsetMetricType(t *testing.T) {
m := makeValidMetric()
m.MetricType = config.MetricDefinition_UNSET
if err := validateMetricDefinition(m); err == nil {
t.Error("Accepted metric definition with unset metric type.")
}
}
// Test that int_buckets can only be set if the metric type is INT_HISTOGRAM.
func TestValidateIntBucketsSetOnlyForIntHistogram(t *testing.T) {
m := makeValidMetric()
m.IntBuckets = &config.IntegerBuckets{}
m.MetricType = config.MetricDefinition_INT_HISTOGRAM
if err := validateMetricDefinition(m); err != nil {
t.Errorf("Rejected valid INT_HISTOGRAM metric definition: %v", err)
}
for _, mt := range metricTypesExcept(config.MetricDefinition_INT_HISTOGRAM) {
m.MetricType = mt
if err := validateMetricDefinition(m); err == nil {
t.Errorf("Accepted metric definition with type %s with int_buckets set.", mt)
}
}
}
// Test that parts can only be set if the metric type is CUSTOM.
func TestValidatePartsSetOnlyForCustom(t *testing.T) {
m := makeValidMetric()
m.Parts = map[string]*config.MetricPart{"hello": nil}
m.MetricType = config.MetricDefinition_CUSTOM
m.MetricDimensions = nil
if err := validateMetricDefinition(m); err != nil {
t.Errorf("Rejected valid CUSTOM metric definition: %v", err)
}
for _, mt := range metricTypesExcept(config.MetricDefinition_CUSTOM) {
m.MetricType = mt
if err := validateMetricDefinition(m); err == nil {
t.Errorf("Accepted metric definition with type %s with parts set.", mt)
}
}
}
// Test that parts can only be set if the metric type is CUSTOM.
func TestValidateProtoSetOnlyForCustom(t *testing.T) {
m := makeValidMetric()
m.ProtoName = "$team_name.test.ProtoName"
m.MetricType = config.MetricDefinition_CUSTOM
m.MetricDimensions = nil
if err := validateMetricDefinition(m); err != nil {
t.Errorf("Rejected valid CUSTOM metric definition: %v", err)
}
for _, mt := range metricTypesExcept(config.MetricDefinition_CUSTOM) {
m.MetricType = mt
if err := validateMetricDefinition(m); err == nil {
t.Errorf("Accepted metric definition with type %s with proto_name set.", mt)
}
}
}
// Test that meta_data must be set.
func TestValidatePartsNoMetadata(t *testing.T) {
m := makeValidMetric()
m.MetaData = nil
if err := validateMetricDefinition(m); err == nil {
t.Error("Accepted metric definition with no meta_data set.")
}
}
func TestValidateMetadataNoExpirationDate(t *testing.T) {
m := makeValidMetadata()
m.ExpirationDate = ""
if err := validateMetadata(m); err == nil {
t.Error("Accepted metadata with no expiration date.")
}
}
func TestValidateMetadataInvalidExpirationDate(t *testing.T) {
m := makeValidMetadata()
m.ExpirationDate = "abcd"
if err := validateMetadata(m); err == nil {
t.Error("Accepted invalid expiration date")
}
}
func TestValidateMetadataExpirationDateTooFar(t *testing.T) {
m := makeValidMetadata()
m.ExpirationDate = time.Now().AddDate(1, 0, 2).Format(dateFormat)
if err := validateMetadata(m); err == nil {
t.Errorf("Accepted expiration date more than 1 year out: %v", m.ExpirationDate)
}
}
func TestValidateMetadataExpirationDateInPast(t *testing.T) {
m := makeValidMetadata()
m.ExpirationDate = "2010/01/01"
if err := validateMetadata(m); err != nil {
t.Errorf("Rejected expiration date in the past: %v", err)
}
}
func TestValidateMetadataInvalidOwner(t *testing.T) {
m := makeValidMetadata()
m.Owner = append(m.Owner, "not a valid email")
if err := validateMetadata(m); err == nil {
t.Error("Accepted owner with invalid email address.")
}
}
func TestValidateMetadataReleaseStageNotSet(t *testing.T) {
m := makeValidMetadata()
m.MaxReleaseStage = config.ReleaseStage_RELEASE_STAGE_NOT_SET
if err := validateMetadata(m); err == nil {
t.Error("Accepted owner with no max_release_stage set.")
}
}
func TestValidateEventCodesMaxEventCodeTooBig(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].MaxEventCode = 1024
m.MetricDimensions[0].EventCodes = map[uint32]string{
1: "hello_world",
}
if err := validateMetricDimensions(m); err == nil {
t.Error("Accepted max_event_code with value no less than 1024.")
}
}
func TestValidateEventCodesIndexLargerThanMax(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].MaxEventCode = 100
m.MetricDimensions[0].EventCodes = map[uint32]string{
1: "hello_world",
101: "blah",
}
if err := validateMetricDimensions(m); err == nil {
t.Error("Accepted event type with index larger than max_event_code.")
}
}
func TestValidateEventCodesNoEventCodes(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].EventCodes = map[uint32]string{}
if err := validateMetricDimensions(m); err == nil {
t.Error("Accepted metric with no event types.")
}
}
func TestValidateEventOccurredNoMax(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].MaxEventCode = 0
if err := validateEventOccurred(m); err == nil {
t.Error("Accepted EVENT_OCCURRED metric with no max_event_code.")
}
}
func TestValidateIntHistogramNoBuckets(t *testing.T) {
m := makeValidMetric()
m.IntBuckets = nil
if err := validateIntHistogram(m); err == nil {
t.Error("Accepted INT_HISTOGRAM metric with no int_buckets.")
}
}
func TestValidateStringUsedEventCodesSet(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
if err := validateStringUsed(m); err == nil {
t.Error("Accepted STRING_USED metric with event_codes set.")
}
}
func TestValidateCustomEventCodesSetOld(t *testing.T) {
m := makeValidMetric()
m.Parts = map[string]*config.MetricPart{"hello": nil}
m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with event_codes set.")
}
}
func TestValidateCustomEventCodesSet(t *testing.T) {
m := makeValidMetric()
m.ProtoName = "test.ProtoName"
m.MetricDimensions[0].EventCodes = map[uint32]string{1: "hello"}
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with event_codes set.")
}
}
func TestValidateCustomNoParts(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].EventCodes = map[uint32]string{}
m.Parts = map[string]*config.MetricPart{}
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with no parts.")
}
}
func TestValidateCustomInvalidPartName(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].EventCodes = map[uint32]string{}
m.Parts = map[string]*config.MetricPart{"_invalid_name": nil}
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with invalid part name.")
}
}
func TestValidateCustomNoProtoName(t *testing.T) {
m := makeValidMetric()
m.Parts = map[string]*config.MetricPart{}
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with no proto_name.")
}
}
func TestValidateCustomInvalidProtoName(t *testing.T) {
m := makeValidMetric()
m.MetricDimensions[0].EventCodes = map[uint32]string{}
m.ProtoName = "_invalid.ProtoName"
if err := validateCustom(m); err == nil {
t.Error("Accepted CUSTOM metric with invalid proto_name.")
}
}