move unit tests into 'testing' directories
diff --git a/testing/endpoint_search_test.go b/testing/endpoint_search_test.go
new file mode 100644
index 0000000..22476cb
--- /dev/null
+++ b/testing/endpoint_search_test.go
@@ -0,0 +1,20 @@
+package testing
+
+import (
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestApplyDefaultsToEndpointOpts(t *testing.T) {
+ eo := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic}
+ eo.ApplyDefaults("compute")
+ expected := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"}
+ th.CheckDeepEquals(t, expected, eo)
+
+ eo = gophercloud.EndpointOpts{Type: "compute"}
+ eo.ApplyDefaults("object-store")
+ expected = gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"}
+ th.CheckDeepEquals(t, expected, eo)
+}
diff --git a/testing/params_test.go b/testing/params_test.go
new file mode 100644
index 0000000..90f3fad
--- /dev/null
+++ b/testing/params_test.go
@@ -0,0 +1,250 @@
+package testing
+
+import (
+ "net/url"
+ "reflect"
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestMaybeString(t *testing.T) {
+ testString := ""
+ var expected *string
+ actual := gophercloud.MaybeString(testString)
+ th.CheckDeepEquals(t, expected, actual)
+
+ testString = "carol"
+ expected = &testString
+ actual = gophercloud.MaybeString(testString)
+ th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestMaybeInt(t *testing.T) {
+ testInt := 0
+ var expected *int
+ actual := gophercloud.MaybeInt(testInt)
+ th.CheckDeepEquals(t, expected, actual)
+
+ testInt = 4
+ expected = &testInt
+ actual = gophercloud.MaybeInt(testInt)
+ th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestBuildQueryString(t *testing.T) {
+ type testVar string
+ opts := struct {
+ J int `q:"j"`
+ R string `q:"r,required"`
+ C bool `q:"c"`
+ S []string `q:"s"`
+ TS []testVar `q:"ts"`
+ TI []int `q:"ti"`
+ }{
+ J: 2,
+ R: "red",
+ C: true,
+ S: []string{"one", "two", "three"},
+ TS: []testVar{"a", "b"},
+ TI: []int{1, 2},
+ }
+ expected := &url.URL{RawQuery: "c=true&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
+ actual, err := gophercloud.BuildQueryString(&opts)
+ if err != nil {
+ t.Errorf("Error building query string: %v", err)
+ }
+ th.CheckDeepEquals(t, expected, actual)
+
+ opts = struct {
+ J int `q:"j"`
+ R string `q:"r,required"`
+ C bool `q:"c"`
+ S []string `q:"s"`
+ TS []testVar `q:"ts"`
+ TI []int `q:"ti"`
+ }{
+ J: 2,
+ C: true,
+ }
+ _, err = gophercloud.BuildQueryString(&opts)
+ if err == nil {
+ t.Errorf("Expected error: 'Required field not set'")
+ }
+ th.CheckDeepEquals(t, expected, actual)
+
+ _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4})
+ if err == nil {
+ t.Errorf("Expected error: 'Options type is not a struct'")
+ }
+}
+
+func TestBuildHeaders(t *testing.T) {
+ testStruct := struct {
+ Accept string `h:"Accept"`
+ Num int `h:"Number,required"`
+ Style bool `h:"Style"`
+ }{
+ Accept: "application/json",
+ Num: 4,
+ Style: true,
+ }
+ expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"}
+ actual, err := gophercloud.BuildHeaders(&testStruct)
+ th.CheckNoErr(t, err)
+ th.CheckDeepEquals(t, expected, actual)
+
+ testStruct.Num = 0
+ _, err = gophercloud.BuildHeaders(&testStruct)
+ if err == nil {
+ t.Errorf("Expected error: 'Required header not set'")
+ }
+
+ _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4})
+ if err == nil {
+ t.Errorf("Expected error: 'Options type is not a struct'")
+ }
+}
+
+func TestQueriesAreEscaped(t *testing.T) {
+ type foo struct {
+ Name string `q:"something"`
+ Shape string `q:"else"`
+ }
+
+ expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
+
+ actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
+ th.AssertNoErr(t, err)
+
+ th.AssertDeepEquals(t, expected, actual)
+}
+
+func TestBuildRequestBody(t *testing.T) {
+ type PasswordCredentials struct {
+ Username string `json:"username" required:"true"`
+ Password string `json:"password" required:"true"`
+ }
+
+ type TokenCredentials struct {
+ ID string `json:"id,omitempty" required:"true"`
+ }
+
+ type orFields struct {
+ Filler int `json:"filler,omitempty"`
+ F1 int `json:"f1,omitempty" or:"F2"`
+ F2 int `json:"f2,omitempty" or:"F1"`
+ }
+
+ // AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
+ // interface.
+ type AuthOptions struct {
+ 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.
+ // Some require both. Your provider's authentication policies will determine
+ // how these fields influence authentication.
+ TenantID string `json:"tenantId,omitempty"`
+ TenantName string `json:"tenantName,omitempty"`
+
+ // TokenCredentials allows users to authenticate (possibly as another user) with an
+ // authentication token ID.
+ TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
+
+ OrFields orFields `json:"or_fields,omitempty"`
+ }
+
+ var successCases = []struct {
+ opts AuthOptions
+ expected map[string]interface{}
+ }{
+ {
+ AuthOptions{
+ PasswordCredentials: PasswordCredentials{
+ Username: "me",
+ Password: "swordfish",
+ },
+ },
+ map[string]interface{}{
+ "auth": map[string]interface{}{
+ "passwordCredentials": map[string]interface{}{
+ "password": "swordfish",
+ "username": "me",
+ },
+ },
+ },
+ },
+ {
+ AuthOptions{
+ TokenCredentials: TokenCredentials{
+ ID: "1234567",
+ },
+ },
+ map[string]interface{}{
+ "auth": map[string]interface{}{
+ "token": map[string]interface{}{
+ "id": "1234567",
+ },
+ },
+ },
+ },
+ }
+
+ for _, successCase := range successCases {
+ actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
+ th.AssertNoErr(t, err)
+ th.AssertDeepEquals(t, successCase.expected, actual)
+ }
+
+ var failCases = []struct {
+ opts AuthOptions
+ expected error
+ }{
+ {
+ AuthOptions{
+ TenantID: "987654321",
+ TenantName: "me",
+ },
+ gophercloud.ErrMissingInput{},
+ },
+ {
+ AuthOptions{
+ TokenCredentials: TokenCredentials{
+ ID: "1234567",
+ },
+ PasswordCredentials: PasswordCredentials{
+ Username: "me",
+ Password: "swordfish",
+ },
+ },
+ gophercloud.ErrMissingInput{},
+ },
+ {
+ AuthOptions{
+ PasswordCredentials: PasswordCredentials{
+ Password: "swordfish",
+ },
+ },
+ gophercloud.ErrMissingInput{},
+ },
+ {
+ AuthOptions{
+ PasswordCredentials: PasswordCredentials{
+ Username: "me",
+ Password: "swordfish",
+ },
+ OrFields: orFields{
+ Filler: 2,
+ },
+ },
+ gophercloud.ErrMissingInput{},
+ },
+ }
+
+ for _, failCase := range failCases {
+ _, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
+ th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err))
+ }
+}
diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go
new file mode 100644
index 0000000..7c0e84e
--- /dev/null
+++ b/testing/provider_client_test.go
@@ -0,0 +1,36 @@
+package testing
+
+import (
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestAuthenticatedHeaders(t *testing.T) {
+ p := &gophercloud.ProviderClient{
+ TokenID: "1234",
+ }
+ expected := map[string]string{"X-Auth-Token": "1234"}
+ actual := p.AuthenticatedHeaders()
+ th.CheckDeepEquals(t, expected, actual)
+}
+
+func TestUserAgent(t *testing.T) {
+ p := &gophercloud.ProviderClient{}
+
+ p.UserAgent.Prepend("custom-user-agent/2.4.0")
+ expected := "custom-user-agent/2.4.0 gophercloud/2.0.0"
+ actual := p.UserAgent.Join()
+ th.CheckEquals(t, expected, actual)
+
+ p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0")
+ expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/2.0.0"
+ actual = p.UserAgent.Join()
+ th.CheckEquals(t, expected, actual)
+
+ p.UserAgent = gophercloud.UserAgent{}
+ expected = "gophercloud/2.0.0"
+ actual = p.UserAgent.Join()
+ th.CheckEquals(t, expected, actual)
+}
diff --git a/testing/service_client_test.go b/testing/service_client_test.go
new file mode 100644
index 0000000..904b303
--- /dev/null
+++ b/testing/service_client_test.go
@@ -0,0 +1,15 @@
+package testing
+
+import (
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestServiceURL(t *testing.T) {
+ c := &gophercloud.ServiceClient{Endpoint: "http://123.45.67.8/"}
+ expected := "http://123.45.67.8/more/parts/here"
+ actual := c.ServiceURL("more", "parts", "here")
+ th.CheckEquals(t, expected, actual)
+}
diff --git a/testing/util_test.go b/testing/util_test.go
new file mode 100644
index 0000000..5985bc3
--- /dev/null
+++ b/testing/util_test.go
@@ -0,0 +1,86 @@
+package testing
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestWaitFor(t *testing.T) {
+ err := gophercloud.WaitFor(5, func() (bool, error) {
+ return true, nil
+ })
+ th.CheckNoErr(t, err)
+}
+
+func TestNormalizeURL(t *testing.T) {
+ urls := []string{
+ "NoSlashAtEnd",
+ "SlashAtEnd/",
+ }
+ expected := []string{
+ "NoSlashAtEnd/",
+ "SlashAtEnd/",
+ }
+ for i := 0; i < len(expected); i++ {
+ th.CheckEquals(t, expected[i], gophercloud.NormalizeURL(urls[i]))
+ }
+
+}
+
+func TestNormalizePathURL(t *testing.T) {
+ baseDir, _ := os.Getwd()
+
+ rawPath := "template.yaml"
+ basePath, _ := filepath.Abs(".")
+ result, _ := gophercloud.NormalizePathURL(basePath, rawPath)
+ expected := strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "template.yaml"}, "/")
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "http://www.google.com"
+ basePath, _ = filepath.Abs(".")
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = "http://www.google.com"
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml"
+ basePath, _ = filepath.Abs(".")
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "very/nested/file.yaml"}, "/")
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml"
+ basePath = "http://www.google.com"
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = "http://www.google.com/very/nested/file.yaml"
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml/"
+ basePath = "http://www.google.com/"
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = "http://www.google.com/very/nested/file.yaml"
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml"
+ basePath = "http://www.google.com/even/more"
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = "http://www.google.com/even/more/very/nested/file.yaml"
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml"
+ basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
+ th.CheckEquals(t, expected, result)
+
+ rawPath = "very/nested/file.yaml/"
+ basePath = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more"}, "/")
+ result, _ = gophercloud.NormalizePathURL(basePath, rawPath)
+ expected = strings.Join([]string{"file:/", filepath.ToSlash(baseDir), "only/file/even/more/very/nested/file.yaml"}, "/")
+ th.CheckEquals(t, expected, result)
+
+}