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 }