...

Source file src/github.com/go-openapi/swag/loading_test.go

Documentation: github.com/go-openapi/swag

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package swag
    16  
    17  import (
    18  	"net/http"
    19  	"net/http/httptest"
    20  	"runtime"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  const (
    28  	validUsername     = "fake-user"
    29  	validPassword     = "correct-password"
    30  	invalidPassword   = "incorrect-password"
    31  	sharedHeaderKey   = "X-Myapp"
    32  	sharedHeaderValue = "MySecretKey"
    33  )
    34  
    35  func TestLoadFromHTTP(t *testing.T) {
    36  	_, err := LoadFromFileOrHTTP("httx://12394:abd")
    37  	require.Error(t, err)
    38  
    39  	serv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
    40  		rw.WriteHeader(http.StatusNotFound)
    41  	}))
    42  	defer serv.Close()
    43  
    44  	_, err = LoadFromFileOrHTTP(serv.URL)
    45  	require.Error(t, err)
    46  
    47  	ts2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
    48  		rw.WriteHeader(http.StatusOK)
    49  		_, _ = rw.Write([]byte("the content"))
    50  	}))
    51  	defer ts2.Close()
    52  
    53  	d, err := LoadFromFileOrHTTP(ts2.URL)
    54  	require.NoError(t, err)
    55  	assert.Equal(t, []byte("the content"), d)
    56  
    57  	ts3 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
    58  		u, p, ok := r.BasicAuth()
    59  		if ok && u == validUsername && p == validPassword {
    60  			rw.WriteHeader(http.StatusOK)
    61  		} else {
    62  			rw.WriteHeader(http.StatusForbidden)
    63  		}
    64  	}))
    65  	defer ts3.Close()
    66  
    67  	// no auth
    68  	_, err = LoadFromFileOrHTTP(ts3.URL)
    69  	require.Error(t, err)
    70  
    71  	// basic auth, invalide credentials
    72  	LoadHTTPBasicAuthUsername = validUsername
    73  	LoadHTTPBasicAuthPassword = invalidPassword
    74  
    75  	_, err = LoadFromFileOrHTTP(ts3.URL)
    76  	require.Error(t, err)
    77  
    78  	// basic auth, valid credentials
    79  	LoadHTTPBasicAuthUsername = validUsername
    80  	LoadHTTPBasicAuthPassword = validPassword
    81  
    82  	_, err = LoadFromFileOrHTTP(ts3.URL)
    83  	require.NoError(t, err)
    84  
    85  	ts4 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
    86  		myHeaders := r.Header[sharedHeaderKey]
    87  		ok := false
    88  		for _, v := range myHeaders {
    89  			if v == sharedHeaderValue {
    90  				ok = true
    91  				break
    92  			}
    93  		}
    94  		if ok {
    95  			rw.WriteHeader(http.StatusOK)
    96  		} else {
    97  			rw.WriteHeader(http.StatusForbidden)
    98  		}
    99  	}))
   100  	defer ts4.Close()
   101  
   102  	_, err = LoadFromFileOrHTTP(ts4.URL)
   103  	require.Error(t, err)
   104  
   105  	LoadHTTPCustomHeaders[sharedHeaderKey] = sharedHeaderValue
   106  
   107  	_, err = LoadFromFileOrHTTP(ts4.URL)
   108  	require.NoError(t, err)
   109  
   110  	// clean up for future tests
   111  	LoadHTTPBasicAuthUsername = ""
   112  	LoadHTTPBasicAuthPassword = ""
   113  	LoadHTTPCustomHeaders = map[string]string{}
   114  }
   115  
   116  func TestLoadHTTPBytes(t *testing.T) {
   117  	_, err := LoadFromFileOrHTTP("httx://12394:abd")
   118  	require.Error(t, err)
   119  
   120  	serv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
   121  		rw.WriteHeader(http.StatusNotFound)
   122  	}))
   123  	defer serv.Close()
   124  
   125  	_, err = LoadFromFileOrHTTP(serv.URL)
   126  	require.Error(t, err)
   127  
   128  	ts2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
   129  		rw.WriteHeader(http.StatusOK)
   130  		_, _ = rw.Write([]byte("the content"))
   131  	}))
   132  	defer ts2.Close()
   133  
   134  	d, err := LoadFromFileOrHTTP(ts2.URL)
   135  	require.NoError(t, err)
   136  	assert.Equal(t, []byte("the content"), d)
   137  }
   138  
   139  func TestLoadStrategy(t *testing.T) {
   140  	loader := func(_ string) ([]byte, error) {
   141  		return []byte(yamlPetStore), nil
   142  	}
   143  	remLoader := func(_ string) ([]byte, error) {
   144  		return []byte("not it"), nil
   145  	}
   146  
   147  	ld := LoadStrategy("blah", loader, remLoader)
   148  	b, _ := ld("")
   149  	assert.Equal(t, []byte(yamlPetStore), b)
   150  
   151  	serv := httptest.NewServer(http.HandlerFunc(yamlPestoreServer))
   152  	defer serv.Close()
   153  
   154  	s, err := YAMLDoc(serv.URL)
   155  	require.NoError(t, err)
   156  	require.NotNil(t, s)
   157  
   158  	ts2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
   159  		rw.WriteHeader(http.StatusNotFound)
   160  		_, _ = rw.Write([]byte("\n"))
   161  	}))
   162  	defer ts2.Close()
   163  	_, err = YAMLDoc(ts2.URL)
   164  	require.Error(t, err)
   165  }
   166  
   167  func TestLoadStrategyFile(t *testing.T) {
   168  	const (
   169  		thisIsIt    = "thisIsIt"
   170  		thisIsNotIt = "not it"
   171  	)
   172  
   173  	type strategyTest struct {
   174  		Title           string
   175  		Path            string
   176  		Expected        string
   177  		ExpectedWindows string
   178  		ExpectError     bool
   179  	}
   180  
   181  	t.Run("with local file strategy", func(t *testing.T) {
   182  		loader := func(called *bool, pth *string) func(string) ([]byte, error) {
   183  			return func(p string) ([]byte, error) {
   184  				*called = true
   185  				*pth = p
   186  				return []byte(thisIsIt), nil
   187  			}
   188  		}
   189  
   190  		remLoader := func(_ string) ([]byte, error) {
   191  			return []byte(thisIsNotIt), nil
   192  		}
   193  
   194  		for _, toPin := range []strategyTest{
   195  			{
   196  				Title:           "valid fully qualified local URI, with rooted path",
   197  				Path:            "file:///a/c/myfile.yaml",
   198  				Expected:        "/a/c/myfile.yaml",
   199  				ExpectedWindows: `\a\c\myfile.yaml`,
   200  			},
   201  			{
   202  				Title:           "local URI with scheme, with host segment before path",
   203  				Path:            "file://a/c/myfile.yaml",
   204  				Expected:        "a/c/myfile.yaml",
   205  				ExpectedWindows: `\\a\c\myfile.yaml`, // UNC host
   206  			},
   207  			{
   208  				Title:           "local URI with scheme, with escaped characters",
   209  				Path:            "file://a/c/myfile%20%28x86%29.yaml",
   210  				Expected:        "a/c/myfile (x86).yaml",
   211  				ExpectedWindows: `\\a\c\myfile (x86).yaml`,
   212  			},
   213  			{
   214  				Title:           "local URI with scheme, rooted, with escaped characters",
   215  				Path:            "file:///a/c/myfile%20%28x86%29.yaml",
   216  				Expected:        "/a/c/myfile (x86).yaml",
   217  				ExpectedWindows: `\a\c\myfile (x86).yaml`,
   218  			},
   219  			{
   220  				Title:           "local URI with scheme, unescaped, with host",
   221  				Path:            "file://a/c/myfile (x86).yaml",
   222  				Expected:        "a/c/myfile (x86).yaml",
   223  				ExpectedWindows: `\\a\c\myfile (x86).yaml`,
   224  			},
   225  			{
   226  				Title:           "local URI with scheme, rooted, unescaped",
   227  				Path:            "file:///a/c/myfile (x86).yaml",
   228  				Expected:        "/a/c/myfile (x86).yaml",
   229  				ExpectedWindows: `\a\c\myfile (x86).yaml`,
   230  			},
   231  			{
   232  				Title:    "file URI with drive letter and backslashes, as a relative Windows path",
   233  				Path:     `file://C:\a\c\myfile.yaml`,
   234  				Expected: `C:\a\c\myfile.yaml`, // outcome on all platforms, not only windows
   235  			},
   236  			{
   237  				Title:           "file URI with drive letter and backslashes, as a rooted Windows path",
   238  				Path:            `file:///C:\a\c\myfile.yaml`,
   239  				Expected:        `/C:\a\c\myfile.yaml`, // on non-windows, this results most likely in a wrong path
   240  				ExpectedWindows: `C:\a\c\myfile.yaml`,  // on windows, we know that C: is a drive letter, so /C: becomes C:
   241  			},
   242  			{
   243  				Title:    "file URI with escaped backslashes",
   244  				Path:     `file://C%3A%5Ca%5Cc%5Cmyfile.yaml`,
   245  				Expected: `C:\a\c\myfile.yaml`, // outcome on all platforms, not only windows
   246  			},
   247  			{
   248  				Title:           "file URI with escaped backslashes, rooted",
   249  				Path:            `file:///C%3A%5Ca%5Cc%5Cmyfile.yaml`,
   250  				Expected:        `/C:\a\c\myfile.yaml`, // outcome on non-windows (most likely not a desired path)
   251  				ExpectedWindows: `C:\a\c\myfile.yaml`,  // outcome on windows
   252  			},
   253  			{
   254  				Title:           "URI with the file scheme, host omitted: relative path with extra dots",
   255  				Path:            `file://./a/c/d/../myfile.yaml`,
   256  				Expected:        `./a/c/d/../myfile.yaml`,
   257  				ExpectedWindows: `a\c\myfile.yaml`, // on windows, extra processing cleans the path
   258  			},
   259  			{
   260  				Title:           "relative URI without the file scheme, rooted path",
   261  				Path:            `/a/c/myfile.yaml`,
   262  				Expected:        `/a/c/myfile.yaml`,
   263  				ExpectedWindows: `\a\c\myfile.yaml`, // there is no drive letter, this would probably result in a wrong path on Windows
   264  			},
   265  			{
   266  				Title:           "relative URI without the file scheme, relative path",
   267  				Path:            `a/c/myfile.yaml`,
   268  				Expected:        `a/c/myfile.yaml`,
   269  				ExpectedWindows: `a\c\myfile.yaml`,
   270  			},
   271  			{
   272  				Title:           "relative URI without the file scheme, relative path with dots",
   273  				Path:            `./a/c/myfile.yaml`,
   274  				Expected:        `./a/c/myfile.yaml`,
   275  				ExpectedWindows: `.\a\c\myfile.yaml`,
   276  			},
   277  			{
   278  				Title:           "relative URI without the file scheme, relative path with extra dots",
   279  				Path:            `./a/c/../myfile.yaml`,
   280  				Expected:        `./a/c/../myfile.yaml`,
   281  				ExpectedWindows: `.\a\c\..\myfile.yaml`,
   282  			},
   283  			{
   284  				Title:           "relative URI without the file scheme, windows slashed-path with drive letter",
   285  				Path:            `A:/a/c/myfile.yaml`,
   286  				Expected:        `A:/a/c/myfile.yaml`, // on non-windows, this results most likely in a wrong path
   287  				ExpectedWindows: `A:\a\c\myfile.yaml`, // on windows, slashes are converted
   288  			},
   289  			{
   290  				Title:           "relative URI without the file scheme, windows backslashed-path with drive letter",
   291  				Path:            `A:\a\c\myfile.yaml`,
   292  				Expected:        `A:\a\c\myfile.yaml`, // on non-windows, this results most likely in a wrong path
   293  				ExpectedWindows: `A:\a\c\myfile.yaml`,
   294  			},
   295  			{
   296  				Title:           "URI with file scheme, host as Windows UNC name",
   297  				Path:            `file://host/share/folder/myfile.yaml`,
   298  				Expected:        `host/share/folder/myfile.yaml`,   // there is no host component accounted for
   299  				ExpectedWindows: `\\host\share\folder\myfile.yaml`, // on windows, the host is interpreted as an UNC host for a file share
   300  			},
   301  			// TODO: invalid URI (cannot unescape/parse path)
   302  		} {
   303  			tc := toPin
   304  			t.Run(tc.Title, func(t *testing.T) {
   305  				var (
   306  					called bool
   307  					pth    string
   308  				)
   309  
   310  				ld := LoadStrategy("local", loader(&called, &pth), remLoader)
   311  				b, err := ld(tc.Path)
   312  				if tc.ExpectError {
   313  					require.Error(t, err)
   314  					assert.True(t, called)
   315  
   316  					return
   317  				}
   318  
   319  				require.NoError(t, err)
   320  				assert.True(t, called)
   321  				assert.Equal(t, []byte(thisIsIt), b)
   322  
   323  				if tc.ExpectedWindows != "" && runtime.GOOS == "windows" {
   324  					assert.Equalf(t, tc.ExpectedWindows, pth,
   325  						"expected local LoadStrategy(%q) to open: %q (windows)",
   326  						tc.Path, tc.ExpectedWindows,
   327  					)
   328  
   329  					return
   330  				}
   331  
   332  				assert.Equalf(t, tc.Expected, pth,
   333  					"expected local LoadStrategy(%q) to open: %q (any OS)",
   334  					tc.Path, tc.Expected,
   335  				)
   336  			})
   337  		}
   338  	})
   339  }
   340  

View as plain text