github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/testing/params_test.go (about)

     1  package testing
     2  
     3  import (
     4  	"net/url"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/vnpaycloud-console/gophercloud/v2"
     9  	th "github.com/vnpaycloud-console/gophercloud/v2/testhelper"
    10  )
    11  
    12  func TestMaybeString(t *testing.T) {
    13  	testString := ""
    14  	var expected *string
    15  	actual := gophercloud.MaybeString(testString)
    16  	th.CheckDeepEquals(t, expected, actual)
    17  
    18  	testString = "carol"
    19  	expected = &testString
    20  	actual = gophercloud.MaybeString(testString)
    21  	th.CheckDeepEquals(t, expected, actual)
    22  }
    23  
    24  func TestMaybeInt(t *testing.T) {
    25  	testInt := 0
    26  	var expected *int
    27  	actual := gophercloud.MaybeInt(testInt)
    28  	th.CheckDeepEquals(t, expected, actual)
    29  
    30  	testInt = 4
    31  	expected = &testInt
    32  	actual = gophercloud.MaybeInt(testInt)
    33  	th.CheckDeepEquals(t, expected, actual)
    34  }
    35  
    36  func TestBuildQueryString(t *testing.T) {
    37  	type testVar string
    38  	iFalse := false
    39  	opts := struct {
    40  		J  int               `q:"j"`
    41  		R  string            `q:"r" required:"true"`
    42  		C  bool              `q:"c"`
    43  		S  []string          `q:"s"`
    44  		TS []testVar         `q:"ts"`
    45  		TI []int             `q:"ti"`
    46  		F  *bool             `q:"f"`
    47  		M  map[string]string `q:"m"`
    48  	}{
    49  		J:  2,
    50  		R:  "red",
    51  		C:  true,
    52  		S:  []string{"one", "two", "three"},
    53  		TS: []testVar{"a", "b"},
    54  		TI: []int{1, 2},
    55  		F:  &iFalse,
    56  		M:  map[string]string{"k1": "success1"},
    57  	}
    58  	expected := &url.URL{RawQuery: "c=true&f=false&j=2&m=%7B%27k1%27%3A%27success1%27%7D&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"}
    59  	actual, err := gophercloud.BuildQueryString(&opts)
    60  	if err != nil {
    61  		t.Errorf("Error building query string: %v", err)
    62  	}
    63  	th.CheckDeepEquals(t, expected, actual)
    64  
    65  	opts = struct {
    66  		J  int               `q:"j"`
    67  		R  string            `q:"r" required:"true"`
    68  		C  bool              `q:"c"`
    69  		S  []string          `q:"s"`
    70  		TS []testVar         `q:"ts"`
    71  		TI []int             `q:"ti"`
    72  		F  *bool             `q:"f"`
    73  		M  map[string]string `q:"m"`
    74  	}{
    75  		J: 2,
    76  		C: true,
    77  	}
    78  	_, err = gophercloud.BuildQueryString(&opts)
    79  	if err == nil {
    80  		t.Errorf("Expected error: 'Required field not set'")
    81  	}
    82  	th.CheckDeepEquals(t, expected, actual)
    83  
    84  	_, err = gophercloud.BuildQueryString(map[string]any{"Number": 4})
    85  	if err == nil {
    86  		t.Errorf("Expected error: 'Options type is not a struct'")
    87  	}
    88  }
    89  
    90  func TestBuildHeaders(t *testing.T) {
    91  	testStruct := struct {
    92  		Accept        string `h:"Accept"`
    93  		ContentLength int64  `h:"Content-Length"`
    94  		Num           int    `h:"Number" required:"true"`
    95  		Style         bool   `h:"Style"`
    96  	}{
    97  		Accept:        "application/json",
    98  		ContentLength: 256,
    99  		Num:           4,
   100  		Style:         true,
   101  	}
   102  	expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true", "Content-Length": "256"}
   103  	actual, err := gophercloud.BuildHeaders(&testStruct)
   104  	th.CheckNoErr(t, err)
   105  	th.CheckDeepEquals(t, expected, actual)
   106  
   107  	testStruct.Num = 0
   108  	_, err = gophercloud.BuildHeaders(&testStruct)
   109  	if err == nil {
   110  		t.Errorf("Expected error: 'Required header not set'")
   111  	}
   112  
   113  	_, err = gophercloud.BuildHeaders(map[string]any{"Number": 4})
   114  	if err == nil {
   115  		t.Errorf("Expected error: 'Options type is not a struct'")
   116  	}
   117  }
   118  
   119  func TestQueriesAreEscaped(t *testing.T) {
   120  	type foo struct {
   121  		Name  string `q:"something"`
   122  		Shape string `q:"else"`
   123  	}
   124  
   125  	expected := &url.URL{RawQuery: "else=Triangl+e&something=blah%2B%3F%21%21foo"}
   126  
   127  	actual, err := gophercloud.BuildQueryString(foo{Name: "blah+?!!foo", Shape: "Triangl e"})
   128  	th.AssertNoErr(t, err)
   129  
   130  	th.AssertDeepEquals(t, expected, actual)
   131  }
   132  
   133  func TestBuildRequestBody(t *testing.T) {
   134  	type PasswordCredentials struct {
   135  		Username string `json:"username" required:"true"`
   136  		Password string `json:"password" required:"true"`
   137  	}
   138  
   139  	type TokenCredentials struct {
   140  		ID string `json:"id,omitempty" required:"true"`
   141  	}
   142  
   143  	type orFields struct {
   144  		Filler int `json:"filler,omitempty"`
   145  		F1     int `json:"f1,omitempty" or:"F2"`
   146  		F2     int `json:"f2,omitempty" or:"F1"`
   147  	}
   148  
   149  	// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder
   150  	// interface.
   151  	type AuthOptions struct {
   152  		PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty" xor:"TokenCredentials"`
   153  
   154  		// The TenantID and TenantName fields are optional for the Identity V2 API.
   155  		// Some providers allow you to specify a TenantName instead of the TenantId.
   156  		// Some require both. Your provider's authentication policies will determine
   157  		// how these fields influence authentication.
   158  		TenantID   string `json:"tenantId,omitempty"`
   159  		TenantName string `json:"tenantName,omitempty"`
   160  
   161  		// TokenCredentials allows users to authenticate (possibly as another user) with an
   162  		// authentication token ID.
   163  		TokenCredentials *TokenCredentials `json:"token,omitempty" xor:"PasswordCredentials"`
   164  
   165  		OrFields *orFields `json:"or_fields,omitempty"`
   166  	}
   167  
   168  	var successCases = []struct {
   169  		name     string
   170  		opts     AuthOptions
   171  		expected map[string]any
   172  	}{
   173  		{
   174  			"Password",
   175  			AuthOptions{
   176  				PasswordCredentials: &PasswordCredentials{
   177  					Username: "me",
   178  					Password: "swordfish",
   179  				},
   180  			},
   181  			map[string]any{
   182  				"auth": map[string]any{
   183  					"passwordCredentials": map[string]any{
   184  						"password": "swordfish",
   185  						"username": "me",
   186  					},
   187  				},
   188  			},
   189  		},
   190  		{
   191  			"Token",
   192  			AuthOptions{
   193  				TokenCredentials: &TokenCredentials{
   194  					ID: "1234567",
   195  				},
   196  			},
   197  			map[string]any{
   198  				"auth": map[string]any{
   199  					"token": map[string]any{
   200  						"id": "1234567",
   201  					},
   202  				},
   203  			},
   204  		},
   205  	}
   206  
   207  	for _, successCase := range successCases {
   208  		t.Run(successCase.name, func(t *testing.T) {
   209  			actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth")
   210  			th.AssertNoErr(t, err)
   211  			th.AssertDeepEquals(t, successCase.expected, actual)
   212  		})
   213  	}
   214  
   215  	var failCases = []struct {
   216  		name     string
   217  		opts     AuthOptions
   218  		expected error
   219  	}{
   220  		{
   221  			"Conflicting tenant name and ID",
   222  			AuthOptions{
   223  				TenantID:   "987654321",
   224  				TenantName: "me",
   225  			},
   226  			gophercloud.ErrMissingInput{},
   227  		},
   228  		{
   229  			"Conflicting password and token auth",
   230  			AuthOptions{
   231  				TokenCredentials: &TokenCredentials{
   232  					ID: "1234567",
   233  				},
   234  				PasswordCredentials: &PasswordCredentials{
   235  					Username: "me",
   236  					Password: "swordfish",
   237  				},
   238  			},
   239  			gophercloud.ErrMissingInput{},
   240  		},
   241  		{
   242  			"Missing Username or UserID",
   243  			AuthOptions{
   244  				PasswordCredentials: &PasswordCredentials{
   245  					Password: "swordfish",
   246  				},
   247  			},
   248  			gophercloud.ErrMissingInput{},
   249  		},
   250  		{
   251  			"Missing filler fields",
   252  			AuthOptions{
   253  				PasswordCredentials: &PasswordCredentials{
   254  					Username: "me",
   255  					Password: "swordfish",
   256  				},
   257  				OrFields: &orFields{
   258  					Filler: 2,
   259  				},
   260  			},
   261  			gophercloud.ErrMissingInput{},
   262  		},
   263  	}
   264  
   265  	for _, failCase := range failCases {
   266  		t.Run(failCase.name, func(t *testing.T) {
   267  			_, err := gophercloud.BuildRequestBody(failCase.opts, "auth")
   268  			th.AssertTypeEquals(t, failCase.expected, err)
   269  		})
   270  	}
   271  
   272  	createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC)
   273  	var complexFields = struct {
   274  		Username  string     `json:"username" required:"true"`
   275  		CreatedAt *time.Time `json:"-"`
   276  	}{
   277  		Username:  "jdoe",
   278  		CreatedAt: &createdAt,
   279  	}
   280  
   281  	expectedComplexFields := map[string]any{
   282  		"username": "jdoe",
   283  	}
   284  
   285  	actual, err := gophercloud.BuildRequestBody(complexFields, "")
   286  	th.AssertNoErr(t, err)
   287  	th.AssertDeepEquals(t, expectedComplexFields, actual)
   288  
   289  }