featurebase/idk/header_test.go
souhailanoor 2bdf1f5081
FB-1597: unifying idk and featurebase (#2160)
* unifying idk and featurebase: first pass

* resolved conflict with master for gitignore & dockerignore

* deleted binaries that were accidentally pushed to git

* combined gitlab jobs for idk & featurebase

* run go fmt for idk

* updated ssh env variable, and made docker password variable in gitlab env variables

* fixed typo assigning variable name

* trying to fix docker login error

* trying a different solution for docker password

* pass registry

* fixed docker login

* updated paths for idk

* exclude idk tests from featurebase test run

* fix vendor error

* update certificates

* grpc needs to be in version 1.38
genproto, which is imported by big query updates the grpc version to 1.47.0
grpc 1.47.0 causes etcd to deadlock when calling etcd.Close()
the fix is to have a replace in go.mod to specify a specific grpc version

* run go mod tidy

* go mod

* run go mod tidy

* exclude bigquery since it is causing issues and undo grpc replace in go.mod

* fix grpc version

* fix formatting error

* update formatting

* attempt to fix formatting

* update path for code coverage

* update to use current branch binaries, not master

* fix for building idk - path updates

* udpate path for binaries

* update job dependecies

* update docker idk tests to use the current branch registry

* update stages for jobs

* updated job dependencies

* not allow idk s3 dump to fail since it is a dependency for integration tests

* update dependecy for idk tests

* update paths for idk build and code coverage

* download featurebase binary from s3

* pass branch name to all setup scripts

* change to current branch instead of master

* updated sonarcloud

* sonarcloud fix and branch name fix

* trying to speed up pipeline run time

* update stage

* branch name fix + sonar cloud

* sonarcloud
2022-07-28 17:23:16 -05:00

423 lines
12 KiB
Go

package idk
import (
"fmt"
"reflect"
"strings"
"testing"
"time"
)
func TestHeaderToField(t *testing.T) {
seven := int64(7)
tests := []struct {
name string
input string
exp Field
expErr string
expLog string
}{
{
name: "empty",
input: "",
expErr: ErrNoFieldSpec.Error(),
},
{
name: "no name",
input: "__String",
expErr: "field '__String' has no sourceName",
},
{
name: "justname",
input: "blah",
expErr: ErrNoFieldSpec.Error(),
},
{
name: "justname__",
input: "blah__",
expErr: "unknown field",
},
{
name: "unknown field",
input: "blah__Ztring",
expErr: "unknown field",
},
{
name: "string",
input: "a__String",
exp: StringField{NameVal: "a", DestNameVal: "a"},
},
{
name: "string-mutex-f",
input: "a__String_F",
exp: StringField{NameVal: "a", DestNameVal: "a"},
},
{
name: "string-mutex",
input: "a__String_T",
exp: StringField{NameVal: "a", DestNameVal: "a", Mutex: true},
},
{
name: "string-quantum",
input: "a__String_F_YM",
exp: StringField{NameVal: "a", DestNameVal: "a", Mutex: false, Quantum: "YM"},
},
{
name: "string-ttl",
input: "a__String_F_YM_30s",
exp: StringField{NameVal: "a", DestNameVal: "a", Mutex: false, Quantum: "YM", TTL: "30s"},
},
{
name: "string-extra_arg",
input: "a__String_F_YM_30s_Z",
exp: StringField{NameVal: "a", DestNameVal: "a", Mutex: false, Quantum: "YM", TTL: "30s"},
expLog: "ignoring extra arguments to StringField a__String_F_YM_30s_Z",
},
{
name: "unknown arg",
input: "a__String_Z",
expErr: "can't interpret 'Z' for StringField.Mutex for field 'a'",
},
{
name: "bool",
input: "z__Bool",
exp: BoolField{NameVal: "z", DestNameVal: "z"},
},
{
name: "bool-extra",
input: "z__Bool_extra_2",
exp: BoolField{NameVal: "z", DestNameVal: "z"},
expLog: "ignoring extra arguments to BoolField",
},
{
name: "id",
input: "myname__ID",
exp: IDField{NameVal: "myname", DestNameVal: "myname"},
},
{
name: "id-mutex",
input: "z__ID_T",
exp: IDField{NameVal: "z", DestNameVal: "z", Mutex: true},
},
{
name: "id-quantum",
input: "z__ID_F_YMD",
exp: IDField{NameVal: "z", DestNameVal: "z", Mutex: false, Quantum: "YMD"},
},
{
name: "id-ttl",
input: "z__ID_F_YMD_30s",
exp: IDField{NameVal: "z", DestNameVal: "z", Mutex: false, Quantum: "YMD", TTL: "30s"},
},
{
name: "id-extra_arg",
input: "z__ID_F_YMD_30s_Z",
exp: IDField{NameVal: "z", DestNameVal: "z", Mutex: false, Quantum: "YMD", TTL: "30s"},
expLog: "ignoring extra arguments to IDField z__ID_F_YMD_30s_Z",
},
{
name: "int",
input: "myname__Int",
exp: IntField{NameVal: "myname", DestNameVal: "myname"},
},
{
name: "int-min",
input: "myname__Int_7",
exp: IntField{NameVal: "myname", DestNameVal: "myname", Min: &seven},
},
{
name: "int-min-max",
input: "myname__Int_7_7",
exp: IntField{NameVal: "myname", DestNameVal: "myname", Min: &seven, Max: &seven},
},
{
name: "int-min-max-foreign",
input: "myname__Int_7_7_findex",
exp: IntField{NameVal: "myname", DestNameVal: "myname", Min: &seven, Max: &seven, ForeignIndex: "findex"},
},
{
name: "int-min-max-extra",
input: "myname__Int_7_7_z_",
exp: IntField{NameVal: "myname", DestNameVal: "myname", Min: &seven, Max: &seven, ForeignIndex: "z"},
expLog: "ignoring extra arguments to IntField",
},
{
name: "int-min-parseerr",
input: "myname__Int_7_8.9",
expErr: "parsing max for",
},
{
name: "int-parserr-max",
input: "myname__Int_blah_7",
expErr: "parsing min for",
},
{
name: "decimal-scale",
input: "myname__Decimal_7",
exp: DecimalField{NameVal: "myname", DestNameVal: "myname", Scale: 7},
},
{
name: "decimal",
input: "myname__Decimal",
exp: DecimalField{NameVal: "myname", DestNameVal: "myname"},
},
{
name: "decimal-err",
input: "myname__Decimal_!",
expErr: "parsing scale for",
},
{
name: "stringarray-quantum",
input: "myname__StringArray_YMD",
exp: StringArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD"},
},
{
name: "stringarray-ttl",
input: "myname__StringArray_YMD_30s",
exp: StringArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD", TTL: "30s"},
},
{
name: "stringarray-extra_arg",
input: "myname__StringArray_YMD_30s_Z",
exp: StringArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD", TTL: "30s"},
expLog: "ignoring extra arguments to StringArrayField",
},
{
name: "idarray-quantum",
input: "myname__IDArray_YMD",
exp: IDArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD"},
},
{
name: "idarray-ttl",
input: "myname__IDArray_YMD_30s",
exp: IDArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD", TTL: "30s"},
},
{
name: "idarray-extra_arg",
input: "myname__IDArray_YMD_30s_Z",
exp: IDArrayField{NameVal: "myname", DestNameVal: "myname", Quantum: "YMD", TTL: "30s"},
expLog: "ignoring extra arguments to IDArrayField",
},
{
name: "date-int",
input: "myname__DateInt",
exp: DateIntField{
NameVal: "myname",
DestNameVal: "myname",
Layout: time.RFC3339,
},
},
{
name: "date-int-layout-epoch",
input: "myname__DateInt_2006-01-02T15:04:05Z07:00_2018-03-04T15:04:05Z",
exp: DateIntField{
NameVal: "myname",
DestNameVal: "myname",
Layout: time.RFC3339,
Epoch: time.Date(2018, time.March, 4, 15, 4, 5, 0, time.UTC),
},
},
{
name: "date-int-layout-epoch-unit",
input: "myname__DateInt_2006-01-02T15:04:05Z07:00_2018-03-04T15:04:05Z_D",
exp: DateIntField{
NameVal: "myname", DestNameVal: "myname",
Layout: time.RFC3339,
Epoch: time.Date(2018, time.March, 4, 15, 4, 5, 0, time.UTC),
Unit: Day,
},
},
{
name: "date-int-layout-epoch-unit-custom",
input: "myname__DateInt_2006-01-02T15:04:05Z07:00_2018-03-04T15:04:05Z_C_10h",
exp: DateIntField{
NameVal: "myname", DestNameVal: "myname",
Layout: time.RFC3339,
Epoch: time.Date(2018, time.March, 4, 15, 4, 5, 0, time.UTC),
Unit: Custom,
CustomUnit: "10h",
},
},
{
name: "date-int-layout-epoch-unit-custom-2",
input: "myname__DateInt_2006-01-02_2018-03-04_D_0",
exp: DateIntField{
NameVal: "myname", DestNameVal: "myname",
Layout: "2006-01-02",
Epoch: time.Date(2018, time.March, 4, 0, 0, 0, 0, time.UTC),
Unit: Day,
},
},
{
name: "date-int-layout-epoch-unit-custom-error",
input: "myname__DateInt_2006-01-02_2018-03d-04_D_0",
expErr: `cannot parse "d-04"`,
},
{
name: "date-int-layout-epoch-unit-custom-layout-badunit",
input: "myname__DateInt_2006-01-02_2018-03-04_Ze_0",
expErr: fmt.Sprintf(ErrFmtUnknownUnit, "ze"),
},
{
name: "date-int-layout-epoch-unit-custom-layout-customuniterror",
input: "myname__DateInt_2006-01-02_2018-03-04_C_127z",
expErr: `parsing custom unit 127z: time: unknown unit "z" in duration "127z"`,
},
{
name: "recordtime",
input: "myname__RecordTime",
exp: RecordTimeField{NameVal: "myname", Layout: "2006-01-02T15:04:05Z07:00", DestNameVal: "myname"},
},
{
name: "recordtime",
input: "__RecordTime",
exp: RecordTimeField{Layout: "2006-01-02T15:04:05Z07:00"},
},
{
name: "recordtime",
input: "myname__RecordTime_2006-01-02",
exp: RecordTimeField{NameVal: "myname", DestNameVal: "myname", Layout: "2006-01-02"},
},
{
name: "recordtime-layout-epoch-unit-custom-error",
input: "myname__RecordTime_2006-01-02_2018-03d-04_D_0",
expErr: `cannot parse "d-04"`,
},
{
name: "recordtime-layout-epoch-unit-custom-layout-badunit",
input: "myname__RecordTime_2006-01-02_2018-03-04_Ze_0",
expErr: "unknown unit",
},
{
name: "multidunder",
input: "multi__dunder__String",
exp: StringField{NameVal: "multi__dunder", DestNameVal: "multi__dunder"},
},
{
name: "multi-dunder-no-fieldspec",
input: "multi__dunder__funder",
expErr: "unknown field 'funder' for",
},
{
name: "map-incompatible-name",
input: "@rbitrary.name*string!___pilosa-name-string__String",
exp: StringField{
NameVal: "@rbitrary.name*string!",
DestNameVal: "pilosa-name-string",
},
},
{
name: "map-compatible-name",
input: "dunderful__name__string___pilosa-name-string__String",
exp: StringField{
NameVal: "dunderful__name__string",
DestNameVal: "pilosa-name-string",
},
},
{
name: "map-dunderful-name",
input: "dunderful__name__string___pilosa__name__string__String",
exp: StringField{
NameVal: "dunderful__name__string",
DestNameVal: "pilosa__name__string",
},
},
{
name: "timestamp",
input: "purchasedate__Timestamp_ms",
exp: TimestampField{
NameVal: "purchasedate",
DestNameVal: "purchasedate",
Granularity: "ms",
Layout: time.RFC3339Nano,
},
},
{
name: "timestamp-epoch",
input: "purchasedate__Timestamp_s_2006-01-02T15:04:05Z07:00_2018-03-04T15:04:05Z_ms",
exp: TimestampField{
NameVal: "purchasedate",
DestNameVal: "purchasedate",
Granularity: "s",
Layout: time.RFC3339,
Epoch: time.Date(2018, time.March, 4, 15, 4, 5, 0, time.UTC),
Unit: Millisecond,
},
},
{
name: "lookup-text",
input: "a__LookupText",
exp: LookupTextField{NameVal: "a", DestNameVal: "a"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
rl := &recordingLogger{}
field, err := HeaderToField(test.input, rl)
if test.expErr != "" {
if err == nil || !strings.Contains(err.Error(), test.expErr) {
t.Fatalf("unexpected error exp/got:\n%s\n%v", test.expErr, err)
}
} else if err != nil {
t.Fatalf("unexpected error: %v", err)
}
errorIfNotEqual(t, test.exp, field)
if test.expLog == "" && len(rl.logs) > 0 {
t.Errorf("unexpected logs: %+v", rl.logs)
} else if test.expLog != "" && len(rl.logs) == 0 {
t.Errorf("expected log: '%s'", test.expLog)
} else if test.expLog != "" {
if !strings.Contains(rl.logs[0], test.expLog) {
t.Errorf("exp/got:\n%s\n%s", test.expLog, rl.logs[0])
}
}
})
}
}
func errorIfNotEqual(t *testing.T, exp, got interface{}) {
t.Helper()
if !reflect.DeepEqual(exp, got) {
t.Errorf("unequal exp/got:\n%+v of %[1]T\n%+v of %[2]T", exp, got)
}
}
func errorIfNotErrMatch(t *testing.T, expErrSubstring string, got error) {
t.Helper()
if expErrSubstring == "" && got != nil {
t.Fatalf("unexpected error: %v", got)
} else if expErrSubstring == "" {
return
} else if expErrSubstring != "" && got == nil {
t.Fatalf("expected error like '%s'", expErrSubstring)
} else if !strings.Contains(got.Error(), expErrSubstring) {
t.Fatalf("unexpected error exp/got:\n%s\n%v", expErrSubstring, got)
}
}
type recordingLogger struct {
logs []string
debugs []string
}
func (r *recordingLogger) Panicf(format string, v ...interface{}) {
r.logs = append(r.logs, fmt.Sprintf("PANIC:"+format, v...))
panic(r.logs)
}
func (r *recordingLogger) Errorf(format string, v ...interface{}) {
r.Printf("ERROR:"+format, v)
}
func (r *recordingLogger) Infof(format string, v ...interface{}) {
r.Printf("INFO:"+format, v)
}
func (r *recordingLogger) Warnf(format string, v ...interface{}) {
r.Printf("WARN:"+format, v)
}
func (r *recordingLogger) Printf(format string, v ...interface{}) {
r.logs = append(r.logs, fmt.Sprintf(format, v...))
}
func (r *recordingLogger) Debugf(format string, v ...interface{}) {
r.debugs = append(r.debugs, fmt.Sprintf(format, v...))
}