1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package charmstore // import "gopkg.in/juju/charmstore.v5-unstable/internal/charmstore"
12
jc "github.com/juju/testing/checkers"
13
gc "gopkg.in/check.v1"
14
"gopkg.in/juju/charm.v6-unstable"
15
"gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
17
"gopkg.in/juju/charmstore.v5-unstable/internal/mongodoc"
18
"gopkg.in/juju/charmstore.v5-unstable/internal/router"
19
"gopkg.in/juju/charmstore.v5-unstable/internal/storetesting"
22
type StoreSearchSuite struct {
23
storetesting.IsolatedMgoESSuite
29
var _ = gc.Suite(&StoreSearchSuite{})
31
func (s *StoreSearchSuite) SetUpTest(c *gc.C) {
32
s.IsolatedMgoESSuite.SetUpTest(c)
34
// Temporarily set LegacyDownloadCountsEnabled to false, so that the real
35
// code path can be reached by tests in this suite.
36
// TODO (frankban): remove this block when removing the legacy counts
38
original := LegacyDownloadCountsEnabled
39
LegacyDownloadCountsEnabled = false
40
s.AddCleanup(func(*gc.C) {
41
LegacyDownloadCountsEnabled = original
44
s.index = SearchIndex{s.ES, s.TestIndex}
45
s.ES.RefreshIndex(".versions")
46
pool, err := NewPool(s.Session.DB("foo"), &s.index, nil, ServerParams{})
47
c.Assert(err, gc.IsNil)
49
s.store = pool.Store()
51
c.Assert(err, gc.IsNil)
54
func (s *StoreSearchSuite) TearDownTest(c *gc.C) {
57
s.IsolatedMgoESSuite.TearDownTest(c)
60
func newEntity(id string, promulgatedRevision int, supportedSeries ...string) *mongodoc.Entity {
61
url := charm.MustParseURL(id)
63
if promulgatedRevision > -1 {
67
purl.Revision = promulgatedRevision
69
if url.Series == "bundle" {
71
} else if url.Series != "" {
72
supportedSeries = []string{url.Series}
74
return &mongodoc.Entity{
76
SupportedSeries: supportedSeries,
78
PromulgatedRevision: promulgatedRevision,
82
type searchEntity struct {
83
entity *mongodoc.Entity
85
bundleData *charm.BundleData
90
var searchEntities = map[string]searchEntity{
91
"wordpress": searchEntity{
92
entity: newEntity("cs:~charmers/precise/wordpress-23", 23),
93
charmMeta: &charm.Meta{
95
Requires: map[string]charm.Relation{
96
"mysql": charm.Relation{
99
Scope: charm.ScopeGlobal,
102
Categories: []string{"wordpress"},
103
Tags: []string{"wordpressTAG"},
105
acl: []string{params.Everyone},
107
"mysql": searchEntity{
108
entity: newEntity("cs:~openstack-charmers/xenial/mysql-7", 7),
109
charmMeta: &charm.Meta{
110
Summary: "Database Engine",
111
Provides: map[string]charm.Relation{
112
"mysql": charm.Relation{
115
Scope: charm.ScopeGlobal,
118
Categories: []string{"mysql"},
119
Tags: []string{"mysqlTAG"},
121
acl: []string{params.Everyone},
124
"varnish": searchEntity{
125
entity: newEntity("cs:~foo/xenial/varnish-1", -1),
126
charmMeta: &charm.Meta{
127
Summary: "Database Engine",
128
Categories: []string{"varnish"},
129
Tags: []string{"varnishTAG"},
131
acl: []string{params.Everyone},
134
"riak": searchEntity{
135
entity: newEntity("cs:~charmers/xenial/riak-67", 67),
136
charmMeta: &charm.Meta{
137
Categories: []string{"riak"},
138
Tags: []string{"riakTAG"},
140
acl: []string{"charmers"},
142
"wordpress-simple": searchEntity{
143
entity: newEntity("cs:~charmers/bundle/wordpress-simple-4", 4),
144
bundleData: &charm.BundleData{
145
Applications: map[string]*charm.ApplicationSpec{
146
"wordpress": &charm.ApplicationSpec{
150
Tags: []string{"wordpress"},
152
acl: []string{params.Everyone},
155
// Note: "squid-forwardproxy" shares a trigram "dpr" with "wordpress".
156
"squid-forwardproxy": searchEntity{
157
entity: newEntity("cs:~charmers/wily/squid-forwardproxy-3", 3),
158
charmMeta: &charm.Meta{},
159
acl: []string{params.Everyone},
162
// Note: "cloud-controller-worker-v2" shares a trigram "wor" with "wordpress".
164
"cloud-controller-worker-v2": searchEntity{
165
entity: newEntity("cs:~cf-charmers/trusty/cloud-controller-worker-v2-7", -1),
166
charmMeta: &charm.Meta{},
167
acl: []string{params.Everyone},
172
func (s *StoreSearchSuite) addEntities(c *gc.C) {
173
for _, ent := range searchEntities {
174
if ent.charmMeta == nil {
180
EntityResolvedURL(ent.entity),
181
storetesting.NewCharm(ent.charmMeta),
186
for _, ent := range searchEntities {
187
if ent.bundleData == nil {
193
EntityResolvedURL(ent.entity),
194
storetesting.NewBundle(ent.bundleData),
199
s.store.pool.statsCache.EvictAll()
200
err := s.store.syncSearch()
201
c.Assert(err, gc.IsNil)
204
func (s *StoreSearchSuite) TestSuccessfulExport(c *gc.C) {
205
s.store.pool.statsCache.EvictAll()
206
for _, ent := range searchEntities {
207
entity, err := s.store.FindEntity(EntityResolvedURL(ent.entity), nil)
208
c.Assert(err, gc.IsNil)
209
var actual json.RawMessage
210
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(entity.URL), &actual)
211
c.Assert(err, gc.IsNil)
212
series := entity.SupportedSeries
213
if ent.bundleData != nil {
214
series = []string{"bundle"}
218
TotalDownloads: int64(ent.downloads),
224
c.Assert(string(actual), jc.JSONEquals, doc)
228
func (s *StoreSearchSuite) TestNoExportDeprecated(c *gc.C) {
229
charmArchive := storetesting.NewCharm(nil)
230
url := router.MustNewResolvedURL("cs:~charmers/saucy/mysql-4", -1)
239
var entity *mongodoc.Entity
240
err := s.store.DB.Entities().FindId("cs:~openstack-charmers/xenial/mysql-7").One(&entity)
241
c.Assert(err, gc.IsNil)
242
present, err := s.store.ES.HasDocument(s.TestIndex, typeName, s.store.ES.getID(entity.URL))
243
c.Assert(err, gc.IsNil)
244
c.Assert(present, gc.Equals, true)
246
err = s.store.DB.Entities().FindId("cs:~charmers/saucy/mysql-4").One(&entity)
247
c.Assert(err, gc.IsNil)
248
present, err = s.store.ES.HasDocument(s.TestIndex, typeName, s.store.ES.getID(entity.URL))
249
c.Assert(err, gc.IsNil)
250
c.Assert(present, gc.Equals, false)
253
func (s *StoreSearchSuite) TestExportOnlyLatest(c *gc.C) {
254
charmArchive := storetesting.NewCharm(nil)
255
url := router.MustNewResolvedURL("cs:~charmers/precise/wordpress-24", -1)
261
[]string{"charmers", params.Everyone},
264
var expected, old *mongodoc.Entity
265
var actual json.RawMessage
266
err := s.store.DB.Entities().FindId("cs:~charmers/precise/wordpress-23").One(&old)
267
c.Assert(err, gc.IsNil)
268
err = s.store.DB.Entities().FindId("cs:~charmers/precise/wordpress-24").One(&expected)
269
c.Assert(err, gc.IsNil)
270
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(old.URL), &actual)
271
c.Assert(err, gc.IsNil)
274
ReadACLs: []string{"charmers", params.Everyone},
275
Series: expected.SupportedSeries,
279
c.Assert(string(actual), jc.JSONEquals, doc)
282
func (s *StoreSearchSuite) TestExportMultiSeriesCharmsCreateExpandedVersions(c *gc.C) {
283
charmArchive := storetesting.NewCharm(nil)
284
url := router.MustNewResolvedURL("cs:~charmers/xenial/juju-gui-24", -1)
290
[]string{"charmers"},
293
charmArchive = storetesting.NewCharm(storetesting.MetaWithSupportedSeries(nil, "trusty", "xenial", "utopic", "vivid", "wily"))
294
url = router.MustNewResolvedURL("cs:~charmers/juju-gui-25", -1)
300
[]string{"charmers"},
303
var expected, old *mongodoc.Entity
304
var actual json.RawMessage
305
err := s.store.DB.Entities().FindId("cs:~charmers/xenial/juju-gui-24").One(&old)
306
c.Assert(err, gc.IsNil)
307
err = s.store.DB.Entities().FindId("cs:~charmers/juju-gui-25").One(&expected)
308
c.Assert(err, gc.IsNil)
309
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(expected.URL), &actual)
310
c.Assert(err, gc.IsNil)
313
ReadACLs: []string{"charmers"},
314
Series: expected.SupportedSeries,
318
c.Assert(string(actual), jc.JSONEquals, doc)
319
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(old.URL), &actual)
320
c.Assert(err, gc.IsNil)
321
expected.URL.Series = old.URL.Series
324
ReadACLs: []string{"charmers"},
325
Series: []string{old.URL.Series},
329
c.Assert(string(actual), jc.JSONEquals, doc)
332
func (s *StoreSearchSuite) TestExportSearchDocument(c *gc.C) {
333
var entity *mongodoc.Entity
334
var actual json.RawMessage
335
err := s.store.DB.Entities().FindId("cs:~charmers/precise/wordpress-23").One(&entity)
336
c.Assert(err, gc.IsNil)
337
doc := SearchDoc{Entity: entity, TotalDownloads: 4000}
338
err = s.store.ES.update(&doc)
339
c.Assert(err, gc.IsNil)
340
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(entity.URL), &actual)
341
c.Assert(err, gc.IsNil)
342
c.Assert(string(actual), jc.JSONEquals, doc)
345
var searchTests = []struct {
349
totalDiff int // len(results) + totalDiff = expected total
352
about: "basic text search",
357
searchEntities["wordpress"].entity,
358
searchEntities["wordpress-simple"].entity,
361
about: "blank text search",
366
searchEntities["cloud-controller-worker-v2"].entity,
367
searchEntities["wordpress"].entity,
368
searchEntities["mysql"].entity,
369
searchEntities["varnish"].entity,
370
searchEntities["squid-forwardproxy"].entity,
371
searchEntities["wordpress-simple"].entity,
374
about: "autocomplete search",
380
searchEntities["wordpress"].entity,
381
searchEntities["wordpress-simple"].entity,
384
about: "non-matching autocomplete search",
391
about: "description filter search",
394
Filters: map[string][]string{
395
"description": {"blog"},
399
searchEntities["wordpress"].entity,
402
about: "name filter search",
405
Filters: map[string][]string{
406
"name": {"wordpress"},
410
searchEntities["wordpress"].entity,
413
about: "owner filter search",
416
Filters: map[string][]string{
421
searchEntities["varnish"].entity,
424
about: "provides filter search",
427
Filters: map[string][]string{
428
"provides": {"mysql"},
432
searchEntities["mysql"].entity,
435
about: "requires filter search",
438
Filters: map[string][]string{
439
"requires": {"mysql"},
443
searchEntities["wordpress"].entity,
446
about: "series filter search",
449
Filters: map[string][]string{
450
"series": {"xenial"},
454
searchEntities["mysql"].entity,
455
searchEntities["varnish"].entity,
458
about: "summary filter search",
461
Filters: map[string][]string{
462
"summary": {"Database engine"},
466
searchEntities["mysql"].entity,
467
searchEntities["varnish"].entity,
470
about: "tags filter search",
473
Filters: map[string][]string{
474
"tags": {"wordpress"},
478
searchEntities["wordpress"].entity,
479
searchEntities["wordpress-simple"].entity,
482
about: "bundle type filter search",
485
Filters: map[string][]string{
490
searchEntities["wordpress-simple"].entity,
493
about: "charm type filter search",
496
Filters: map[string][]string{
501
searchEntities["cloud-controller-worker-v2"].entity,
502
searchEntities["wordpress"].entity,
503
searchEntities["mysql"].entity,
504
searchEntities["varnish"].entity,
505
searchEntities["squid-forwardproxy"].entity,
508
about: "charm & bundle type filter search",
511
Filters: map[string][]string{
512
"type": {"charm", "bundle"},
516
searchEntities["cloud-controller-worker-v2"].entity,
517
searchEntities["wordpress"].entity,
518
searchEntities["mysql"].entity,
519
searchEntities["varnish"].entity,
520
searchEntities["squid-forwardproxy"].entity,
521
searchEntities["wordpress-simple"].entity,
524
about: "invalid filter search",
527
Filters: map[string][]string{
528
"no such filter": {"foo"},
532
searchEntities["cloud-controller-worker-v2"].entity,
533
searchEntities["wordpress"].entity,
534
searchEntities["mysql"].entity,
535
searchEntities["varnish"].entity,
536
searchEntities["squid-forwardproxy"].entity,
537
searchEntities["wordpress-simple"].entity,
540
about: "valid & invalid filter search",
543
Filters: map[string][]string{
544
"no such filter": {"foo"},
549
searchEntities["cloud-controller-worker-v2"].entity,
550
searchEntities["wordpress"].entity,
551
searchEntities["mysql"].entity,
552
searchEntities["varnish"].entity,
553
searchEntities["squid-forwardproxy"].entity,
556
about: "paginated search",
558
Filters: map[string][]string{
565
about: "additional groups",
567
Groups: []string{"charmers"},
570
searchEntities["riak"].entity,
571
searchEntities["cloud-controller-worker-v2"].entity,
572
searchEntities["wordpress"].entity,
573
searchEntities["mysql"].entity,
574
searchEntities["varnish"].entity,
575
searchEntities["squid-forwardproxy"].entity,
576
searchEntities["wordpress-simple"].entity,
579
about: "admin search",
584
searchEntities["riak"].entity,
585
searchEntities["cloud-controller-worker-v2"].entity,
586
searchEntities["wordpress"].entity,
587
searchEntities["mysql"].entity,
588
searchEntities["varnish"].entity,
589
searchEntities["squid-forwardproxy"].entity,
590
searchEntities["wordpress-simple"].entity,
593
about: "charm tags filter search",
596
Filters: map[string][]string{
597
"tags": {"wordpressTAG"},
601
searchEntities["wordpress"].entity,
604
about: "blank owner filter search",
607
Filters: map[string][]string{
612
searchEntities["wordpress"].entity,
613
searchEntities["mysql"].entity,
614
searchEntities["squid-forwardproxy"].entity,
615
searchEntities["wordpress-simple"].entity,
618
about: "promulgated search",
621
Filters: map[string][]string{
622
"promulgated": {"1"},
626
searchEntities["wordpress"].entity,
627
searchEntities["mysql"].entity,
628
searchEntities["squid-forwardproxy"].entity,
629
searchEntities["wordpress-simple"].entity,
632
about: "not promulgated search",
635
Filters: map[string][]string{
636
"promulgated": {"0"},
640
searchEntities["cloud-controller-worker-v2"].entity,
641
searchEntities["varnish"].entity,
644
about: "owner and promulgated filter search",
647
Filters: map[string][]string{
648
"promulgated": {"1"},
649
"owner": {"openstack-charmers"},
653
searchEntities["mysql"].entity,
658
func (s *StoreSearchSuite) TestSearches(c *gc.C) {
659
s.store.ES.Database.RefreshIndex(s.TestIndex)
660
for i, test := range searchTests {
661
c.Logf("test %d: %s", i, test.about)
662
res, err := s.store.Search(test.sp)
663
c.Assert(err, gc.IsNil)
664
sort.Sort(resolvedURLsByString(res.Results))
665
sort.Sort(resolvedURLsByString(test.results))
666
c.Check(Entities(res.Results), jc.DeepEquals, test.results)
667
c.Check(res.Total, gc.Equals, len(test.results)+test.totalDiff)
671
type resolvedURLsByString Entities
673
func (r resolvedURLsByString) Less(i, j int) bool {
674
return r[i].URL.String() < r[j].URL.String()
677
func (r resolvedURLsByString) Swap(i, j int) {
678
r[i], r[j] = r[j], r[i]
681
func (r resolvedURLsByString) Len() int {
685
func (s *StoreSearchSuite) TestPaginatedSearch(c *gc.C) {
686
err := s.store.ES.Database.RefreshIndex(s.TestIndex)
687
c.Assert(err, gc.IsNil)
692
res, err := s.store.Search(sp)
693
c.Assert(err, gc.IsNil)
694
c.Assert(res.Results, gc.HasLen, 1)
695
c.Assert(res.Total, gc.Equals, 2)
698
func (s *StoreSearchSuite) TestLimitTestSearch(c *gc.C) {
699
err := s.store.ES.Database.RefreshIndex(s.TestIndex)
700
c.Assert(err, gc.IsNil)
705
res, err := s.store.Search(sp)
706
c.Assert(err, gc.IsNil)
707
c.Assert(res.Results, gc.HasLen, 1)
710
func (s *StoreSearchSuite) TestPromulgatedRank(c *gc.C) {
711
charmArchive := storetesting.NewCharm(nil)
712
ent := newEntity("cs:~charmers/xenial/varnish-1", 1)
716
EntityResolvedURL(ent),
718
[]string{ent.URL.User, params.Everyone},
721
s.store.ES.Database.RefreshIndex(s.TestIndex)
723
Filters: map[string][]string{
727
res, err := s.store.Search(sp)
728
c.Assert(err, gc.IsNil)
729
c.Assert(Entities(res.Results), jc.DeepEquals, Entities{
731
searchEntities["varnish"].entity,
735
func (s *StoreSearchSuite) TestSorting(c *gc.C) {
736
s.store.ES.Database.RefreshIndex(s.TestIndex)
742
about: "name ascending",
745
searchEntities["cloud-controller-worker-v2"].entity,
746
searchEntities["mysql"].entity,
747
searchEntities["squid-forwardproxy"].entity,
748
searchEntities["varnish"].entity,
749
searchEntities["wordpress"].entity,
750
searchEntities["wordpress-simple"].entity,
753
about: "name descending",
756
searchEntities["wordpress-simple"].entity,
757
searchEntities["wordpress"].entity,
758
searchEntities["varnish"].entity,
759
searchEntities["squid-forwardproxy"].entity,
760
searchEntities["mysql"].entity,
761
searchEntities["cloud-controller-worker-v2"].entity,
764
about: "series ascending",
765
sortQuery: "series,name",
767
searchEntities["wordpress-simple"].entity,
768
searchEntities["wordpress"].entity,
769
searchEntities["cloud-controller-worker-v2"].entity,
770
searchEntities["squid-forwardproxy"].entity,
771
searchEntities["mysql"].entity,
772
searchEntities["varnish"].entity,
775
about: "series descending",
776
sortQuery: "-series,name",
778
searchEntities["mysql"].entity,
779
searchEntities["varnish"].entity,
780
searchEntities["squid-forwardproxy"].entity,
781
searchEntities["cloud-controller-worker-v2"].entity,
782
searchEntities["wordpress"].entity,
783
searchEntities["wordpress-simple"].entity,
786
about: "owner ascending",
787
sortQuery: "owner,name",
789
searchEntities["cloud-controller-worker-v2"].entity,
790
searchEntities["squid-forwardproxy"].entity,
791
searchEntities["wordpress"].entity,
792
searchEntities["wordpress-simple"].entity,
793
searchEntities["varnish"].entity,
794
searchEntities["mysql"].entity,
797
about: "owner descending",
798
sortQuery: "-owner,name",
800
searchEntities["mysql"].entity,
801
searchEntities["varnish"].entity,
802
searchEntities["squid-forwardproxy"].entity,
803
searchEntities["wordpress"].entity,
804
searchEntities["wordpress-simple"].entity,
805
searchEntities["cloud-controller-worker-v2"].entity,
808
about: "downloads ascending",
809
sortQuery: "downloads",
811
searchEntities["wordpress"].entity,
812
searchEntities["wordpress-simple"].entity,
813
searchEntities["squid-forwardproxy"].entity,
814
searchEntities["mysql"].entity,
815
searchEntities["cloud-controller-worker-v2"].entity,
816
searchEntities["varnish"].entity,
819
about: "downloads descending",
820
sortQuery: "-downloads",
822
searchEntities["varnish"].entity,
823
searchEntities["cloud-controller-worker-v2"].entity,
824
searchEntities["mysql"].entity,
825
searchEntities["squid-forwardproxy"].entity,
826
searchEntities["wordpress-simple"].entity,
827
searchEntities["wordpress"].entity,
830
for i, test := range tests {
831
c.Logf("test %d. %s", i, test.about)
833
err := sp.ParseSortFields(test.sortQuery)
834
c.Assert(err, gc.IsNil)
835
res, err := s.store.Search(sp)
836
c.Assert(err, gc.IsNil)
837
c.Assert(Entities(res.Results), jc.DeepEquals, test.results)
838
c.Assert(res.Total, gc.Equals, len(test.results))
842
func (s *StoreSearchSuite) TestBoosting(c *gc.C) {
843
s.store.ES.Database.RefreshIndex(s.TestIndex)
845
res, err := s.store.Search(sp)
846
c.Assert(err, gc.IsNil)
847
c.Assert(Entities(res.Results), jc.DeepEquals, Entities{
848
searchEntities["wordpress-simple"].entity,
849
searchEntities["mysql"].entity,
850
searchEntities["wordpress"].entity,
851
searchEntities["squid-forwardproxy"].entity,
852
searchEntities["varnish"].entity,
853
searchEntities["cloud-controller-worker-v2"].entity,
857
func (s *StoreSearchSuite) TestEnsureIndex(c *gc.C) {
858
s.store.ES.Index = s.TestIndex + "-ensure-index"
859
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
860
indexes, err := s.ES.ListIndexesForAlias(s.store.ES.Index)
861
c.Assert(err, gc.Equals, nil)
862
c.Assert(indexes, gc.HasLen, 0)
863
err = s.store.ES.ensureIndexes(false)
864
c.Assert(err, gc.Equals, nil)
865
indexes, err = s.ES.ListIndexesForAlias(s.store.ES.Index)
866
c.Assert(err, gc.Equals, nil)
867
c.Assert(indexes, gc.HasLen, 1)
869
err = s.store.ES.ensureIndexes(false)
870
c.Assert(err, gc.Equals, nil)
871
indexes, err = s.ES.ListIndexesForAlias(s.store.ES.Index)
872
c.Assert(err, gc.Equals, nil)
873
c.Assert(indexes, gc.HasLen, 1)
874
c.Assert(indexes[0], gc.Equals, index)
877
func (s *StoreSearchSuite) TestEnsureConcurrent(c *gc.C) {
878
s.store.ES.Index = s.TestIndex + "-ensure-index-conc"
879
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
880
indexes, err := s.ES.ListIndexesForAlias(s.store.ES.Index)
881
c.Assert(err, gc.Equals, nil)
882
c.Assert(indexes, gc.HasLen, 0)
883
var wg sync.WaitGroup
886
err := s.store.ES.ensureIndexes(false)
887
c.Check(err, gc.Equals, nil)
890
err = s.store.ES.ensureIndexes(false)
891
c.Assert(err, gc.Equals, nil)
892
indexes, err = s.ES.ListIndexesForAlias(s.store.ES.Index)
893
c.Assert(err, gc.Equals, nil)
894
c.Assert(indexes, gc.HasLen, 1)
898
func (s *StoreSearchSuite) TestEnsureIndexForce(c *gc.C) {
899
s.store.ES.Index = s.TestIndex + "-ensure-index-force"
900
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
901
indexes, err := s.ES.ListIndexesForAlias(s.store.ES.Index)
902
c.Assert(err, gc.Equals, nil)
903
c.Assert(indexes, gc.HasLen, 0)
904
err = s.store.ES.ensureIndexes(false)
905
c.Assert(err, gc.Equals, nil)
906
indexes, err = s.ES.ListIndexesForAlias(s.store.ES.Index)
907
c.Assert(err, gc.Equals, nil)
908
c.Assert(indexes, gc.HasLen, 1)
910
err = s.store.ES.ensureIndexes(true)
911
c.Assert(err, gc.Equals, nil)
912
indexes, err = s.ES.ListIndexesForAlias(s.store.ES.Index)
913
c.Assert(err, gc.Equals, nil)
914
c.Assert(indexes, gc.HasLen, 1)
915
c.Assert(indexes[0], gc.Not(gc.Equals), index)
918
func (s *StoreSearchSuite) TestGetCurrentVersionNoVersion(c *gc.C) {
919
s.store.ES.Index = s.TestIndex + "-current-version"
920
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
921
v, dv, err := s.store.ES.getCurrentVersion()
922
c.Assert(err, gc.Equals, nil)
923
c.Assert(v, gc.Equals, version{})
924
c.Assert(dv, gc.Equals, int64(0))
927
func (s *StoreSearchSuite) TestGetCurrentVersionWithVersion(c *gc.C) {
928
s.store.ES.Index = s.TestIndex + "-current-version"
929
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
930
index, err := s.store.ES.newIndex()
931
c.Assert(err, gc.Equals, nil)
932
updated, err := s.store.ES.updateVersion(version{1, index}, 0)
933
c.Assert(err, gc.Equals, nil)
934
c.Assert(updated, gc.Equals, true)
935
v, dv, err := s.store.ES.getCurrentVersion()
936
c.Assert(err, gc.Equals, nil)
937
c.Assert(v, gc.Equals, version{1, index})
938
c.Assert(dv, gc.Equals, int64(1))
941
func (s *StoreSearchSuite) TestUpdateVersionNew(c *gc.C) {
942
s.store.ES.Index = s.TestIndex + "-update-version"
943
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
944
index, err := s.store.ES.newIndex()
945
c.Assert(err, gc.Equals, nil)
946
updated, err := s.store.ES.updateVersion(version{1, index}, 0)
947
c.Assert(err, gc.Equals, nil)
948
c.Assert(updated, gc.Equals, true)
951
func (s *StoreSearchSuite) TestUpdateVersionUpdate(c *gc.C) {
952
s.store.ES.Index = s.TestIndex + "-update-version"
953
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
954
index, err := s.store.ES.newIndex()
955
c.Assert(err, gc.Equals, nil)
956
updated, err := s.store.ES.updateVersion(version{1, index}, 0)
957
c.Assert(err, gc.Equals, nil)
958
c.Assert(updated, gc.Equals, true)
959
index, err = s.store.ES.newIndex()
960
c.Assert(err, gc.Equals, nil)
961
updated, err = s.store.ES.updateVersion(version{2, index}, 1)
962
c.Assert(err, gc.Equals, nil)
963
c.Assert(updated, gc.Equals, true)
966
func (s *StoreSearchSuite) TestUpdateCreateConflict(c *gc.C) {
967
s.store.ES.Index = s.TestIndex + "-update-version"
968
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
969
index, err := s.store.ES.newIndex()
970
c.Assert(err, gc.Equals, nil)
971
updated, err := s.store.ES.updateVersion(version{1, index}, 0)
972
c.Assert(err, gc.Equals, nil)
973
c.Assert(updated, gc.Equals, true)
974
index, err = s.store.ES.newIndex()
975
c.Assert(err, gc.Equals, nil)
976
updated, err = s.store.ES.updateVersion(version{1, index}, 0)
977
c.Assert(err, gc.Equals, nil)
978
c.Assert(updated, gc.Equals, false)
981
func (s *StoreSearchSuite) TestUpdateConflict(c *gc.C) {
982
s.store.ES.Index = s.TestIndex + "-update-version"
983
defer s.ES.DeleteDocument(".versions", "version", s.store.ES.Index)
984
index, err := s.store.ES.newIndex()
985
c.Assert(err, gc.Equals, nil)
986
updated, err := s.store.ES.updateVersion(version{1, index}, 0)
987
c.Assert(err, gc.Equals, nil)
988
c.Assert(updated, gc.Equals, true)
989
index, err = s.store.ES.newIndex()
990
c.Assert(err, gc.Equals, nil)
991
updated, err = s.store.ES.updateVersion(version{1, index}, 3)
992
c.Assert(err, gc.Equals, nil)
993
c.Assert(updated, gc.Equals, false)
996
func (s *StoreSearchSuite) TestMultiSeriesCharmFiltersSeriesCorrectly(c *gc.C) {
997
charmArchive := storetesting.NewCharm(storetesting.MetaWithSupportedSeries(nil, "trusty", "xenial", "utopic", "vivid", "wily"))
998
url := router.MustNewResolvedURL("cs:~charmers/juju-gui-25", -1)
1004
[]string{url.URL.User, params.Everyone},
1007
s.store.ES.Database.RefreshIndex(s.TestIndex)
1008
filterTests := []struct {
1019
for i, test := range filterTests {
1020
c.Logf("%d. %s", i, test.series)
1021
res, err := s.store.Search(SearchParams{
1022
Filters: map[string][]string{
1023
"name": []string{"juju-gui"},
1024
"series": []string{test.series},
1027
c.Assert(err, gc.IsNil)
1029
c.Assert(res.Results, gc.HasLen, 0)
1032
c.Assert(res.Results, gc.HasLen, 1)
1033
c.Assert(res.Results[0].URL.String(), gc.Equals, url.String())
1037
func (s *StoreSearchSuite) TestMultiSeriesCharmSortsSeriesCorrectly(c *gc.C) {
1038
charmArchive := storetesting.NewCharm(storetesting.MetaWithSupportedSeries(nil, "trusty", "xenial", "utopic", "vivid", "wily"))
1039
url := router.MustNewResolvedURL("cs:~charmers/juju-gui-25", -1)
1045
[]string{url.URL.User, params.Everyone},
1048
s.store.ES.Database.RefreshIndex(s.TestIndex)
1050
sp.ParseSortFields("-series", "owner")
1051
res, err := s.store.Search(sp)
1052
c.Assert(err, gc.IsNil)
1053
c.Assert(Entities(res.Results), jc.DeepEquals, Entities{
1054
newEntity("cs:~charmers/juju-gui-25", -1, "trusty", "xenial", "utopic", "vivid", "wily"),
1055
newEntity("cs:~foo/xenial/varnish-1", -1),
1056
newEntity("cs:~openstack-charmers/xenial/mysql-7", 7),
1057
newEntity("cs:~charmers/wily/squid-forwardproxy-3", 3),
1058
searchEntities["cloud-controller-worker-v2"].entity,
1059
newEntity("cs:~charmers/precise/wordpress-23", 23),
1060
newEntity("cs:~charmers/bundle/wordpress-simple-4", 4),
1064
func (s *StoreSearchSuite) TestOnlyIndexStableCharms(c *gc.C) {
1065
ch := storetesting.NewCharm(&charm.Meta{
1068
id := router.MustNewResolvedURL("~test/xenial/test-0", -1)
1069
err := s.store.AddCharmWithArchive(id, ch)
1070
c.Assert(err, gc.IsNil)
1071
err = s.store.SetPerms(&id.URL, "read", "test", params.Everyone)
1072
c.Assert(err, gc.IsNil)
1073
err = s.store.SetPerms(&id.URL, "development.read", "test", params.Everyone)
1074
c.Assert(err, gc.IsNil)
1075
err = s.store.SetPerms(&id.URL, "stable.read", "test", params.Everyone)
1076
c.Assert(err, gc.IsNil)
1078
var actual json.RawMessage
1080
err = s.store.UpdateSearch(id)
1081
c.Assert(err, gc.IsNil)
1082
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(&id.URL), &actual)
1083
c.Assert(err, gc.ErrorMatches, "elasticsearch document not found")
1085
err = s.store.Publish(id, nil, params.DevelopmentChannel)
1086
c.Assert(err, gc.IsNil)
1087
err = s.store.UpdateSearch(id)
1088
c.Assert(err, gc.IsNil)
1089
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(&id.URL), &actual)
1090
c.Assert(err, gc.ErrorMatches, "elasticsearch document not found")
1092
err = s.store.Publish(id, nil, params.StableChannel)
1093
c.Assert(err, gc.IsNil)
1094
err = s.store.UpdateSearch(id)
1095
c.Assert(err, gc.IsNil)
1096
err = s.store.ES.GetDocument(s.TestIndex, typeName, s.store.ES.getID(&id.URL), &actual)
1097
c.Assert(err, gc.IsNil)
1099
entity, err := s.store.FindEntity(id, nil)
1100
c.Assert(err, gc.IsNil)
1103
ReadACLs: []string{"test", params.Everyone},
1104
Series: []string{"xenial"},
1108
c.Assert(string(actual), jc.JSONEquals, doc)
1111
// addCharmForSearch adds a charm to the specified store such that it
1112
// will be indexed in search. In order that it is indexed it is
1113
// automatically published on the stable channel.
1114
func addCharmForSearch(c *gc.C, s *Store, id *router.ResolvedURL, ch charm.Charm, acl []string, downloads int) {
1115
err := s.AddCharmWithArchive(id, ch)
1116
c.Assert(err, gc.IsNil)
1117
for i := 0; i < downloads; i++ {
1118
err := s.IncrementDownloadCounts(id)
1119
c.Assert(err, gc.IsNil)
1121
err = s.SetPerms(&id.URL, "stable.read", acl...)
1122
c.Assert(err, gc.IsNil)
1123
err = s.Publish(id, nil, params.StableChannel)
1124
c.Assert(err, gc.IsNil)
1127
// addBundleForSearch adds a bundle to the specified store such that it
1128
// will be indexed in search. In order that it is indexed it is
1129
// automatically published on the stable channel.
1130
func addBundleForSearch(c *gc.C, s *Store, id *router.ResolvedURL, b charm.Bundle, acl []string, downloads int) {
1131
err := s.AddBundleWithArchive(id, b)
1132
c.Assert(err, gc.IsNil)
1133
for i := 0; i < downloads; i++ {
1134
err := s.IncrementDownloadCounts(id)
1135
c.Assert(err, gc.IsNil)
1137
err = s.SetPerms(&id.URL, "stable.read", acl...)
1138
c.Assert(err, gc.IsNil)
1139
err = s.Publish(id, nil, params.StableChannel)
1140
c.Assert(err, gc.IsNil)
1143
type Entities []*mongodoc.Entity
1145
func (es Entities) GoString() string {
1149
func (es Entities) String() string {
1150
urls := make([]string, len(es))
1151
for i, e := range es {
1152
urls[i] = e.URL.String()
1154
return "[" + strings.Join(urls, ", ") + "]"