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