~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/juju/charmstore.v5-unstable/internal/charmstore/search_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package charmstore // import "gopkg.in/juju/charmstore.v5-unstable/internal/charmstore"
 
5
 
 
6
import (
 
7
        "encoding/json"
 
8
        "sort"
 
9
        "strings"
 
10
        "sync"
 
11
 
 
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"
 
16
 
 
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"
 
20
)
 
21
 
 
22
type StoreSearchSuite struct {
 
23
        storetesting.IsolatedMgoESSuite
 
24
        pool  *Pool
 
25
        store *Store
 
26
        index SearchIndex
 
27
}
 
28
 
 
29
var _ = gc.Suite(&StoreSearchSuite{})
 
30
 
 
31
func (s *StoreSearchSuite) SetUpTest(c *gc.C) {
 
32
        s.IsolatedMgoESSuite.SetUpTest(c)
 
33
 
 
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
 
37
        // logic.
 
38
        original := LegacyDownloadCountsEnabled
 
39
        LegacyDownloadCountsEnabled = false
 
40
        s.AddCleanup(func(*gc.C) {
 
41
                LegacyDownloadCountsEnabled = original
 
42
        })
 
43
 
 
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)
 
48
        s.pool = pool
 
49
        s.store = pool.Store()
 
50
        s.addEntities(c)
 
51
        c.Assert(err, gc.IsNil)
 
52
}
 
53
 
 
54
func (s *StoreSearchSuite) TearDownTest(c *gc.C) {
 
55
        s.store.Close()
 
56
        s.pool.Close()
 
57
        s.IsolatedMgoESSuite.TearDownTest(c)
 
58
}
 
59
 
 
60
func newEntity(id string, promulgatedRevision int, supportedSeries ...string) *mongodoc.Entity {
 
61
        url := charm.MustParseURL(id)
 
62
        var purl *charm.URL
 
63
        if promulgatedRevision > -1 {
 
64
                purl = new(charm.URL)
 
65
                *purl = *url
 
66
                purl.User = ""
 
67
                purl.Revision = promulgatedRevision
 
68
        }
 
69
        if url.Series == "bundle" {
 
70
                supportedSeries = nil
 
71
        } else if url.Series != "" {
 
72
                supportedSeries = []string{url.Series}
 
73
        }
 
74
        return &mongodoc.Entity{
 
75
                URL:                 url,
 
76
                SupportedSeries:     supportedSeries,
 
77
                PromulgatedURL:      purl,
 
78
                PromulgatedRevision: promulgatedRevision,
 
79
        }
 
80
}
 
81
 
 
82
type searchEntity struct {
 
83
        entity     *mongodoc.Entity
 
84
        charmMeta  *charm.Meta
 
85
        bundleData *charm.BundleData
 
86
        acl        []string
 
87
        downloads  int
 
88
}
 
89
 
 
90
var searchEntities = map[string]searchEntity{
 
91
        "wordpress": searchEntity{
 
92
                entity: newEntity("cs:~charmers/precise/wordpress-23", 23),
 
93
                charmMeta: &charm.Meta{
 
94
                        Description: "blog",
 
95
                        Requires: map[string]charm.Relation{
 
96
                                "mysql": charm.Relation{
 
97
                                        Name:      "mysql",
 
98
                                        Interface: "mysql",
 
99
                                        Scope:     charm.ScopeGlobal,
 
100
                                },
 
101
                        },
 
102
                        Categories: []string{"wordpress"},
 
103
                        Tags:       []string{"wordpressTAG"},
 
104
                },
 
105
                acl: []string{params.Everyone},
 
106
        },
 
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{
 
113
                                        Name:      "mysql",
 
114
                                        Interface: "mysql",
 
115
                                        Scope:     charm.ScopeGlobal,
 
116
                                },
 
117
                        },
 
118
                        Categories: []string{"mysql"},
 
119
                        Tags:       []string{"mysqlTAG"},
 
120
                },
 
121
                acl:       []string{params.Everyone},
 
122
                downloads: 3,
 
123
        },
 
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"},
 
130
                },
 
131
                acl:       []string{params.Everyone},
 
132
                downloads: 5,
 
133
        },
 
134
        "riak": searchEntity{
 
135
                entity: newEntity("cs:~charmers/xenial/riak-67", 67),
 
136
                charmMeta: &charm.Meta{
 
137
                        Categories: []string{"riak"},
 
138
                        Tags:       []string{"riakTAG"},
 
139
                },
 
140
                acl: []string{"charmers"},
 
141
        },
 
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{
 
147
                                        Charm: "wordpress",
 
148
                                },
 
149
                        },
 
150
                        Tags: []string{"wordpress"},
 
151
                },
 
152
                acl:       []string{params.Everyone},
 
153
                downloads: 1,
 
154
        },
 
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},
 
160
                downloads: 2,
 
161
        },
 
162
        // Note: "cloud-controller-worker-v2" shares a trigram "wor" with "wordpress".
 
163
 
 
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},
 
168
                downloads: 4,
 
169
        },
 
170
}
 
171
 
 
172
func (s *StoreSearchSuite) addEntities(c *gc.C) {
 
173
        for _, ent := range searchEntities {
 
174
                if ent.charmMeta == nil {
 
175
                        continue
 
176
                }
 
177
                addCharmForSearch(
 
178
                        c,
 
179
                        s.store,
 
180
                        EntityResolvedURL(ent.entity),
 
181
                        storetesting.NewCharm(ent.charmMeta),
 
182
                        ent.acl,
 
183
                        ent.downloads,
 
184
                )
 
185
        }
 
186
        for _, ent := range searchEntities {
 
187
                if ent.bundleData == nil {
 
188
                        continue
 
189
                }
 
190
                addBundleForSearch(
 
191
                        c,
 
192
                        s.store,
 
193
                        EntityResolvedURL(ent.entity),
 
194
                        storetesting.NewBundle(ent.bundleData),
 
195
                        ent.acl,
 
196
                        ent.downloads,
 
197
                )
 
198
        }
 
199
        s.store.pool.statsCache.EvictAll()
 
200
        err := s.store.syncSearch()
 
201
        c.Assert(err, gc.IsNil)
 
202
}
 
203
 
 
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"}
 
215
                }
 
216
                doc := SearchDoc{
 
217
                        Entity:         entity,
 
218
                        TotalDownloads: int64(ent.downloads),
 
219
                        ReadACLs:       ent.acl,
 
220
                        Series:         series,
 
221
                        AllSeries:      true,
 
222
                        SingleSeries:   true,
 
223
                }
 
224
                c.Assert(string(actual), jc.JSONEquals, doc)
 
225
        }
 
226
}
 
227
 
 
228
func (s *StoreSearchSuite) TestNoExportDeprecated(c *gc.C) {
 
229
        charmArchive := storetesting.NewCharm(nil)
 
230
        url := router.MustNewResolvedURL("cs:~charmers/saucy/mysql-4", -1)
 
231
        addCharmForSearch(
 
232
                c,
 
233
                s.store,
 
234
                url,
 
235
                charmArchive,
 
236
                nil,
 
237
                0,
 
238
        )
 
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)
 
245
 
 
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)
 
251
}
 
252
 
 
253
func (s *StoreSearchSuite) TestExportOnlyLatest(c *gc.C) {
 
254
        charmArchive := storetesting.NewCharm(nil)
 
255
        url := router.MustNewResolvedURL("cs:~charmers/precise/wordpress-24", -1)
 
256
        addCharmForSearch(
 
257
                c,
 
258
                s.store,
 
259
                url,
 
260
                charmArchive,
 
261
                []string{"charmers", params.Everyone},
 
262
                0,
 
263
        )
 
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)
 
272
        doc := SearchDoc{
 
273
                Entity:       expected,
 
274
                ReadACLs:     []string{"charmers", params.Everyone},
 
275
                Series:       expected.SupportedSeries,
 
276
                SingleSeries: true,
 
277
                AllSeries:    true,
 
278
        }
 
279
        c.Assert(string(actual), jc.JSONEquals, doc)
 
280
}
 
281
 
 
282
func (s *StoreSearchSuite) TestExportMultiSeriesCharmsCreateExpandedVersions(c *gc.C) {
 
283
        charmArchive := storetesting.NewCharm(nil)
 
284
        url := router.MustNewResolvedURL("cs:~charmers/xenial/juju-gui-24", -1)
 
285
        addCharmForSearch(
 
286
                c,
 
287
                s.store,
 
288
                url,
 
289
                charmArchive,
 
290
                []string{"charmers"},
 
291
                0,
 
292
        )
 
293
        charmArchive = storetesting.NewCharm(storetesting.MetaWithSupportedSeries(nil, "trusty", "xenial", "utopic", "vivid", "wily"))
 
294
        url = router.MustNewResolvedURL("cs:~charmers/juju-gui-25", -1)
 
295
        addCharmForSearch(
 
296
                c,
 
297
                s.store,
 
298
                url,
 
299
                charmArchive,
 
300
                []string{"charmers"},
 
301
                0,
 
302
        )
 
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)
 
311
        doc := SearchDoc{
 
312
                Entity:       expected,
 
313
                ReadACLs:     []string{"charmers"},
 
314
                Series:       expected.SupportedSeries,
 
315
                SingleSeries: false,
 
316
                AllSeries:    true,
 
317
        }
 
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
 
322
        doc = SearchDoc{
 
323
                Entity:       expected,
 
324
                ReadACLs:     []string{"charmers"},
 
325
                Series:       []string{old.URL.Series},
 
326
                SingleSeries: true,
 
327
                AllSeries:    false,
 
328
        }
 
329
        c.Assert(string(actual), jc.JSONEquals, doc)
 
330
}
 
331
 
 
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)
 
343
}
 
344
 
 
345
var searchTests = []struct {
 
346
        about     string
 
347
        sp        SearchParams
 
348
        results   Entities
 
349
        totalDiff int // len(results) + totalDiff = expected total
 
350
}{
 
351
        {
 
352
                about: "basic text search",
 
353
                sp: SearchParams{
 
354
                        Text: "wordpress",
 
355
                },
 
356
                results: Entities{
 
357
                        searchEntities["wordpress"].entity,
 
358
                        searchEntities["wordpress-simple"].entity,
 
359
                },
 
360
        }, {
 
361
                about: "blank text search",
 
362
                sp: SearchParams{
 
363
                        Text: "",
 
364
                },
 
365
                results: Entities{
 
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,
 
372
                },
 
373
        }, {
 
374
                about: "autocomplete search",
 
375
                sp: SearchParams{
 
376
                        Text:         "word",
 
377
                        AutoComplete: true,
 
378
                },
 
379
                results: Entities{
 
380
                        searchEntities["wordpress"].entity,
 
381
                        searchEntities["wordpress-simple"].entity,
 
382
                },
 
383
        }, {
 
384
                about: "non-matching autocomplete search",
 
385
                sp: SearchParams{
 
386
                        Text:         "worm",
 
387
                        AutoComplete: true,
 
388
                },
 
389
                results: Entities{},
 
390
        }, {
 
391
                about: "description filter search",
 
392
                sp: SearchParams{
 
393
                        Text: "",
 
394
                        Filters: map[string][]string{
 
395
                                "description": {"blog"},
 
396
                        },
 
397
                },
 
398
                results: Entities{
 
399
                        searchEntities["wordpress"].entity,
 
400
                },
 
401
        }, {
 
402
                about: "name filter search",
 
403
                sp: SearchParams{
 
404
                        Text: "",
 
405
                        Filters: map[string][]string{
 
406
                                "name": {"wordpress"},
 
407
                        },
 
408
                },
 
409
                results: Entities{
 
410
                        searchEntities["wordpress"].entity,
 
411
                },
 
412
        }, {
 
413
                about: "owner filter search",
 
414
                sp: SearchParams{
 
415
                        Text: "",
 
416
                        Filters: map[string][]string{
 
417
                                "owner": {"foo"},
 
418
                        },
 
419
                },
 
420
                results: Entities{
 
421
                        searchEntities["varnish"].entity,
 
422
                },
 
423
        }, {
 
424
                about: "provides filter search",
 
425
                sp: SearchParams{
 
426
                        Text: "",
 
427
                        Filters: map[string][]string{
 
428
                                "provides": {"mysql"},
 
429
                        },
 
430
                },
 
431
                results: Entities{
 
432
                        searchEntities["mysql"].entity,
 
433
                },
 
434
        }, {
 
435
                about: "requires filter search",
 
436
                sp: SearchParams{
 
437
                        Text: "",
 
438
                        Filters: map[string][]string{
 
439
                                "requires": {"mysql"},
 
440
                        },
 
441
                },
 
442
                results: Entities{
 
443
                        searchEntities["wordpress"].entity,
 
444
                },
 
445
        }, {
 
446
                about: "series filter search",
 
447
                sp: SearchParams{
 
448
                        Text: "",
 
449
                        Filters: map[string][]string{
 
450
                                "series": {"xenial"},
 
451
                        },
 
452
                },
 
453
                results: Entities{
 
454
                        searchEntities["mysql"].entity,
 
455
                        searchEntities["varnish"].entity,
 
456
                },
 
457
        }, {
 
458
                about: "summary filter search",
 
459
                sp: SearchParams{
 
460
                        Text: "",
 
461
                        Filters: map[string][]string{
 
462
                                "summary": {"Database engine"},
 
463
                        },
 
464
                },
 
465
                results: Entities{
 
466
                        searchEntities["mysql"].entity,
 
467
                        searchEntities["varnish"].entity,
 
468
                },
 
469
        }, {
 
470
                about: "tags filter search",
 
471
                sp: SearchParams{
 
472
                        Text: "",
 
473
                        Filters: map[string][]string{
 
474
                                "tags": {"wordpress"},
 
475
                        },
 
476
                },
 
477
                results: Entities{
 
478
                        searchEntities["wordpress"].entity,
 
479
                        searchEntities["wordpress-simple"].entity,
 
480
                },
 
481
        }, {
 
482
                about: "bundle type filter search",
 
483
                sp: SearchParams{
 
484
                        Text: "",
 
485
                        Filters: map[string][]string{
 
486
                                "type": {"bundle"},
 
487
                        },
 
488
                },
 
489
                results: Entities{
 
490
                        searchEntities["wordpress-simple"].entity,
 
491
                },
 
492
        }, {
 
493
                about: "charm type filter search",
 
494
                sp: SearchParams{
 
495
                        Text: "",
 
496
                        Filters: map[string][]string{
 
497
                                "type": {"charm"},
 
498
                        },
 
499
                },
 
500
                results: Entities{
 
501
                        searchEntities["cloud-controller-worker-v2"].entity,
 
502
                        searchEntities["wordpress"].entity,
 
503
                        searchEntities["mysql"].entity,
 
504
                        searchEntities["varnish"].entity,
 
505
                        searchEntities["squid-forwardproxy"].entity,
 
506
                },
 
507
        }, {
 
508
                about: "charm & bundle type filter search",
 
509
                sp: SearchParams{
 
510
                        Text: "",
 
511
                        Filters: map[string][]string{
 
512
                                "type": {"charm", "bundle"},
 
513
                        },
 
514
                },
 
515
                results: Entities{
 
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,
 
522
                },
 
523
        }, {
 
524
                about: "invalid filter search",
 
525
                sp: SearchParams{
 
526
                        Text: "",
 
527
                        Filters: map[string][]string{
 
528
                                "no such filter": {"foo"},
 
529
                        },
 
530
                },
 
531
                results: Entities{
 
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,
 
538
                },
 
539
        }, {
 
540
                about: "valid & invalid filter search",
 
541
                sp: SearchParams{
 
542
                        Text: "",
 
543
                        Filters: map[string][]string{
 
544
                                "no such filter": {"foo"},
 
545
                                "type":           {"charm"},
 
546
                        },
 
547
                },
 
548
                results: Entities{
 
549
                        searchEntities["cloud-controller-worker-v2"].entity,
 
550
                        searchEntities["wordpress"].entity,
 
551
                        searchEntities["mysql"].entity,
 
552
                        searchEntities["varnish"].entity,
 
553
                        searchEntities["squid-forwardproxy"].entity,
 
554
                },
 
555
        }, {
 
556
                about: "paginated search",
 
557
                sp: SearchParams{
 
558
                        Filters: map[string][]string{
 
559
                                "name": {"mysql"},
 
560
                        },
 
561
                        Skip: 1,
 
562
                },
 
563
                totalDiff: +1,
 
564
        }, {
 
565
                about: "additional groups",
 
566
                sp: SearchParams{
 
567
                        Groups: []string{"charmers"},
 
568
                },
 
569
                results: Entities{
 
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,
 
577
                },
 
578
        }, {
 
579
                about: "admin search",
 
580
                sp: SearchParams{
 
581
                        Admin: true,
 
582
                },
 
583
                results: Entities{
 
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,
 
591
                },
 
592
        }, {
 
593
                about: "charm tags filter search",
 
594
                sp: SearchParams{
 
595
                        Text: "",
 
596
                        Filters: map[string][]string{
 
597
                                "tags": {"wordpressTAG"},
 
598
                        },
 
599
                },
 
600
                results: Entities{
 
601
                        searchEntities["wordpress"].entity,
 
602
                },
 
603
        }, {
 
604
                about: "blank owner filter search",
 
605
                sp: SearchParams{
 
606
                        Text: "",
 
607
                        Filters: map[string][]string{
 
608
                                "owner": {""},
 
609
                        },
 
610
                },
 
611
                results: Entities{
 
612
                        searchEntities["wordpress"].entity,
 
613
                        searchEntities["mysql"].entity,
 
614
                        searchEntities["squid-forwardproxy"].entity,
 
615
                        searchEntities["wordpress-simple"].entity,
 
616
                },
 
617
        }, {
 
618
                about: "promulgated search",
 
619
                sp: SearchParams{
 
620
                        Text: "",
 
621
                        Filters: map[string][]string{
 
622
                                "promulgated": {"1"},
 
623
                        },
 
624
                },
 
625
                results: Entities{
 
626
                        searchEntities["wordpress"].entity,
 
627
                        searchEntities["mysql"].entity,
 
628
                        searchEntities["squid-forwardproxy"].entity,
 
629
                        searchEntities["wordpress-simple"].entity,
 
630
                },
 
631
        }, {
 
632
                about: "not promulgated search",
 
633
                sp: SearchParams{
 
634
                        Text: "",
 
635
                        Filters: map[string][]string{
 
636
                                "promulgated": {"0"},
 
637
                        },
 
638
                },
 
639
                results: Entities{
 
640
                        searchEntities["cloud-controller-worker-v2"].entity,
 
641
                        searchEntities["varnish"].entity,
 
642
                },
 
643
        }, {
 
644
                about: "owner and promulgated filter search",
 
645
                sp: SearchParams{
 
646
                        Text: "",
 
647
                        Filters: map[string][]string{
 
648
                                "promulgated": {"1"},
 
649
                                "owner":       {"openstack-charmers"},
 
650
                        },
 
651
                },
 
652
                results: Entities{
 
653
                        searchEntities["mysql"].entity,
 
654
                },
 
655
        },
 
656
}
 
657
 
 
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)
 
668
        }
 
669
}
 
670
 
 
671
type resolvedURLsByString Entities
 
672
 
 
673
func (r resolvedURLsByString) Less(i, j int) bool {
 
674
        return r[i].URL.String() < r[j].URL.String()
 
675
}
 
676
 
 
677
func (r resolvedURLsByString) Swap(i, j int) {
 
678
        r[i], r[j] = r[j], r[i]
 
679
}
 
680
 
 
681
func (r resolvedURLsByString) Len() int {
 
682
        return len(r)
 
683
}
 
684
 
 
685
func (s *StoreSearchSuite) TestPaginatedSearch(c *gc.C) {
 
686
        err := s.store.ES.Database.RefreshIndex(s.TestIndex)
 
687
        c.Assert(err, gc.IsNil)
 
688
        sp := SearchParams{
 
689
                Text: "wordpress",
 
690
                Skip: 1,
 
691
        }
 
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)
 
696
}
 
697
 
 
698
func (s *StoreSearchSuite) TestLimitTestSearch(c *gc.C) {
 
699
        err := s.store.ES.Database.RefreshIndex(s.TestIndex)
 
700
        c.Assert(err, gc.IsNil)
 
701
        sp := SearchParams{
 
702
                Text:  "wordpress",
 
703
                Limit: 1,
 
704
        }
 
705
        res, err := s.store.Search(sp)
 
706
        c.Assert(err, gc.IsNil)
 
707
        c.Assert(res.Results, gc.HasLen, 1)
 
708
}
 
709
 
 
710
func (s *StoreSearchSuite) TestPromulgatedRank(c *gc.C) {
 
711
        charmArchive := storetesting.NewCharm(nil)
 
712
        ent := newEntity("cs:~charmers/xenial/varnish-1", 1)
 
713
        addCharmForSearch(
 
714
                c,
 
715
                s.store,
 
716
                EntityResolvedURL(ent),
 
717
                charmArchive,
 
718
                []string{ent.URL.User, params.Everyone},
 
719
                0,
 
720
        )
 
721
        s.store.ES.Database.RefreshIndex(s.TestIndex)
 
722
        sp := SearchParams{
 
723
                Filters: map[string][]string{
 
724
                        "name": {"varnish"},
 
725
                },
 
726
        }
 
727
        res, err := s.store.Search(sp)
 
728
        c.Assert(err, gc.IsNil)
 
729
        c.Assert(Entities(res.Results), jc.DeepEquals, Entities{
 
730
                ent,
 
731
                searchEntities["varnish"].entity,
 
732
        })
 
733
}
 
734
 
 
735
func (s *StoreSearchSuite) TestSorting(c *gc.C) {
 
736
        s.store.ES.Database.RefreshIndex(s.TestIndex)
 
737
        tests := []struct {
 
738
                about     string
 
739
                sortQuery string
 
740
                results   Entities
 
741
        }{{
 
742
                about:     "name ascending",
 
743
                sortQuery: "name",
 
744
                results: Entities{
 
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,
 
751
                },
 
752
        }, {
 
753
                about:     "name descending",
 
754
                sortQuery: "-name",
 
755
                results: Entities{
 
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,
 
762
                },
 
763
        }, {
 
764
                about:     "series ascending",
 
765
                sortQuery: "series,name",
 
766
                results: Entities{
 
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,
 
773
                },
 
774
        }, {
 
775
                about:     "series descending",
 
776
                sortQuery: "-series,name",
 
777
                results: Entities{
 
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,
 
784
                },
 
785
        }, {
 
786
                about:     "owner ascending",
 
787
                sortQuery: "owner,name",
 
788
                results: Entities{
 
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,
 
795
                },
 
796
        }, {
 
797
                about:     "owner descending",
 
798
                sortQuery: "-owner,name",
 
799
                results: Entities{
 
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,
 
806
                },
 
807
        }, {
 
808
                about:     "downloads ascending",
 
809
                sortQuery: "downloads",
 
810
                results: Entities{
 
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,
 
817
                },
 
818
        }, {
 
819
                about:     "downloads descending",
 
820
                sortQuery: "-downloads",
 
821
                results: Entities{
 
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,
 
828
                },
 
829
        }}
 
830
        for i, test := range tests {
 
831
                c.Logf("test %d. %s", i, test.about)
 
832
                var sp SearchParams
 
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))
 
839
        }
 
840
}
 
841
 
 
842
func (s *StoreSearchSuite) TestBoosting(c *gc.C) {
 
843
        s.store.ES.Database.RefreshIndex(s.TestIndex)
 
844
        var sp SearchParams
 
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,
 
854
        })
 
855
}
 
856
 
 
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)
 
868
        index := indexes[0]
 
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)
 
875
}
 
876
 
 
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
 
884
        wg.Add(1)
 
885
        go func() {
 
886
                err := s.store.ES.ensureIndexes(false)
 
887
                c.Check(err, gc.Equals, nil)
 
888
                wg.Done()
 
889
        }()
 
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)
 
895
        wg.Wait()
 
896
}
 
897
 
 
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)
 
909
        index := indexes[0]
 
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)
 
916
}
 
917
 
 
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))
 
925
}
 
926
 
 
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))
 
939
}
 
940
 
 
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)
 
949
}
 
950
 
 
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)
 
964
}
 
965
 
 
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)
 
979
}
 
980
 
 
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)
 
994
}
 
995
 
 
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)
 
999
        addCharmForSearch(
 
1000
                c,
 
1001
                s.store,
 
1002
                url,
 
1003
                charmArchive,
 
1004
                []string{url.URL.User, params.Everyone},
 
1005
                0,
 
1006
        )
 
1007
        s.store.ES.Database.RefreshIndex(s.TestIndex)
 
1008
        filterTests := []struct {
 
1009
                series   string
 
1010
                notFound bool
 
1011
        }{{
 
1012
                series: "xenial",
 
1013
        }, {
 
1014
                series: "vivid",
 
1015
        }, {
 
1016
                series:   "sauch",
 
1017
                notFound: true,
 
1018
        }}
 
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},
 
1025
                        },
 
1026
                })
 
1027
                c.Assert(err, gc.IsNil)
 
1028
                if test.notFound {
 
1029
                        c.Assert(res.Results, gc.HasLen, 0)
 
1030
                        continue
 
1031
                }
 
1032
                c.Assert(res.Results, gc.HasLen, 1)
 
1033
                c.Assert(res.Results[0].URL.String(), gc.Equals, url.String())
 
1034
        }
 
1035
}
 
1036
 
 
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)
 
1040
        addCharmForSearch(
 
1041
                c,
 
1042
                s.store,
 
1043
                url,
 
1044
                charmArchive,
 
1045
                []string{url.URL.User, params.Everyone},
 
1046
                0,
 
1047
        )
 
1048
        s.store.ES.Database.RefreshIndex(s.TestIndex)
 
1049
        var sp SearchParams
 
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),
 
1061
        })
 
1062
}
 
1063
 
 
1064
func (s *StoreSearchSuite) TestOnlyIndexStableCharms(c *gc.C) {
 
1065
        ch := storetesting.NewCharm(&charm.Meta{
 
1066
                Name: "test",
 
1067
        })
 
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)
 
1077
 
 
1078
        var actual json.RawMessage
 
1079
 
 
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")
 
1084
 
 
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")
 
1091
 
 
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)
 
1098
 
 
1099
        entity, err := s.store.FindEntity(id, nil)
 
1100
        c.Assert(err, gc.IsNil)
 
1101
        doc := SearchDoc{
 
1102
                Entity:       entity,
 
1103
                ReadACLs:     []string{"test", params.Everyone},
 
1104
                Series:       []string{"xenial"},
 
1105
                AllSeries:    true,
 
1106
                SingleSeries: true,
 
1107
        }
 
1108
        c.Assert(string(actual), jc.JSONEquals, doc)
 
1109
}
 
1110
 
 
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)
 
1120
        }
 
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)
 
1125
}
 
1126
 
 
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)
 
1136
        }
 
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)
 
1141
}
 
1142
 
 
1143
type Entities []*mongodoc.Entity
 
1144
 
 
1145
func (es Entities) GoString() string {
 
1146
        return es.String()
 
1147
}
 
1148
 
 
1149
func (es Entities) String() string {
 
1150
        urls := make([]string, len(es))
 
1151
        for i, e := range es {
 
1152
                urls[i] = e.URL.String()
 
1153
        }
 
1154
        return "[" + strings.Join(urls, ", ") + "]"
 
1155
}