diff --git a/management/internals/network_map_db/db_store.go b/management/internals/network_map_db/db_store.go index c3a82805b..8dc16f734 100644 --- a/management/internals/network_map_db/db_store.go +++ b/management/internals/network_map_db/db_store.go @@ -99,6 +99,9 @@ func FromSqlTypesToSharedTypes(src reflect.Value, dst reflect.Value) error { s := srcField.Interface().(json.RawMessage) json.Unmarshal(s, dstField.Addr().Interface()) case "[]string": + if srcField.IsNil() { + return nil + } dstv := reflect.MakeSlice(dstField.Type(), srcField.Len(), srcField.Cap()) reflect.Copy(dstv, srcField) dstField.Set(dstv) diff --git a/management/internals/network_map_db/pgsql/another_test.go b/management/internals/network_map_db/pgsql/another_test.go deleted file mode 100644 index dc1ff3af8..000000000 --- a/management/internals/network_map_db/pgsql/another_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package networkmap_pgsql - -import ( - "database/sql" - "encoding/json" - "reflect" - "testing" - "time" - - networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" - "github.com/stretchr/testify/assert" -) - -type g1 struct { - Name sql.NullString -} - -type g2 struct { - Name string -} - -type g11 struct { - Name sql.NullString - PublicId sql.NullString -} - -type g22 struct { - Name string - PublicId string -} - -type g111 struct { - Name sql.NullString - TrueOrFalse sql.NullBool -} - -type g222 struct { - Name string - TrueOrFalse bool -} - -type g1111 struct { - Time sql.NullTime -} - -type g2222 struct { - Time time.Time -} - -type g11111 struct { - Blob json.RawMessage -} - -type embeddedS struct { - Name string - SomeField int -} -type g22222 struct { - Blob embeddedS -} - -type t1 struct { - Field string `nmap:"skip"` -} - -type dt1 struct { - Field string -} - -type o1 struct { - Field string `nmap:"map_to:AnotherField"` -} - -type do1 struct { - AnotherField string -} - -type ptr1 struct { - Field sql.NullString -} - -type ptro1 struct { - Field *string -} - -type i1 struct { - Field sql.NullInt64 -} - -type io1 struct { - Field int -} - -func TestOne(t *testing.T) { - - src := g1{Name: sql.NullString{String: "string", Valid: true}} - dst := g2{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) - assert.Equal(t, g2{Name: "string"}, dst) - - src = g1{Name: sql.NullString{Valid: false}} - dst = g2{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) - assert.Equal(t, g2{Name: ""}, dst) - - src1 := g11{Name: sql.NullString{String: "aaa", Valid: true}, PublicId: sql.NullString{String: "id", Valid: true}} - dst1 := g22{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src1), reflect.ValueOf(&dst1))) - assert.Equal(t, g22{Name: "aaa", PublicId: "id"}, dst1) - - src2 := g111{Name: sql.NullString{String: "aaa", Valid: true}, TrueOrFalse: sql.NullBool{Bool: true, Valid: true}} - dst2 := g222{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src2), reflect.ValueOf(&dst2))) - assert.Equal(t, g222{Name: "aaa", TrueOrFalse: true}, dst2) - - jb, _ := json.Marshal(embeddedS{Name: "blob-name", SomeField: 1}) - src3 := g11111{Blob: json.RawMessage(jb)} - dst3 := g22222{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src3), reflect.ValueOf(&dst3))) - assert.Equal(t, g22222{Blob: embeddedS{Name: "blob-name", SomeField: 1}}, dst3) - - src4 := g11111{} - dst4 := g22222{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src4), reflect.ValueOf(&dst4))) - assert.Equal(t, g22222{}, dst4) - - src5 := t1{Field: "shouldskip"} - dst5 := dt1{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src5), reflect.ValueOf(&dst5))) - assert.Equal(t, dt1{}, dst5) - - src6 := o1{Field: "fieldvalue"} - dst6 := do1{} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src6), reflect.ValueOf(&dst6))) - assert.Equal(t, do1{AnotherField: "fieldvalue"}, dst6) - - src7 := i1{Field: sql.NullInt64{Int64: int64(1), Valid: true}} - dst7 := io1{Field: 1} - assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src7), reflect.ValueOf(&dst7))) - assert.Equal(t, io1{Field: 1}, dst7) -} diff --git a/management/internals/network_map_db/pgsql/sql_type_conversion_test.go b/management/internals/network_map_db/pgsql/sql_type_conversion_test.go new file mode 100644 index 000000000..c2e64a4e1 --- /dev/null +++ b/management/internals/network_map_db/pgsql/sql_type_conversion_test.go @@ -0,0 +1,201 @@ +package networkmap_pgsql + +import ( + "database/sql" + "encoding/json" + "reflect" + "testing" + "time" + + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" + "github.com/stretchr/testify/assert" +) + +func TestNullStringSupport(t *testing.T) { + src := withNullString{Name: sql.NullString{String: "string", Valid: true}} + dst := withString{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, withString{Name: "string"}, dst) + + src = withNullString{Name: sql.NullString{Valid: false}} + dst = withString{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, withString{Name: ""}, dst) +} + +func TestNullBoolSupport(t *testing.T) { + src := withNullBool{TrueOrFalse: sql.NullBool{Bool: true, Valid: true}} + dst := withBool{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, withBool{TrueOrFalse: true}, dst) + +} + +func TestRawJsonSupport(t *testing.T) { + jb, _ := json.Marshal(embeddedS{Name: "blob-name", SomeField: 1}) + src := withRawJson{Blob: json.RawMessage(jb)} + dst := fromJson{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, fromJson{Blob: embeddedS{Name: "blob-name", SomeField: 1}}, dst) + + src1 := withRawJson{} + dst1 := fromJson{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src1), reflect.ValueOf(&dst1))) + assert.Equal(t, fromJson{}, dst1) +} + +func TestShouldSkipTag(t *testing.T) { + src5 := withSkipTag{Field: "shouldskip"} + dst5 := emptySkipTagTarget{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src5), reflect.ValueOf(&dst5))) + assert.Equal(t, emptySkipTagTarget{}, dst5) + +} + +func TestMapToTag(t *testing.T) { + src6 := withMapToTag{Field: "fieldvalue"} + dst6 := mapToTagTarget{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src6), reflect.ValueOf(&dst6))) + assert.Equal(t, mapToTagTarget{AnotherField: "fieldvalue"}, dst6) +} + +func TestNullableInt64Support(t *testing.T) { + src := withInt64{Field: sql.NullInt64{Int64: int64(1), Valid: true}} + dst := int64Target{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, int64Target{Field: 1}, dst) +} + +func TestNullableTimeSupport(t *testing.T) { + now := time.Now() + src := withNullableTime{Field: sql.NullTime{Time: now, Valid: true}} + dst := nullableTimeTarget{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, nullableTimeTarget{Field: now}, dst) +} + +func TestNullableTimePointerSupport(t *testing.T) { + now := time.Now() + src := withNullableTime{Field: sql.NullTime{Time: now, Valid: true}} + dst := nullableTimePointerTarget{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, nullableTimePointerTarget{Field: &now}, dst) +} + +func TestStringSLiceSupport(t *testing.T) { + src := withStringSlice{Field: []string{"one"}} + dst := withStringSlice{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, withStringSlice{Field: []string{"one"}}, dst) +} + +func TestNullStringSLiceSupport(t *testing.T) { + src := withStringSlice{} + dst := withStringSlice{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, withStringSlice{}, dst) +} + +func TestWithMultipleFields(t *testing.T) { + now := time.Now() + src := withMultipleFields{ + Field1: sql.NullString{String: "aaa", Valid: true}, + Field2: sql.NullBool{Bool: true, Valid: true}, + Field3: sql.NullTime{Time: now, Valid: true}, + Field4: sql.NullInt64{Int64: 1, Valid: true}, + Field5: "another", + } + dst := multipleFieldsTarget{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst))) + assert.Equal(t, multipleFieldsTarget{ + Field1: "aaa", + Field2: true, + Field3: now, + Field4: 1, + Field5: "another", + }, dst) +} + +type withNullString struct { + Name sql.NullString +} + +type withString struct { + Name string +} + +type withMultipleFields struct { + Field1 sql.NullString + Field2 sql.NullBool + Field3 sql.NullTime + Field4 sql.NullInt64 + Field5 string +} + +type multipleFieldsTarget struct { + Field1 string + Field2 bool + Field3 time.Time + Field4 int64 + Field5 string +} + +type withNullBool struct { + TrueOrFalse sql.NullBool +} + +type withBool struct { + TrueOrFalse bool +} + +type withRawJson struct { + Blob json.RawMessage +} + +type embeddedS struct { + Name string + SomeField int +} +type fromJson struct { + Blob embeddedS +} + +type withSkipTag struct { + Field string `nmap:"skip"` +} + +type emptySkipTagTarget struct { + Field string +} + +type withMapToTag struct { + Field string `nmap:"map_to:AnotherField"` +} + +type mapToTagTarget struct { + AnotherField string +} + +type withInt64 struct { + Field sql.NullInt64 +} + +type int64Target struct { + Field int +} + +type withNullableTime struct { + Field sql.NullTime +} + +type nullableTimeTarget struct { + Field time.Time +} + +type nullableTimePointerTarget struct { + Field *time.Time +} + +type withStringSlice struct { + Field []string +}