github.com/emreu/go-swagger@v0.22.1/generator/template_repo_test.go (about)

     1  package generator
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"testing"
     9  
    10  	"github.com/go-openapi/loads"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  var (
    15  	singleTemplate        = `test`
    16  	multipleDefinitions   = `{{ define "T1" }}T1{{end}}{{ define "T2" }}T2{{end}}`
    17  	dependantTemplate     = `{{ template "T1" }}D1`
    18  	cirularDeps1          = `{{ define "T1" }}{{ .Name }}: {{ range .Children }}{{ template "T2" . }}{{end}}{{end}}{{template "T1" . }}`
    19  	cirularDeps2          = `{{ define "T2" }}{{if .Recurse }}{{ template "T1" . }}{{ else }}Children{{end}}{{end}}`
    20  	customHeader          = `custom header`
    21  	customMultiple        = `{{define "bindprimitiveparam" }}custom primitive{{end}}`
    22  	customNewTemplate     = `new template`
    23  	customExistingUsesNew = `{{define "bindprimitiveparam" }}{{ template "newtemplate" }}{{end}}`
    24  	// Test template environment
    25  	copyright        = `{{ .Copyright }}`
    26  	targetImportPath = `{{ .TargetImportPath }}`
    27  	funcTpl          = `
    28  Pascalize={{ pascalize "WeArePonies_Of_the_round table" }}
    29  Snakize={{ snakize "WeArePonies_Of_the_round table" }}
    30  Humanize={{ humanize "WeArePonies_Of_the_round table" }}
    31  PluralizeFirstWord={{ pluralizeFirstWord "pony of the round table" }}
    32  PluralizeFirstOfOneWord={{ pluralizeFirstWord "dwarf" }}
    33  PluralizeFirstOfNoWord={{ pluralizeFirstWord "" }}
    34  StripPackage={{ stripPackage "prefix.suffix" "xyz"}}
    35  StripNoPackage={{ stripPackage "suffix" "xyz"}}
    36  StripEmptyPackage={{ stripPackage "" "xyz" }}
    37  DropPackage={{ dropPackage "prefix.suffix" }}
    38  DropNoPackage={{ dropPackage "suffix" }}
    39  DropEmptyPackage={{ dropPackage "" }}
    40  ImportRuntime={{ contains .DefaultImports "github.com/go-openapi/runtime"}}
    41  DoNotImport={{ contains .DefaultImports "github.com/go-openapi/xruntime"}}
    42  PadSurround1={{ padSurround "padme" "-" 3 12}}
    43  PadSurround2={{ padSurround "padme" "-" 0 12}}
    44  Json={{ json .DefaultImports }}
    45  PrettyJson={{ prettyjson . }}
    46  Snakize1={{ snakize "endingInOsNameLinux" }}
    47  Snakize2={{ snakize "endingInArchNameLinuxAmd64" }}
    48  Snakize3={{ snakize "endingInTest" }}
    49  toPackage1={{ toPackage "a/b-c/d-e" }}
    50  toPackage2={{ toPackage "a.a/b_c/d_e" }}
    51  toPackage3={{ toPackage "d_e" }}
    52  toPackage4={{ toPackage "d-e" }}
    53  toPackageName={{ toPackageName "d-e/f-g" }}
    54  PascalizeSpecialChar1={{ pascalize "+1" }}
    55  PascalizeSpecialChar2={{ pascalize "-1" }}
    56  PascalizeSpecialChar3={{ pascalize "1" }}
    57  PascalizeSpecialChar4={{ pascalize "-" }}
    58  PascalizeSpecialChar5={{ pascalize "+" }}
    59  `
    60  )
    61  
    62  func TestTemplates_CustomTemplates(t *testing.T) {
    63  
    64  	var buf bytes.Buffer
    65  	headerTempl, err := templates.Get("bindprimitiveparam")
    66  
    67  	assert.Nil(t, err)
    68  
    69  	err = headerTempl.Execute(&buf, nil)
    70  
    71  	assert.Nil(t, err)
    72  	assert.Equal(t, "\n", buf.String())
    73  
    74  	buf.Reset()
    75  	err = templates.AddFile("bindprimitiveparam", customHeader)
    76  
    77  	assert.Nil(t, err)
    78  	headerTempl, err = templates.Get("bindprimitiveparam")
    79  
    80  	assert.Nil(t, err)
    81  
    82  	err = headerTempl.Execute(&buf, nil)
    83  
    84  	assert.Nil(t, err)
    85  	assert.Equal(t, "custom header", buf.String())
    86  
    87  }
    88  
    89  func TestTemplates_CustomTemplatesMultiple(t *testing.T) {
    90  	var buf bytes.Buffer
    91  
    92  	err := templates.AddFile("differentFileName", customMultiple)
    93  
    94  	assert.Nil(t, err)
    95  	headerTempl, err := templates.Get("bindprimitiveparam")
    96  
    97  	assert.Nil(t, err)
    98  
    99  	err = headerTempl.Execute(&buf, nil)
   100  
   101  	assert.Nil(t, err)
   102  	assert.Equal(t, "custom primitive", buf.String())
   103  }
   104  
   105  func TestTemplates_CustomNewTemplates(t *testing.T) {
   106  	var buf bytes.Buffer
   107  
   108  	err := templates.AddFile("newtemplate", customNewTemplate)
   109  	assert.Nil(t, err)
   110  
   111  	err = templates.AddFile("existingUsesNew", customExistingUsesNew)
   112  	assert.Nil(t, err)
   113  
   114  	headerTempl, err := templates.Get("bindprimitiveparam")
   115  	assert.Nil(t, err)
   116  
   117  	err = headerTempl.Execute(&buf, nil)
   118  	assert.Nil(t, err)
   119  
   120  	assert.Equal(t, "new template", buf.String())
   121  }
   122  
   123  func TestTemplates_RepoLoadingTemplates(t *testing.T) {
   124  
   125  	repo := NewRepository(nil)
   126  
   127  	err := repo.AddFile("simple", singleTemplate)
   128  	assert.NoError(t, err)
   129  
   130  	templ, err := repo.Get("simple")
   131  
   132  	assert.Nil(t, err)
   133  
   134  	var b bytes.Buffer
   135  
   136  	err = templ.Execute(&b, nil)
   137  
   138  	assert.Nil(t, err)
   139  
   140  	assert.Equal(t, "test", b.String())
   141  }
   142  
   143  func TestTemplates_RepoLoadsAllTemplatesDefined(t *testing.T) {
   144  
   145  	var b bytes.Buffer
   146  	repo := NewRepository(nil)
   147  
   148  	err := repo.AddFile("multiple", multipleDefinitions)
   149  	assert.NoError(t, err)
   150  
   151  	templ, err := repo.Get("multiple")
   152  	assert.Nil(t, err)
   153  	err = templ.Execute(&b, nil)
   154  	assert.Nil(t, err)
   155  
   156  	assert.Equal(t, "", b.String())
   157  
   158  	templ, err = repo.Get("T1")
   159  	assert.Nil(t, err)
   160  	err = templ.Execute(&b, nil)
   161  	assert.Nil(t, err)
   162  
   163  	assert.Equal(t, "T1", b.String())
   164  }
   165  
   166  type testData struct {
   167  	Children []testData
   168  	Name     string
   169  	Recurse  bool
   170  }
   171  
   172  func TestTemplates_RepoLoadsAllDependantTemplates(t *testing.T) {
   173  
   174  	var b bytes.Buffer
   175  	repo := NewRepository(nil)
   176  
   177  	err := repo.AddFile("multiple", multipleDefinitions)
   178  	assert.NoError(t, err)
   179  	err = repo.AddFile("dependant", dependantTemplate)
   180  	assert.NoError(t, err)
   181  
   182  	templ, err := repo.Get("dependant")
   183  	assert.Nil(t, err)
   184  
   185  	err = templ.Execute(&b, nil)
   186  
   187  	assert.Nil(t, err)
   188  
   189  	assert.Equal(t, "T1D1", b.String())
   190  
   191  }
   192  
   193  func TestTemplates_RepoRecursiveTemplates(t *testing.T) {
   194  
   195  	var b bytes.Buffer
   196  	repo := NewRepository(nil)
   197  
   198  	err := repo.AddFile("c1", cirularDeps1)
   199  	assert.NoError(t, err)
   200  	err = repo.AddFile("c2", cirularDeps2)
   201  	assert.NoError(t, err)
   202  
   203  	templ, err := repo.Get("c1")
   204  	assert.Nil(t, err)
   205  	data := testData{
   206  		Name: "Root",
   207  		Children: []testData{
   208  			{Recurse: false},
   209  		},
   210  	}
   211  	expected := `Root: Children`
   212  	err = templ.Execute(&b, data)
   213  
   214  	assert.Nil(t, err)
   215  
   216  	assert.Equal(t, expected, b.String())
   217  
   218  	data = testData{
   219  		Name: "Root",
   220  		Children: []testData{
   221  			{Name: "Child1", Recurse: true, Children: []testData{{Name: "Child2"}}},
   222  		},
   223  	}
   224  
   225  	b.Reset()
   226  
   227  	expected = `Root: Child1: Children`
   228  
   229  	err = templ.Execute(&b, data)
   230  
   231  	assert.Nil(t, err)
   232  
   233  	assert.Equal(t, expected, b.String())
   234  
   235  	data = testData{
   236  		Name: "Root",
   237  		Children: []testData{
   238  			{Name: "Child1", Recurse: false, Children: []testData{{Name: "Child2"}}},
   239  		},
   240  	}
   241  
   242  	b.Reset()
   243  
   244  	expected = `Root: Children`
   245  
   246  	err = templ.Execute(&b, data)
   247  
   248  	assert.Nil(t, err)
   249  
   250  	assert.Equal(t, expected, b.String())
   251  }
   252  
   253  // Test that definitions are available to templates
   254  // TODO: should test also with the codeGenApp context
   255  
   256  // Test copyright definition
   257  func TestTemplates_DefinitionCopyright(t *testing.T) {
   258  	log.SetOutput(os.Stdout)
   259  
   260  	repo := NewRepository(nil)
   261  
   262  	err := repo.AddFile("copyright", copyright)
   263  	assert.NoError(t, err)
   264  
   265  	templ, err := repo.Get("copyright")
   266  	assert.Nil(t, err)
   267  
   268  	opts := opts()
   269  	opts.Copyright = "My copyright clause"
   270  	expected := opts.Copyright
   271  
   272  	// executes template against model definitions
   273  	genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts)
   274  	assert.Nil(t, err)
   275  
   276  	rendered := bytes.NewBuffer(nil)
   277  	err = templ.Execute(rendered, genModel)
   278  	assert.Nil(t, err)
   279  
   280  	assert.Equal(t, expected, rendered.String())
   281  
   282  	// executes template against operations definitions
   283  	genOperation, err := getOperationEnvironment("get", "/media/search", "../fixtures/codegen/instagram.yml", opts)
   284  	assert.Nil(t, err)
   285  
   286  	rendered.Reset()
   287  
   288  	err = templ.Execute(rendered, genOperation)
   289  	assert.Nil(t, err)
   290  
   291  	assert.Equal(t, expected, rendered.String())
   292  
   293  }
   294  
   295  // Test TargetImportPath definition
   296  func TestTemplates_DefinitionTargetImportPath(t *testing.T) {
   297  	log.SetOutput(os.Stdout)
   298  
   299  	repo := NewRepository(nil)
   300  
   301  	err := repo.AddFile("targetimportpath", targetImportPath)
   302  	assert.NoError(t, err)
   303  
   304  	templ, err := repo.Get("targetimportpath")
   305  	assert.Nil(t, err)
   306  
   307  	opts := opts()
   308  	// Non existing target would panic: to be tested too, but in another module
   309  	opts.Target = "../fixtures"
   310  	var expected = "github.com/go-swagger/go-swagger/fixtures"
   311  
   312  	// executes template against model definitions
   313  	genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts)
   314  	assert.Nil(t, err)
   315  
   316  	rendered := bytes.NewBuffer(nil)
   317  	err = templ.Execute(rendered, genModel)
   318  	assert.Nil(t, err)
   319  
   320  	assert.Equal(t, expected, rendered.String())
   321  
   322  	// executes template against operations definitions
   323  	genOperation, err := getOperationEnvironment("get", "/media/search", "../fixtures/codegen/instagram.yml", opts)
   324  	assert.Nil(t, err)
   325  
   326  	rendered.Reset()
   327  
   328  	err = templ.Execute(rendered, genOperation)
   329  	assert.Nil(t, err)
   330  
   331  	assert.Equal(t, expected, rendered.String())
   332  
   333  }
   334  
   335  // Simulates a definition environment for model templates
   336  func getModelEnvironment(spec string, opts *GenOpts) (*GenDefinition, error) {
   337  	// Don't want stderr output to pollute CI
   338  	log.SetOutput(ioutil.Discard)
   339  	defer log.SetOutput(os.Stdout)
   340  
   341  	specDoc, err := loads.Spec("../fixtures/codegen/todolist.models.yml")
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	definitions := specDoc.Spec().Definitions
   346  
   347  	for k, schema := range definitions {
   348  		genModel, err := makeGenDefinition(k, "models", schema, specDoc, opts)
   349  		if err != nil {
   350  			return nil, err
   351  		}
   352  		// One is enough
   353  		return genModel, nil
   354  	}
   355  	return nil, nil
   356  }
   357  
   358  // Simulates a definition environment for operation templates
   359  func getOperationEnvironment(operation string, path string, spec string, opts *GenOpts) (*GenOperation, error) {
   360  	// Don't want stderr output to pollute CI
   361  	log.SetOutput(ioutil.Discard)
   362  	defer log.SetOutput(os.Stdout)
   363  
   364  	b, err := methodPathOpBuilder(operation, path, spec)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  	b.GenOpts = opts
   369  	g, err := b.MakeOperation()
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	return &g, nil
   374  }
   375  
   376  // Exercises FuncMap
   377  // Just running basic tests to make sure the function map works and all functions are available as expected.
   378  // More complete unit tests are provided by go-openapi/swag.
   379  // NOTE: We note that functions StripPackage() and DropPackage() behave the same way... and StripPackage()
   380  // function is not sensitive to its second arg... Probably not what was intended in the first place but not
   381  // blocking anyone for now.
   382  func TestTemplates_FuncMap(t *testing.T) {
   383  	log.SetOutput(os.Stdout)
   384  
   385  	err := templates.AddFile("functpl", funcTpl)
   386  	if assert.NoError(t, err) {
   387  		templ, err := templates.Get("functpl")
   388  		if assert.Nil(t, err) {
   389  			opts := opts()
   390  			// executes template against model definitions
   391  			genModel, err := getModelEnvironment("../fixtures/codegen/todolist.models.yml", opts)
   392  			if assert.Nil(t, err) {
   393  				rendered := bytes.NewBuffer(nil)
   394  				err = templ.Execute(rendered, genModel)
   395  				if assert.Nil(t, err) {
   396  					assert.Contains(t, rendered.String(), "Pascalize=WeArePoniesOfTheRoundTable\n")
   397  					assert.Contains(t, rendered.String(), "Snakize=we_are_ponies_of_the_round_table\n")
   398  					assert.Contains(t, rendered.String(), "Humanize=we are ponies of the round table\n")
   399  					assert.Contains(t, rendered.String(), "PluralizeFirstWord=ponies of the round table\n")
   400  					assert.Contains(t, rendered.String(), "PluralizeFirstOfOneWord=dwarves\n")
   401  					assert.Contains(t, rendered.String(), "PluralizeFirstOfNoWord=\n")
   402  					assert.Contains(t, rendered.String(), "StripPackage=suffix\n")
   403  					assert.Contains(t, rendered.String(), "StripNoPackage=suffix\n")
   404  					assert.Contains(t, rendered.String(), "StripEmptyPackage=\n")
   405  					assert.Contains(t, rendered.String(), "DropPackage=suffix\n")
   406  					assert.Contains(t, rendered.String(), "DropNoPackage=suffix\n")
   407  					assert.Contains(t, rendered.String(), "DropEmptyPackage=\n")
   408  					assert.Contains(t, rendered.String(), "DropEmptyPackage=\n")
   409  					assert.Contains(t, rendered.String(), "ImportRuntime=true\n")
   410  					assert.Contains(t, rendered.String(), "DoNotImport=false\n")
   411  					assert.Contains(t, rendered.String(), "PadSurround1=-,-,-,padme,-,-,-,-,-,-,-,-\n")
   412  					assert.Contains(t, rendered.String(), "PadSurround2=padme,-,-,-,-,-,-,-,-,-,-,-\n")
   413  					assert.Contains(t, rendered.String(), "Json=[\"github.com/go-openapi/errors\",\"github.com/go-openapi/runtime\",\"github.com/go-openapi/swag\",\"github.com/go-openapi/validate\"]")
   414  					assert.Contains(t, rendered.String(), "\"TargetImportPath\": \"github.com/go-swagger/go-swagger/generator\"")
   415  					assert.Contains(t, rendered.String(), "Snakize1=ending_in_os_name_linux_swagger\n")
   416  					assert.Contains(t, rendered.String(), "Snakize2=ending_in_arch_name_linux_amd64_swagger\n")
   417  					assert.Contains(t, rendered.String(), "Snakize3=ending_in_test_swagger\n")
   418  					assert.Contains(t, rendered.String(), "toPackage1=a/b-c/d_e\n")
   419  					assert.Contains(t, rendered.String(), "toPackage2=a.a/b_c/d_e\n")
   420  					assert.Contains(t, rendered.String(), "toPackage3=d_e\n")
   421  					assert.Contains(t, rendered.String(), "toPackage4=d_e\n")
   422  					assert.Contains(t, rendered.String(), "toPackageName=f_g\n")
   423  					assert.Contains(t, rendered.String(), "PascalizeSpecialChar1=Plus1\n")
   424  					assert.Contains(t, rendered.String(), "PascalizeSpecialChar2=Minus1\n")
   425  					assert.Contains(t, rendered.String(), "PascalizeSpecialChar3=Nr1\n")
   426  					assert.Contains(t, rendered.String(), "PascalizeSpecialChar4=Minus\n")
   427  					assert.Contains(t, rendered.String(), "PascalizeSpecialChar5=Plus\n")
   428  				}
   429  			}
   430  		}
   431  	}
   432  }
   433  
   434  // AddFile() global package function (protected vs unprotected)
   435  // Mostly unused in tests, since the Repository.AddFile()
   436  // is generally preferred.
   437  func TestTemplates_AddFile(t *testing.T) {
   438  	log.SetOutput(os.Stdout)
   439  
   440  	// unprotected
   441  	err := AddFile("functpl", funcTpl)
   442  	if assert.NoError(t, err) {
   443  		_, erg := templates.Get("functpl")
   444  		assert.Nil(t, erg)
   445  	}
   446  	// protected
   447  	err = AddFile("schemabody", funcTpl)
   448  	assert.Error(t, err)
   449  	assert.Contains(t, err.Error(), "cannot overwrite protected template")
   450  }
   451  
   452  // Test LoadDir
   453  func TestTemplates_LoadDir(t *testing.T) {
   454  	log.SetOutput(os.Stdout)
   455  
   456  	// Fails
   457  	err := templates.LoadDir("")
   458  	assert.Error(t, err)
   459  	assert.Contains(t, err.Error(), "could not complete")
   460  
   461  	// Fails again (from any dir?)
   462  	err = templates.LoadDir("templates")
   463  	assert.Error(t, err)
   464  	assert.Contains(t, err.Error(), "cannot overwrite protected template")
   465  
   466  	// TODO: success case
   467  	// To force a success, we need to empty the global list of protected
   468  	// templates...
   469  	origProtectedTemplates := protectedTemplates
   470  
   471  	defer func() {
   472  		// Restore variable initialized with package
   473  		protectedTemplates = origProtectedTemplates
   474  	}()
   475  
   476  	protectedTemplates = make(map[string]bool)
   477  	repo := NewRepository(FuncMap)
   478  	err = repo.LoadDir("templates")
   479  	assert.NoError(t, err)
   480  }
   481  
   482  // Test LoadDir
   483  func TestTemplates_SetAllowOverride(t *testing.T) {
   484  	log.SetOutput(os.Stdout)
   485  
   486  	// adding protected file with allowOverride set to false fails
   487  	templates.SetAllowOverride(false)
   488  	err := templates.AddFile("schemabody", "some data")
   489  	assert.Contains(t, err.Error(), "cannot overwrite protected template schemabody")
   490  
   491  	// adding protected file with allowOverride set to true should not fail
   492  	templates.SetAllowOverride(true)
   493  	err = templates.AddFile("schemabody", "some data")
   494  	assert.NoError(t, err)
   495  }
   496  
   497  // Test LoadContrib
   498  func TestTemplates_LoadContrib(t *testing.T) {
   499  	tests := []struct {
   500  		name      string
   501  		template  string
   502  		wantError bool
   503  	}{
   504  		{
   505  			name:      "None_existing_contributor_tempalte",
   506  			template:  "NonExistingContributorTemplate",
   507  			wantError: true,
   508  		},
   509  		{
   510  			name:      "Existing_contributor",
   511  			template:  "stratoscale",
   512  			wantError: false,
   513  		},
   514  	}
   515  
   516  	for _, tt := range tests {
   517  		t.Run(tt.name, func(t *testing.T) {
   518  			err := templates.LoadContrib(tt.template)
   519  			if tt.wantError {
   520  				assert.Error(t, err)
   521  			} else {
   522  				assert.NoError(t, err)
   523  			}
   524  		})
   525  	}
   526  }
   527  
   528  // TODO: test error case in LoadDefaults()
   529  // test DumpTemplates()
   530  func TestTemplates_DumpTemplates(t *testing.T) {
   531  	buf := bytes.NewBuffer(nil)
   532  	log.SetOutput(buf)
   533  	defer func() {
   534  		log.SetOutput(os.Stdout)
   535  	}()
   536  
   537  	templates.DumpTemplates()
   538  	assert.NotEmpty(t, buf)
   539  	// Sample output
   540  	assert.Contains(t, buf.String(), "## tupleSerializer")
   541  	assert.Contains(t, buf.String(), "Defined in `tupleserializer.gotmpl`")
   542  	assert.Contains(t, buf.String(), "####requires \n - schemaType")
   543  	//fmt.Println(buf)
   544  }
   545  
   546  // Go literal initializer func
   547  func TestTemplates_GoSliceInitializer(t *testing.T) {
   548  	a0 := []interface{}{"a", "b"}
   549  	res, err := goSliceInitializer(a0)
   550  	assert.NoError(t, err)
   551  	assert.Equal(t, `{"a","b",}`, res)
   552  
   553  	a1 := []interface{}{[]interface{}{"a", "b"}, []interface{}{"c", "d"}}
   554  	res, err = goSliceInitializer(a1)
   555  	assert.NoError(t, err)
   556  	assert.Equal(t, `{{"a","b",},{"c","d",},}`, res)
   557  
   558  	a2 := map[string]interface{}{"a": "y", "b": "z"}
   559  	res, err = goSliceInitializer(a2)
   560  	assert.NoError(t, err)
   561  	assert.Equal(t, `{"a":"y","b":"z",}`, res)
   562  }