github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/writer/yaml_test.go (about)

     1  package writer_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/ghodss/yaml"
    10  
    11  	"github.com/Masterminds/semver"
    12  	"github.com/heroku/color"
    13  	"github.com/sclevine/spec"
    14  	"github.com/sclevine/spec/report"
    15  
    16  	"github.com/buildpacks/lifecycle/api"
    17  
    18  	"github.com/YousefHaggyHeroku/pack"
    19  	pubbldr "github.com/YousefHaggyHeroku/pack/builder"
    20  	"github.com/YousefHaggyHeroku/pack/internal/builder"
    21  	"github.com/YousefHaggyHeroku/pack/internal/builder/writer"
    22  	"github.com/YousefHaggyHeroku/pack/internal/config"
    23  	"github.com/YousefHaggyHeroku/pack/internal/dist"
    24  	ilogging "github.com/YousefHaggyHeroku/pack/internal/logging"
    25  	h "github.com/YousefHaggyHeroku/pack/testhelpers"
    26  )
    27  
    28  func TestYAML(t *testing.T) {
    29  	color.Disable(true)
    30  	defer color.Disable(false)
    31  	spec.Run(t, "Builder Writer", testYAML, spec.Parallel(), spec.Report(report.Terminal{}))
    32  }
    33  
    34  func testYAML(t *testing.T, when spec.G, it spec.S) {
    35  	const (
    36  		expectedRemoteRunImages = `    run_images:
    37          - name: first/local
    38            user_configured: true
    39          - name: second/local
    40            user_configured: true
    41          - name: some/run-image
    42          - name: first/default
    43          - name: second/default`
    44  
    45  		expectedLocalRunImages = `    run_images:
    46          - name: first/local
    47            user_configured: true
    48          - name: second/local
    49            user_configured: true
    50          - name: some/run-image
    51          - name: first/local-default
    52          - name: second/local-default`
    53  
    54  		expectedBuildpacks = `    buildpacks:
    55          - id: test.top.nested
    56            version: test.top.nested.version
    57          - id: test.nested
    58            homepage: http://geocities.com/top-bp
    59          - id: test.bp.one
    60            version: test.bp.one.version
    61            homepage: http://geocities.com/cool-bp
    62          - id: test.bp.two
    63            version: test.bp.two.version
    64          - id: test.bp.three
    65            version: test.bp.three.version`
    66  
    67  		expectedDetectionOrder = `    detection_order:
    68          - buildpacks:
    69              - id: test.top.nested
    70                version: test.top.nested.version
    71                buildpacks:
    72                  - id: test.nested
    73                    homepage: http://geocities.com/top-bp
    74                    buildpacks:
    75                      - id: test.bp.one
    76                        version: test.bp.one.version
    77                        homepage: http://geocities.com/cool-bp
    78                        optional: true
    79                  - id: test.bp.three
    80                    version: test.bp.three.version
    81                    optional: true
    82                  - id: test.nested.two
    83                    version: test.nested.two.version
    84                    buildpacks:
    85                      - id: test.bp.one
    86                        version: test.bp.one.version
    87                        homepage: http://geocities.com/cool-bp
    88                        optional: true
    89                        cyclic: true
    90              - id: test.bp.two
    91                version: test.bp.two.version
    92                optional: true
    93          - id: test.bp.three
    94            version: test.bp.three.version`
    95  		expectedStackWithMixins = `    stack:
    96          id: test.stack.id
    97          mixins:
    98              - mixin1
    99              - mixin2
   100              - build:mixin3
   101              - build:mixin4`
   102  	)
   103  
   104  	var (
   105  		assert = h.NewAssertionManager(t)
   106  		outBuf bytes.Buffer
   107  
   108  		remoteInfo *pack.BuilderInfo
   109  		localInfo  *pack.BuilderInfo
   110  
   111  		expectedRemoteInfo = fmt.Sprintf(`remote_info:
   112      description: Some remote description
   113      created_by:
   114          name: Pack CLI
   115          version: 1.2.3
   116      stack:
   117          id: test.stack.id
   118      lifecycle:
   119          version: 6.7.8
   120          buildpack_apis:
   121              deprecated: []
   122              supported:
   123                  - "1.2"
   124                  - "2.3"
   125          platform_apis:
   126              deprecated:
   127                  - "0.1"
   128                  - "1.2"
   129              supported:
   130                  - "4.5"
   131  %s
   132  %s
   133  %s`, expectedRemoteRunImages, expectedBuildpacks, expectedDetectionOrder)
   134  
   135  		expectedLocalInfo = fmt.Sprintf(`local_info:
   136      description: Some local description
   137      created_by:
   138          name: Pack CLI
   139          version: 4.5.6
   140      stack:
   141          id: test.stack.id
   142      lifecycle:
   143          version: 4.5.6
   144          buildpack_apis:
   145              deprecated:
   146                  - "4.5"
   147                  - "6.7"
   148              supported:
   149                  - "8.9"
   150                  - "10.11"
   151          platform_apis:
   152              deprecated: []
   153              supported:
   154                  - "7.8"
   155  %s
   156  %s
   157  %s`, expectedLocalRunImages, expectedBuildpacks, expectedDetectionOrder)
   158  
   159  		expectedPrettifiedYAML = fmt.Sprintf(`    builder_name: test-builder
   160      trusted: false
   161      default: false
   162  %s
   163  %s`, expectedRemoteInfo, expectedLocalInfo)
   164  	)
   165  
   166  	when("Print", func() {
   167  		it.Before(func() {
   168  			remoteInfo = &pack.BuilderInfo{
   169  				Description:     "Some remote description",
   170  				Stack:           "test.stack.id",
   171  				Mixins:          []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"},
   172  				RunImage:        "some/run-image",
   173  				RunImageMirrors: []string{"first/default", "second/default"},
   174  				Buildpacks:      buildpacks,
   175  				Order:           order,
   176  				BuildpackLayers: dist.BuildpackLayers{},
   177  				Lifecycle: builder.LifecycleDescriptor{
   178  					Info: builder.LifecycleInfo{
   179  						Version: &builder.Version{
   180  							Version: *semver.MustParse("6.7.8"),
   181  						},
   182  					},
   183  					APIs: builder.LifecycleAPIs{
   184  						Buildpack: builder.APIVersions{
   185  							Deprecated: nil,
   186  							Supported:  builder.APISet{api.MustParse("1.2"), api.MustParse("2.3")},
   187  						},
   188  						Platform: builder.APIVersions{
   189  							Deprecated: builder.APISet{api.MustParse("0.1"), api.MustParse("1.2")},
   190  							Supported:  builder.APISet{api.MustParse("4.5")},
   191  						},
   192  					},
   193  				},
   194  				CreatedBy: builder.CreatorMetadata{
   195  					Name:    "Pack CLI",
   196  					Version: "1.2.3",
   197  				},
   198  			}
   199  
   200  			localInfo = &pack.BuilderInfo{
   201  				Description:     "Some local description",
   202  				Stack:           "test.stack.id",
   203  				Mixins:          []string{"mixin1", "mixin2", "build:mixin3", "build:mixin4"},
   204  				RunImage:        "some/run-image",
   205  				RunImageMirrors: []string{"first/local-default", "second/local-default"},
   206  				Buildpacks:      buildpacks,
   207  				Order:           order,
   208  				BuildpackLayers: dist.BuildpackLayers{},
   209  				Lifecycle: builder.LifecycleDescriptor{
   210  					Info: builder.LifecycleInfo{
   211  						Version: &builder.Version{
   212  							Version: *semver.MustParse("4.5.6"),
   213  						},
   214  					},
   215  					APIs: builder.LifecycleAPIs{
   216  						Buildpack: builder.APIVersions{
   217  							Deprecated: builder.APISet{api.MustParse("4.5"), api.MustParse("6.7")},
   218  							Supported:  builder.APISet{api.MustParse("8.9"), api.MustParse("10.11")},
   219  						},
   220  						Platform: builder.APIVersions{
   221  							Deprecated: nil,
   222  							Supported:  builder.APISet{api.MustParse("7.8")},
   223  						},
   224  					},
   225  				},
   226  				CreatedBy: builder.CreatorMetadata{
   227  					Name:    "Pack CLI",
   228  					Version: "4.5.6",
   229  				},
   230  			}
   231  		})
   232  
   233  		it("prints both local remote builders as valid YAML", func() {
   234  			yamlWriter := writer.NewYAML()
   235  
   236  			logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   237  			err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo)
   238  			assert.Nil(err)
   239  
   240  			prettyYAML, err := validYAML(outBuf)
   241  			assert.Nil(err)
   242  
   243  			assert.Contains(prettyYAML, expectedPrettifiedYAML)
   244  		})
   245  
   246  		when("builder doesn't exist locally or remotely", func() {
   247  			it("returns an error", func() {
   248  				yamlWriter := writer.NewYAML()
   249  
   250  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   251  				err := yamlWriter.Print(logger, localRunImages, nil, nil, nil, nil, sharedBuilderInfo)
   252  				assert.ErrorWithMessage(err, "unable to find builder 'test-builder' locally or remotely")
   253  			})
   254  		})
   255  
   256  		when("builder doesn't exist locally", func() {
   257  			it("shows null for local builder, and normal output for remote", func() {
   258  				yamlWriter := writer.NewYAML()
   259  
   260  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   261  				err := yamlWriter.Print(logger, localRunImages, nil, remoteInfo, nil, nil, sharedBuilderInfo)
   262  				assert.Nil(err)
   263  
   264  				prettyYAML, err := validYAML(outBuf)
   265  				assert.Nil(err)
   266  
   267  				assert.ContainsYAML(prettyYAML, `local_info: null`)
   268  				assert.ContainsYAML(prettyYAML, expectedRemoteInfo)
   269  			})
   270  		})
   271  
   272  		when("builder doesn't exist remotely", func() {
   273  			it("shows null for remote builder, and normal output for local", func() {
   274  				yamlWriter := writer.NewYAML()
   275  
   276  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   277  				err := yamlWriter.Print(logger, localRunImages, localInfo, nil, nil, nil, sharedBuilderInfo)
   278  				assert.Nil(err)
   279  
   280  				prettyYAML, err := validYAML(outBuf)
   281  				assert.Nil(err)
   282  
   283  				assert.ContainsYAML(prettyYAML, `remote_info: null`)
   284  				assert.ContainsYAML(prettyYAML, expectedLocalInfo)
   285  			})
   286  		})
   287  
   288  		when("localErr is an error", func() {
   289  			it("returns the error, and doesn't write any yaml output", func() {
   290  				expectedErr := errors.New("failed to retrieve local info")
   291  
   292  				yamlWriter := writer.NewYAML()
   293  
   294  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   295  				err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, expectedErr, nil, sharedBuilderInfo)
   296  				assert.ErrorWithMessage(err, "preparing output for 'test-builder': failed to retrieve local info")
   297  
   298  				assert.Equal(outBuf.String(), "")
   299  			})
   300  		})
   301  
   302  		when("remoteErr is an error", func() {
   303  			it("returns the error, and doesn't write any yaml output", func() {
   304  				expectedErr := errors.New("failed to retrieve remote info")
   305  
   306  				yamlWriter := writer.NewYAML()
   307  
   308  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
   309  				err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, expectedErr, sharedBuilderInfo)
   310  				assert.ErrorWithMessage(err, "preparing output for 'test-builder': failed to retrieve remote info")
   311  
   312  				assert.Equal(outBuf.String(), "")
   313  			})
   314  		})
   315  
   316  		when("logger is verbose", func() {
   317  			it("displays mixins associated with the stack", func() {
   318  				yamlWriter := writer.NewYAML()
   319  
   320  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose())
   321  				err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo)
   322  				assert.Nil(err)
   323  
   324  				prettifiedYAML, err := validYAML(outBuf)
   325  				assert.Nil(err)
   326  
   327  				assert.ContainsYAML(prettifiedYAML, expectedStackWithMixins)
   328  			})
   329  		})
   330  
   331  		when("no run images are specified", func() {
   332  			it("displays run images as empty list", func() {
   333  				localInfo.RunImage = ""
   334  				localInfo.RunImageMirrors = []string{}
   335  				remoteInfo.RunImage = ""
   336  				remoteInfo.RunImageMirrors = []string{}
   337  				emptyLocalRunImages := []config.RunImage{}
   338  
   339  				yamlWriter := writer.NewYAML()
   340  
   341  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose())
   342  				err := yamlWriter.Print(logger, emptyLocalRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo)
   343  				assert.Nil(err)
   344  
   345  				prettifiedYAML, err := validYAML(outBuf)
   346  				assert.Nil(err)
   347  
   348  				assert.ContainsYAML(prettifiedYAML, `run_images: []`)
   349  			})
   350  		})
   351  
   352  		when("no buildpacks are specified", func() {
   353  			it("displays buildpacks as empty list", func() {
   354  				localInfo.Buildpacks = []dist.BuildpackInfo{}
   355  				remoteInfo.Buildpacks = []dist.BuildpackInfo{}
   356  
   357  				yamlWriter := writer.NewYAML()
   358  
   359  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose())
   360  				err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo)
   361  				assert.Nil(err)
   362  
   363  				prettifiedYAML, err := validYAML(outBuf)
   364  				assert.Nil(err)
   365  
   366  				assert.ContainsYAML(prettifiedYAML, `buildpacks: []`)
   367  			})
   368  		})
   369  
   370  		when("no detection order is specified", func() {
   371  			it("displays detection order as empty list", func() {
   372  				localInfo.Order = pubbldr.DetectionOrder{}
   373  				remoteInfo.Order = pubbldr.DetectionOrder{}
   374  
   375  				yamlWriter := writer.NewYAML()
   376  
   377  				logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose())
   378  				err := yamlWriter.Print(logger, localRunImages, localInfo, remoteInfo, nil, nil, sharedBuilderInfo)
   379  				assert.Nil(err)
   380  
   381  				prettifiedYAML, err := validYAML(outBuf)
   382  				assert.Nil(err)
   383  
   384  				assert.ContainsYAML(prettifiedYAML, `detection_order: []`)
   385  			})
   386  		})
   387  	})
   388  }
   389  
   390  func validYAML(source bytes.Buffer) (string, error) {
   391  	err := yaml.Unmarshal(source.Bytes(), &struct{}{})
   392  	if err != nil {
   393  		return "", fmt.Errorf("failed to unmarshal to yaml: %w", err)
   394  	}
   395  
   396  	return source.String(), nil
   397  }