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

     1  package testing
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/vnpaycloud-console/gophercloud/v2"
    11  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/utils"
    12  	th "github.com/vnpaycloud-console/gophercloud/v2/testhelper"
    13  )
    14  
    15  func setupVersionHandler() {
    16  	th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    17  		fmt.Fprintf(w, `
    18  			{
    19  				"versions": {
    20  					"values": [
    21  						{
    22  							"status": "stable",
    23  							"id": "v3.0",
    24  							"links": [
    25  								{ "href": "%s/v3.0", "rel": "self" }
    26  							]
    27  						},
    28  						{
    29  							"status": "stable",
    30  							"id": "v2.0",
    31  							"links": [
    32  								{ "href": "%s/v2.0", "rel": "self" }
    33  							]
    34  						}
    35  					]
    36  				}
    37  			}
    38  		`, th.Server.URL, th.Server.URL)
    39  	})
    40  	// Compute v2.1 API
    41  	th.Mux.HandleFunc("/compute/v2.1/", func(w http.ResponseWriter, r *http.Request) {
    42  		fmt.Fprintf(w, `
    43  			{
    44  				"version": {
    45  					"id": "v2.1",
    46  					"status": "CURRENT",
    47  					"version": "2.90",
    48  					"min_version": "2.1",
    49  					"updated": "2013-07-23T11:33:21Z",
    50  					"links": [
    51  						{
    52  							"rel": "self",
    53  							"href": "%s/compute/v2.1/"
    54  						},
    55  						{
    56  							"rel": "describedby",
    57  							"type": "text/html",
    58  							"href": "http://docs.openstack.org/"
    59  						}
    60  					],
    61  					"media-types": [
    62  						{
    63  							"base": "application/json",
    64  							"type": "application/vnd.openstack.compute+json;version=2.1"
    65  						}
    66  					]
    67  				}
    68  			}
    69  		`, th.Server.URL)
    70  	})
    71  	// Compute v2 API
    72  	th.Mux.HandleFunc("/compute/v2/", func(w http.ResponseWriter, r *http.Request) {
    73  		fmt.Fprintf(w, `
    74  			{
    75  				"version": {
    76  					"id": "v2.0",
    77  					"status": "SUPPORTED",
    78  					"version": "",
    79  					"min_version": "",
    80  					"updated": "2011-01-21T11:33:21Z",
    81  					"links": [
    82  						{
    83  							"rel": "self",
    84  							"href": "%s/compute/v2/"
    85  						},
    86  						{
    87  							"rel": "describedby",
    88  							"type": "text/html",
    89  							"href": "http://docs.openstack.org/"
    90  						}
    91  					],
    92  					"media-types": [
    93  						{
    94  							"base": "application/json",
    95  							"type": "application/vnd.openstack.compute+json;version=2"
    96  						}
    97  					]
    98  				}
    99  			}
   100  		`, th.Server.URL)
   101  	})
   102  	// Ironic API
   103  	th.Mux.HandleFunc("/ironic/v1/", func(w http.ResponseWriter, r *http.Request) {
   104  		fmt.Fprintf(w, `
   105  		{
   106  			"name": "OpenStack Ironic API",
   107  			"description": "Ironic is an OpenStack project which enables the provision and management of baremetal machines.",
   108  			"default_version": {
   109  				"id": "v1",
   110  				"links": [
   111  					{
   112  						"href": "%s/ironic/v1/",
   113  						"rel": "self"
   114  					}
   115  				],
   116  				"status": "CURRENT",
   117  				"min_version": "1.1",
   118  				"version": "1.87"
   119  			},
   120  			"versions": [
   121  				{
   122  					"id": "v1",
   123  					"links": [
   124  						{
   125  							"href": "%s/ironic/v1/",
   126  							"rel": "self"
   127  						}
   128  					],
   129  					"status": "CURRENT",
   130  					"min_version": "1.1",
   131  					"version": "1.87"
   132  				}
   133  			]
   134  		}
   135  		`, th.Server.URL, th.Server.URL)
   136  	})
   137  	// Ironic multi-version
   138  	th.Mux.HandleFunc("/ironic/v1.2/", func(w http.ResponseWriter, r *http.Request) {
   139  		fmt.Fprintf(w, `
   140  		{
   141  			"name": "OpenStack Ironic API",
   142  			"description": "Ironic is an OpenStack project which enables the provision and management of baremetal machines.",
   143  			"default_version": {
   144  				"id": "v1",
   145  				"links": [
   146  					{
   147  						"href": "%s/ironic/v1/",
   148  						"rel": "self"
   149  					}
   150  				],
   151  				"status": "CURRENT",
   152  				"min_version": "1.1",
   153  				"version": "1.87"
   154  			},
   155  			"versions": [
   156  				{
   157  					"id": "v1",
   158  					"links": [
   159  						{
   160  							"href": "%s/ironic/v1/",
   161  							"rel": "self"
   162  						}
   163  					],
   164  					"status": "CURRENT",
   165  					"min_version": "1.1",
   166  					"version": "1.87"
   167  				},
   168  				{
   169  					"id": "v1.2",
   170  					"links": [
   171  						{
   172  							"href": "%s/ironic/v1/",
   173  							"rel": "self"
   174  						}
   175  					],
   176  					"status": "CURRENT",
   177  					"min_version": "1.2",
   178  					"version": "1.90"
   179  				}
   180  			]
   181  		}
   182  		`, th.Server.URL, th.Server.URL, th.Server.URL)
   183  	})
   184  }
   185  
   186  func TestChooseVersion(t *testing.T) {
   187  	th.SetupHTTP()
   188  	defer th.TeardownHTTP()
   189  	setupVersionHandler()
   190  
   191  	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "blarg"}
   192  	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "hargl"}
   193  
   194  	c := &gophercloud.ProviderClient{
   195  		IdentityBase:     th.Endpoint(),
   196  		IdentityEndpoint: "",
   197  	}
   198  	v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3})
   199  
   200  	if err != nil {
   201  		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
   202  	}
   203  
   204  	if v != v3 {
   205  		t.Errorf("Expected %#v to win, but %#v did instead", v3, v)
   206  	}
   207  
   208  	expected := th.Endpoint() + "v3.0/"
   209  	if endpoint != expected {
   210  		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
   211  	}
   212  }
   213  
   214  func TestChooseVersionOpinionatedLink(t *testing.T) {
   215  	th.SetupHTTP()
   216  	defer th.TeardownHTTP()
   217  	setupVersionHandler()
   218  
   219  	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "nope"}
   220  	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "northis"}
   221  
   222  	c := &gophercloud.ProviderClient{
   223  		IdentityBase:     th.Endpoint(),
   224  		IdentityEndpoint: th.Endpoint() + "v2.0/",
   225  	}
   226  	v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3})
   227  	if err != nil {
   228  		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
   229  	}
   230  
   231  	if v != v2 {
   232  		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
   233  	}
   234  
   235  	expected := th.Endpoint() + "v2.0/"
   236  	if endpoint != expected {
   237  		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
   238  	}
   239  }
   240  
   241  func TestChooseVersionFromSuffix(t *testing.T) {
   242  	th.SetupHTTP()
   243  	defer th.TeardownHTTP()
   244  
   245  	v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "/v2.0/"}
   246  	v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "/v3.0/"}
   247  
   248  	c := &gophercloud.ProviderClient{
   249  		IdentityBase:     th.Endpoint(),
   250  		IdentityEndpoint: th.Endpoint() + "v2.0/",
   251  	}
   252  	v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3})
   253  	if err != nil {
   254  		t.Fatalf("Unexpected error from ChooseVersion: %v", err)
   255  	}
   256  
   257  	if v != v2 {
   258  		t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
   259  	}
   260  
   261  	expected := th.Endpoint() + "v2.0/"
   262  	if endpoint != expected {
   263  		t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
   264  	}
   265  }
   266  
   267  type getSupportedServiceMicroversions struct {
   268  	Endpoint    string
   269  	ExpectedMax string
   270  	ExpectedMin string
   271  	ExpectedErr bool
   272  }
   273  
   274  func TestGetSupportedVersions(t *testing.T) {
   275  	th.SetupHTTP()
   276  	defer th.TeardownHTTP()
   277  	setupVersionHandler()
   278  
   279  	tests := []getSupportedServiceMicroversions{
   280  		{
   281  			// v2 does not support microversions and returns error
   282  			Endpoint:    th.Endpoint() + "compute/v2/",
   283  			ExpectedMax: "",
   284  			ExpectedMin: "",
   285  			ExpectedErr: true,
   286  		},
   287  		{
   288  			Endpoint:    th.Endpoint() + "compute/v2.1/",
   289  			ExpectedMax: "2.90",
   290  			ExpectedMin: "2.1",
   291  			ExpectedErr: false,
   292  		},
   293  		{
   294  			Endpoint:    th.Endpoint() + "ironic/v1/",
   295  			ExpectedMax: "1.87",
   296  			ExpectedMin: "1.1",
   297  			ExpectedErr: false,
   298  		},
   299  		{
   300  			// This endpoint returns multiple versions, which is not supported
   301  			Endpoint:    th.Endpoint() + "ironic/v1.2/",
   302  			ExpectedMax: "not-relevant",
   303  			ExpectedMin: "not-relevant",
   304  			ExpectedErr: true,
   305  		},
   306  	}
   307  
   308  	for _, test := range tests {
   309  		c := &gophercloud.ProviderClient{
   310  			IdentityBase:     th.Endpoint(),
   311  			IdentityEndpoint: th.Endpoint() + "v2.0/",
   312  		}
   313  
   314  		client := &gophercloud.ServiceClient{
   315  			ProviderClient: c,
   316  			Endpoint:       test.Endpoint,
   317  		}
   318  
   319  		supported, err := utils.GetSupportedMicroversions(context.TODO(), client)
   320  
   321  		if test.ExpectedErr {
   322  			if err == nil {
   323  				t.Error("Expected error but got none!")
   324  			}
   325  			// Check for reasonable error message
   326  			if !strings.Contains(err.Error(), "not supported") {
   327  				t.Error("Expected error to contain 'not supported' but it did not!")
   328  			}
   329  			// No point parsing and comparing versions after error, so continue to next test case
   330  			continue
   331  		} else {
   332  			if err != nil {
   333  				t.Errorf("Expected no error but got %s", err.Error())
   334  			}
   335  		}
   336  
   337  		min := fmt.Sprintf("%d.%d", supported.MinMajor, supported.MinMinor)
   338  		max := fmt.Sprintf("%d.%d", supported.MaxMajor, supported.MaxMinor)
   339  
   340  		if (min != test.ExpectedMin) || (max != test.ExpectedMax) {
   341  			t.Errorf("Expected min=%s and max=%s but got min=%s and max=%s", test.ExpectedMin, test.ExpectedMax, min, max)
   342  		}
   343  	}
   344  }
   345  
   346  type microversionSupported struct {
   347  	Version    string
   348  	MinVersion string
   349  	MaxVersion string
   350  	Supported  bool
   351  	Error      bool
   352  }
   353  
   354  func TestMicroversionSupported(t *testing.T) {
   355  	tests := []microversionSupported{
   356  		{
   357  			// Checking min version
   358  			Version:    "2.1",
   359  			MinVersion: "2.1",
   360  			MaxVersion: "2.90",
   361  			Supported:  true,
   362  			Error:      false,
   363  		},
   364  		{
   365  			// Checking max version
   366  			Version:    "2.90",
   367  			MinVersion: "2.1",
   368  			MaxVersion: "2.90",
   369  			Supported:  true,
   370  			Error:      false,
   371  		},
   372  		{
   373  			// Checking too high version
   374  			Version:    "2.95",
   375  			MinVersion: "2.1",
   376  			MaxVersion: "2.90",
   377  			Supported:  false,
   378  			Error:      false,
   379  		},
   380  		{
   381  			// Checking too low version
   382  			Version:    "2.1",
   383  			MinVersion: "2.53",
   384  			MaxVersion: "2.90",
   385  			Supported:  false,
   386  			Error:      false,
   387  		},
   388  		{
   389  			// Invalid version
   390  			Version:    "2.1.53",
   391  			MinVersion: "2.53",
   392  			MaxVersion: "2.90",
   393  			Supported:  false,
   394  			Error:      true,
   395  		},
   396  	}
   397  
   398  	for _, test := range tests {
   399  		var err error
   400  		var supportedVersions utils.SupportedMicroversions
   401  		supportedVersions.MaxMajor, supportedVersions.MaxMinor, err = utils.ParseMicroversion(test.MaxVersion)
   402  		if err != nil {
   403  			t.Error("Error parsing MaxVersion!")
   404  		}
   405  		supportedVersions.MinMajor, supportedVersions.MinMinor, err = utils.ParseMicroversion(test.MinVersion)
   406  		if err != nil {
   407  			t.Error("Error parsing MinVersion!")
   408  		}
   409  
   410  		supported, err := supportedVersions.IsSupported(test.Version)
   411  		if test.Error {
   412  			if err == nil {
   413  				t.Error("Expected error but got none!")
   414  			}
   415  		} else {
   416  			if err != nil {
   417  				t.Errorf("Expected no error but got %s", err.Error())
   418  			}
   419  		}
   420  		if test.Supported != supported {
   421  			t.Errorf("Expected supported=%t to be %t, when version=%s, min=%s and max=%s",
   422  				supported, test.Supported, test.Version, test.MinVersion, test.MaxVersion)
   423  		}
   424  	}
   425  }