func (s *StoreSuite) TestRedundantUpdate(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} pub, err := s.store.CharmPublisher(urls, "digest-0") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // All charms are already on digest-0. pub, err = s.store.CharmPublisher(urls, "digest-0") c.Assert(err, gc.ErrorMatches, "charm is up-to-date") c.Assert(err, gc.Equals, charmstore.ErrRedundantUpdate) c.Assert(pub, gc.IsNil) // Now add a second revision just for wordpress-b. pub, err = s.store.CharmPublisher(urls[1:], "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // Same digest bumps revision because one of them was old. pub, err = s.store.CharmPublisher(urls, "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 2) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) }
func (s *GitDeployerSuite) TestInstall(c *gc.C) { // Prepare. info := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info, nil) c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Install. err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Check content. data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello") target := charm.NewGitDir(s.targetPath) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-1")) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 2) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Deployed charm "cs:s/c-1"\.`) c.Assert(lines[1], gc.Matches, `[0-9a-f]{7} Imported charm "cs:s/c-1"\.`) }
func (s *charmsSuite) TestUploadBumpsRevision(c *gc.C) { // Add the dummy charm with revision 1. ch := charmtesting.Charms.Bundle(c.MkDir(), "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) _, err = s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) // Now try uploading the same revision and verify it gets bumped, // and the BundleURL and BundleSha256 are calculated. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-2") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 2) c.Assert(sch.IsUploaded(), jc.IsTrue) // No more checks for these two here, because they // are verified in TestUploadRespectsLocalRevision. c.Assert(sch.BundleURL(), gc.Not(gc.Equals), "") c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), "") }
func (s *StoreSuite) TestLockUpdatesExpires(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} // Initiate an update of B only to force a partial conflict. lock1, err := s.store.LockUpdates(urls[1:]) c.Assert(err, gc.IsNil) // Hack time to force an expiration. locks := s.Session.DB("juju").C("locks") selector := bson.M{"_id": urlB.String()} update := bson.M{"time": bson.Now().Add(-charmstore.UpdateTimeout - 10e9)} err = locks.Update(selector, update) c.Check(err, gc.IsNil) // Works due to expiration of previous lock. lock2, err := s.store.LockUpdates(urls) c.Assert(err, gc.IsNil) defer lock2.Unlock() // The expired lock was forcefully killed. Unlocking it must // not interfere with lock2 which is still alive. lock1.Unlock() // The above statement was a NOOP and lock2 is still in effect, // so attempting another lock must necessarily fail. lock3, err := s.store.LockUpdates(urls) c.Check(err, gc.Equals, charmstore.ErrUpdateConflict) c.Check(lock3, gc.IsNil) }
func (s *StoreSuite) TestBranchLocation(c *gc.C) { charmURL := charm.MustParseURL("cs:series/name") location := s.store.BranchLocation(charmURL) c.Assert(location, gc.Equals, "lp:charms/series/name") charmURL = charm.MustParseURL("cs:~user/series/name") location = s.store.BranchLocation(charmURL) c.Assert(location, gc.Equals, "lp:~user/charms/series/name/trunk") }
func (s *StoreSuite) TestGetCacheImplicitRevision(c *gc.C) { base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) }
func (s *StoreSuite) TestPublishCharmDistro(c *gc.C) { branch := s.dummyBranch(c, "~joe/charms/oneiric/dummy/trunk") // The Distro call will look for bare /charms, first. gitjujutesting.Server.Response(200, jsonType, []byte("{}")) // And then it picks up the tips. data := fmt.Sprintf(`[`+ `["file://%s", "rev1", ["oneiric", "precise"]],`+ `["file://%s", "%s", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/trunk", "rev2", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/skip-me", "rev3", []]`+ `]`, branch.path(), branch.path(), branch.digest()) gitjujutesting.Server.Response(200, jsonType, []byte(data)) apiBase := lpad.APIBase(gitjujutesting.Server.URL) err := charmstore.PublishCharmsDistro(s.store, apiBase) // Should have a single failure from the trunk branch that doesn't // exist. The redundant update with the known digest should be // ignored, and skip-me isn't a supported branch name so it's // ignored as well. c.Assert(err, gc.ErrorMatches, `1 branch\(es\) failed to be published`) berr := err.(charmstore.PublishBranchErrors)[0] c.Assert(berr.URL, gc.Equals, "file:///non-existent/~jeff/charms/precise/bad/trunk") c.Assert(berr.Err, gc.ErrorMatches, "(?s).*bzr: ERROR: Not a branch.*") for _, url := range []string{"cs:oneiric/dummy", "cs:precise/dummy-0", "cs:~joe/oneiric/dummy-0"} { dummy, err := s.store.CharmInfo(charm.MustParseURL(url)) c.Assert(err, gc.IsNil) c.Assert(dummy.Meta().Name, gc.Equals, "dummy") } // The known digest should have been ignored, so revision is still at 0. _, err = s.store.CharmInfo(charm.MustParseURL("cs:~joe/oneiric/dummy-1")) c.Assert(err, gc.Equals, charmstore.ErrNotFound) // bare /charms lookup req := gitjujutesting.Server.WaitRequest() c.Assert(req.Method, gc.Equals, "GET") c.Assert(req.URL.Path, gc.Equals, "/charms") // tips request req = gitjujutesting.Server.WaitRequest() c.Assert(req.Method, gc.Equals, "GET") c.Assert(req.URL.Path, gc.Equals, "/charms") c.Assert(req.Form["ws.op"], gc.DeepEquals, []string{"getBranchTips"}) c.Assert(req.Form["since"], gc.IsNil) // Request must be signed by juju. c.Assert(req.Header.Get("Authorization"), gc.Matches, `.*oauth_consumer_key="juju".*`) }
func (s *LocalRepoSuite) TestMissingRepo(c *gc.C) { c.Assert(os.RemoveAll(s.repo.Path), gc.IsNil) _, err := charm.Latest(s.repo, charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) c.Assert(ioutil.WriteFile(s.repo.Path, nil, 0666), gc.IsNil) _, err = charm.Latest(s.repo, charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) }
func (s *URLSuite) TestMustParseURL(c *gc.C) { url := charm.MustParseURL("cs:series/name") c.Assert(url, gc.DeepEquals, &charm.URL{Reference: charm.Reference{"cs", "", "name", -1}, Series: "series"}) f := func() { charm.MustParseURL("local:@@/name") } c.Assert(f, gc.PanicMatches, "charm URL has invalid series: .*") f = func() { charm.MustParseURL("cs:~user") } c.Assert(f, gc.PanicMatches, "charm URL without charm name: .*") f = func() { charm.MustParseURL("cs:~user") } c.Assert(f, gc.PanicMatches, "charm URL without charm name: .*") f = func() { charm.MustParseURL("cs:name") } c.Assert(f, gc.PanicMatches, "charm url series is not resolved") }
func (s *StoreSuite) TestLatest(c *gc.C) { urls := []*charm.URL{ charm.MustParseURL("cs:series/good"), charm.MustParseURL("cs:series/good-2"), charm.MustParseURL("cs:series/good-99"), } revInfo, err := s.store.Latest(urls...) c.Assert(err, gc.IsNil) c.Assert(revInfo, gc.DeepEquals, []charm.CharmRevision{ {23, "c89d9b522cebbd68061048ed2910180e1b63b6afaa373d1fe1c47ff9970be126", nil}, {23, "c89d9b522cebbd68061048ed2910180e1b63b6afaa373d1fe1c47ff9970be126", nil}, {23, "c89d9b522cebbd68061048ed2910180e1b63b6afaa373d1fe1c47ff9970be126", nil}, }) }
func (s *StoreSuite) TestInfo(c *gc.C) { charmURLs := []charm.Location{ charm.MustParseURL("cs:series/good"), charm.MustParseURL("cs:series/better"), charm.MustParseURL("cs:series/best"), } infos, err := s.store.Info(charmURLs...) c.Assert(err, gc.IsNil) c.Assert(infos, gc.HasLen, 3) expected := []int{23, 24, 25} for i, info := range infos { c.Assert(info.Errors, gc.IsNil) c.Assert(info.Revision, gc.Equals, expected[i]) } }
func (s *StoreSuite) TestGetBadCache(c *gc.C) { c.Assert(os.Mkdir(filepath.Join(charm.CacheDir, "cache"), 0777), gc.IsNil) base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") name := charm.Quote(revCharmURL.String()) + ".charm" err := ioutil.WriteFile(filepath.Join(charm.CacheDir, "cache", name), nil, 0666) c.Assert(err, gc.IsNil) ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) }
func (s *clientSuite) TestAddLocalCharm(c *gc.C) { charmArchive := charmtesting.Charms.Bundle(c.MkDir(), "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", charmArchive.Meta().Name, charmArchive.Revision()), ) client := s.APIState.Client() // Test the sanity checks first. _, err := client.AddLocalCharm(charm.MustParseURL("cs:quantal/wordpress-1"), nil) c.Assert(err, gc.ErrorMatches, `expected charm URL with local: schema, got "cs:quantal/wordpress-1"`) // Upload an archive with its original revision. savedURL, err := client.AddLocalCharm(curl, charmArchive) c.Assert(err, gc.IsNil) c.Assert(savedURL.String(), gc.Equals, curl.String()) // Upload a charm directory with changed revision. charmDir := charmtesting.Charms.ClonedDir(c.MkDir(), "dummy") charmDir.SetDiskRevision(42) savedURL, err = client.AddLocalCharm(curl, charmDir) c.Assert(err, gc.IsNil) c.Assert(savedURL.Revision, gc.Equals, 42) // Upload a charm directory again, revision should be bumped. savedURL, err = client.AddLocalCharm(curl, charmDir) c.Assert(err, gc.IsNil) c.Assert(savedURL.String(), gc.Equals, curl.WithRevision(43).String()) // Finally, try the NotImplementedError by mocking the server // address to a handler that returns 405 Method Not Allowed for // POST. lis, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) defer lis.Close() url := fmt.Sprintf("http://%v", lis.Addr()) http.HandleFunc("/charms", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } }) go func() { http.Serve(lis, nil) }() api.SetServerRoot(client, url) _, err = client.AddLocalCharm(curl, charmArchive) c.Assert(err, jc.Satisfies, params.IsCodeNotImplemented) }
func (s *StoreSuite) TestCharmPublishError(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} // Publish one successfully to bump the revision so we can // make sure it is being correctly set below. pub, err := s.store.CharmPublisher(urls, "one-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "beforeWrite"}) c.Assert(err, gc.ErrorMatches, "beforeWrite") pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "afterWrite"}) c.Assert(err, gc.ErrorMatches, "afterWrite") // Still at the original charm revision that succeeded first. info, err := s.store.CharmInfo(url) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Digest(), gc.Equals, "one-digest") }
func (s *StoreSuite) TestInfoError(c *gc.C) { charmURL := charm.MustParseURL("cs:series/borken") info, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Errors, gc.DeepEquals, []string{"badness"}) }
func (s *FilterSuite) TestCharmErrorEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag().String()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below assertNoChange := func() { s.BackingState.StartSync() select { case <-f.ConfigEvents(): c.Fatalf("unexpected config event") case <-time.After(coretesting.ShortWait): } } // Check setting an invalid charm URL does not send events. err = f.SetCharm(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) // Filter died after the error, so restart it. f, err = newFilter(s.uniter, s.unit.Tag().String()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below // Check with a nil charm URL, again no changes. err = f.SetCharm(nil) c.Assert(err, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) }
func (s *DeploySuite) TestCharmDir(c *gc.C) { charmtesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 1, 0) }
func (s *DeploySuite) TestNumUnits(c *gc.C) { charmtesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "-n", "13") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 13, 0) }
func (s *DeploySuite) TestSubordinateCharm(c *gc.C) { charmtesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "local:logging") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/logging-1") s.AssertService(c, "logging", curl, 0, 0) }
func (s *StoreSuite) TestInfoWarning(c *gc.C) { charmURL := charm.MustParseURL("cs:series/unwise") info, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Warnings, gc.DeepEquals, []string{"foolishness"}) }
func (s *DeployLocalSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) curl := charm.MustParseURL("local:quantal/dummy") charm, err := testing.PutCharm(s.State, curl, s.repo, false) c.Assert(err, gc.IsNil) s.charm = charm }
func (s *LocalRepoSuite) TestMultipleVersions(c *gc.C) { charmURL := charm.MustParseURL("local:quantal/upgrade") s.addDir("upgrade1") rev, err := charm.Latest(s.repo, charmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 1) ch, err := s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) s.addDir("upgrade2") rev, err = charm.Latest(s.repo, charmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) ch, err = s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 2) revCharmURL := charmURL.WithRevision(1) rev, err = charm.Latest(s.repo, revCharmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) ch, err = s.repo.Get(revCharmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) badRevCharmURL := charmURL.WithRevision(33) rev, err = charm.Latest(s.repo, badRevCharmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) _, err = s.repo.Get(badRevCharmURL) s.checkNotFoundErr(c, err, badRevCharmURL) }
func (s *StoreSuite) TestConflictingUpdate(c *gc.C) { // This test checks that if for whatever reason the locking // safety-net fails, adding two charms in parallel still // results in a sane outcome. url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} pub1, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub1.Revision(), gc.Equals, 0) pub2, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub2.Revision(), gc.Equals, 0) // The first publishing attempt should work. err = pub2.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // Attempting to finish the second attempt should break, // since it lost the race and the given revision is already // in place. err = pub1.Publish(&FakeCharmDir{}) c.Assert(err, gc.Equals, charmstore.ErrUpdateConflict) }
func (s *SSHSuite) TestSSHCommand(c *gc.C) { m := s.makeMachines(3, c, true) ch := charmtesting.Charms.Dir("dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) srv := s.AddTestingService(c, "mysql", dummy) s.addUnit(srv, m[0], c) srv = s.AddTestingService(c, "mongodb", dummy) s.addUnit(srv, m[1], c) s.addUnit(srv, m[2], c) for i, t := range sshTests { c.Logf("test %d: %s -> %s\n", i, t.about, t.args) ctx := coretesting.Context(c) jujucmd := cmd.NewSuperCommand(cmd.SuperCommandParams{}) jujucmd.Register(envcmd.Wrap(&SSHCommand{})) code := cmd.Main(jujucmd, ctx, t.args) c.Check(code, gc.Equals, 0) c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, t.result) } }
func (s *UnitSuite) TestUnitCharm(c *gc.C) { preventUnitDestroyRemove(c, s.unit) curl, ok := s.unit.CharmURL() c.Assert(ok, gc.Equals, false) c.Assert(curl, gc.IsNil) err := s.unit.SetCharmURL(nil) c.Assert(err, gc.ErrorMatches, "cannot set nil charm url") err = s.unit.SetCharmURL(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, gc.ErrorMatches, `unknown charm url "cs:missing/one-1"`) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(curl, gc.DeepEquals, s.charm.URL()) err = s.unit.Destroy() c.Assert(err, gc.IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(curl, gc.DeepEquals, s.charm.URL()) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is dead`) }
func (s *StoreSuite) TestInfoDNSError(c *gc.C) { store := charm.NewStore("http://127.1.2.3") charmURL := charm.MustParseURL("cs:series/good") resp, err := store.Info(charmURL) c.Assert(resp, gc.IsNil) expect := `Cannot access the charm store. .*` c.Assert(err, gc.ErrorMatches, expect) }
func (s *StoreSuite) TestError(c *gc.C) { charmURL := charm.MustParseURL("cs:series/borken") expect := `charm info errors for "cs:series/borken": badness` _, err := charm.Latest(s.store, charmURL) c.Assert(err, gc.ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, gc.ErrorMatches, expect) }
func (s *StoreSuite) TestMissing(c *gc.C) { charmURL := charm.MustParseURL("cs:series/missing") expect := `charm not found: cs:series/missing` _, err := charm.Latest(s.store, charmURL) c.Assert(err, gc.ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, gc.ErrorMatches, expect) }
func (s *StoreSuite) TestNilMetadata(c *gc.C) { base := "cs:series/good" charmURL := charm.MustParseURL(base) _, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.Metadata, gc.HasLen, 0) }
func (s *StoreSuite) TestEventWithDigest(c *gc.C) { charmURL := charm.MustParseURL("cs:series/good") event, err := s.store.Event(charmURL, "the-digest") c.Assert(err, gc.IsNil) c.Assert(event.Errors, gc.IsNil) c.Assert(event.Revision, gc.Equals, 23) c.Assert(event.Digest, gc.Equals, "the-digest") }