github.com/jdolitsky/cnab-go@v0.7.1-beta1/bundle/bundle_test.go (about)

     1  package bundle
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"testing"
     7  
     8  	"github.com/deislabs/cnab-go/bundle/definition"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	yaml "gopkg.in/yaml.v2"
    13  )
    14  
    15  func TestReadTopLevelProperties(t *testing.T) {
    16  	json := `{
    17  		"schemaVersion": "v1.0.0-WD",
    18  		"name": "foo",
    19  		"version": "1.0",
    20  		"images": {},
    21  		"credentials": {},
    22  		"custom": {}
    23  	}`
    24  	bundle, err := Unmarshal([]byte(json))
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	assert.Equal(t, "v1.0.0-WD", bundle.SchemaVersion)
    29  	if bundle.Name != "foo" {
    30  		t.Errorf("Expected name 'foo', got '%s'", bundle.Name)
    31  	}
    32  	if bundle.Version != "1.0" {
    33  		t.Errorf("Expected version '1.0', got '%s'", bundle.Version)
    34  	}
    35  	if len(bundle.Images) != 0 {
    36  		t.Errorf("Expected no images, got %d", len(bundle.Images))
    37  	}
    38  	if len(bundle.Credentials) != 0 {
    39  		t.Errorf("Expected no credentials, got %d", len(bundle.Credentials))
    40  	}
    41  	if len(bundle.Custom) != 0 {
    42  		t.Errorf("Expected no custom extensions, got %d", len(bundle.Custom))
    43  	}
    44  }
    45  
    46  func TestReadImageProperties(t *testing.T) {
    47  	data, err := ioutil.ReadFile("../testdata/bundles/foo.json")
    48  	if err != nil {
    49  		t.Errorf("cannot read bundle file: %v", err)
    50  	}
    51  
    52  	bundle, err := Unmarshal(data)
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	if len(bundle.Images) != 2 {
    57  		t.Errorf("Expected 2 images, got %d", len(bundle.Images))
    58  	}
    59  	image1 := bundle.Images["image1"]
    60  	if image1.Description != "image1" {
    61  		t.Errorf("Expected description 'image1', got '%s'", image1.Description)
    62  	}
    63  	if image1.Image != "urn:image1uri" {
    64  		t.Errorf("Expected Image 'urn:image1uri', got '%s'", image1.Image)
    65  	}
    66  }
    67  
    68  func TestReadCredentialProperties(t *testing.T) {
    69  	data, err := ioutil.ReadFile("../testdata/bundles/foo.json")
    70  	if err != nil {
    71  		t.Errorf("cannot read bundle file: %v", err)
    72  	}
    73  
    74  	bundle, err := Unmarshal(data)
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	if len(bundle.Credentials) != 3 {
    79  		t.Errorf("Expected 3 credentials, got %d", len(bundle.Credentials))
    80  	}
    81  	f := bundle.Credentials["foo"]
    82  	if f.Path != "pfoo" {
    83  		t.Errorf("Expected path 'pfoo', got '%s'", f.Path)
    84  	}
    85  	if f.EnvironmentVariable != "" {
    86  		t.Errorf("Expected env '', got '%s'", f.EnvironmentVariable)
    87  	}
    88  	b := bundle.Credentials["bar"]
    89  	if b.Path != "" {
    90  		t.Errorf("Expected path '', got '%s'", b.Path)
    91  	}
    92  	if b.EnvironmentVariable != "ebar" {
    93  		t.Errorf("Expected env 'ebar', got '%s'", b.EnvironmentVariable)
    94  	}
    95  	q := bundle.Credentials["quux"]
    96  	if q.Path != "pquux" {
    97  		t.Errorf("Expected path 'pquux', got '%s'", q.Path)
    98  	}
    99  	if q.EnvironmentVariable != "equux" {
   100  		t.Errorf("Expected env 'equux', got '%s'", q.EnvironmentVariable)
   101  	}
   102  }
   103  
   104  func TestValuesOrDefaults(t *testing.T) {
   105  	is := assert.New(t)
   106  	vals := map[string]interface{}{
   107  		"port":    8080,
   108  		"host":    "localhost",
   109  		"enabled": true,
   110  	}
   111  	b := &Bundle{
   112  		Definitions: map[string]*definition.Schema{
   113  			"portType": {
   114  				Type:    "integer",
   115  				Default: 1234,
   116  			},
   117  			"hostType": {
   118  				Type:    "string",
   119  				Default: "locahost.localdomain",
   120  			},
   121  			"replicaCountType": {
   122  				Type:    "integer",
   123  				Default: 3,
   124  			},
   125  			"enabledType": {
   126  				Type:    "boolean",
   127  				Default: false,
   128  			},
   129  		},
   130  		Parameters: map[string]Parameter{
   131  			"port": {
   132  				Definition: "portType",
   133  			},
   134  			"host": {
   135  				Definition: "hostType",
   136  			},
   137  			"enabled": {
   138  				Definition: "enabledType",
   139  			},
   140  			"replicaCount": {
   141  				Definition: "replicaCountType",
   142  			},
   143  		},
   144  	}
   145  
   146  	vod, err := ValuesOrDefaults(vals, b)
   147  
   148  	is.NoError(err)
   149  	is.True(vod["enabled"].(bool))
   150  	is.Equal(vod["host"].(string), "localhost")
   151  	is.Equal(vod["port"].(int), 8080)
   152  	is.Equal(vod["replicaCount"].(int), 3)
   153  
   154  	// This should err out because of type problem
   155  	vals["replicaCount"] = "banana"
   156  	_, err = ValuesOrDefaults(vals, b)
   157  	is.Error(err)
   158  
   159  	// Check for panic when zero value Bundle is passed
   160  	_, err = ValuesOrDefaults(vals, &Bundle{})
   161  	is.NoError(err)
   162  }
   163  
   164  func TestValuesOrDefaults_NoParameter(t *testing.T) {
   165  	is := assert.New(t)
   166  	vals := map[string]interface{}{}
   167  	b := &Bundle{}
   168  	vod, err := ValuesOrDefaults(vals, b)
   169  	is.NoError(err)
   170  	is.Len(vod, 0)
   171  }
   172  
   173  func TestValuesOrDefaults_Required(t *testing.T) {
   174  	is := assert.New(t)
   175  	vals := map[string]interface{}{
   176  		"enabled": true,
   177  	}
   178  	b := &Bundle{
   179  		Definitions: map[string]*definition.Schema{
   180  			"minType": {
   181  				Type: "integer",
   182  			},
   183  			"enabledType": {
   184  				Type:    "boolean",
   185  				Default: false,
   186  			},
   187  		},
   188  		Parameters: map[string]Parameter{
   189  			"minimum": {
   190  				Definition: "minType",
   191  				Required:   true,
   192  			},
   193  			"enabled": {
   194  				Definition: "enabledType",
   195  			},
   196  		},
   197  	}
   198  
   199  	_, err := ValuesOrDefaults(vals, b)
   200  	is.Error(err)
   201  
   202  	// It is unclear what the outcome should be when the user supplies
   203  	// empty values on purpose. For now, we will assume those meet the
   204  	// minimum definition of "required", and that other rules will
   205  	// correct for empty values.
   206  	//
   207  	// Example: It makes perfect sense for a user to specify --set minimum=0
   208  	// and in so doing meet the requirement that a value be specified.
   209  	vals["minimum"] = 0
   210  	res, err := ValuesOrDefaults(vals, b)
   211  	is.NoError(err)
   212  	is.Equal(0, res["minimum"])
   213  }
   214  
   215  func TestValidateVersionTag(t *testing.T) {
   216  	is := assert.New(t)
   217  
   218  	img := InvocationImage{BaseImage{}}
   219  	b := Bundle{
   220  		Version:          "latest",
   221  		SchemaVersion:    "99.98",
   222  		InvocationImages: []InvocationImage{img},
   223  	}
   224  
   225  	err := b.Validate()
   226  	is.EqualError(err, "'latest' is not a valid bundle version")
   227  }
   228  
   229  func TestValidateSchemaVersion(t *testing.T) {
   230  	is := assert.New(t)
   231  
   232  	img := InvocationImage{BaseImage{}}
   233  	b := Bundle{
   234  		Version:          "0.1.0",
   235  		SchemaVersion:    "99.98",
   236  		InvocationImages: []InvocationImage{img},
   237  	}
   238  
   239  	err := b.Validate()
   240  	is.Nil(err, "valid bundle schema failed to validate")
   241  }
   242  
   243  func TestValidateSchemaVersionWithPrefix(t *testing.T) {
   244  	is := assert.New(t)
   245  
   246  	img := InvocationImage{BaseImage{}}
   247  	b := Bundle{
   248  		Version:          "0.1.0",
   249  		SchemaVersion:    "v99.98",
   250  		InvocationImages: []InvocationImage{img},
   251  	}
   252  
   253  	err := b.Validate()
   254  	is.Nil(err, "valid bundle schema failed to validate")
   255  }
   256  
   257  func TestValidateMissingSchemaVersion(t *testing.T) {
   258  	is := assert.New(t)
   259  
   260  	img := InvocationImage{BaseImage{}}
   261  	b := Bundle{
   262  		Version:          "0.1.0",
   263  		InvocationImages: []InvocationImage{img},
   264  	}
   265  
   266  	err := b.Validate()
   267  	is.EqualError(err, "invalid bundle schema version \"\": Invalid Semantic Version")
   268  }
   269  
   270  func TestValidateInvalidSchemaVersion(t *testing.T) {
   271  	is := assert.New(t)
   272  
   273  	img := InvocationImage{BaseImage{}}
   274  	b := Bundle{
   275  		Version:          "0.1.0",
   276  		SchemaVersion:    ".1",
   277  		InvocationImages: []InvocationImage{img},
   278  	}
   279  
   280  	err := b.Validate()
   281  	is.EqualError(err, "invalid bundle schema version \".1\": Invalid Semantic Version")
   282  }
   283  
   284  func TestValidateBundle_RequiresInvocationImage(t *testing.T) {
   285  	b := Bundle{
   286  		Name:          "bar",
   287  		SchemaVersion: "99.98",
   288  		Version:       "0.1.0",
   289  	}
   290  
   291  	err := b.Validate()
   292  	if err == nil {
   293  		t.Fatal("Validate should have failed because the bundle has no invocation images")
   294  	}
   295  
   296  	b.InvocationImages = append(b.InvocationImages, InvocationImage{})
   297  
   298  	err = b.Validate()
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  }
   303  
   304  func TestValidateRequiredExtensions(t *testing.T) {
   305  	is := assert.New(t)
   306  
   307  	img := InvocationImage{BaseImage{}}
   308  	b := Bundle{
   309  		Version:          "0.1.0",
   310  		SchemaVersion:    "99.98",
   311  		InvocationImages: []InvocationImage{img},
   312  		RequiredExtensions: []string{
   313  			"my.custom.extension",
   314  		},
   315  	}
   316  
   317  	// Verify the error when a required extension is not present in custom
   318  	err := b.Validate()
   319  	is.EqualError(err, "required extension 'my.custom.extension' is not defined in the Custom section of the bundle")
   320  
   321  	// Add corresponding entry in custom
   322  	b.Custom = map[string]interface{}{
   323  		"my.custom.extension": true,
   324  	}
   325  
   326  	err = b.Validate()
   327  	is.NoError(err)
   328  
   329  	// Add duplicate required extension
   330  	b.RequiredExtensions = append(b.RequiredExtensions, "my.custom.extension")
   331  
   332  	err = b.Validate()
   333  	is.EqualError(err, "required extension 'my.custom.extension' is already declared")
   334  }
   335  
   336  func TestReadCustomAndRequiredExtensions(t *testing.T) {
   337  	data, err := ioutil.ReadFile("../testdata/bundles/foo.json")
   338  	if err != nil {
   339  		t.Errorf("cannot read bundle file: %v", err)
   340  	}
   341  
   342  	bundle, err := Unmarshal(data)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  
   347  	if len(bundle.Custom) != 2 {
   348  		t.Errorf("Expected 2 custom extensions, got %d", len(bundle.Custom))
   349  	}
   350  
   351  	duffleExtI, ok := bundle.Custom["com.example.duffle-bag"]
   352  	if !ok {
   353  		t.Fatal("Expected the com.example.duffle-bag extension")
   354  	}
   355  	duffleExt, ok := duffleExtI.(map[string]interface{})
   356  	if !ok {
   357  		t.Fatalf("Expected the com.example.duffle-bag to be of type map[string]interface{} but got %T ", duffleExtI)
   358  	}
   359  	assert.Equal(t, "PNG", duffleExt["iconType"])
   360  	assert.Equal(t, "https://example.com/icon.png", duffleExt["icon"])
   361  
   362  	backupExtI, ok := bundle.Custom["com.example.backup-preferences"]
   363  	if !ok {
   364  		t.Fatal("Expected the com.example.backup-preferences extension")
   365  	}
   366  	backupExt, ok := backupExtI.(map[string]interface{})
   367  	if !ok {
   368  		t.Fatalf("Expected the com.example.backup-preferences to be of type map[string]interface{} but got %T ", backupExtI)
   369  	}
   370  	assert.Equal(t, true, backupExt["enabled"])
   371  	assert.Equal(t, "daily", backupExt["frequency"])
   372  
   373  	if len(bundle.RequiredExtensions) != 1 {
   374  		t.Errorf("Expected 1 required extension, got %d", len(bundle.RequiredExtensions))
   375  	}
   376  	assert.Equal(t, "com.example.duffle-bag", bundle.RequiredExtensions[0])
   377  }
   378  
   379  func TestOutputs_Marshall(t *testing.T) {
   380  	bundleJSON := `
   381  	{ 
   382  		"outputs":{ 
   383  		   "clientCert":{ 
   384  			  "contentEncoding":"base64",
   385  			  "contentMediaType":"application/x-x509-user-cert",
   386  			  "path":"/cnab/app/outputs/clientCert",
   387  			  "definition":"clientCert"
   388  		   },
   389  		   "hostName":{ 
   390  			  "applyTo":[ 
   391  				 "install"
   392  			  ],
   393  			  "description":"the hostname produced installing the bundle",
   394  			  "path":"/cnab/app/outputs/hostname",
   395  			  "definition":"hostType"
   396  		   },
   397  		   "port":{ 
   398  			  "path":"/cnab/app/outputs/port",
   399  			  "definition":"portType"
   400  		   }
   401  		}
   402  	 }`
   403  
   404  	bundle, err := Unmarshal([]byte(bundleJSON))
   405  	assert.NoError(t, err, "should have unmarshalled the bundle")
   406  	require.NotNil(t, bundle.Outputs, "test must fail, not outputs found")
   407  	assert.Equal(t, 3, len(bundle.Outputs))
   408  
   409  	clientCert, ok := bundle.Outputs["clientCert"]
   410  	require.True(t, ok, "expected clientCert to exist as an output")
   411  	assert.Equal(t, "clientCert", clientCert.Definition)
   412  	assert.Equal(t, "/cnab/app/outputs/clientCert", clientCert.Path, "clientCert path was not the expected value")
   413  
   414  	hostName, ok := bundle.Outputs["hostName"]
   415  	require.True(t, ok, "expected hostname to exist as an output")
   416  	assert.Equal(t, "hostType", hostName.Definition)
   417  	assert.Equal(t, "/cnab/app/outputs/hostname", hostName.Path, "hostName path was not the expected value")
   418  
   419  	port, ok := bundle.Outputs["port"]
   420  	require.True(t, ok, "expected port to exist as an output")
   421  	assert.Equal(t, "portType", port.Definition)
   422  	assert.Equal(t, "/cnab/app/outputs/port", port.Path, "port path was not the expected value")
   423  }
   424  
   425  var exampleCred = Credential{
   426  	Description: "a password",
   427  	Location: Location{
   428  		EnvironmentVariable: "PASSWORD",
   429  		Path:                "/cnab/app/path",
   430  	},
   431  }
   432  
   433  var exampleBundle = &Bundle{
   434  	SchemaVersion: "v1.0.0-WD",
   435  	Name:          "testBundle",
   436  	Description:   "something",
   437  	Version:       "1.0",
   438  	License:       "MIT License",
   439  	Credentials: map[string]Credential{
   440  		"password": exampleCred,
   441  	},
   442  	Images: map[string]Image{
   443  		"server": {
   444  			BaseImage: BaseImage{
   445  				Image:     "nginx:1.0",
   446  				ImageType: "docker",
   447  			},
   448  			Description: "complicated",
   449  		},
   450  	},
   451  	InvocationImages: []InvocationImage{
   452  		{
   453  			BaseImage: BaseImage{
   454  				Image:     "deislabs/invocation-image:1.0",
   455  				ImageType: "docker",
   456  				Labels: map[string]string{
   457  					"os": "Linux",
   458  				},
   459  			},
   460  		},
   461  	},
   462  	Definitions: map[string]*definition.Schema{
   463  		"portType": {
   464  			Type:    "integer",
   465  			Default: 1234,
   466  		},
   467  		"hostType": {
   468  			Type:    "string",
   469  			Default: "locahost.localdomain",
   470  		},
   471  		"replicaCountType": {
   472  			Type:    "integer",
   473  			Default: 3,
   474  		},
   475  		"enabledType": {
   476  			Type:    "boolean",
   477  			Default: false,
   478  		},
   479  		"clientCert": {
   480  			Type:            "string",
   481  			ContentEncoding: "base64",
   482  		},
   483  		"productKeyType": {
   484  			Type: "string",
   485  		},
   486  	},
   487  	Parameters: map[string]Parameter{
   488  		"port": {
   489  			Definition: "portType",
   490  			Destination: &Location{
   491  				EnvironmentVariable: "PORT",
   492  				Path:                "/path/to/port",
   493  			},
   494  			Required: true,
   495  		},
   496  		"host": {
   497  			Definition: "hostType",
   498  			Destination: &Location{
   499  				EnvironmentVariable: "HOST",
   500  			},
   501  			Required: true,
   502  		},
   503  		"enabled": {
   504  			Definition: "enabledType",
   505  			Destination: &Location{
   506  				EnvironmentVariable: "ENABLED",
   507  			},
   508  		},
   509  		"replicaCount": {
   510  			Definition: "replicaCountType",
   511  			Destination: &Location{
   512  				EnvironmentVariable: "REPLICA_COUNT",
   513  			},
   514  		},
   515  		"productKey": {
   516  			Definition: "productKeyType",
   517  			Destination: &Location{
   518  				EnvironmentVariable: "PRODUCT_KEY",
   519  			},
   520  		},
   521  	},
   522  	Outputs: map[string]Output{
   523  		"clientCert": {
   524  			Path:       "/cnab/app/outputs/blah",
   525  			Definition: "clientCert",
   526  		},
   527  	},
   528  }
   529  
   530  func TestBundleMarshallAllThings(t *testing.T) {
   531  	expectedJSON, err := ioutil.ReadFile("../testdata/bundles/canonical-bundle.json")
   532  	require.NoError(t, err, "couldn't read test data")
   533  
   534  	var buf bytes.Buffer
   535  
   536  	_, err = exampleBundle.WriteTo(&buf)
   537  	require.NoError(t, err, "test requires output")
   538  	assert.Equal(t, string(expectedJSON), buf.String(), "output should match expected canonical json")
   539  }
   540  
   541  func TestBundleYamlRoundtrip(t *testing.T) {
   542  	bytes, err := yaml.Marshal(exampleBundle)
   543  	require.NoError(t, err, "should have been able to yaml.Marshal bundle")
   544  
   545  	expectedYAML, err := ioutil.ReadFile("../testdata/bundles/bundle.yaml")
   546  	require.NoError(t, err, "couldn't read test data")
   547  
   548  	assert.Equal(t, string(expectedYAML), string(bytes), "marshaled bytes should match expected yaml representation")
   549  
   550  	var roundTripBun Bundle
   551  	err = yaml.UnmarshalStrict(bytes, &roundTripBun)
   552  	require.NoError(t, err, "should have been able to yaml.UnmarshalStrict bundle")
   553  
   554  	assert.Equal(t, exampleBundle, &roundTripBun, "after a roundtrip yaml marshal/unmarshal, the bundle does not match expected")
   555  }
   556  
   557  func TestValidateABundleAndParams(t *testing.T) {
   558  
   559  	bun, err := ioutil.ReadFile("../testdata/bundles/foo.json")
   560  	require.NoError(t, err, "couldn't read test bundle")
   561  
   562  	bundle, err := Unmarshal(bun)
   563  	require.NoError(t, err, "the bundle should have been valid")
   564  
   565  	def, ok := bundle.Definitions["complexThing"]
   566  	require.True(t, ok, "test failed because definition not found")
   567  
   568  	testData := struct {
   569  		Port int    `json:"port"`
   570  		Host string `json:"hostName"`
   571  	}{
   572  		Host: "validhost",
   573  		Port: 8080,
   574  	}
   575  	valErrors, err := def.Validate(testData)
   576  	assert.NoError(t, err, "validation should not have resulted in an error")
   577  	assert.Empty(t, valErrors, "validation should have been successful")
   578  
   579  	testData2 := struct {
   580  		Host string `json:"hostName"`
   581  	}{
   582  		Host: "validhost",
   583  	}
   584  	valErrors, err = def.Validate(testData2)
   585  	assert.NoError(t, err, "validation should not have encountered an error")
   586  	assert.NotEmpty(t, valErrors, "validation should not have been successful")
   587  
   588  	testData3 := struct {
   589  		Port int    `json:"port"`
   590  		Host string `json:"hostName"`
   591  	}{
   592  		Host: "validhost",
   593  		Port: 80,
   594  	}
   595  	valErrors, err = def.Validate(testData3)
   596  	assert.NoError(t, err, "should not have encountered an error with the validator")
   597  	assert.NotEmpty(t, valErrors, "validation should not have been successful")
   598  }
   599  
   600  func TestBundle_RoundTrip(t *testing.T) {
   601  	testCases := []struct {
   602  		name     string
   603  		testFile string
   604  	}{
   605  		{name: "EmptyJson", testFile: "testdata/empty.json"},
   606  		{name: "MinimalJson", testFile: "testdata/minimal.json"},
   607  	}
   608  	for _, tc := range testCases {
   609  		t.Run(tc.name, func(t *testing.T) {
   610  			wantData, err := ioutil.ReadFile(tc.testFile)
   611  			if err != nil {
   612  				t.Fatal(err)
   613  			}
   614  
   615  			bun, err := Unmarshal(wantData)
   616  			if err != nil {
   617  				t.Fatal(err)
   618  			}
   619  
   620  			output := &bytes.Buffer{}
   621  			_, err = bun.WriteTo(output)
   622  			require.NoError(t, err, "writing the bundle to json failed")
   623  
   624  			gotData := output.String()
   625  			assert.Equal(t, string(wantData), gotData)
   626  		})
   627  	}
   628  }
   629  
   630  func TestDigestPresent(t *testing.T) {
   631  	bun, err := ioutil.ReadFile("../testdata/bundles/digest.json")
   632  	require.NoError(t, err, "couldn't read test bundle")
   633  
   634  	bundle, err := Unmarshal(bun)
   635  	require.NoError(t, err, "the bundle should have been valid")
   636  
   637  	require.Equal(t, 1, len(bundle.InvocationImages), "there should be one invocation image in the bundle")
   638  	assert.Equal(t,
   639  		"sha256:decafbad71b4175951f29eb96035604c8cc372c99affa2e6d05cde6e8e20cc9a",
   640  		bundle.InvocationImages[0].Digest,
   641  	)
   642  
   643  	image, ok := bundle.Images["my-microservice"]
   644  	require.True(t, ok, "there should been an image named my-microservice in the bundle")
   645  	assert.Equal(
   646  		t,
   647  		"sha256:beefcacef6c04336a17761db2004813982abe0e87ab727a376c291e09391ea61",
   648  		image.Digest,
   649  	)
   650  }
   651  
   652  func TestImageDeepCopy(t *testing.T) {
   653  	origImg := Image{
   654  		Description: "my image",
   655  		BaseImage: BaseImage{
   656  			Image:     "alpine",
   657  			ImageType: "docker",
   658  			Labels: map[string]string{
   659  				"origLabel": "origLabelValue",
   660  			},
   661  			Digest: "abc1234",
   662  			Size:   2,
   663  		},
   664  	}
   665  
   666  	newImg := origImg.DeepCopy()
   667  
   668  	newImg.Description = "my new image"
   669  	newImg.Image = "debian"
   670  	newImg.Labels["origLabel"] = "newLabelValue"
   671  	newImg.Digest = "123abcd"
   672  
   673  	assert.Equal(t, "my image", origImg.Description)
   674  	assert.Equal(t, "alpine", origImg.Image)
   675  	assert.Equal(t, map[string]string{"origLabel": "origLabelValue"}, origImg.Labels)
   676  	assert.Equal(t, "abc1234", origImg.Digest)
   677  
   678  	assert.Equal(t, "my new image", newImg.Description)
   679  	assert.Equal(t, "debian", newImg.Image)
   680  	assert.Equal(t, map[string]string{"origLabel": "newLabelValue"}, newImg.Labels)
   681  	assert.Equal(t, "123abcd", newImg.Digest)
   682  }