Fix testhelper.deepDiffEqual (#374)
* Fix deepdiff comparison with maps
* Fix RemainingKeys function
* Fix unit tests
* Another fix of RemainingKeys
* RemainingKeys cleanup
* Simplifying RemainingKeys
* Revert continue on invalid. Fix broken tests
Related-PROD: PROD-28126
Change-Id: Ifc5afaf1278c7cff3a89b23a1fd1876aac1dff34
diff --git a/internal/testing/util_test.go b/internal/testing/util_test.go
index a4f03ef..5cede6d 100644
--- a/internal/testing/util_test.go
+++ b/internal/testing/util_test.go
@@ -1,36 +1,42 @@
package testing
import (
+ "reflect"
"testing"
"gerrit.mcp.mirantis.net/debian/gophercloud.git/internal"
- th "gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper"
)
func TestRemainingKeys(t *testing.T) {
type User struct {
- FirstName string `json:"first_name"`
- LastName string `json:"last_name"`
- City string
+ UserID string `json:"user_id"`
+ Username string `json:"username"`
+ Location string `json:"-"`
+ CreatedAt string `json:"-"`
+ Status string
+ IsAdmin bool
}
- userStruct := User{
- FirstName: "John",
- LastName: "Doe",
- }
-
- userMap := map[string]interface{}{
- "first_name": "John",
- "last_name": "Doe",
- "city": "Honolulu",
- "state": "Hawaii",
+ userResponse := map[string]interface{}{
+ "user_id": "abcd1234",
+ "username": "jdoe",
+ "location": "Hawaii",
+ "created_at": "2017-06-08T02:49:03.000000",
+ "status": "active",
+ "is_admin": "true",
+ "custom_field": "foo",
}
expected := map[string]interface{}{
- "city": "Honolulu",
- "state": "Hawaii",
+ "created_at": "2017-06-08T02:49:03.000000",
+ "is_admin": "true",
+ "custom_field": "foo",
}
- actual := internal.RemainingKeys(userStruct, userMap)
- th.AssertDeepEquals(t, expected, actual)
+ actual := internal.RemainingKeys(User{}, userResponse)
+
+ isEqual := reflect.DeepEqual(expected, actual)
+ if !isEqual {
+ t.Fatalf("expected %s but got %s", expected, actual)
+ }
}
diff --git a/internal/util.go b/internal/util.go
index f7e10d2..8efb283 100644
--- a/internal/util.go
+++ b/internal/util.go
@@ -2,23 +2,31 @@
import (
"reflect"
+ "strings"
)
-// RemainingKeys will inspect a struct and compare it to a map. Any key that
-// is not defined in a JSON tag of the struct will be added to the extras map
-// and returned.
+// RemainingKeys will inspect a struct and compare it to a map. Any struct
+// field that does not have a JSON tag that matches a key in the map or
+// a matching lower-case field in the map will be returned as an extra.
//
// This is useful for determining the extra fields returned in response bodies
// for resources that can contain an arbitrary or dynamic number of fields.
func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) {
extras = make(map[string]interface{})
+ for k, v := range m {
+ extras[k] = v
+ }
+
valueOf := reflect.ValueOf(s)
typeOf := reflect.TypeOf(s)
for i := 0; i < valueOf.NumField(); i++ {
field := typeOf.Field(i)
- tagValue := field.Tag.Get("json")
- if _, ok := m[tagValue]; !ok {
- extras[tagValue] = m[tagValue]
+
+ lowerField := strings.ToLower(field.Name)
+ delete(extras, lowerField)
+
+ if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" {
+ delete(extras, tagValue)
}
}
diff --git a/openstack/cdn/v1/base/testing/requests_test.go b/openstack/cdn/v1/base/testing/requests_test.go
index 69a8256..216a271 100644
--- a/openstack/cdn/v1/base/testing/requests_test.go
+++ b/openstack/cdn/v1/base/testing/requests_test.go
@@ -17,16 +17,18 @@
th.CheckNoErr(t, err)
expected := base.HomeDocument{
- "rel/cdn": map[string]interface{}{
- "href-template": "services{?marker,limit}",
- "href-vars": map[string]interface{}{
- "marker": "param/marker",
- "limit": "param/limit",
- },
- "hints": map[string]interface{}{
- "allow": []string{"GET"},
- "formats": map[string]interface{}{
- "application/json": map[string]interface{}{},
+ "resources": map[string]interface{}{
+ "rel/cdn": map[string]interface{}{
+ "href-template": "services{?marker,limit}",
+ "href-vars": map[string]interface{}{
+ "marker": "param/marker",
+ "limit": "param/limit",
+ },
+ "hints": map[string]interface{}{
+ "allow": []interface{}{"GET"},
+ "formats": map[string]interface{}{
+ "application/json": map[string]interface{}{},
+ },
},
},
},
diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go
index 939a530..ba91bcf 100644
--- a/openstack/compute/v2/servers/testing/fixtures.go
+++ b/openstack/compute/v2/servers/testing/fixtures.go
@@ -870,17 +870,17 @@
"public": []servers.Address{
{
Version: 4,
- Address: "80.56.136.39",
+ Address: "50.56.176.35",
},
{
Version: 6,
- Address: "2001:4800:790e:510:be76:4eff:fe04:82a8",
+ Address: "2001:4800:790e:510:be76:4eff:fe04:84a8",
},
},
"private": []servers.Address{
{
Version: 4,
- Address: "10.880.3.154",
+ Address: "10.180.3.155",
},
},
}
@@ -901,7 +901,7 @@
},
{
"version": 6,
- "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
+ "addr": "2001:4800:790e:510:be76:4eff:fe04:84a8"
}
],
"private": [
@@ -923,7 +923,7 @@
},
{
Version: 6,
- Address: "2001:4800:780e:510:be76:4eff:fe04:84a8",
+ Address: "2001:4800:790e:510:be76:4eff:fe04:84a8",
},
}
@@ -942,7 +942,7 @@
},
{
"version": 6,
- "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8"
+ "addr": "2001:4800:790e:510:be76:4eff:fe04:84a8"
}
]
}`)
diff --git a/openstack/db/v1/configurations/testing/fixtures.go b/openstack/db/v1/configurations/testing/fixtures.go
index 63d028a..e36d610 100644
--- a/openstack/db/v1/configurations/testing/fixtures.go
+++ b/openstack/db/v1/configurations/testing/fixtures.go
@@ -154,6 +154,6 @@
Updated: timeVal,
Values: map[string]interface{}{
"collation_server": "latin1_swedish_ci",
- "connect_timeout": 120,
+ "connect_timeout": float64(120),
},
}
diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
index 584e7fb..36fa4bd 100644
--- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
+++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go
@@ -94,7 +94,7 @@
Name: "d",
IsPublic: true,
ExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false", "snapshot_support": "True"},
- RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "True"},
+ RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false"},
},
}
diff --git a/testhelper/convenience.go b/testhelper/convenience.go
index f21c3f9..25f6720 100644
--- a/testhelper/convenience.go
+++ b/testhelper/convenience.go
@@ -167,7 +167,7 @@
for _, k := range keys {
expectedValue := expected.MapIndex(k)
- actualValue := expected.MapIndex(k)
+ actualValue := actual.MapIndex(k)
if !expectedValue.IsValid() {
logDifference(path, nil, actual.Interface())
diff --git a/testing/params_test.go b/testing/params_test.go
index 9d771a4..edfcb39 100644
--- a/testing/params_test.go
+++ b/testing/params_test.go
@@ -144,7 +144,7 @@
// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
// interface.
type AuthOptions struct {
- PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
+ PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
// The TenantID and TenantName fields are optional for the Identity V2 API.
// Some providers allow you to specify a TenantName instead of the TenantId.
@@ -155,9 +155,9 @@
// TokenCredentials allows users to authenticate (possibly as another user) with an
// authentication token ID.
- TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
+ TokenCredentials *TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
- OrFields orFields `json:"or_fields,omitempty"`
+ OrFields *orFields `json:"or_fields,omitempty"`
}
var successCases = []struct {
@@ -166,7 +166,7 @@
}{
{
AuthOptions{
- PasswordCredentials: PasswordCredentials{
+ PasswordCredentials: &PasswordCredentials{
Username: "me",
Password: "swordfish",
},
@@ -182,7 +182,7 @@
},
{
AuthOptions{
- TokenCredentials: TokenCredentials{
+ TokenCredentials: &TokenCredentials{
ID: "1234567",
},
},
@@ -215,10 +215,10 @@
},
{
AuthOptions{
- TokenCredentials: TokenCredentials{
+ TokenCredentials: &TokenCredentials{
ID: "1234567",
},
- PasswordCredentials: PasswordCredentials{
+ PasswordCredentials: &PasswordCredentials{
Username: "me",
Password: "swordfish",
},
@@ -227,7 +227,7 @@
},
{
AuthOptions{
- PasswordCredentials: PasswordCredentials{
+ PasswordCredentials: &PasswordCredentials{
Password: "swordfish",
},
},
@@ -235,11 +235,11 @@
},
{
AuthOptions{
- PasswordCredentials: PasswordCredentials{
+ PasswordCredentials: &PasswordCredentials{
Username: "me",
Password: "swordfish",
},
- OrFields: orFields{
+ OrFields: &orFields{
Filler: 2,
},
},