github.com/6543-forks/go-swagger@v0.26.0/generator/client_test.go (about)

     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 generator
    16  
    17  import (
    18  	"io/ioutil"
    19  	"log"
    20  	"os"
    21  	"path/filepath"
    22  	"strconv"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  const (
    31  	basicFixture = "../fixtures/petstores/petstore.json"
    32  )
    33  
    34  func testClientGenOpts() *GenOpts {
    35  	g := &GenOpts{}
    36  	g.Target = "."
    37  	g.APIPackage = defaultAPIPackage
    38  	g.ModelPackage = defaultModelPackage
    39  	g.ServerPackage = defaultServerPackage
    40  	g.ClientPackage = defaultClientPackage
    41  	g.Principal = ""
    42  	g.IncludeModel = true
    43  	g.IncludeHandler = true
    44  	g.IncludeParameters = true
    45  	g.IncludeResponses = true
    46  	g.IncludeSupport = true
    47  	g.TemplateDir = ""
    48  	g.DumpData = false
    49  	g.IsClient = true
    50  	if err := g.EnsureDefaults(); err != nil {
    51  		panic(err)
    52  	}
    53  	return g
    54  }
    55  
    56  func Test_GenerateClient(t *testing.T) {
    57  	log.SetOutput(ioutil.Discard)
    58  
    59  	// exercise safeguards
    60  	err := GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, nil)
    61  	assert.Error(t, err)
    62  
    63  	opts := testClientGenOpts()
    64  	opts.TemplateDir = "dir/nowhere"
    65  	err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts)
    66  	assert.Error(t, err)
    67  
    68  	opts = testClientGenOpts()
    69  	opts.TemplateDir = "http://nowhere.com"
    70  	err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts)
    71  	assert.Error(t, err)
    72  
    73  	opts = testClientGenOpts()
    74  	opts.Spec = "dir/nowhere.yaml"
    75  	err = GenerateClient("test", []string{"model1"}, []string{"op1", "op2"}, opts)
    76  	assert.Error(t, err)
    77  
    78  	opts = testClientGenOpts()
    79  	opts.Spec = basicFixture
    80  	err = GenerateClient("test", []string{"model1"}, []string{}, opts)
    81  	assert.Error(t, err)
    82  
    83  	opts = testClientGenOpts()
    84  	// bad content in spec (HTML...)
    85  	opts.Spec = "https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v2.0/json/petstore.json"
    86  	err = GenerateClient("test", []string{}, []string{}, opts)
    87  	assert.Error(t, err)
    88  
    89  	opts = testClientGenOpts()
    90  	// no operations selected
    91  	opts.Spec = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml"
    92  	err = GenerateClient("test", []string{}, []string{"wrongOperationID"}, opts)
    93  	assert.Error(t, err)
    94  
    95  	opts = testClientGenOpts()
    96  	// generate remote spec
    97  	opts.Spec = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/yaml/petstore.yaml"
    98  	cwd, _ := os.Getwd()
    99  	tft, _ := ioutil.TempDir(cwd, "generated")
   100  	defer func() {
   101  		_ = os.RemoveAll(tft)
   102  	}()
   103  	opts.Target = tft
   104  	opts.IsClient = true
   105  	DefaultSectionOpts(opts)
   106  
   107  	defer func() {
   108  		_ = os.RemoveAll(opts.Target)
   109  	}()
   110  	err = GenerateClient("test", []string{}, []string{}, opts)
   111  	assert.NoError(t, err)
   112  
   113  	// just checks this does not fail
   114  	origStdout := os.Stdout
   115  	defer func() {
   116  		os.Stdout = origStdout
   117  	}()
   118  	tgt, _ := ioutil.TempDir(cwd, "dumped")
   119  	defer func() {
   120  		_ = os.RemoveAll(tgt)
   121  	}()
   122  	os.Stdout, _ = os.Create(filepath.Join(tgt, "stdout"))
   123  	opts.DumpData = true
   124  	err = GenerateClient("test", []string{}, []string{}, opts)
   125  	assert.NoError(t, err)
   126  	_, err = os.Stat(filepath.Join(tgt, "stdout"))
   127  	assert.NoError(t, err)
   128  }
   129  
   130  func assertImports(t testing.TB, baseImport, code string) {
   131  	assertRegexpInCode(t, baseImport, code)
   132  	assertRegexpInCode(t, `"`+baseImport+`/abc_linux"`, code)
   133  	assertRegexpInCode(t, `"`+baseImport+`/abc_linux"`, code)
   134  	assertRegexpInCode(t, `"`+baseImport+`/abc_test"`, code)
   135  	assertRegexpInCode(t, `apiops\s+"`+baseImport+`/api"`, code)
   136  	assertRegexpInCode(t, `"`+baseImport+`/custom"`, code)
   137  	assertRegexpInCode(t, `"`+baseImport+`/hash_tag_donuts"`, code)
   138  	assertRegexpInCode(t, `"`+baseImport+`/nr123abc"`, code)
   139  	assertRegexpInCode(t, `"`+baseImport+`/nr_at_donuts"`, code)
   140  	assertRegexpInCode(t, `"`+baseImport+`/plus_donuts`, code)
   141  	assertRegexpInCode(t, `strfmtops "`+baseImport+`/strfmt`, code)
   142  	assertRegexpInCode(t, `"`+baseImport+`/forced`, code)
   143  	assertRegexpInCode(t, `"`+baseImport+`/nr12nasty`, code)
   144  	assertRegexpInCode(t, `"`+baseImport+`/override`, code)
   145  	assertRegexpInCode(t, `"`+baseImport+`/gtl`, code)
   146  	assertRegexpInCode(t, `"`+baseImport+`/operationsops`, code)
   147  }
   148  
   149  func TestClient(t *testing.T) {
   150  	log.SetOutput(ioutil.Discard)
   151  
   152  	base := os.Getenv("GOPATH")
   153  	if base == "" {
   154  		base = "."
   155  	} else {
   156  		base = filepath.Join(base, "src")
   157  		err := os.MkdirAll(base, 0755)
   158  		require.NoError(t, err)
   159  	}
   160  	targetdir, err := ioutil.TempDir(base, "swagger_nogo")
   161  	require.NoError(t, err, "Failed to create a test target directory: %v", err)
   162  
   163  	defer func() {
   164  		_ = os.RemoveAll(targetdir)
   165  		log.SetOutput(os.Stdout)
   166  	}()
   167  
   168  	tests := []struct {
   169  		name      string
   170  		spec      string
   171  		template  string
   172  		wantError bool
   173  		prepare   func(opts *GenOpts)
   174  		verify    func(testing.TB, string)
   175  	}{
   176  		{
   177  			name:      "InvalidSpec",
   178  			wantError: true,
   179  			prepare: func(opts *GenOpts) {
   180  				opts.Spec = invalidSpecExample
   181  				opts.ValidateSpec = true
   182  			},
   183  		},
   184  		{
   185  			name: "BaseImportDisabled",
   186  			prepare: func(opts *GenOpts) {
   187  				opts.LanguageOpts.BaseImportFunc = nil
   188  			},
   189  			wantError: false,
   190  		},
   191  		{
   192  			name:      "Non_existing_contributor_template",
   193  			template:  "NonExistingContributorTemplate",
   194  			wantError: true,
   195  		},
   196  		{
   197  			name:      "Existing_contributor",
   198  			template:  "stratoscale",
   199  			wantError: false,
   200  		},
   201  		{
   202  			name:      "packages mangling",
   203  			wantError: false,
   204  			spec:      filepath.Join("..", "fixtures", "bugs", "2111", "fixture-2111.yaml"),
   205  			verify: func(t testing.TB, target string) {
   206  				require.True(t, fileExists(target, "client"))
   207  
   208  				// assert package generation based on mangled tags
   209  				target = filepath.Join(target, "client")
   210  				assert.True(t, fileExists(target, "abc_linux"))
   211  				assert.True(t, fileExists(target, "abc_test"))
   212  				assert.True(t, fileExists(target, "api"))
   213  				assert.True(t, fileExists(target, "custom"))
   214  				assert.True(t, fileExists(target, "hash_tag_donuts"))
   215  				assert.True(t, fileExists(target, "nr123abc"))
   216  				assert.True(t, fileExists(target, "nr_at_donuts"))
   217  				assert.True(t, fileExists(target, "operations"))
   218  				assert.True(t, fileExists(target, "plus_donuts"))
   219  				assert.True(t, fileExists(target, "strfmt"))
   220  				assert.True(t, fileExists(target, "forced"))
   221  				assert.True(t, fileExists(target, "gtl"))
   222  				assert.True(t, fileExists(target, "nr12nasty"))
   223  				assert.True(t, fileExists(target, "override"))
   224  				assert.True(t, fileExists(target, "operationsops"))
   225  
   226  				buf, err := ioutil.ReadFile(filepath.Join(target, "foo_client.go"))
   227  				require.NoError(t, err)
   228  
   229  				// assert client import, with deconfliction
   230  				code := string(buf)
   231  				baseImport := `swagger_nogo\d+/packages_mangling/client`
   232  				assertImports(t, baseImport, code)
   233  
   234  				assertInCode(t, `cli.Strfmt = strfmtops.New(transport, formats)`, code)
   235  				assertInCode(t, `cli.API = apiops.New(transport, formats)`, code)
   236  				assertInCode(t, `cli.Operations = operations.New(transport, formats)`, code)
   237  			},
   238  		},
   239  		{
   240  			name:      "packages flattening",
   241  			wantError: false,
   242  			spec:      filepath.Join("..", "fixtures", "bugs", "2111", "fixture-2111.yaml"),
   243  			prepare: func(opts *GenOpts) {
   244  				opts.SkipTagPackages = true
   245  			},
   246  			verify: func(t testing.TB, target string) {
   247  				require.True(t, fileExists(target, "client"))
   248  
   249  				// packages are not created here
   250  				target = filepath.Join(target, "client")
   251  				assert.False(t, fileExists(target, "abc_linux"))
   252  				assert.False(t, fileExists(target, "abc_test"))
   253  				assert.False(t, fileExists(target, "api"))
   254  				assert.False(t, fileExists(target, "custom"))
   255  				assert.False(t, fileExists(target, "hash_tag_donuts"))
   256  				assert.False(t, fileExists(target, "nr123abc"))
   257  				assert.False(t, fileExists(target, "nr_at_donuts"))
   258  				assert.False(t, fileExists(target, "plus_donuts"))
   259  				assert.False(t, fileExists(target, "strfmt"))
   260  				assert.False(t, fileExists(target, "forced"))
   261  				assert.False(t, fileExists(target, "gtl"))
   262  				assert.False(t, fileExists(target, "nr12nasty"))
   263  				assert.False(t, fileExists(target, "override"))
   264  				assert.False(t, fileExists(target, "operationsops"))
   265  
   266  				assert.True(t, fileExists(target, "operations"))
   267  			},
   268  		},
   269  		{
   270  			name:      "name with trailing API",
   271  			spec:      filepath.Join("..", "fixtures", "bugs", "2278", "fixture-2278.yaml"),
   272  			wantError: false,
   273  		},
   274  	}
   275  
   276  	for i, tt := range tests {
   277  		t.Run(tt.name, func(t *testing.T) {
   278  			opts := testClientGenOpts()
   279  			opts.Spec = basicFixture
   280  			opts.Target = filepath.Join(targetdir, opts.LanguageOpts.ManglePackageName(tt.name, "client_test"+strconv.Itoa(i)))
   281  			err := os.MkdirAll(opts.Target, 0755)
   282  			require.NoError(t, err)
   283  
   284  			if tt.spec == "" {
   285  				opts.Spec = basicFixture
   286  			} else {
   287  				opts.Spec = tt.spec
   288  			}
   289  			opts.Template = tt.template
   290  
   291  			if tt.prepare != nil {
   292  				tt.prepare(opts)
   293  			}
   294  
   295  			err = GenerateClient("foo", nil, nil, opts)
   296  			if tt.wantError {
   297  				require.Errorf(t, err, "expected an error for client build fixture: %s", opts.Spec)
   298  			} else {
   299  				require.NoError(t, err, "unexpected error for client build fixture: %s", opts.Spec)
   300  			}
   301  
   302  			if tt.verify != nil {
   303  				tt.verify(t, opts.Target)
   304  			}
   305  		})
   306  	}
   307  }
   308  
   309  func TestGenClient_1518(t *testing.T) {
   310  	// test client response handling when unexpected success response kicks in
   311  	log.SetOutput(ioutil.Discard)
   312  	defer func() {
   313  		log.SetOutput(os.Stdout)
   314  	}()
   315  
   316  	opts := testClientGenOpts()
   317  	opts.Spec = filepath.Join("..", "fixtures", "bugs", "1518", "fixture-1518.yaml")
   318  
   319  	cwd, _ := os.Getwd()
   320  	tft, _ := ioutil.TempDir(cwd, "generated")
   321  	opts.Target = tft
   322  
   323  	defer func() {
   324  		_ = os.RemoveAll(opts.Target)
   325  	}()
   326  
   327  	err := GenerateClient("client", []string{}, []string{}, opts)
   328  	if !assert.NoError(t, err) {
   329  		t.FailNow()
   330  	}
   331  
   332  	fixtureConfig := map[string][]string{
   333  		"client/operations/operations_client.go": { // generated file
   334  			// expected code lines
   335  			`success, ok := result.(*GetRecords1OK)`,
   336  			`if ok {`,
   337  			`return success, nil`,
   338  			`msg := fmt.Sprintf(`,
   339  			`panic(msg)`,
   340  			// expected code lines
   341  			`success, ok := result.(*GetRecords2OK)`,
   342  			`if ok {`,
   343  			`return success, nil`,
   344  			`unexpectedSuccess := result.(*GetRecords2Default)`,
   345  			`return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())`,
   346  			// expected code lines
   347  			`switch value := result.(type) {`,
   348  			`case *GetRecords3OK:`,
   349  			`return value, nil, nil`,
   350  			`case *GetRecords3Created:`,
   351  			`return nil, value, nil`,
   352  			`msg := fmt.Sprintf(`,
   353  			`panic(msg)`,
   354  			// expected code lines
   355  			`switch value := result.(type) {`,
   356  			`case *GetRecords4OK:`,
   357  			`return value, nil, nil`,
   358  			`case *GetRecords4Created:`,
   359  			`return nil, value, nil`,
   360  			`unexpectedSuccess := result.(*GetRecords4Default)`,
   361  			`return nil, nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code())`,
   362  		},
   363  	}
   364  
   365  	for fileToInspect, expectedCode := range fixtureConfig {
   366  		code, err := ioutil.ReadFile(filepath.Join(opts.Target, filepath.FromSlash(fileToInspect)))
   367  		require.NoError(t, err)
   368  		for line, codeLine := range expectedCode {
   369  			if !assertInCode(t, strings.TrimSpace(codeLine), string(code)) {
   370  				t.Logf("Code expected did not match in codegenfile %s for expected line %d: %q", fileToInspect, line, expectedCode[line])
   371  			}
   372  		}
   373  	}
   374  }