github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/generator/shared_test.go (about)

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