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