gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/snapasserts/validation_sets_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snapasserts_test
    21  
    22  import (
    23  	"fmt"
    24  	"math/rand"
    25  	"sort"
    26  	"strconv"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"gitee.com/mysnapcore/mysnapd/asserts"
    31  	"gitee.com/mysnapcore/mysnapd/asserts/assertstest"
    32  	"gitee.com/mysnapcore/mysnapd/asserts/snapasserts"
    33  	"gitee.com/mysnapcore/mysnapd/snap"
    34  	"gitee.com/mysnapcore/mysnapd/snap/naming"
    35  )
    36  
    37  type validationSetsSuite struct{}
    38  
    39  var _ = Suite(&validationSetsSuite{})
    40  
    41  func (s *validationSetsSuite) TestAddFromSameSequence(c *C) {
    42  	mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{
    43  		"type":         "validation-set",
    44  		"authority-id": "account-id",
    45  		"series":       "16",
    46  		"account-id":   "account-id",
    47  		"name":         "my-snap-ctl",
    48  		"sequence":     "1",
    49  		"snaps": []interface{}{
    50  			map[string]interface{}{
    51  				"name":     "my-snap",
    52  				"id":       "mysnapididididididididididididid",
    53  				"presence": "required",
    54  				"revision": "7",
    55  			},
    56  		},
    57  	}).(*asserts.ValidationSet)
    58  
    59  	mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{
    60  		"type":         "validation-set",
    61  		"authority-id": "account-id",
    62  		"series":       "16",
    63  		"account-id":   "account-id",
    64  		"name":         "my-snap-ctl",
    65  		"sequence":     "2",
    66  		"snaps": []interface{}{
    67  			map[string]interface{}{
    68  				"name":     "my-snap",
    69  				"id":       "mysnapididididididididididididid",
    70  				"presence": "required",
    71  				"revision": "8",
    72  			},
    73  		},
    74  	}).(*asserts.ValidationSet)
    75  
    76  	valsets := snapasserts.NewValidationSets()
    77  	err := valsets.Add(mySnapAt7Valset)
    78  	c.Assert(err, IsNil)
    79  	err = valsets.Add(mySnapAt8Valset)
    80  	c.Check(err, ErrorMatches, `cannot add a second validation-set under "account-id/my-snap-ctl"`)
    81  }
    82  
    83  func (s *validationSetsSuite) TestIntersections(c *C) {
    84  	mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{
    85  		"type":         "validation-set",
    86  		"authority-id": "account-id",
    87  		"series":       "16",
    88  		"account-id":   "account-id",
    89  		"name":         "my-snap-ctl",
    90  		"sequence":     "1",
    91  		"snaps": []interface{}{
    92  			map[string]interface{}{
    93  				"name":     "my-snap",
    94  				"id":       "mysnapididididididididididididid",
    95  				"presence": "required",
    96  				"revision": "7",
    97  			},
    98  		},
    99  	}).(*asserts.ValidationSet)
   100  
   101  	mySnapAt7Valset2 := assertstest.FakeAssertion(map[string]interface{}{
   102  		"type":         "validation-set",
   103  		"authority-id": "account-id",
   104  		"series":       "16",
   105  		"account-id":   "account-id",
   106  		"name":         "my-snap-ctl2",
   107  		"sequence":     "2",
   108  		"snaps": []interface{}{
   109  			map[string]interface{}{
   110  				"name":     "my-snap",
   111  				"id":       "mysnapididididididididididididid",
   112  				"presence": "required",
   113  				"revision": "7",
   114  			},
   115  		},
   116  	}).(*asserts.ValidationSet)
   117  
   118  	mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{
   119  		"type":         "validation-set",
   120  		"authority-id": "account-id",
   121  		"series":       "16",
   122  		"account-id":   "account-id",
   123  		"name":         "my-snap-ctl-other",
   124  		"sequence":     "1",
   125  		"snaps": []interface{}{
   126  			map[string]interface{}{
   127  				"name":     "my-snap",
   128  				"id":       "mysnapididididididididididididid",
   129  				"presence": "required",
   130  				"revision": "8",
   131  			},
   132  		},
   133  	}).(*asserts.ValidationSet)
   134  
   135  	mySnapAt8OptValset := assertstest.FakeAssertion(map[string]interface{}{
   136  		"type":         "validation-set",
   137  		"authority-id": "account-id",
   138  		"series":       "16",
   139  		"account-id":   "account-id",
   140  		"name":         "my-snap-ctl-opt",
   141  		"sequence":     "1",
   142  		"snaps": []interface{}{
   143  			map[string]interface{}{
   144  				"name":     "my-snap",
   145  				"id":       "mysnapididididididididididididid",
   146  				"presence": "optional",
   147  				"revision": "8",
   148  			},
   149  		},
   150  	}).(*asserts.ValidationSet)
   151  
   152  	mySnapInvalidValset := assertstest.FakeAssertion(map[string]interface{}{
   153  		"type":         "validation-set",
   154  		"authority-id": "account-id",
   155  		"series":       "16",
   156  		"account-id":   "account-id",
   157  		"name":         "my-snap-ctl-inv",
   158  		"sequence":     "1",
   159  		"snaps": []interface{}{
   160  			map[string]interface{}{
   161  				"name":     "my-snap",
   162  				"id":       "mysnapididididididididididididid",
   163  				"presence": "invalid",
   164  			},
   165  		},
   166  	}).(*asserts.ValidationSet)
   167  
   168  	mySnapAt7OptValset := assertstest.FakeAssertion(map[string]interface{}{
   169  		"type":         "validation-set",
   170  		"authority-id": "account-id",
   171  		"series":       "16",
   172  		"account-id":   "account-id",
   173  		"name":         "my-snap-ctl-opt2",
   174  		"sequence":     "1",
   175  		"snaps": []interface{}{
   176  			map[string]interface{}{
   177  				"name":     "my-snap",
   178  				"id":       "mysnapididididididididididididid",
   179  				"presence": "optional",
   180  				"revision": "7",
   181  			},
   182  		},
   183  	}).(*asserts.ValidationSet)
   184  
   185  	mySnapReqValset := assertstest.FakeAssertion(map[string]interface{}{
   186  		"type":         "validation-set",
   187  		"authority-id": "account-id",
   188  		"series":       "16",
   189  		"account-id":   "account-id",
   190  		"name":         "my-snap-ctl-req-only",
   191  		"sequence":     "1",
   192  		"snaps": []interface{}{
   193  			map[string]interface{}{
   194  				"name":     "my-snap",
   195  				"id":       "mysnapididididididididididididid",
   196  				"presence": "required",
   197  			},
   198  		},
   199  	}).(*asserts.ValidationSet)
   200  
   201  	mySnapOptValset := assertstest.FakeAssertion(map[string]interface{}{
   202  		"type":         "validation-set",
   203  		"authority-id": "account-id",
   204  		"series":       "16",
   205  		"account-id":   "account-id",
   206  		"name":         "my-snap-ctl-opt-only",
   207  		"sequence":     "1",
   208  		"snaps": []interface{}{
   209  			map[string]interface{}{
   210  				"name":     "my-snap",
   211  				"id":       "mysnapididididididididididididid",
   212  				"presence": "optional",
   213  			},
   214  		},
   215  	}).(*asserts.ValidationSet)
   216  
   217  	tests := []struct {
   218  		sets        []*asserts.ValidationSet
   219  		conflictErr string
   220  	}{
   221  		{[]*asserts.ValidationSet{mySnapAt7Valset}, ""},
   222  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt7Valset2}, ""},
   223  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`},
   224  		{[]*asserts.ValidationSet{mySnapAt8Valset, mySnapAt8OptValset}, ""},
   225  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`},
   226  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`},
   227  		{[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`},
   228  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset}, ""},
   229  		{[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt8OptValset}, ""},
   230  		{[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset}, ""}, // no conflict but interpreted as invalid
   231  		{[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl,account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\)`},
   232  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`},
   233  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapReqValset}, ""},
   234  		{[]*asserts.ValidationSet{mySnapReqValset, mySnapAt7Valset}, ""},
   235  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset}, ""},
   236  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset, mySnapAt7OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`},
   237  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapAt7OptValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`},
   238  		{[]*asserts.ValidationSet{mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`},
   239  		{[]*asserts.ValidationSet{mySnapInvalidValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`},
   240  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapOptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`},
   241  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapOptValset}, ""},
   242  		{[]*asserts.ValidationSet{mySnapOptValset, mySnapAt7Valset}, ""},
   243  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset}, ""},
   244  		{[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset, mySnapAt7OptValset}, ""}, // no conflict but interpreted as invalid
   245  		{[]*asserts.ValidationSet{mySnapInvalidValset, mySnapOptValset}, ""},
   246  		{[]*asserts.ValidationSet{mySnapOptValset, mySnapInvalidValset}, ""},
   247  		{[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\) or at any revision \(account-id/my-snap-ctl-req-only\)`},
   248  	}
   249  
   250  	for _, t := range tests {
   251  		valsets := snapasserts.NewValidationSets()
   252  		cSets := make(map[string]*asserts.ValidationSet)
   253  		for _, valset := range t.sets {
   254  			err := valsets.Add(valset)
   255  			c.Assert(err, IsNil)
   256  			// mySnapOptValset never influcens an outcome
   257  			if valset != mySnapOptValset {
   258  				cSets[fmt.Sprintf("%s/%s", valset.AccountID(), valset.Name())] = valset
   259  			}
   260  		}
   261  		err := valsets.Conflict()
   262  		if t.conflictErr == "" {
   263  			c.Check(err, IsNil)
   264  		} else {
   265  			c.Check(err, ErrorMatches, t.conflictErr)
   266  			ce := err.(*snapasserts.ValidationSetsConflictError)
   267  			c.Check(ce.Sets, DeepEquals, cSets)
   268  		}
   269  	}
   270  }
   271  
   272  func (s *validationSetsSuite) TestCheckInstalledSnapsNoValidationSets(c *C) {
   273  	valsets := snapasserts.NewValidationSets()
   274  	snaps := []*snapasserts.InstalledSnap{
   275  		snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)),
   276  	}
   277  	err := valsets.CheckInstalledSnaps(snaps, nil)
   278  	c.Assert(err, IsNil)
   279  }
   280  
   281  func (s *validationSetsSuite) TestCheckInstalledSnaps(c *C) {
   282  	// require: snapB rev 3, snapC rev 2.
   283  	// invalid: snapA
   284  	vs1 := assertstest.FakeAssertion(map[string]interface{}{
   285  		"type":         "validation-set",
   286  		"authority-id": "acme",
   287  		"series":       "16",
   288  		"account-id":   "acme",
   289  		"name":         "fooname",
   290  		"sequence":     "1",
   291  		"snaps": []interface{}{
   292  			map[string]interface{}{
   293  				"name":     "snap-a",
   294  				"id":       "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa",
   295  				"presence": "invalid",
   296  			},
   297  			map[string]interface{}{
   298  				"name":     "snap-b",
   299  				"id":       "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb",
   300  				"revision": "3",
   301  				"presence": "required",
   302  			},
   303  			map[string]interface{}{
   304  				"name":     "snap-c",
   305  				"id":       "mysnapcccccccccccccccccccccccccc",
   306  				"revision": "2",
   307  				"presence": "optional",
   308  			},
   309  		},
   310  	}).(*asserts.ValidationSet)
   311  
   312  	// require: snapD any rev
   313  	// optional: snapE any rev
   314  	vs2 := assertstest.FakeAssertion(map[string]interface{}{
   315  		"type":         "validation-set",
   316  		"authority-id": "acme",
   317  		"series":       "16",
   318  		"account-id":   "acme",
   319  		"name":         "barname",
   320  		"sequence":     "3",
   321  		"snaps": []interface{}{
   322  			map[string]interface{}{
   323  				"name":     "snap-d",
   324  				"id":       "mysnapdddddddddddddddddddddddddd",
   325  				"presence": "required",
   326  			},
   327  			map[string]interface{}{
   328  				"name":     "snap-e",
   329  				"id":       "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee",
   330  				"presence": "optional",
   331  			},
   332  		},
   333  	}).(*asserts.ValidationSet)
   334  
   335  	// optional: snapE any rev
   336  	// note: since it only has an optional snap, acme/bazname is not expected
   337  	// not be invalid by any of the checks.
   338  	vs3 := assertstest.FakeAssertion(map[string]interface{}{
   339  		"type":         "validation-set",
   340  		"authority-id": "acme",
   341  		"series":       "16",
   342  		"account-id":   "acme",
   343  		"name":         "bazname",
   344  		"sequence":     "2",
   345  		"snaps": []interface{}{
   346  			map[string]interface{}{
   347  				"name":     "snap-e",
   348  				"id":       "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee",
   349  				"presence": "optional",
   350  			},
   351  		},
   352  	}).(*asserts.ValidationSet)
   353  
   354  	// invalid: snapA
   355  	vs4 := assertstest.FakeAssertion(map[string]interface{}{
   356  		"type":         "validation-set",
   357  		"authority-id": "acme",
   358  		"series":       "16",
   359  		"account-id":   "acme",
   360  		"name":         "booname",
   361  		"sequence":     "1",
   362  		"snaps": []interface{}{
   363  			map[string]interface{}{
   364  				"name":     "snap-a",
   365  				"id":       "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa",
   366  				"presence": "invalid",
   367  			},
   368  		},
   369  	}).(*asserts.ValidationSet)
   370  
   371  	vs5 := assertstest.FakeAssertion(map[string]interface{}{
   372  		"type":         "validation-set",
   373  		"authority-id": "acme",
   374  		"series":       "16",
   375  		"account-id":   "acme",
   376  		"name":         "huhname",
   377  		"sequence":     "1",
   378  		"snaps": []interface{}{
   379  			map[string]interface{}{
   380  				"name":     "snap-f",
   381  				"id":       "mysnapffffffffffffffffffffffffff",
   382  				"revision": "4",
   383  				"presence": "required",
   384  			},
   385  		},
   386  	}).(*asserts.ValidationSet)
   387  
   388  	vs6 := assertstest.FakeAssertion(map[string]interface{}{
   389  		"type":         "validation-set",
   390  		"authority-id": "acme",
   391  		"series":       "16",
   392  		"account-id":   "acme",
   393  		"name":         "duhname",
   394  		"sequence":     "1",
   395  		"snaps": []interface{}{
   396  			map[string]interface{}{
   397  				"name":     "snap-f",
   398  				"id":       "mysnapffffffffffffffffffffffffff",
   399  				"revision": "4",
   400  				"presence": "required",
   401  			},
   402  		},
   403  	}).(*asserts.ValidationSet)
   404  
   405  	vs7 := assertstest.FakeAssertion(map[string]interface{}{
   406  		"type":         "validation-set",
   407  		"authority-id": "acme",
   408  		"series":       "16",
   409  		"account-id":   "acme",
   410  		"name":         "bahname",
   411  		"sequence":     "1",
   412  		"snaps": []interface{}{
   413  			map[string]interface{}{
   414  				"name":     "snap-f",
   415  				"id":       "mysnapffffffffffffffffffffffffff",
   416  				"presence": "required",
   417  			},
   418  		},
   419  	}).(*asserts.ValidationSet)
   420  
   421  	valsets := snapasserts.NewValidationSets()
   422  	c.Assert(valsets.Add(vs1), IsNil)
   423  	c.Assert(valsets.Add(vs2), IsNil)
   424  	c.Assert(valsets.Add(vs3), IsNil)
   425  	c.Assert(valsets.Add(vs4), IsNil)
   426  	c.Assert(valsets.Add(vs5), IsNil)
   427  	c.Assert(valsets.Add(vs6), IsNil)
   428  	c.Assert(valsets.Add(vs7), IsNil)
   429  
   430  	snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1))
   431  	snapAlocal := snapasserts.NewInstalledSnap("snap-a", "", snap.R("x2"))
   432  	snapB := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(3))
   433  	snapBinvRev := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(8))
   434  	snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3"))
   435  	snapC := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(2))
   436  	snapCinvRev := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(99))
   437  	snapD := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(2))
   438  	snapDrev99 := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(99))
   439  	snapDlocal := snapasserts.NewInstalledSnap("snap-d", "", snap.R("x3"))
   440  	snapE := snapasserts.NewInstalledSnap("snap-e", "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", snap.R(2))
   441  	snapF := snapasserts.NewInstalledSnap("snap-f", "mysnapffffffffffffffffffffffffff", snap.R(4))
   442  	// extra snap, not referenced by any validation set
   443  	snapZ := snapasserts.NewInstalledSnap("snap-z", "mysnapzzzzzzzzzzzzzzzzzzzzzzzzzz", snap.R(1))
   444  
   445  	tests := []struct {
   446  		snaps            []*snapasserts.InstalledSnap
   447  		expectedInvalid  map[string][]string
   448  		expectedMissing  map[string]map[snap.Revision][]string
   449  		expectedWrongRev map[string]map[snap.Revision][]string
   450  	}{
   451  		{
   452  			// required snaps not installed
   453  			snaps: nil,
   454  			expectedMissing: map[string]map[snap.Revision][]string{
   455  				"snap-b": {
   456  					snap.R(3): {"acme/fooname"},
   457  				},
   458  				"snap-d": {
   459  					snap.R(0): {"acme/barname"},
   460  				},
   461  				"snap-f": {
   462  					snap.R(0): {"acme/bahname"},
   463  					snap.R(4): {"acme/duhname", "acme/huhname"},
   464  				},
   465  			},
   466  		},
   467  		{
   468  			// required snaps not installed
   469  			snaps: []*snapasserts.InstalledSnap{
   470  				snapZ,
   471  			},
   472  			expectedMissing: map[string]map[snap.Revision][]string{
   473  				"snap-b": {
   474  					snap.R(3): {"acme/fooname"},
   475  				},
   476  				"snap-d": {
   477  					snap.R(0): {"acme/barname"},
   478  				},
   479  				"snap-f": {
   480  					snap.R(0): {"acme/bahname"},
   481  					snap.R(4): {"acme/duhname", "acme/huhname"},
   482  				},
   483  			},
   484  		},
   485  		{
   486  			snaps: []*snapasserts.InstalledSnap{
   487  				// covered by acme/fooname validation-set
   488  				snapB,
   489  				// covered by acme/barname validation-set. snap-e not installed but optional
   490  				snapDrev99,
   491  				// covered by acme/duhname and acme/huhname
   492  				snapF,
   493  			},
   494  			// ale fine
   495  		},
   496  		{
   497  			snaps: []*snapasserts.InstalledSnap{
   498  				// covered by acme/fooname validation-set and acme/booname, snap-a presence is invalid
   499  				snapA,
   500  				snapB,
   501  				// covered by acme/barname validation-set. snap-e not installed but optional
   502  				snapDrev99,
   503  				// covered by acme/duhname and acme/huhname
   504  				snapF,
   505  			},
   506  			expectedInvalid: map[string][]string{
   507  				"snap-a": {"acme/booname", "acme/fooname"},
   508  			},
   509  		},
   510  		{
   511  			snaps: []*snapasserts.InstalledSnap{
   512  				// covered by acme/fooname and acme/booname validation-sets, snapB missing, snap-a presence is invalid
   513  				snapA,
   514  				// covered by acme/barname validation-set. snap-e not installed but optional
   515  				snapDrev99,
   516  				snapF,
   517  			},
   518  			expectedInvalid: map[string][]string{
   519  				"snap-a": {"acme/booname", "acme/fooname"},
   520  			},
   521  			expectedMissing: map[string]map[snap.Revision][]string{
   522  				"snap-b": {
   523  					snap.R(3): {"acme/fooname"},
   524  				},
   525  			},
   526  		},
   527  		{
   528  			snaps: []*snapasserts.InstalledSnap{
   529  				// covered by acme/fooname validation-set
   530  				snapB,
   531  				snapC,
   532  				// covered by acme/barname validation-set. snap-e not installed but optional
   533  				snapD,
   534  				// covered by acme/duhname and acme/huhname
   535  				snapF,
   536  			},
   537  			// ale fine
   538  		},
   539  		{
   540  			snaps: []*snapasserts.InstalledSnap{
   541  				// covered by acme/fooname validation-set, snap-c optional but wrong revision
   542  				snapB,
   543  				snapCinvRev,
   544  				// covered by acme/barname validation-set. snap-e not installed but optional
   545  				snapD,
   546  				// covered by acme/duhname and acme/huhname
   547  				snapF,
   548  			},
   549  			expectedWrongRev: map[string]map[snap.Revision][]string{
   550  				"snap-c": {
   551  					snap.R(2): {"acme/fooname"},
   552  				},
   553  			},
   554  		},
   555  		{
   556  			snaps: []*snapasserts.InstalledSnap{
   557  				// covered by acme/fooname validation-set but wrong revision
   558  				snapBinvRev,
   559  				// covered by acme/barname validation-set.
   560  				snapD,
   561  				// covered by acme/duhname and acme/huhname
   562  				snapF,
   563  			},
   564  			expectedWrongRev: map[string]map[snap.Revision][]string{
   565  				"snap-b": {
   566  					snap.R(3): {"acme/fooname"},
   567  				},
   568  			},
   569  		},
   570  		{
   571  			snaps: []*snapasserts.InstalledSnap{
   572  				// covered by acme/fooname validation-set
   573  				snapB,
   574  				// covered by acme/barname validation-set. snap-d not installed.
   575  				snapE,
   576  				// covered by acme/duhname and acme/huhname
   577  				snapF,
   578  			},
   579  			expectedMissing: map[string]map[snap.Revision][]string{
   580  				"snap-d": {
   581  					snap.R(0): {"acme/barname"},
   582  				},
   583  			},
   584  		},
   585  		{
   586  			snaps: []*snapasserts.InstalledSnap{
   587  				// required snaps from acme/fooname are not installed.
   588  				// covered by acme/barname validation-set
   589  				snapDrev99,
   590  				snapE,
   591  				// covered by acme/duhname and acme/huhname
   592  				snapF,
   593  			},
   594  			expectedMissing: map[string]map[snap.Revision][]string{
   595  				"snap-b": {
   596  					snap.R(3): {"acme/fooname"},
   597  				},
   598  			},
   599  		},
   600  		{
   601  			snaps: []*snapasserts.InstalledSnap{
   602  				// covered by acme/fooname validation-set, required missing.
   603  				snapC,
   604  				// covered by acme/barname validation-set, required missing.
   605  				snapE,
   606  				// covered by acme/duhname and acme/huhname
   607  				snapF,
   608  			},
   609  			expectedMissing: map[string]map[snap.Revision][]string{
   610  				"snap-b": {
   611  					snap.R(3): {"acme/fooname"},
   612  				},
   613  				"snap-d": {
   614  					snap.R(0): {"acme/barname"},
   615  				},
   616  			},
   617  		},
   618  		// local snaps
   619  		{
   620  			snaps: []*snapasserts.InstalledSnap{
   621  				// covered by acme/fooname validation-set.
   622  				snapB,
   623  				// covered by acme/barname validation-set, local snap-d.
   624  				snapDlocal,
   625  				// covered by acme/duhname and acme/huhname
   626  				snapF,
   627  			},
   628  			// all fine
   629  		},
   630  		{
   631  			snaps: []*snapasserts.InstalledSnap{
   632  				// covered by acme/fooname validation-set, snap-a is invalid.
   633  				snapAlocal,
   634  				snapB,
   635  				// covered by acme/barname validation-set.
   636  				snapD,
   637  				snapF,
   638  			},
   639  			expectedInvalid: map[string][]string{
   640  				"snap-a": {"acme/booname", "acme/fooname"},
   641  			},
   642  		},
   643  		{
   644  			snaps: []*snapasserts.InstalledSnap{
   645  				// covered by acme/fooname validation-set, snap-b is wrong rev (local).
   646  				snapBlocal,
   647  				// covered by acme/barname validation-set.
   648  				snapD,
   649  				// covered by acme/duhname and acme/huhname
   650  				snapF,
   651  			},
   652  			expectedWrongRev: map[string]map[snap.Revision][]string{
   653  				"snap-b": {
   654  					snap.R(3): {"acme/fooname"},
   655  				},
   656  			},
   657  		},
   658  	}
   659  
   660  	checkSets := func(snapsToValidationSets map[string][]string, vs map[string]*asserts.ValidationSet) {
   661  		for _, vsetKeys := range snapsToValidationSets {
   662  			for _, key := range vsetKeys {
   663  				vset, ok := vs[key]
   664  				c.Assert(ok, Equals, true)
   665  				c.Assert(vset.AccountID()+"/"+vset.Name(), Equals, key)
   666  			}
   667  		}
   668  	}
   669  
   670  	expectedSets := make(map[string]*asserts.ValidationSet, 7)
   671  	for _, vs := range []*asserts.ValidationSet{vs1, vs2, vs3, vs4, vs5, vs6, vs7} {
   672  		expectedSets[fmt.Sprintf("%s/%s", vs.AccountID(), vs.Name())] = vs
   673  	}
   674  
   675  	for i, tc := range tests {
   676  		err := valsets.CheckInstalledSnaps(tc.snaps, nil)
   677  		if err == nil {
   678  			c.Assert(tc.expectedInvalid, IsNil)
   679  			c.Assert(tc.expectedMissing, IsNil)
   680  			c.Assert(tc.expectedWrongRev, IsNil)
   681  			continue
   682  		}
   683  		verr, ok := err.(*snapasserts.ValidationSetsValidationError)
   684  		c.Assert(ok, Equals, true, Commentf("#%d", i))
   685  		c.Assert(verr.InvalidSnaps, DeepEquals, tc.expectedInvalid, Commentf("#%d", i))
   686  		c.Assert(verr.MissingSnaps, DeepEquals, tc.expectedMissing, Commentf("#%d", i))
   687  		c.Assert(verr.WrongRevisionSnaps, DeepEquals, tc.expectedWrongRev, Commentf("#%d", i))
   688  		c.Assert(verr.Sets, DeepEquals, expectedSets)
   689  		checkSets(verr.InvalidSnaps, verr.Sets)
   690  	}
   691  }
   692  
   693  func (s *validationSetsSuite) TestCheckInstalledSnapsIgnoreValidation(c *C) {
   694  	// require: snapB rev 3, snapC rev 2.
   695  	// invalid: snapA
   696  	vs := assertstest.FakeAssertion(map[string]interface{}{
   697  		"type":         "validation-set",
   698  		"authority-id": "acme",
   699  		"series":       "16",
   700  		"account-id":   "acme",
   701  		"name":         "fooname",
   702  		"sequence":     "1",
   703  		"snaps": []interface{}{
   704  			map[string]interface{}{
   705  				"name":     "snap-a",
   706  				"id":       "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa",
   707  				"presence": "invalid",
   708  			},
   709  			map[string]interface{}{
   710  				"name":     "snap-b",
   711  				"id":       "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb",
   712  				"revision": "3",
   713  				"presence": "required",
   714  			},
   715  			map[string]interface{}{
   716  				"name":     "snap-c",
   717  				"id":       "mysnapcccccccccccccccccccccccccc",
   718  				"revision": "2",
   719  				"presence": "optional",
   720  			},
   721  		},
   722  	}).(*asserts.ValidationSet)
   723  
   724  	valsets := snapasserts.NewValidationSets()
   725  	c.Assert(valsets.Add(vs), IsNil)
   726  
   727  	snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1))
   728  	snapB := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(3))
   729  	snapBinvRev := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(8))
   730  
   731  	// validity check
   732  	c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapA, snapB}, nil), ErrorMatches, "validation sets assertions are not met:\n"+
   733  		"- invalid snaps:\n"+
   734  		"  - snap-a \\(invalid for sets acme/fooname\\)")
   735  	// snapA is invalid but ignore-validation is set so it's ok
   736  	c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapA, snapB}, map[string]bool{"snap-a": true}), IsNil)
   737  
   738  	// validity check
   739  	c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapBinvRev}, nil), ErrorMatches, "validation sets assertions are not met:\n"+
   740  		"- snaps at wrong revisions:\n"+
   741  		"  - snap-b \\(required at revision 3 by sets acme/fooname\\)")
   742  	// snapB is at the wrong revision, but ignore-validation is set so it's ok
   743  	c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapBinvRev}, map[string]bool{"snap-b": true}), IsNil)
   744  }
   745  
   746  func (s *validationSetsSuite) TestCheckInstalledSnapsErrorFormat(c *C) {
   747  	vs1 := assertstest.FakeAssertion(map[string]interface{}{
   748  		"type":         "validation-set",
   749  		"authority-id": "acme",
   750  		"series":       "16",
   751  		"account-id":   "acme",
   752  		"name":         "fooname",
   753  		"sequence":     "1",
   754  		"snaps": []interface{}{
   755  			map[string]interface{}{
   756  				"name":     "snap-a",
   757  				"id":       "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa",
   758  				"presence": "invalid",
   759  			},
   760  			map[string]interface{}{
   761  				"name":     "snap-b",
   762  				"id":       "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb",
   763  				"revision": "3",
   764  				"presence": "required",
   765  			},
   766  		},
   767  	}).(*asserts.ValidationSet)
   768  	vs2 := assertstest.FakeAssertion(map[string]interface{}{
   769  		"type":         "validation-set",
   770  		"authority-id": "acme",
   771  		"series":       "16",
   772  		"account-id":   "acme",
   773  		"name":         "barname",
   774  		"sequence":     "2",
   775  		"snaps": []interface{}{
   776  			map[string]interface{}{
   777  				"name":     "snap-b",
   778  				"id":       "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb",
   779  				"presence": "required",
   780  			},
   781  		},
   782  	}).(*asserts.ValidationSet)
   783  
   784  	valsets := snapasserts.NewValidationSets()
   785  	c.Assert(valsets.Add(vs1), IsNil)
   786  	c.Assert(valsets.Add(vs2), IsNil)
   787  
   788  	// not strictly important, but ensures test data makes sense and avoids confusing results
   789  	c.Assert(valsets.Conflict(), IsNil)
   790  
   791  	snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1))
   792  	snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3"))
   793  
   794  	tests := []struct {
   795  		snaps    []*snapasserts.InstalledSnap
   796  		errorMsg string
   797  	}{
   798  		{
   799  			nil,
   800  			"validation sets assertions are not met:\n" +
   801  				"- missing required snaps:\n" +
   802  				"  - snap-b \\(required at any revision by sets acme/barname, at revision 3 by sets acme/fooname\\)",
   803  		},
   804  		{
   805  			[]*snapasserts.InstalledSnap{snapA},
   806  			"validation sets assertions are not met:\n" +
   807  				"- missing required snaps:\n" +
   808  				"  - snap-b \\(required at any revision by sets acme/barname, at revision 3 by sets acme/fooname\\)\n" +
   809  				"- invalid snaps:\n" +
   810  				"  - snap-a \\(invalid for sets acme/fooname\\)",
   811  		},
   812  		{
   813  			[]*snapasserts.InstalledSnap{snapBlocal},
   814  			"validation sets assertions are not met:\n" +
   815  				"- snaps at wrong revisions:\n" +
   816  				"  - snap-b \\(required at revision 3 by sets acme/fooname\\)",
   817  		},
   818  	}
   819  
   820  	for i, tc := range tests {
   821  		err := valsets.CheckInstalledSnaps(tc.snaps, nil)
   822  		c.Assert(err, NotNil, Commentf("#%d", i))
   823  		c.Assert(err, ErrorMatches, tc.errorMsg, Commentf("#%d: ", i))
   824  	}
   825  }
   826  
   827  func (s *validationSetsSuite) TestSortByRevision(c *C) {
   828  	revs := []snap.Revision{snap.R(10), snap.R(4), snap.R(5), snap.R(-1)}
   829  
   830  	sort.Sort(snapasserts.ByRevision(revs))
   831  	c.Assert(revs, DeepEquals, []snap.Revision{snap.R(-1), snap.R(4), snap.R(5), snap.R(10)})
   832  }
   833  
   834  func (s *validationSetsSuite) TestCheckPresenceRequired(c *C) {
   835  	valset1 := assertstest.FakeAssertion(map[string]interface{}{
   836  		"type":         "validation-set",
   837  		"authority-id": "account-id",
   838  		"series":       "16",
   839  		"account-id":   "account-id",
   840  		"name":         "my-snap-ctl",
   841  		"sequence":     "1",
   842  		"snaps": []interface{}{
   843  			map[string]interface{}{
   844  				"name":     "my-snap",
   845  				"id":       "mysnapididididididididididididid",
   846  				"presence": "required",
   847  				"revision": "7",
   848  			},
   849  			map[string]interface{}{
   850  				"name":     "other-snap",
   851  				"id":       "123456ididididididididididididid",
   852  				"presence": "optional",
   853  			},
   854  		},
   855  	}).(*asserts.ValidationSet)
   856  
   857  	valset2 := assertstest.FakeAssertion(map[string]interface{}{
   858  		"type":         "validation-set",
   859  		"authority-id": "account-id",
   860  		"series":       "16",
   861  		"account-id":   "account-id",
   862  		"name":         "my-snap-ctl2",
   863  		"sequence":     "2",
   864  		"snaps": []interface{}{
   865  			map[string]interface{}{
   866  				"name":     "my-snap",
   867  				"id":       "mysnapididididididididididididid",
   868  				"presence": "required",
   869  				"revision": "7",
   870  			},
   871  			map[string]interface{}{
   872  				"name":     "other-snap",
   873  				"id":       "123456ididididididididididididid",
   874  				"presence": "invalid",
   875  			},
   876  		},
   877  	}).(*asserts.ValidationSet)
   878  
   879  	// my-snap required but no specific revision set.
   880  	valset3 := assertstest.FakeAssertion(map[string]interface{}{
   881  		"type":         "validation-set",
   882  		"authority-id": "account-id",
   883  		"series":       "16",
   884  		"account-id":   "account-id",
   885  		"name":         "my-snap-ctl3",
   886  		"sequence":     "1",
   887  		"snaps": []interface{}{
   888  			map[string]interface{}{
   889  				"name":     "my-snap",
   890  				"id":       "mysnapididididididididididididid",
   891  				"presence": "required",
   892  			},
   893  		},
   894  	}).(*asserts.ValidationSet)
   895  
   896  	valsets := snapasserts.NewValidationSets()
   897  
   898  	// no validation sets
   899  	vsKeys, _, err := valsets.CheckPresenceRequired(naming.Snap("my-snap"))
   900  	c.Assert(err, IsNil)
   901  	c.Check(vsKeys, HasLen, 0)
   902  
   903  	c.Assert(valsets.Add(valset1), IsNil)
   904  	c.Assert(valsets.Add(valset2), IsNil)
   905  	c.Assert(valsets.Add(valset3), IsNil)
   906  
   907  	// validity
   908  	c.Assert(valsets.Conflict(), IsNil)
   909  
   910  	vsKeys, rev, err := valsets.CheckPresenceRequired(naming.Snap("my-snap"))
   911  	c.Assert(err, IsNil)
   912  	c.Check(rev, DeepEquals, snap.Revision{N: 7})
   913  	c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2", "16/account-id/my-snap-ctl3/1"})
   914  
   915  	vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("my-snap", "mysnapididididididididididididid"))
   916  	c.Assert(err, IsNil)
   917  	c.Check(rev, DeepEquals, snap.Revision{N: 7})
   918  	c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2", "16/account-id/my-snap-ctl3/1"})
   919  
   920  	// other-snap is not required
   921  	vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("other-snap"))
   922  	c.Assert(err, ErrorMatches, `unexpected presence "invalid" for snap "other-snap"`)
   923  	pr, ok := err.(*snapasserts.PresenceConstraintError)
   924  	c.Assert(ok, Equals, true)
   925  	c.Check(pr.SnapName, Equals, "other-snap")
   926  	c.Check(pr.Presence, Equals, asserts.PresenceInvalid)
   927  	c.Check(rev, DeepEquals, snap.Revision{N: 0})
   928  	c.Check(vsKeys, HasLen, 0)
   929  
   930  	// unknown snap is not required
   931  	vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid"))
   932  	c.Assert(err, IsNil)
   933  	c.Check(rev, DeepEquals, snap.Revision{N: 0})
   934  	c.Check(vsKeys, HasLen, 0)
   935  
   936  	// just one set, required but no revision specified
   937  	valsets = snapasserts.NewValidationSets()
   938  	c.Assert(valsets.Add(valset3), IsNil)
   939  	vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("my-snap"))
   940  	c.Assert(err, IsNil)
   941  	c.Check(rev, DeepEquals, snap.Revision{N: 0})
   942  	c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl3/1"})
   943  }
   944  
   945  func (s *validationSetsSuite) TestIsPresenceInvalid(c *C) {
   946  	valset1 := assertstest.FakeAssertion(map[string]interface{}{
   947  		"type":         "validation-set",
   948  		"authority-id": "account-id",
   949  		"series":       "16",
   950  		"account-id":   "account-id",
   951  		"name":         "my-snap-ctl",
   952  		"sequence":     "1",
   953  		"snaps": []interface{}{
   954  			map[string]interface{}{
   955  				"name":     "my-snap",
   956  				"id":       "mysnapididididididididididididid",
   957  				"presence": "invalid",
   958  			},
   959  			map[string]interface{}{
   960  				"name":     "other-snap",
   961  				"id":       "123456ididididididididididididid",
   962  				"presence": "optional",
   963  			},
   964  		},
   965  	}).(*asserts.ValidationSet)
   966  
   967  	valset2 := assertstest.FakeAssertion(map[string]interface{}{
   968  		"type":         "validation-set",
   969  		"authority-id": "account-id",
   970  		"series":       "16",
   971  		"account-id":   "account-id",
   972  		"name":         "my-snap-ctl2",
   973  		"sequence":     "2",
   974  		"snaps": []interface{}{
   975  			map[string]interface{}{
   976  				"name":     "my-snap",
   977  				"id":       "mysnapididididididididididididid",
   978  				"presence": "invalid",
   979  			},
   980  		},
   981  	}).(*asserts.ValidationSet)
   982  
   983  	valsets := snapasserts.NewValidationSets()
   984  
   985  	// no validation sets
   986  	vsKeys, err := valsets.CheckPresenceInvalid(naming.Snap("my-snap"))
   987  	c.Assert(err, IsNil)
   988  	c.Check(vsKeys, HasLen, 0)
   989  
   990  	c.Assert(valsets.Add(valset1), IsNil)
   991  	c.Assert(valsets.Add(valset2), IsNil)
   992  
   993  	// validity
   994  	c.Assert(valsets.Conflict(), IsNil)
   995  
   996  	// invalid in two sets
   997  	vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("my-snap"))
   998  	c.Assert(err, IsNil)
   999  	c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2"})
  1000  
  1001  	vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("my-snap", "mysnapididididididididididididid"))
  1002  	c.Assert(err, IsNil)
  1003  	c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2"})
  1004  
  1005  	// other-snap isn't invalid
  1006  	vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("other-snap"))
  1007  	c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`)
  1008  	pr, ok := err.(*snapasserts.PresenceConstraintError)
  1009  	c.Assert(ok, Equals, true)
  1010  	c.Check(pr.SnapName, Equals, "other-snap")
  1011  	c.Check(pr.Presence, Equals, asserts.PresenceOptional)
  1012  	c.Check(vsKeys, HasLen, 0)
  1013  
  1014  	vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("other-snap", "123456ididididididididididididid"))
  1015  	c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`)
  1016  	c.Check(vsKeys, HasLen, 0)
  1017  
  1018  	// unknown snap isn't invalid
  1019  	vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid"))
  1020  	c.Assert(err, IsNil)
  1021  	c.Check(vsKeys, HasLen, 0)
  1022  }
  1023  
  1024  func (s *validationSetsSuite) TestParseValidationSet(c *C) {
  1025  	for _, tc := range []struct {
  1026  		input    string
  1027  		errMsg   string
  1028  		account  string
  1029  		name     string
  1030  		sequence int
  1031  	}{
  1032  		{
  1033  			input:   "foo/bar",
  1034  			account: "foo",
  1035  			name:    "bar",
  1036  		},
  1037  		{
  1038  			input:    "foo/bar=9",
  1039  			account:  "foo",
  1040  			name:     "bar",
  1041  			sequence: 9,
  1042  		},
  1043  		{
  1044  			input:  "foo",
  1045  			errMsg: `cannot parse validation set "foo": expected a single account/name`,
  1046  		},
  1047  		{
  1048  			input:  "foo/bar/baz",
  1049  			errMsg: `cannot parse validation set "foo/bar/baz": expected a single account/name`,
  1050  		},
  1051  		{
  1052  			input:  "",
  1053  			errMsg: `cannot parse validation set "": expected a single account/name`,
  1054  		},
  1055  		{
  1056  			input:  "foo=1",
  1057  			errMsg: `cannot parse validation set "foo=1": expected a single account/name`,
  1058  		},
  1059  		{
  1060  			input:  "foo/bar=x",
  1061  			errMsg: `cannot parse validation set "foo/bar=x": invalid sequence: strconv.Atoi: parsing "x": invalid syntax`,
  1062  		},
  1063  		{
  1064  			input:  "foo=bar=",
  1065  			errMsg: `cannot parse validation set "foo=bar=": expected account/name=seq`,
  1066  		},
  1067  		{
  1068  			input:  "$foo/bar",
  1069  			errMsg: `cannot parse validation set "\$foo/bar": invalid account ID "\$foo"`,
  1070  		},
  1071  		{
  1072  			input:  "foo/$bar",
  1073  			errMsg: `cannot parse validation set "foo/\$bar": invalid validation set name "\$bar"`,
  1074  		},
  1075  	} {
  1076  		account, name, seq, err := snapasserts.ParseValidationSet(tc.input)
  1077  		if tc.errMsg != "" {
  1078  			c.Assert(err, ErrorMatches, tc.errMsg)
  1079  		} else {
  1080  			c.Assert(err, IsNil)
  1081  		}
  1082  		c.Check(account, Equals, tc.account)
  1083  		c.Check(name, Equals, tc.name)
  1084  		c.Check(seq, Equals, tc.sequence)
  1085  	}
  1086  }
  1087  
  1088  func (s *validationSetsSuite) TestValidationSetKeyFormat(c *C) {
  1089  	series, acc, name := "a", "b", "c"
  1090  	sequence := 1
  1091  
  1092  	valSet := assertstest.FakeAssertion(map[string]interface{}{
  1093  		"type":         "validation-set",
  1094  		"authority-id": acc,
  1095  		"series":       series,
  1096  		"account-id":   acc,
  1097  		"name":         name,
  1098  		"sequence":     strconv.Itoa(sequence),
  1099  		"snaps": []interface{}{
  1100  			map[string]interface{}{
  1101  				"name":     "my-snap",
  1102  				"id":       "mysnapididididididididididididid",
  1103  				"presence": "required",
  1104  			},
  1105  		},
  1106  	}).(*asserts.ValidationSet)
  1107  
  1108  	valSetKey := snapasserts.NewValidationSetKey(valSet)
  1109  	c.Assert(valSetKey.String(), Equals, fmt.Sprintf("%s/%s/%s/%d", series, acc, name, sequence))
  1110  }
  1111  
  1112  func (s *validationSetsSuite) TestValidationSetKeySliceSort(c *C) {
  1113  	valSets := snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1", "2/a/a/2", "a/a/a/1"})
  1114  	rand.Shuffle(len(valSets), func(x, y int) {
  1115  		valSets[x], valSets[y] = valSets[y], valSets[x]
  1116  	})
  1117  
  1118  	sort.Sort(valSets)
  1119  	c.Assert(valSets, DeepEquals, snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1", "2/a/a/2", "a/a/a/1"}))
  1120  }
  1121  
  1122  func (s *validationSetsSuite) TestValidationSetKeySliceCommaSeparated(c *C) {
  1123  	valSets := snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1"})
  1124  	c.Assert(valSets.CommaSeparated(), Equals, "1/a/a/1,1/a/b/1,1/a/b/2,2/a/a/1")
  1125  }
  1126  
  1127  func (s *validationSetsSuite) TestValidationSetKeyComponents(c *C) {
  1128  	valsetKey := snapasserts.NewValidationSetKey(assertstest.FakeAssertion(map[string]interface{}{
  1129  		"type":         "validation-set",
  1130  		"series":       "a",
  1131  		"authority-id": "b",
  1132  		"account-id":   "b",
  1133  		"name":         "c",
  1134  		"sequence":     "13",
  1135  		"snaps": []interface{}{
  1136  			map[string]interface{}{
  1137  				"name":     "my-snap",
  1138  				"id":       "mysnapididididididididididididid",
  1139  				"presence": "required",
  1140  			},
  1141  		},
  1142  	}).(*asserts.ValidationSet))
  1143  	c.Assert(valsetKey.Components(), DeepEquals, []string{"a", "b", "c", "13"})
  1144  }