github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/generator/shared_test.go (about)

     1  package generator
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"runtime"
    11  	"testing"
    12  
    13  	"github.com/go-openapi/analysis"
    14  	"github.com/go-openapi/loads"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  const (
    20  	defaultAPIPackage    = "operations"
    21  	defaultClientPackage = "client"
    22  	defaultModelPackage  = "models"
    23  	defaultServerPackage = "restapi"
    24  )
    25  
    26  // Perform common initialization of template repository before running tests.
    27  // This allows to run tests unitarily (e.g. go test -run xxx ).
    28  func TestMain(m *testing.M) {
    29  	// initializations to run tests in this package
    30  	log.SetFlags(log.LstdFlags | log.Lshortfile)
    31  	templates.LoadDefaults()
    32  	initSchemaValidationTest()
    33  	os.Exit(m.Run())
    34  }
    35  
    36  func opts() *GenOpts {
    37  	var opts GenOpts
    38  	opts.IncludeValidator = true
    39  	opts.IncludeModel = true
    40  	if err := opts.EnsureDefaults(); err != nil {
    41  		panic(err)
    42  	}
    43  	return &opts
    44  }
    45  
    46  func testGenOpts() *GenOpts {
    47  	g := &GenOpts{}
    48  	g.Target = "."
    49  	g.APIPackage = defaultAPIPackage
    50  	g.ModelPackage = defaultModelPackage
    51  	g.ServerPackage = defaultServerPackage
    52  	g.ClientPackage = defaultClientPackage
    53  	g.Principal = ""
    54  	g.DefaultScheme = "http"
    55  	g.IncludeModel = true
    56  	g.IncludeValidator = true
    57  	g.IncludeModel = true
    58  	g.IncludeHandler = true
    59  	g.IncludeParameters = true
    60  	g.IncludeResponses = true
    61  	g.IncludeMain = false
    62  	g.IncludeSupport = true
    63  	g.ExcludeSpec = true
    64  	g.TemplateDir = ""
    65  	g.DumpData = false
    66  
    67  	if err := g.EnsureDefaults(); err != nil {
    68  		panic(err)
    69  	}
    70  	return g
    71  }
    72  
    73  // TODO: there is a catch, since these methods are sensitive
    74  // to the CWD of the current swagger command (or go
    75  // generate when working on resulting template)
    76  // NOTE:
    77  // Errors in CheckOpts are hard to simulate since
    78  // they occur only on os.Getwd() errors
    79  // Windows style path is difficult to test on unix
    80  // since the filepath pkg is platform dependent
    81  func TestShared_CheckOpts(t *testing.T) {
    82  	testPath := filepath.Join("a", "b", "b")
    83  
    84  	log.SetOutput(io.Discard)
    85  	defer log.SetOutput(os.Stdout)
    86  
    87  	var opts = new(GenOpts)
    88  	_ = opts.EnsureDefaults()
    89  	cwd, _ := os.Getwd()
    90  	opts.Spec = "../fixtures/codegen/simplesearch.yml"
    91  
    92  	opts.Target = filepath.Join(".", "a", "b", "c")
    93  	opts.ServerPackage = filepath.Join(cwd, "a", "b", "c")
    94  	err := opts.CheckOpts()
    95  	assert.Error(t, err)
    96  
    97  	opts.Target = filepath.Join(cwd, "a", "b", "c")
    98  	opts.ServerPackage = testPath
    99  	opts.Spec = filepath.Join(cwd, "nowhere", "swagger.yaml")
   100  	err = opts.CheckOpts()
   101  	assert.Error(t, err)
   102  
   103  	opts.Target = filepath.Join(cwd, "a", "b", "c")
   104  	opts.ServerPackage = testPath
   105  	opts.Spec = "https://ab/c"
   106  	err = opts.CheckOpts()
   107  	assert.NoError(t, err)
   108  
   109  	opts.Target = filepath.Join(cwd, "a", "b", "c")
   110  	opts.ServerPackage = testPath
   111  	opts.Spec = "http://ab/c"
   112  	err = opts.CheckOpts()
   113  	assert.NoError(t, err)
   114  
   115  	opts.Target = filepath.Join("a", "b", "c")
   116  	opts.ServerPackage = testPath
   117  	opts.Spec = filepath.Join(cwd, "..", "fixtures", "codegen", "swagger-codegen-tests.json")
   118  	err = opts.CheckOpts()
   119  	assert.NoError(t, err)
   120  
   121  	opts.Target = filepath.Join("a", "b", "c")
   122  	opts.ServerPackage = testPath
   123  	opts.Spec = filepath.Join("..", "fixtures", "codegen", "swagger-codegen-tests.json")
   124  	err = opts.CheckOpts()
   125  	assert.NoError(t, err)
   126  
   127  	opts = nil
   128  	err = opts.CheckOpts()
   129  	assert.Error(t, err)
   130  }
   131  
   132  func TestShared_EnsureDefaults(t *testing.T) {
   133  	opts := &GenOpts{}
   134  	require.NoError(t, opts.EnsureDefaults())
   135  	assert.True(t, opts.defaultsEnsured)
   136  	opts.DefaultConsumes = "https"
   137  	_ = opts.EnsureDefaults()
   138  	assert.Equal(t, "https", opts.DefaultConsumes)
   139  }
   140  
   141  // TargetPath and SpecPath are used in server.gotmpl
   142  // as template variables: {{ .TestTargetPath }} and
   143  // {{ .SpecPath }}, to construct the go generate
   144  // directive.
   145  func TestShared_TargetPath(t *testing.T) {
   146  	log.SetOutput(io.Discard)
   147  	defer log.SetOutput(os.Stdout)
   148  
   149  	cwd, _ := os.Getwd()
   150  
   151  	// relative target
   152  	var opts = new(GenOpts)
   153  	_ = opts.EnsureDefaults()
   154  	opts.Target = filepath.Join(".", "a", "b", "c")
   155  	opts.ServerPackage = "y"
   156  	expected := filepath.Join("..", "..", "c")
   157  	result := opts.TargetPath()
   158  	assert.Equal(t, expected, result)
   159  
   160  	// relative target, server path
   161  	opts = new(GenOpts)
   162  	_ = opts.EnsureDefaults()
   163  	opts.Target = filepath.Join(".", "a", "b", "c")
   164  	opts.ServerPackage = "y/z"
   165  	expected = filepath.Join("..", "..", "..", "c")
   166  	result = opts.TargetPath()
   167  	assert.Equal(t, expected, result)
   168  
   169  	// absolute target
   170  	opts = new(GenOpts)
   171  	_ = opts.EnsureDefaults()
   172  	opts.Target = filepath.Join(cwd, "a", "b", "c")
   173  	opts.ServerPackage = "y"
   174  	expected = filepath.Join("..", "..", "c")
   175  	result = opts.TargetPath()
   176  	assert.Equal(t, expected, result)
   177  
   178  	// absolute target, server path
   179  	opts = new(GenOpts)
   180  	_ = opts.EnsureDefaults()
   181  	opts.Target = filepath.Join(cwd, "a", "b", "c")
   182  	opts.ServerPackage = path.Join("y", "z")
   183  	expected = filepath.Join("..", "..", "..", "c")
   184  	result = opts.TargetPath()
   185  	assert.Equal(t, expected, result)
   186  }
   187  
   188  // NOTE: file://url is not supported
   189  func TestShared_SpecPath(t *testing.T) {
   190  	log.SetOutput(io.Discard)
   191  	defer log.SetOutput(os.Stdout)
   192  
   193  	cwd, _ := os.Getwd()
   194  
   195  	// http URL spec
   196  	var opts = new(GenOpts)
   197  	_ = opts.EnsureDefaults()
   198  	opts.Spec = "http://a/b/c"
   199  	opts.ServerPackage = "y"
   200  	expected := opts.Spec
   201  	result := opts.SpecPath()
   202  	assert.Equal(t, expected, result)
   203  
   204  	// https URL spec
   205  	opts = new(GenOpts)
   206  	_ = opts.EnsureDefaults()
   207  	opts.Spec = "https://a/b/c"
   208  	opts.ServerPackage = "y"
   209  	expected = opts.Spec
   210  	result = opts.SpecPath()
   211  	assert.Equal(t, expected, result)
   212  
   213  	// relative spec
   214  	opts = new(GenOpts)
   215  	_ = opts.EnsureDefaults()
   216  	opts.Spec = filepath.Join(".", "a", "b", "c")
   217  	opts.Target = "d"
   218  	opts.ServerPackage = "y"
   219  	expected = filepath.Join("..", "..", "a", "b", "c")
   220  	result = opts.SpecPath()
   221  	assert.Equal(t, expected, result)
   222  
   223  	// relative spec, server path
   224  	opts = new(GenOpts)
   225  	_ = opts.EnsureDefaults()
   226  	opts.Spec = filepath.Join(".", "a", "b", "c")
   227  	opts.Target = filepath.Join("d", "e")
   228  	opts.ServerPackage = "y/z"
   229  	expected = filepath.Join("..", "..", "..", "..", "a", "b", "c")
   230  	result = opts.SpecPath()
   231  	assert.Equal(t, expected, result)
   232  
   233  	// relative spec, server path
   234  	opts = new(GenOpts)
   235  	_ = opts.EnsureDefaults()
   236  	opts.Spec = filepath.Join(".", "a", "b", "c")
   237  	opts.Target = filepath.Join(".", "a", "b")
   238  	opts.ServerPackage = "y/z"
   239  	expected = filepath.Join("..", "..", "c")
   240  	result = opts.SpecPath()
   241  	assert.Equal(t, expected, result)
   242  
   243  	// absolute spec
   244  	opts = new(GenOpts)
   245  	_ = opts.EnsureDefaults()
   246  	opts.Spec = filepath.Join(cwd, "a", "b", "c")
   247  	opts.ServerPackage = "y"
   248  	expected = filepath.Join("..", "a", "b", "c")
   249  	result = opts.SpecPath()
   250  	assert.Equal(t, expected, result)
   251  
   252  	// absolute spec, server path
   253  	opts = new(GenOpts)
   254  	_ = opts.EnsureDefaults()
   255  	opts.Spec = filepath.Join("..", "a", "b", "c")
   256  	opts.Target = ""
   257  	opts.ServerPackage = path.Join("y", "z")
   258  	expected = filepath.Join("..", "..", "..", "a", "b", "c")
   259  	result = opts.SpecPath()
   260  	assert.Equal(t, expected, result)
   261  
   262  	if runtime.GOOS == "windows" {
   263  		opts = new(GenOpts)
   264  		_ = opts.EnsureDefaults()
   265  		opts.Spec = filepath.Join("a", "b", "c")
   266  		opts.Target = filepath.Join("Z:", "e", "f", "f")
   267  		opts.ServerPackage = "y/z"
   268  		expected, _ = filepath.Abs(opts.Spec)
   269  		result = opts.SpecPath()
   270  		assert.Equal(t, expected, result)
   271  	}
   272  }
   273  
   274  // Low level testing: templates not found (higher level calls raise panic(), see above)
   275  func TestShared_NotFoundTemplate(t *testing.T) {
   276  	log.SetOutput(io.Discard)
   277  	defer log.SetOutput(os.Stdout)
   278  
   279  	opts := testGenOpts()
   280  	tplOpts := TemplateOpts{
   281  		Name:       "NotFound",
   282  		Source:     "asset:notfound",
   283  		Target:     ".",
   284  		FileName:   "test_notfound.go",
   285  		SkipExists: false,
   286  		SkipFormat: false,
   287  	}
   288  
   289  	buf, err := opts.render(&tplOpts, nil)
   290  	require.Errorf(t, err, "Error should be handled here")
   291  	assert.Nilf(t, buf, "Upon error, GenOpts.render() should return nil buffer")
   292  }
   293  
   294  // Low level testing: invalid template => Get() returns not found (higher level calls raise panic(), see above)
   295  // TODO: better error discrimination between absent definition and non-parsing template
   296  func TestShared_GarbledTemplate(t *testing.T) {
   297  	log.SetOutput(io.Discard)
   298  	defer log.SetOutput(os.Stdout)
   299  
   300  	garbled := "func x {{;;; garbled"
   301  
   302  	_ = templates.AddFile("garbled", garbled)
   303  	opts := testGenOpts()
   304  
   305  	tplOpts := TemplateOpts{
   306  		Name:       "Garbled",
   307  		Source:     "asset:garbled",
   308  		Target:     ".",
   309  		FileName:   "test_garbled.go",
   310  		SkipExists: false,
   311  		SkipFormat: false,
   312  	}
   313  
   314  	buf, err := opts.render(&tplOpts, nil)
   315  	require.Errorf(t, err, "Error should be handled here")
   316  	assert.Nilf(t, buf, "Upon error, GenOpts.render() should return nil buffer")
   317  }
   318  
   319  // Template execution failure
   320  type myTemplateData struct {
   321  }
   322  
   323  func (*myTemplateData) MyFaultyMethod() (string, error) {
   324  	return "", fmt.Errorf("myFaultyError")
   325  }
   326  
   327  func TestShared_ExecTemplate(t *testing.T) {
   328  	log.SetOutput(io.Discard)
   329  	defer log.SetOutput(os.Stdout)
   330  
   331  	// Not a failure: no value data
   332  	execfailure1 := "func x {{ .NotInData }}"
   333  
   334  	_ = templates.AddFile("execfailure1", execfailure1)
   335  	opts := testGenOpts()
   336  
   337  	tplOpts := TemplateOpts{
   338  		Name:       "execFailure1",
   339  		Source:     "asset:execfailure1",
   340  		Target:     ".",
   341  		FileName:   "test_execfailure1.go",
   342  		SkipExists: false,
   343  		SkipFormat: false,
   344  	}
   345  
   346  	buf1, err := opts.render(&tplOpts, nil)
   347  	assert.NoError(t, err, "Template rendering should put <no value> instead of missing data, and report no error")
   348  	assert.Equal(t, string(buf1), "func x <no value>")
   349  
   350  	execfailure2 := "func {{ .MyFaultyMethod }}"
   351  
   352  	_ = templates.AddFile("execfailure2", execfailure2)
   353  	opts = testGenOpts()
   354  	tplOpts2 := TemplateOpts{
   355  		Name:       "execFailure2",
   356  		Source:     "asset:execfailure2",
   357  		Target:     ".",
   358  		FileName:   "test_execfailure2.go",
   359  		SkipExists: false,
   360  		SkipFormat: false,
   361  	}
   362  
   363  	data := new(myTemplateData)
   364  	buf2, err := opts.render(&tplOpts2, data)
   365  	assert.Error(t, err, "Error should be handled here: missing func in template yields an error")
   366  	assert.Contains(t, err.Error(), "template execution failed")
   367  	assert.Nil(t, buf2, "Upon error, GenOpts.render() should return nil buffer")
   368  }
   369  
   370  // Test correctly parsed templates, with bad formatting
   371  func TestShared_BadFormatTemplate(t *testing.T) {
   372  	log.SetOutput(io.Discard)
   373  
   374  	defer func() {
   375  		_ = os.Remove("test_badformat.gol")
   376  		_ = os.Remove("test_badformat2.gol")
   377  		log.SetOutput(os.Stdout)
   378  		Debug = false
   379  	}()
   380  
   381  	// Not skipping format
   382  	badFormat := "func x {;;; garbled"
   383  
   384  	Debug = true
   385  	_ = templates.AddFile("badformat", badFormat)
   386  
   387  	opts := testGenOpts()
   388  	opts.LanguageOpts = GoLangOpts()
   389  	tplOpts := TemplateOpts{
   390  		Name:   "badformat",
   391  		Source: "asset:badformat",
   392  		Target: ".",
   393  		// Extension ".gol" won't mess with go if cleanup is not performed
   394  		FileName:   "test_badformat.gol",
   395  		SkipExists: false,
   396  		SkipFormat: false,
   397  	}
   398  
   399  	data := appGenerator{
   400  		Name:    "badtest",
   401  		Package: "wrongpkg",
   402  	}
   403  
   404  	err := opts.write(&tplOpts, data)
   405  	defer func() {
   406  		_ = os.Remove(tplOpts.FileName)
   407  	}()
   408  
   409  	// The badly formatted file has been dumped for debugging purposes
   410  	_, exists := os.Stat(tplOpts.FileName)
   411  	assert.True(t, !os.IsNotExist(exists), "The template file has not been generated as expected")
   412  
   413  	require.Error(t, err)
   414  	assert.Contains(t, err.Error(), "source formatting on generated source")
   415  
   416  	// Skipping format
   417  	opts = testGenOpts()
   418  	opts.LanguageOpts = GoLangOpts()
   419  	tplOpts2 := TemplateOpts{
   420  		Name:       "badformat2",
   421  		Source:     "asset:badformat",
   422  		Target:     ".",
   423  		FileName:   "test_badformat2.gol",
   424  		SkipExists: false,
   425  		SkipFormat: true,
   426  	}
   427  
   428  	err2 := opts.write(&tplOpts2, data)
   429  
   430  	// The unformatted file has been dumped without format checks
   431  	_, exists2 := os.Stat(tplOpts2.FileName)
   432  	assert.True(t, !os.IsNotExist(exists2), "The template file has not been generated as expected")
   433  	_ = os.Remove(tplOpts2.FileName)
   434  
   435  	assert.Nil(t, err2)
   436  
   437  	// os.RemoveAll(filepath.Join(filepath.FromSlash(dr),"restapi"))
   438  }
   439  
   440  // Test dir creation
   441  func TestShared_DirectoryTemplate(t *testing.T) {
   442  	log.SetOutput(io.Discard)
   443  
   444  	defer func() {
   445  		_ = os.RemoveAll("TestGenDir")
   446  		log.SetOutput(os.Stdout)
   447  	}()
   448  
   449  	// Not skipping format
   450  	content := "func x {}"
   451  
   452  	_ = templates.AddFile("gendir", content)
   453  
   454  	opts := testGenOpts()
   455  	opts.LanguageOpts = GoLangOpts()
   456  	tplOpts := TemplateOpts{
   457  		Name:   "gendir",
   458  		Source: "asset:gendir",
   459  		Target: "TestGenDir",
   460  		// Extension ".gol" won't mess with go if cleanup is not performed
   461  		FileName:   "test_gendir.gol",
   462  		SkipExists: false,
   463  		SkipFormat: true,
   464  	}
   465  
   466  	data := appGenerator{
   467  		Name:    "gentest",
   468  		Package: "stubpkg",
   469  	}
   470  
   471  	err := opts.write(&tplOpts, data)
   472  
   473  	// The badly formatted file has been dumped for debugging purposes
   474  	_, exists := os.Stat(filepath.Join(tplOpts.Target, tplOpts.FileName))
   475  	assert.True(t, !os.IsNotExist(exists), "The template file has not been generated as expected")
   476  	_ = os.RemoveAll(tplOpts.Target)
   477  
   478  	assert.Nil(t, err)
   479  }
   480  
   481  // Test templates which are not assets (open in file)
   482  // Low level testing: templates loaded from file
   483  func TestShared_LoadTemplate(t *testing.T) {
   484  	log.SetOutput(io.Discard)
   485  	defer log.SetOutput(os.Stdout)
   486  
   487  	opts := testGenOpts()
   488  	tplOpts := TemplateOpts{
   489  		Name:       "File",
   490  		Source:     "File",
   491  		Target:     ".",
   492  		FileName:   "file.go",
   493  		SkipExists: false,
   494  		SkipFormat: false,
   495  	}
   496  
   497  	buf, err := opts.render(&tplOpts, nil)
   498  	assert.Error(t, err, "Error should be handled here")
   499  	assert.Contains(t, err.Error(), "open File")
   500  	assert.Contains(t, err.Error(), "error while opening")
   501  	assert.Nil(t, buf, "Upon error, GenOpts.render() should return nil buffer")
   502  
   503  	opts.TemplateDir = filepath.Join(".", "myTemplateDir")
   504  	buf, err = opts.render(&tplOpts, nil)
   505  	assert.Error(t, err, "Error should be handled here")
   506  	assert.Contains(t, err.Error(), "open "+filepath.Join("myTemplateDir", "File"))
   507  	assert.Contains(t, err.Error(), "error while opening")
   508  	assert.Nil(t, buf, "Upon error, GenOpts.render() should return nil buffer")
   509  
   510  }
   511  
   512  func TestShared_AppNameOrDefault(t *testing.T) {
   513  	specPath := filepath.Join("..", "fixtures", "codegen", "shipyard.yml")
   514  	specDoc, err := loads.Spec(specPath)
   515  	require.NoError(t, err)
   516  
   517  	require.NotNil(t, specDoc.Spec().Info)
   518  	specDoc.Spec().Info.Title = "    "
   519  	assert.Equal(t, "Xyz", appNameOrDefault(specDoc, "  ", "xyz"))
   520  	specDoc.Spec().Info.Title = "test"
   521  	assert.Equal(t, "Xyz", appNameOrDefault(specDoc, "  ", "xyz"))
   522  
   523  	opts := testGenOpts()
   524  	opts.Spec = specPath
   525  	_, err = opts.validateAndFlattenSpec()
   526  	require.NoError(t, err)
   527  
   528  	// more aggressive fixture on $refs, with validation errors, but flatten ok
   529  	specPath = filepath.Join("..", "fixtures", "bugs", "1429", "swagger.yaml")
   530  	specDoc, err = loads.Spec(specPath)
   531  	require.NoError(t, err)
   532  
   533  	opts.Spec = specPath
   534  	opts.FlattenOpts.BasePath = specDoc.SpecFilePath()
   535  	opts.FlattenOpts.Spec = analysis.New(specDoc.Spec())
   536  	opts.FlattenOpts.Minimal = true
   537  	err = analysis.Flatten(*opts.FlattenOpts)
   538  	assert.NoError(t, err)
   539  
   540  	specDoc, _ = loads.Spec(specPath) // needs reload
   541  	opts.FlattenOpts.Spec = analysis.New(specDoc.Spec())
   542  	opts.FlattenOpts.Minimal = false
   543  	err = analysis.Flatten(*opts.FlattenOpts)
   544  	assert.NoError(t, err)
   545  }
   546  
   547  func TestShared_GatherModel(t *testing.T) {
   548  	specPath := filepath.Join("..", "fixtures", "codegen", "shipyard.yml")
   549  
   550  	specDoc, err := loads.Spec(specPath)
   551  	require.NoError(t, err)
   552  
   553  	_, err = gatherModels(specDoc, []string{"unknown"})
   554  	assert.Error(t, err)
   555  
   556  	res, err := gatherModels(specDoc, []string{"Image", "Application"})
   557  	require.NoError(t, err)
   558  	assert.Len(t, res, 2)
   559  
   560  	res, err = gatherModels(specDoc, []string{"Image", "Application"})
   561  	require.NoError(t, err)
   562  	assert.Len(t, res, 2)
   563  
   564  	res, err = gatherModels(specDoc, []string{})
   565  	require.NoError(t, err)
   566  	assert.Len(t, res, 4)
   567  }
   568  
   569  func TestShared_DumpWrongData(t *testing.T) {
   570  	assert.Error(t, dumpData(struct {
   571  		A func() string
   572  		B string
   573  	}{
   574  		A: func() string { return "" },
   575  		B: "xyz",
   576  	}))
   577  
   578  	assert.NoError(t, dumpData(struct {
   579  		A func() string `json:"-"`
   580  		B string
   581  	}{
   582  		A: func() string { return "" },
   583  		B: "xyz",
   584  	}))
   585  
   586  	assert.NoError(t, dumpData(struct {
   587  		a func() string
   588  		B string
   589  	}{
   590  		a: func() string { return "" },
   591  		B: "xyz",
   592  	}))
   593  }
   594  
   595  func TestResolvePrincipal(t *testing.T) {
   596  	for _, toPin := range []struct {
   597  		Title     string
   598  		Principal string
   599  		Expected  []string
   600  	}{
   601  		{
   602  			Title: "defaults", Principal: "",
   603  			Expected: []string{"", "interface{}", ""},
   604  		},
   605  		{
   606  			Title: "with base import", Principal: "auth.Principal",
   607  			Expected: []string{"auth", "auth.Principal", "auth"},
   608  		},
   609  		{
   610  			Title: "with full import", Principal: "github.com/myproject/auth.Principal",
   611  			Expected: []string{"auth", "auth.Principal", "github.com/myproject/auth"},
   612  		},
   613  		{
   614  			Title: "with name conflict", Principal: "github.com/myproject/middleware.Principal",
   615  			Expected: []string{"auth", "auth.Principal", "github.com/myproject/middleware"},
   616  		},
   617  		{
   618  			Title: "with name conflict (2)", Principal: "github.com/myproject/principal.Principal",
   619  			Expected: []string{"auth", "auth.Principal", "github.com/myproject/principal"},
   620  		},
   621  	} {
   622  		fixture := toPin
   623  		t.Run(fixture.Title, func(t *testing.T) {
   624  			t.Parallel()
   625  			opts := &GenOpts{GenOptsCommon: GenOptsCommon{Principal: fixture.Principal}}
   626  			err := opts.EnsureDefaults()
   627  			require.NoError(t, err)
   628  			alias, principal, target := opts.resolvePrincipal()
   629  			require.Equal(t, fixture.Expected[0], alias)
   630  			require.Equal(t, fixture.Expected[1], principal)
   631  			require.Equal(t, fixture.Expected[2], target)
   632  		})
   633  	}
   634  }
   635  
   636  func TestDefaultImports(t *testing.T) {
   637  	for i, toPin := range []struct {
   638  		Title    string
   639  		Opts     *GenOpts
   640  		Expected map[string]string
   641  	}{
   642  		{
   643  			Title: "defaults",
   644  			Opts:  &GenOpts{},
   645  			Expected: map[string]string{
   646  				"models": "github.com/go-swagger/go-swagger/generator/models",
   647  			},
   648  		},
   649  		{
   650  			Title: "with base import",
   651  			Opts: &GenOpts{
   652  				GenOptsCommon: GenOptsCommon{
   653  					Principal: "ext.Principal",
   654  				},
   655  			},
   656  			Expected: map[string]string{
   657  				"ext":    "github.com/go-swagger/go-swagger/generator/ext",
   658  				"models": "github.com/go-swagger/go-swagger/generator/models",
   659  			},
   660  		},
   661  		{
   662  			Title: "with full import",
   663  			Opts: &GenOpts{
   664  				GenOptsCommon: GenOptsCommon{
   665  					Principal: "github.com/myproject/identity.Principal",
   666  				},
   667  			},
   668  			Expected: map[string]string{
   669  				"identity": "github.com/myproject/identity",
   670  				"models":   "github.com/go-swagger/go-swagger/generator/models",
   671  			},
   672  		},
   673  		{
   674  			Title: "with name conflict",
   675  			Opts: &GenOpts{
   676  				GenOptsCommon: GenOptsCommon{
   677  					Principal: "github.com/myproject/middleware.Principal",
   678  				},
   679  			},
   680  			Expected: map[string]string{
   681  				"auth":   "github.com/myproject/middleware",
   682  				"models": "github.com/go-swagger/go-swagger/generator/models",
   683  			},
   684  		},
   685  		{
   686  			Title: "with name conflict (2)",
   687  			Opts: &GenOpts{
   688  				GenOptsCommon: GenOptsCommon{
   689  					Principal: "github.com/myproject/principal.Principal",
   690  				},
   691  			},
   692  			Expected: map[string]string{
   693  				"auth":   "github.com/myproject/principal",
   694  				"models": "github.com/go-swagger/go-swagger/generator/models",
   695  			},
   696  		},
   697  		{
   698  			Title: "alternate target for models",
   699  			Opts: &GenOpts{
   700  				GenOptsCommon: GenOptsCommon{
   701  					ModelPackage: "target/bespoke",
   702  				},
   703  			},
   704  			Expected: map[string]string{
   705  				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
   706  			},
   707  		},
   708  		{
   709  			Title: "with existing models",
   710  			Opts: &GenOpts{
   711  				GenOptsCommon: GenOptsCommon{
   712  					ExistingModels: "github.com/myproject/target/bespoke",
   713  				},
   714  			},
   715  			Expected: map[string]string{
   716  				"models": "github.com/myproject/target/bespoke",
   717  			},
   718  		},
   719  		// issue #2362
   720  		{
   721  			Title: "relative principal, in dedicated package under generated target",
   722  			Opts: &GenOpts{
   723  				GenOptsCommon: GenOptsCommon{
   724  					Principal:    "auth.Principal",
   725  					ModelPackage: "target/bespoke",
   726  				},
   727  			},
   728  			Expected: map[string]string{
   729  				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
   730  				"auth":    "github.com/go-swagger/go-swagger/generator/auth",
   731  			},
   732  		},
   733  		{
   734  			Title: "relative principal in models (1)",
   735  			Opts: &GenOpts{
   736  				GenOptsCommon: GenOptsCommon{
   737  					Principal:    "bespoke.Principal",
   738  					ModelPackage: "target/bespoke",
   739  				},
   740  			},
   741  			Expected: map[string]string{
   742  				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
   743  			},
   744  		},
   745  		{
   746  			Title: "relative principal in models (2)",
   747  			Opts: &GenOpts{
   748  				GenOptsCommon: GenOptsCommon{
   749  					Principal:    "target/bespoke.Principal",
   750  					ModelPackage: "target/bespoke",
   751  				},
   752  			},
   753  			Expected: map[string]string{
   754  				"bespoke": "github.com/go-swagger/go-swagger/generator/target/bespoke",
   755  			},
   756  		},
   757  		{
   758  			Title: "relative principal: not detected",
   759  			// NOTE: this case will probably not build: no way to determine the user intent
   760  			Opts: &GenOpts{
   761  				GenOptsCommon: GenOptsCommon{
   762  					Principal:    "target/auth.Principal",
   763  					ModelPackage: "target/models",
   764  				},
   765  			},
   766  			Expected: map[string]string{
   767  				"models": "github.com/go-swagger/go-swagger/generator/target/models",
   768  				"auth":   "target/auth",
   769  			},
   770  		},
   771  	} {
   772  		fixture := toPin
   773  		t.Run(fixture.Title, func(t *testing.T) {
   774  			t.Parallel()
   775  			err := fixture.Opts.EnsureDefaults()
   776  			require.NoError(t, err)
   777  			imports := fixture.Opts.defaultImports()
   778  			require.EqualValuesf(t, fixture.Expected, imports, "unexpected imports generated with fixture %q[%d]", fixture.Title, i)
   779  		})
   780  	}
   781  }
   782  
   783  func TestShared_Issue2113(t *testing.T) {
   784  	log.SetOutput(io.Discard)
   785  	defer log.SetOutput(os.Stdout)
   786  
   787  	// acknowledge fix in go-openapi/spec
   788  	specPath := filepath.Join("..", "fixtures", "bugs", "2113", "base.yaml")
   789  	_, err := loads.Spec(specPath)
   790  	require.NoError(t, err)
   791  
   792  	opts := testGenOpts()
   793  	opts.Spec = specPath
   794  	opts.Spec = specPath
   795  	opts.ValidateSpec = true
   796  	_, err = opts.validateAndFlattenSpec()
   797  	assert.NoError(t, err)
   798  }