~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/juju/charmstore.v5-unstable/internal/v5/list_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 v5_test // import "gopkg.in/juju/charmstore.v5-unstable/internal/v5"
 
5
 
 
6
import (
 
7
        "bytes"
 
8
        "encoding/json"
 
9
        "net/http"
 
10
        "sort"
 
11
        "strings"
 
12
 
 
13
        "github.com/juju/loggo"
 
14
        jc "github.com/juju/testing/checkers"
 
15
        "github.com/juju/testing/httptesting"
 
16
        gc "gopkg.in/check.v1"
 
17
        "gopkg.in/juju/charm.v6-unstable"
 
18
        "gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
 
19
        "gopkg.in/macaroon-bakery.v1/bakery/checkers"
 
20
        "gopkg.in/macaroon-bakery.v1/httpbakery"
 
21
        "gopkg.in/macaroon.v1"
 
22
 
 
23
        "gopkg.in/juju/charmstore.v5-unstable/internal/router"
 
24
        "gopkg.in/juju/charmstore.v5-unstable/internal/storetesting"
 
25
)
 
26
 
 
27
type ListSuite struct {
 
28
        commonSuite
 
29
}
 
30
 
 
31
var _ = gc.Suite(&ListSuite{})
 
32
 
 
33
var exportListTestCharms = map[string]*router.ResolvedURL{
 
34
        "wordpress": newResolvedURL("cs:~charmers/precise/wordpress-23", 23),
 
35
        "mysql":     newResolvedURL("cs:~openstack-charmers/trusty/mysql-7", 7),
 
36
        "varnish":   newResolvedURL("cs:~foo/trusty/varnish-1", -1),
 
37
        "riak":      newResolvedURL("cs:~charmers/trusty/riak-67", 67),
 
38
}
 
39
 
 
40
var exportListTestBundles = map[string]*router.ResolvedURL{
 
41
        "wordpress-simple": newResolvedURL("cs:~charmers/bundle/wordpress-simple-4", 4),
 
42
}
 
43
 
 
44
func (s *ListSuite) SetUpSuite(c *gc.C) {
 
45
        s.enableIdentity = true
 
46
        s.commonSuite.SetUpSuite(c)
 
47
}
 
48
 
 
49
func (s *ListSuite) SetUpTest(c *gc.C) {
 
50
        s.commonSuite.SetUpTest(c)
 
51
        s.addCharmsToStore(c)
 
52
        // hide the riak charm
 
53
        err := s.store.SetPerms(charm.MustParseURL("cs:~charmers/riak"), "stable.read", "charmers", "test-user")
 
54
        c.Assert(err, gc.IsNil)
 
55
}
 
56
 
 
57
func (s *ListSuite) addCharmsToStore(c *gc.C) {
 
58
        for name, id := range exportListTestCharms {
 
59
                s.addPublicCharm(c, getListCharm(name), id)
 
60
        }
 
61
        for name, id := range exportListTestBundles {
 
62
                s.addPublicBundle(c, getListBundle(name), id, false)
 
63
        }
 
64
}
 
65
 
 
66
func getListCharm(name string) *storetesting.Charm {
 
67
        ca := storetesting.Charms.CharmDir(name)
 
68
        meta := ca.Meta()
 
69
        meta.Categories = append(strings.Split(name, "-"), "bar")
 
70
        return storetesting.NewCharm(meta)
 
71
}
 
72
 
 
73
func getListBundle(name string) *storetesting.Bundle {
 
74
        ba := storetesting.Charms.BundleDir(name)
 
75
        data := ba.Data()
 
76
        data.Tags = append(strings.Split(name, "-"), "baz")
 
77
        return storetesting.NewBundle(data)
 
78
}
 
79
 
 
80
func (s *ListSuite) TestSuccessfulList(c *gc.C) {
 
81
        tests := []struct {
 
82
                about   string
 
83
                query   string
 
84
                results []*router.ResolvedURL
 
85
        }{{
 
86
                about: "bare list",
 
87
                query: "",
 
88
                results: []*router.ResolvedURL{
 
89
                        exportTestBundles["wordpress-simple"],
 
90
                        exportTestCharms["wordpress"],
 
91
                        exportTestCharms["varnish"],
 
92
                        exportTestCharms["mysql"],
 
93
                },
 
94
        }, {
 
95
                about: "name filter list",
 
96
                query: "name=mysql",
 
97
                results: []*router.ResolvedURL{
 
98
                        exportTestCharms["mysql"],
 
99
                },
 
100
        }, {
 
101
                about: "owner filter list",
 
102
                query: "owner=foo",
 
103
                results: []*router.ResolvedURL{
 
104
                        exportTestCharms["varnish"],
 
105
                },
 
106
        }, {
 
107
                about: "series filter list",
 
108
                query: "series=trusty",
 
109
                results: []*router.ResolvedURL{
 
110
                        exportTestCharms["varnish"],
 
111
                        exportTestCharms["mysql"],
 
112
                },
 
113
        }, {
 
114
                about: "type filter list",
 
115
                query: "type=bundle",
 
116
                results: []*router.ResolvedURL{
 
117
                        exportTestBundles["wordpress-simple"],
 
118
                },
 
119
        }, {
 
120
                about: "promulgated",
 
121
                query: "promulgated=1",
 
122
                results: []*router.ResolvedURL{
 
123
                        exportTestBundles["wordpress-simple"],
 
124
                        exportTestCharms["wordpress"],
 
125
                        exportTestCharms["mysql"],
 
126
                },
 
127
        }, {
 
128
                about: "not promulgated",
 
129
                query: "promulgated=0",
 
130
                results: []*router.ResolvedURL{
 
131
                        exportTestCharms["varnish"],
 
132
                },
 
133
        }, {
 
134
                about: "promulgated with owner",
 
135
                query: "promulgated=1&owner=openstack-charmers",
 
136
                results: []*router.ResolvedURL{
 
137
                        exportTestCharms["mysql"],
 
138
                },
 
139
        }}
 
140
        for i, test := range tests {
 
141
                c.Logf("test %d. %s", i, test.about)
 
142
                rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
143
                        Handler: s.srv,
 
144
                        URL:     storeURL("list?" + test.query),
 
145
                })
 
146
                var sr params.ListResponse
 
147
                err := json.Unmarshal(rec.Body.Bytes(), &sr)
 
148
                c.Assert(err, gc.IsNil)
 
149
                c.Assert(sr.Results, gc.HasLen, len(test.results))
 
150
                c.Logf("results: %s", rec.Body.Bytes())
 
151
                for i := range test.results {
 
152
                        c.Assert(sr.Results[i].Id.String(), gc.Equals, test.results[i].PreferredURL().String(), gc.Commentf("element %d"))
 
153
                }
 
154
        }
 
155
}
 
156
 
 
157
func (s *ListSuite) TestMetadataFields(c *gc.C) {
 
158
        tests := []struct {
 
159
                about string
 
160
                query string
 
161
                meta  map[string]interface{}
 
162
        }{{
 
163
                about: "archive-size",
 
164
                query: "name=mysql&include=archive-size",
 
165
                meta: map[string]interface{}{
 
166
                        "archive-size": params.ArchiveSizeResponse{getListCharm("mysql").Size()},
 
167
                },
 
168
        }, {
 
169
                about: "bundle-metadata",
 
170
                query: "name=wordpress-simple&type=bundle&include=bundle-metadata",
 
171
                meta: map[string]interface{}{
 
172
                        "bundle-metadata": getListBundle("wordpress-simple").Data(),
 
173
                },
 
174
        }, {
 
175
                about: "bundle-machine-count",
 
176
                query: "name=wordpress-simple&type=bundle&include=bundle-machine-count",
 
177
                meta: map[string]interface{}{
 
178
                        "bundle-machine-count": params.BundleCount{2},
 
179
                },
 
180
        }, {
 
181
                about: "bundle-unit-count",
 
182
                query: "name=wordpress-simple&type=bundle&include=bundle-unit-count",
 
183
                meta: map[string]interface{}{
 
184
                        "bundle-unit-count": params.BundleCount{2},
 
185
                },
 
186
        }, {
 
187
                about: "charm-actions",
 
188
                query: "name=wordpress&type=charm&include=charm-actions",
 
189
                meta: map[string]interface{}{
 
190
                        "charm-actions": getListCharm("wordpress").Actions(),
 
191
                },
 
192
        }, {
 
193
                about: "charm-config",
 
194
                query: "name=wordpress&type=charm&include=charm-config",
 
195
                meta: map[string]interface{}{
 
196
                        "charm-config": getListCharm("wordpress").Config(),
 
197
                },
 
198
        }, {
 
199
                about: "charm-related",
 
200
                query: "name=wordpress&type=charm&include=charm-related",
 
201
                meta: map[string]interface{}{
 
202
                        "charm-related": params.RelatedResponse{
 
203
                                Provides: map[string][]params.EntityResult{
 
204
                                        "mysql": {
 
205
                                                {
 
206
                                                        Id: exportTestCharms["mysql"].PreferredURL(),
 
207
                                                },
 
208
                                        },
 
209
                                        "varnish": {
 
210
                                                {
 
211
                                                        Id: exportTestCharms["varnish"].PreferredURL(),
 
212
                                                },
 
213
                                        },
 
214
                                },
 
215
                        },
 
216
                },
 
217
        }, {
 
218
                about: "multiple values",
 
219
                query: "name=wordpress&type=charm&include=charm-related&include=charm-config",
 
220
                meta: map[string]interface{}{
 
221
                        "charm-related": params.RelatedResponse{
 
222
                                Provides: map[string][]params.EntityResult{
 
223
                                        "mysql": {
 
224
                                                {
 
225
                                                        Id: exportTestCharms["mysql"].PreferredURL(),
 
226
                                                },
 
227
                                        },
 
228
                                        "varnish": {
 
229
                                                {
 
230
                                                        Id: exportTestCharms["varnish"].PreferredURL(),
 
231
                                                },
 
232
                                        },
 
233
                                },
 
234
                        },
 
235
                        "charm-config": getListCharm("wordpress").Config(),
 
236
                },
 
237
        }}
 
238
        for i, test := range tests {
 
239
                c.Logf("test %d. %s", i, test.about)
 
240
                rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
241
                        Handler: s.srv,
 
242
                        URL:     storeURL("list?" + test.query),
 
243
                })
 
244
                c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
245
                var sr struct {
 
246
                        Results []struct {
 
247
                                Meta json.RawMessage
 
248
                        }
 
249
                }
 
250
                err := json.Unmarshal(rec.Body.Bytes(), &sr)
 
251
                c.Assert(err, gc.IsNil)
 
252
                c.Assert(sr.Results, gc.HasLen, 1)
 
253
                c.Assert(string(sr.Results[0].Meta), jc.JSONEquals, test.meta)
 
254
        }
 
255
}
 
256
 
 
257
func (s *ListSuite) TestListIncludeError(c *gc.C) {
 
258
        // Perform a list for all charms, including the
 
259
        // manifest, which will try to retrieve all charm
 
260
        // blobs.
 
261
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
262
                Handler: s.srv,
 
263
                URL:     storeURL("list?type=charm&include=manifest"),
 
264
        })
 
265
        c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
266
        var resp params.ListResponse
 
267
        err := json.Unmarshal(rec.Body.Bytes(), &resp)
 
268
        // cs:riak will not be found because it is not visible to "everyone".
 
269
        c.Assert(resp.Results, gc.HasLen, len(exportTestCharms)-1)
 
270
 
 
271
        // Now remove one of the blobs. The list should still
 
272
        // work, but only return a single result.
 
273
        entity, err := s.store.FindEntity(newResolvedURL("~charmers/precise/wordpress-23", 23), nil)
 
274
        c.Assert(err, gc.IsNil)
 
275
        err = s.store.BlobStore.Remove(entity.BlobName)
 
276
        c.Assert(err, gc.IsNil)
 
277
 
 
278
        // Now list again - we should get one result less
 
279
        // (and the error will be logged).
 
280
 
 
281
        // Register a logger that so that we can check the logging output.
 
282
        // It will be automatically removed later because IsolatedMgoESSuite
 
283
        // uses LoggingSuite.
 
284
        var tw loggo.TestWriter
 
285
        err = loggo.RegisterWriter("test-log", &tw, loggo.DEBUG)
 
286
        c.Assert(err, gc.IsNil)
 
287
 
 
288
        rec = httptesting.DoRequest(c, httptesting.DoRequestParams{
 
289
                Handler: s.srv,
 
290
                URL:     storeURL("list?type=charm&include=manifest"),
 
291
        })
 
292
        c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
293
        resp = params.ListResponse{}
 
294
        err = json.Unmarshal(rec.Body.Bytes(), &resp)
 
295
        // cs:riak will not be found because it is not visible to "everyone".
 
296
        // cs:wordpress will not be found because it has no manifest.
 
297
        c.Assert(resp.Results, gc.HasLen, len(exportTestCharms)-2)
 
298
 
 
299
        c.Assert(tw.Log(), jc.LogMatches, []string{"cannot retrieve metadata for cs:precise/wordpress-23: cannot open archive data for cs:precise/wordpress-23: .*"})
 
300
}
 
301
 
 
302
func (s *ListSuite) TestSortingList(c *gc.C) {
 
303
        tests := []struct {
 
304
                about   string
 
305
                query   string
 
306
                results []*router.ResolvedURL
 
307
        }{{
 
308
                about: "name ascending",
 
309
                query: "sort=name",
 
310
                results: []*router.ResolvedURL{
 
311
                        exportTestCharms["mysql"],
 
312
                        exportTestCharms["varnish"],
 
313
                        exportTestCharms["wordpress"],
 
314
                        exportTestBundles["wordpress-simple"],
 
315
                },
 
316
        }, {
 
317
                about: "name descending",
 
318
                query: "sort=-name",
 
319
                results: []*router.ResolvedURL{
 
320
                        exportTestBundles["wordpress-simple"],
 
321
                        exportTestCharms["wordpress"],
 
322
                        exportTestCharms["varnish"],
 
323
                        exportTestCharms["mysql"],
 
324
                },
 
325
        }, {
 
326
                about: "series ascending",
 
327
                query: "sort=series,name",
 
328
                results: []*router.ResolvedURL{
 
329
                        exportTestBundles["wordpress-simple"],
 
330
                        exportTestCharms["wordpress"],
 
331
                        exportTestCharms["mysql"],
 
332
                        exportTestCharms["varnish"],
 
333
                },
 
334
        }, {
 
335
                about: "series descending",
 
336
                query: "sort=-series&sort=name",
 
337
                results: []*router.ResolvedURL{
 
338
                        exportTestCharms["mysql"],
 
339
                        exportTestCharms["varnish"],
 
340
                        exportTestCharms["wordpress"],
 
341
                        exportTestBundles["wordpress-simple"],
 
342
                },
 
343
        }, {
 
344
                about: "owner ascending",
 
345
                query: "sort=owner,name",
 
346
                results: []*router.ResolvedURL{
 
347
                        exportTestCharms["wordpress"],
 
348
                        exportTestBundles["wordpress-simple"],
 
349
                        exportTestCharms["varnish"],
 
350
                        exportTestCharms["mysql"],
 
351
                },
 
352
        }, {
 
353
                about: "owner descending",
 
354
                query: "sort=-owner&sort=name",
 
355
                results: []*router.ResolvedURL{
 
356
                        exportTestCharms["mysql"],
 
357
                        exportTestCharms["varnish"],
 
358
                        exportTestCharms["wordpress"],
 
359
                        exportTestBundles["wordpress-simple"],
 
360
                },
 
361
        }}
 
362
        for i, test := range tests {
 
363
                c.Logf("test %d. %s", i, test.about)
 
364
                rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
365
                        Handler: s.srv,
 
366
                        URL:     storeURL("list?" + test.query),
 
367
                })
 
368
                var sr params.ListResponse
 
369
                err := json.Unmarshal(rec.Body.Bytes(), &sr)
 
370
                c.Assert(err, gc.IsNil)
 
371
                c.Assert(sr.Results, gc.HasLen, len(test.results), gc.Commentf("expected %#v", test.results))
 
372
                c.Logf("results: %s", rec.Body.Bytes())
 
373
                for i := range test.results {
 
374
                        c.Assert(sr.Results[i].Id.String(), gc.Equals, test.results[i].PreferredURL().String(), gc.Commentf("element %d"))
 
375
                }
 
376
        }
 
377
}
 
378
 
 
379
func (s *ListSuite) TestSortUnsupportedListField(c *gc.C) {
 
380
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
381
                Handler: s.srv,
 
382
                URL:     storeURL("list?sort=text"),
 
383
        })
 
384
        var e params.Error
 
385
        err := json.Unmarshal(rec.Body.Bytes(), &e)
 
386
        c.Assert(err, gc.IsNil)
 
387
        c.Assert(e.Code, gc.Equals, params.ErrBadRequest)
 
388
        c.Assert(e.Message, gc.Equals, "invalid sort field: unrecognized sort parameter \"text\"")
 
389
}
 
390
 
 
391
func (s *ListSuite) TestGetLatestRevisionOnly(c *gc.C) {
 
392
        id := newResolvedURL("cs:~charmers/precise/wordpress-24", 24)
 
393
        s.addPublicCharm(c, getListCharm("wordpress"), id)
 
394
 
 
395
        testresults := []*router.ResolvedURL{
 
396
                exportTestBundles["wordpress-simple"],
 
397
                id,
 
398
                exportTestCharms["varnish"],
 
399
                exportTestCharms["mysql"],
 
400
        }
 
401
 
 
402
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
403
                Handler: s.srv,
 
404
                URL:     storeURL("list"),
 
405
        })
 
406
        var sr params.ListResponse
 
407
        err := json.Unmarshal(rec.Body.Bytes(), &sr)
 
408
        c.Assert(err, gc.IsNil)
 
409
        c.Assert(sr.Results, gc.HasLen, 4, gc.Commentf("expected %#v", testresults))
 
410
        c.Logf("results: %s", rec.Body.Bytes())
 
411
        for i := range testresults {
 
412
                c.Assert(sr.Results[i].Id.String(), gc.Equals, testresults[i].PreferredURL().String(), gc.Commentf("element %d"))
 
413
        }
 
414
 
 
415
        testresults = []*router.ResolvedURL{
 
416
                exportTestCharms["mysql"],
 
417
                exportTestCharms["varnish"],
 
418
                id,
 
419
                exportTestBundles["wordpress-simple"],
 
420
        }
 
421
        rec = httptesting.DoRequest(c, httptesting.DoRequestParams{
 
422
                Handler: s.srv,
 
423
                URL:     storeURL("list?sort=name"),
 
424
        })
 
425
        err = json.Unmarshal(rec.Body.Bytes(), &sr)
 
426
        c.Assert(err, gc.IsNil)
 
427
        c.Assert(sr.Results, gc.HasLen, 4, gc.Commentf("expected %#v", testresults))
 
428
        c.Logf("results: %s", rec.Body.Bytes())
 
429
        for i := range testresults {
 
430
                c.Assert(sr.Results[i].Id.String(), gc.Equals, testresults[i].PreferredURL().String(), gc.Commentf("element %d"))
 
431
        }
 
432
}
 
433
 
 
434
func (s *ListSuite) assertPut(c *gc.C, url string, val interface{}) {
 
435
        body, err := json.Marshal(val)
 
436
        c.Assert(err, gc.IsNil)
 
437
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
438
                Handler: s.srv,
 
439
                URL:     storeURL(url),
 
440
                Method:  "PUT",
 
441
                Header: http.Header{
 
442
                        "Content-Type": {"application/json"},
 
443
                },
 
444
                Username: testUsername,
 
445
                Password: testPassword,
 
446
                Body:     bytes.NewReader(body),
 
447
        })
 
448
        c.Assert(rec.Code, gc.Equals, http.StatusOK, gc.Commentf("headers: %v, body: %s", rec.HeaderMap, rec.Body.String()))
 
449
        c.Assert(rec.Body.String(), gc.HasLen, 0)
 
450
}
 
451
 
 
452
func (s *ListSuite) TestListWithAdminCredentials(c *gc.C) {
 
453
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
454
                Handler:  s.srv,
 
455
                URL:      storeURL("list"),
 
456
                Username: testUsername,
 
457
                Password: testPassword,
 
458
        })
 
459
        c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
460
        expected := []*router.ResolvedURL{
 
461
                exportTestCharms["mysql"],
 
462
                exportTestCharms["wordpress"],
 
463
                exportTestCharms["riak"],
 
464
                exportTestCharms["varnish"],
 
465
                exportTestBundles["wordpress-simple"],
 
466
        }
 
467
        var sr params.ListResponse
 
468
        err := json.Unmarshal(rec.Body.Bytes(), &sr)
 
469
        c.Assert(err, gc.IsNil)
 
470
        assertListResultSet(c, sr, expected)
 
471
}
 
472
 
 
473
func (s *ListSuite) TestListWithUserMacaroon(c *gc.C) {
 
474
        m, err := s.store.Bakery.NewMacaroon("", nil, []checkers.Caveat{
 
475
                checkers.DeclaredCaveat("username", "test-user"),
 
476
        })
 
477
        c.Assert(err, gc.IsNil)
 
478
        macaroonCookie, err := httpbakery.NewCookie(macaroon.Slice{m})
 
479
        c.Assert(err, gc.IsNil)
 
480
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
481
                Handler: s.srv,
 
482
                URL:     storeURL("list"),
 
483
                Cookies: []*http.Cookie{macaroonCookie},
 
484
        })
 
485
        c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
486
        expected := []*router.ResolvedURL{
 
487
                exportTestCharms["mysql"],
 
488
                exportTestCharms["wordpress"],
 
489
                exportTestCharms["riak"],
 
490
                exportTestCharms["varnish"],
 
491
                exportTestBundles["wordpress-simple"],
 
492
        }
 
493
        var sr params.ListResponse
 
494
        err = json.Unmarshal(rec.Body.Bytes(), &sr)
 
495
        c.Assert(err, gc.IsNil)
 
496
        assertListResultSet(c, sr, expected)
 
497
}
 
498
 
 
499
func (s *ListSuite) TestSearchWithBadAdminCredentialsAndACookie(c *gc.C) {
 
500
        m, err := s.store.Bakery.NewMacaroon("", nil, []checkers.Caveat{
 
501
                checkers.DeclaredCaveat("username", "test-user"),
 
502
        })
 
503
        c.Assert(err, gc.IsNil)
 
504
        macaroonCookie, err := httpbakery.NewCookie(macaroon.Slice{m})
 
505
        c.Assert(err, gc.IsNil)
 
506
        rec := httptesting.DoRequest(c, httptesting.DoRequestParams{
 
507
                Handler:  s.srv,
 
508
                URL:      storeURL("list"),
 
509
                Cookies:  []*http.Cookie{macaroonCookie},
 
510
                Username: testUsername,
 
511
                Password: "bad-password",
 
512
        })
 
513
        c.Assert(rec.Code, gc.Equals, http.StatusOK)
 
514
        expected := []*router.ResolvedURL{
 
515
                exportTestCharms["mysql"],
 
516
                exportTestCharms["wordpress"],
 
517
                exportTestCharms["varnish"],
 
518
                exportTestBundles["wordpress-simple"],
 
519
        }
 
520
        var sr params.ListResponse
 
521
        err = json.Unmarshal(rec.Body.Bytes(), &sr)
 
522
        c.Assert(err, gc.IsNil)
 
523
        assertListResultSet(c, sr, expected)
 
524
}
 
525
 
 
526
func assertListResultSet(c *gc.C, sr params.ListResponse, expected []*router.ResolvedURL) {
 
527
        sort.Sort(listResultById(sr.Results))
 
528
        sort.Sort(resolvedURLByPreferredURL(expected))
 
529
        c.Assert(sr.Results, gc.HasLen, len(expected), gc.Commentf("expected %#v", expected))
 
530
        for i := range expected {
 
531
                c.Assert(sr.Results[i].Id.String(), gc.Equals, expected[i].PreferredURL().String(), gc.Commentf("element %d"))
 
532
        }
 
533
}
 
534
 
 
535
type listResultById []params.EntityResult
 
536
 
 
537
func (s listResultById) Len() int      { return len(s) }
 
538
func (s listResultById) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 
539
func (s listResultById) Less(i, j int) bool {
 
540
        return s[i].Id.String() < s[j].Id.String()
 
541
}