Merge lp:~jtv/gwacl/extract-roundtrippers into lp:gwacl

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: 94
Merged at revision: 95
Proposed branch: lp:~jtv/gwacl/extract-roundtrippers
Merge into: lp:gwacl
Diff against target: 700 lines (+335/-291)
7 files modified
storage_base_test.go (+0/-13)
storage_test.go (+0/-38)
testhelpers_apiobjects.go (+138/-0)
testhelpers_factory.go (+49/-0)
testhelpers_http.go (+58/-0)
testhelpers_misc.go (+16/-240)
testhelpers_x509dispatch.go (+74/-0)
To merge this branch: bzr merge lp:~jtv/gwacl/extract-roundtrippers
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+160590@code.launchpad.net

Commit message

Extract a bunch of our test helpers into separate files.

Description of the change

The ad-hoc test helpers, and our tests in general, were really getting out of hand. Before I added more crud of my own I thought I'd rearrange what we have a bit first, to make it more maintainable.

Turned out the thing I'll be needing was already in there, under a cryptic name. So good thing I did the cleanup first. It may look as if I'm creating a whole bunch of shared internal API for the package, but actually the visibility of these items doesn't change just because I move them.

We'll have to remember that when working in Go: even an unexported little ad-hoc helper will be globally visible within the package, and may move out to a different file! We have to name and document it as such. In this case, I documented where I could.

There were a few problems with the random-string functions, which aren't really very good for general testing. I doubt we should be exporting these. Yes, it'll be a pain to change all the calls when we start importing them from some reusable test package, but that's probably better than inviting third parties to rely on our test helpers and be stuck with them as public API.

I also re-did the two bool/text conversion functions, which just duplicated existing fmt functionality.

Jeroen

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good, a fairly mechanical change so if it compiles, ship it! ;)

[0]

=== added file 'testhelpers_apiobjects.go'
=== added file 'testhelpers_http.go'
=== renamed file 'test_helpers.go' => 'testhelpers_misc.go'
etc…

I seem to remember that Go does not compile files named test_* when not running the tests. I cannot find the doc where this is documented though… but if it's true, then it would be worth keeping the 'test_' prefix for all these files so that they won't be used at all, except when running the tests.

review: Approve
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> Looks good, a fairly mechanical change so if it compiles, ship it! ;)

Thanks. I tried moving this into a separate directory, but that seemed to require a separate package, which I wasn't prepared to create.

> [0]
>
> === added file 'testhelpers_apiobjects.go'
> === added file 'testhelpers_http.go'
> === renamed file 'test_helpers.go' => 'testhelpers_misc.go'
> etc…
>
> I seem to remember that Go does not compile files named test_* when not
> running the tests. I cannot find the doc where this is documented though… but
> if it's true, then it would be worth keeping the 'test_' prefix for all these
> files so that they won't be used at all, except when running the tests.

I couldn't find any mention of it, either on golang.org or by searching the internet at large. And actually I'd expect unused, unexported code to be left out of the library anyway given the way dependencies work in Go. Basically, all the information is in the object file, and they went to great lengths to avoid pulling in unneeded dependencies.

In that model it would make perfect sense to strip dead code out of the object file very aggressively. Otherwise you could have an unused function (about which the compiler does not complain!) pulling in any number of unneeded packages. I'd try this out, except we're not currently working at a code scale where it matters. :)

Jeroen

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'storage_base_test.go'
2--- storage_base_test.go 2013-04-22 16:00:31 +0000
3+++ storage_base_test.go 2013-04-24 10:18:32 +0000
4@@ -257,19 +257,6 @@
5 c.Assert(context.getClient(), Equals, context.client)
6 }
7
8-// TestTransport is used as an http.Client.Transport for testing. The only
9-// requirement is that it adhere to the http.RoundTripper interface.
10-type TestTransport struct {
11- Request *http.Request
12- Response *http.Response
13- Error error
14-}
15-
16-func (t *TestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
17- t.Request = req
18- return t.Response, t.Error
19-}
20-
21 // Convenience factory to create a StorageContext with a random name and
22 // random base64-encoded key.
23 func makeStorageContext() *StorageContext {
24
25=== modified file 'storage_test.go'
26--- storage_test.go 2013-04-22 16:00:31 +0000
27+++ storage_test.go 2013-04-24 10:18:32 +0000
28@@ -5,7 +5,6 @@
29
30 import (
31 "bytes"
32- "encoding/base64"
33 "fmt"
34 "io/ioutil"
35 . "launchpad.net/gocheck"
36@@ -13,35 +12,6 @@
37 "net/url"
38 )
39
40-func b64(s string) string {
41- return base64.StdEncoding.EncodeToString([]byte(s))
42-}
43-
44-type TestTransport2Exchange struct {
45- Request *http.Request
46- Response *http.Response
47- Error error
48-}
49-
50-// TestTransport2 is used as an http.Client.Transport for testing. The only
51-// requirement is that it adhere to the http.RoundTripper interface.
52-type TestTransport2 struct {
53- Exchanges []*TestTransport2Exchange
54- ExchangeCount int
55-}
56-
57-func (t *TestTransport2) AddExchange(response *http.Response, error error) {
58- exchange := TestTransport2Exchange{Response: response, Error: error}
59- t.Exchanges = append(t.Exchanges, &exchange)
60-}
61-
62-func (t *TestTransport2) RoundTrip(req *http.Request) (resp *http.Response, err error) {
63- exchange := t.Exchanges[t.ExchangeCount]
64- t.ExchangeCount += 1
65- exchange.Request = req
66- return exchange.Response, exchange.Error
67-}
68-
69 type testUploadBlockBlob struct{}
70
71 var _ = Suite(&testUploadBlockBlob{})
72@@ -87,14 +57,6 @@
73 assertBlockListSent(c, context, []string{b64("0"), b64("1")}, transport.Exchanges[2])
74 }
75
76-func makeFakeCreatedResponse() *http.Response {
77- return &http.Response{
78- Status: fmt.Sprintf("%d", http.StatusCreated),
79- StatusCode: http.StatusCreated,
80- Body: Empty,
81- }
82-}
83-
84 func uploadRandomBlob(c *C, context *StorageContext, size int) []byte {
85 data := MakeRandomByteSlice(size)
86 err := context.UploadBlockBlob(
87
88=== added file 'testhelpers_apiobjects.go'
89--- testhelpers_apiobjects.go 1970-01-01 00:00:00 +0000
90+++ testhelpers_apiobjects.go 2013-04-24 10:18:32 +0000
91@@ -0,0 +1,138 @@
92+// Copyright 2013 Canonical Ltd. This software is licensed under the
93+// GNU Lesser General Public License version 3 (see the file COPYING).
94+//
95+// Test helpers to fake objects that go into, or come out of, the Azure API.
96+
97+package gwacl
98+
99+import (
100+ "math/rand"
101+)
102+
103+type endpointParams struct {
104+ setname string
105+ localport int
106+ name string
107+ port int
108+ protocol string
109+}
110+
111+func makeEndpoint(params endpointParams) *InputEndpoint {
112+ if params.setname == "" {
113+ params.setname = MakeRandomString(10)
114+ }
115+ if params.name == "" {
116+ params.name = MakeRandomString(10)
117+ }
118+ if params.protocol == "" {
119+ params.protocol = MakeRandomString(10)
120+ }
121+ if params.localport == 0 {
122+ params.localport = rand.Intn(65535)
123+ }
124+ if params.port == 0 {
125+ params.port = rand.Intn(65535)
126+ }
127+
128+ return &InputEndpoint{
129+ LoadBalancedEndpointSetName: params.setname,
130+ LocalPort: params.localport,
131+ Name: params.name,
132+ Port: params.port,
133+ Protocol: params.protocol}
134+}
135+
136+func makeLinuxProvisioningConfiguration() *LinuxProvisioningConfiguration {
137+ hostname := MakeRandomString(10)
138+ username := MakeRandomString(10)
139+ password := MakeRandomString(10)
140+ disable_ssh := MakeRandomBool()
141+
142+ return &LinuxProvisioningConfiguration{
143+ ConfigurationSetType: "LinuxProvisioningConfiguration",
144+ Hostname: hostname,
145+ Username: username,
146+ Password: password,
147+ DisableSSHPasswordAuthentication: disable_ssh}
148+}
149+
150+func makeOSVirtualHardDisk() *OSVirtualHardDisk {
151+ HostCaching := BoolToString(MakeRandomBool())
152+ DiskLabel := MakeRandomString(10)
153+ DiskName := MakeRandomString(10)
154+ MediaLink := MakeRandomString(10)
155+ SourceImageName := MakeRandomString(10)
156+
157+ return &OSVirtualHardDisk{
158+ HostCaching: HostCaching,
159+ DiskLabel: DiskLabel,
160+ DiskName: DiskName,
161+ MediaLink: MediaLink,
162+ SourceImageName: SourceImageName}
163+}
164+
165+func makeRole() *Role {
166+ RoleSize := "ExtraSmall"
167+ RoleName := MakeRandomString(10)
168+ RoleType := "PersistentVMRole"
169+ config := makeLinuxProvisioningConfiguration()
170+ configset := []LinuxProvisioningConfiguration{*config}
171+
172+ return &Role{
173+ RoleSize: RoleSize,
174+ RoleName: RoleName,
175+ RoleType: RoleType,
176+ ConfigurationSets: configset}
177+}
178+
179+func makeDnsServer() *DnsServer {
180+ name := MakeRandomString(10)
181+ address := MakeRandomString(10)
182+
183+ return &DnsServer{
184+ Name: name,
185+ Address: address}
186+}
187+
188+func makeDeployment() *Deployment {
189+ Name := MakeRandomString(10)
190+ DeploymentSlot := "Staging"
191+ Label := MakeRandomString(10)
192+ VirtualNetworkName := MakeRandomString(10)
193+ role := makeRole()
194+ RoleList := []Role{*role}
195+ Dns := []DnsServer{*makeDnsServer()}
196+
197+ return &Deployment{
198+ XMLNS: XMLNS,
199+ XMLNS_I: XMLNS_I,
200+ Name: Name,
201+ DeploymentSlot: DeploymentSlot,
202+ Label: Label,
203+ RoleList: RoleList,
204+ VirtualNetworkName: VirtualNetworkName,
205+ DNS: Dns,
206+ }
207+}
208+
209+func makeCreateStorageServiceInput() *CreateStorageServiceInput {
210+ ServiceName := MakeRandomString(10)
211+ Description := MakeRandomString(10)
212+ Label := MakeRandomString(10)
213+ AffinityGroup := MakeRandomString(10)
214+ Location := MakeRandomString(10)
215+ GeoReplicationEnabled := BoolToString(MakeRandomBool())
216+ ExtendedProperties := []ExtendedProperty{{
217+ Name: MakeRandomString(10),
218+ Value: MakeRandomString(10)}}
219+
220+ return &CreateStorageServiceInput{
221+ XMLNS: XMLNS,
222+ ServiceName: ServiceName,
223+ Description: Description,
224+ Label: Label,
225+ AffinityGroup: AffinityGroup,
226+ Location: Location,
227+ GeoReplicationEnabled: GeoReplicationEnabled,
228+ ExtendedProperties: ExtendedProperties}
229+}
230
231=== added file 'testhelpers_factory.go'
232--- testhelpers_factory.go 1970-01-01 00:00:00 +0000
233+++ testhelpers_factory.go 2013-04-24 10:18:32 +0000
234@@ -0,0 +1,49 @@
235+// Copyright 2013 Canonical Ltd. This software is licensed under the
236+// GNU Lesser General Public License version 3 (see the file COPYING).
237+//
238+// Factories for various types of objects that tests need to create.
239+
240+package gwacl
241+
242+import (
243+ "math/rand"
244+ "time"
245+)
246+
247+// This should be refactored at some point, it does not belong in here.
248+// Perhaps we can add it to gocheck, or start a testtools-like project.
249+const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz"
250+
251+// MakeRandomString returns an arbitrary string of alphanumerical characters.
252+// TODO: This isn't really a random string, more of a random identifier.
253+func MakeRandomString(length int) string {
254+ return string(MakeRandomByteSlice(length))
255+}
256+
257+// MakeRandomString returns a slice of arbitrary bytes, all corresponding to
258+// alphanumerical characters in ASCII.
259+// TODO: This isn't really very random. Good tests need zero and "high" values.
260+func MakeRandomByteSlice(length int) []byte {
261+ dest := make([]byte, length)
262+ for i := range dest {
263+ num := rand.Intn(len(chars))
264+ rand_char := chars[num]
265+ dest[i] = rand_char
266+ }
267+ return dest
268+}
269+
270+// MakeRandomBool returns an arbitrary bool value (true or false).
271+func MakeRandomBool() bool {
272+ v := rand.Intn(2)
273+ if v == 0 {
274+ return false
275+ }
276+ return true
277+}
278+
279+func init() {
280+ // Seed the pseudo-random number generator. Without this, each test run
281+ // will get the same sequence of results from the math/rand package.
282+ rand.Seed(int64(time.Now().Nanosecond()))
283+}
284
285=== added file 'testhelpers_http.go'
286--- testhelpers_http.go 1970-01-01 00:00:00 +0000
287+++ testhelpers_http.go 2013-04-24 10:18:32 +0000
288@@ -0,0 +1,58 @@
289+// Copyright 2013 Canonical Ltd. This software is licensed under the
290+// GNU Lesser General Public License version 3 (see the file COPYING).
291+//
292+// Test helpers for dealing with http requests through the http package.
293+
294+package gwacl
295+
296+import (
297+ "fmt"
298+ "net/http"
299+)
300+
301+// TestTransport is used as an http.Client.Transport for testing. The only
302+// requirement is that it adhere to the http.RoundTripper interface.
303+type TestTransport struct {
304+ Request *http.Request
305+ Response *http.Response
306+ Error error
307+}
308+
309+func (t *TestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
310+ t.Request = req
311+ return t.Response, t.Error
312+}
313+
314+type TestTransport2Exchange struct {
315+ Request *http.Request
316+ Response *http.Response
317+ Error error
318+}
319+
320+// TestTransport2 is used as an http.Client.Transport for testing. The only
321+// requirement is that it adhere to the http.RoundTripper interface.
322+type TestTransport2 struct {
323+ Exchanges []*TestTransport2Exchange
324+ ExchangeCount int
325+}
326+
327+func (t *TestTransport2) AddExchange(response *http.Response, error error) {
328+ exchange := TestTransport2Exchange{Response: response, Error: error}
329+ t.Exchanges = append(t.Exchanges, &exchange)
330+}
331+
332+func (t *TestTransport2) RoundTrip(req *http.Request) (resp *http.Response, err error) {
333+ exchange := t.Exchanges[t.ExchangeCount]
334+ t.ExchangeCount += 1
335+ exchange.Request = req
336+ return exchange.Response, exchange.Error
337+}
338+
339+// makeFakeCreatedResponse returns an HTTP response with the Created status.
340+func makeFakeCreatedResponse() *http.Response {
341+ return &http.Response{
342+ Status: fmt.Sprintf("%d", http.StatusCreated),
343+ StatusCode: http.StatusCreated,
344+ Body: Empty,
345+ }
346+}
347
348=== renamed file 'test_helpers.go' => 'testhelpers_misc.go'
349--- test_helpers.go 2013-04-19 14:44:13 +0000
350+++ testhelpers_misc.go 2013-04-24 10:18:32 +0000
351@@ -5,254 +5,30 @@
352
353 import (
354 "bytes"
355+ "encoding/base64"
356 "fmt"
357 "io"
358 "io/ioutil"
359- "math/rand"
360- "net/http"
361- "strings"
362- "time"
363 )
364
365-// This should be refactored at some point, it does not belong in here.
366-// Perhaps we can add it to gocheck, or start a testtools-like project.
367-const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz"
368+// b64 is shorthand for base64-encoding a string.
369+func b64(s string) string {
370+ return base64.StdEncoding.EncodeToString([]byte(s))
371+}
372
373 // A Reader and ReadCloser that EOFs immediately.
374 var Empty io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
375
376-func MakeRandomString(length int) string {
377- return string(MakeRandomByteSlice(length))
378-}
379-
380-func MakeRandomByteSlice(length int) []byte {
381- dest := make([]byte, length)
382- for i := range dest {
383- num := rand.Intn(len(chars))
384- rand_char := chars[num]
385- dest[i] = rand_char
386- }
387- return dest
388-}
389-
390-func MakeRandomBool() bool {
391- v := rand.Intn(2)
392- if v == 0 {
393- return false
394- }
395- return true
396-}
397-
398+// BoolToString represents a boolean value as a string ("true" or "false").
399 func BoolToString(v bool) string {
400- if v {
401- return "true"
402- }
403- return "false"
404-}
405-
406-func StringToBool(v string) bool {
407- switch strings.ToLower(v) {
408- case "true":
409- return true
410- case "false":
411- return false
412- }
413- panic(fmt.Errorf("can't convert '%s' to a bool", v))
414-}
415-
416-type endpointParams struct {
417- setname string
418- localport int
419- name string
420- port int
421- protocol string
422-}
423-
424-func makeEndpoint(params endpointParams) *InputEndpoint {
425- if params.setname == "" {
426- params.setname = MakeRandomString(10)
427- }
428- if params.name == "" {
429- params.name = MakeRandomString(10)
430- }
431- if params.protocol == "" {
432- params.protocol = MakeRandomString(10)
433- }
434- if params.localport == 0 {
435- params.localport = rand.Intn(65535)
436- }
437- if params.port == 0 {
438- params.port = rand.Intn(65535)
439- }
440-
441- return &InputEndpoint{
442- LoadBalancedEndpointSetName: params.setname,
443- LocalPort: params.localport,
444- Name: params.name,
445- Port: params.port,
446- Protocol: params.protocol}
447-}
448-
449-func makeLinuxProvisioningConfiguration() *LinuxProvisioningConfiguration {
450- hostname := MakeRandomString(10)
451- username := MakeRandomString(10)
452- password := MakeRandomString(10)
453- disable_ssh := MakeRandomBool()
454-
455- return &LinuxProvisioningConfiguration{
456- ConfigurationSetType: "LinuxProvisioningConfiguration",
457- Hostname: hostname,
458- Username: username,
459- Password: password,
460- DisableSSHPasswordAuthentication: disable_ssh}
461-}
462-
463-func makeOSVirtualHardDisk() *OSVirtualHardDisk {
464- HostCaching := BoolToString(MakeRandomBool())
465- DiskLabel := MakeRandomString(10)
466- DiskName := MakeRandomString(10)
467- MediaLink := MakeRandomString(10)
468- SourceImageName := MakeRandomString(10)
469-
470- return &OSVirtualHardDisk{
471- HostCaching: HostCaching,
472- DiskLabel: DiskLabel,
473- DiskName: DiskName,
474- MediaLink: MediaLink,
475- SourceImageName: SourceImageName}
476-}
477-
478-func makeRole() *Role {
479- RoleSize := "ExtraSmall"
480- RoleName := MakeRandomString(10)
481- RoleType := "PersistentVMRole"
482- config := makeLinuxProvisioningConfiguration()
483- configset := []LinuxProvisioningConfiguration{*config}
484-
485- return &Role{
486- RoleSize: RoleSize,
487- RoleName: RoleName,
488- RoleType: RoleType,
489- ConfigurationSets: configset}
490-}
491-
492-func makeDnsServer() *DnsServer {
493- name := MakeRandomString(10)
494- address := MakeRandomString(10)
495-
496- return &DnsServer{
497- Name: name,
498- Address: address}
499-}
500-
501-func makeDeployment() *Deployment {
502- Name := MakeRandomString(10)
503- DeploymentSlot := "Staging"
504- Label := MakeRandomString(10)
505- VirtualNetworkName := MakeRandomString(10)
506- role := makeRole()
507- RoleList := []Role{*role}
508- Dns := []DnsServer{*makeDnsServer()}
509-
510- return &Deployment{
511- XMLNS: XMLNS,
512- XMLNS_I: XMLNS_I,
513- Name: Name,
514- DeploymentSlot: DeploymentSlot,
515- Label: Label,
516- RoleList: RoleList,
517- VirtualNetworkName: VirtualNetworkName,
518- DNS: Dns,
519- }
520-}
521-
522-func makeCreateStorageServiceInput() *CreateStorageServiceInput {
523- ServiceName := MakeRandomString(10)
524- Description := MakeRandomString(10)
525- Label := MakeRandomString(10)
526- AffinityGroup := MakeRandomString(10)
527- Location := MakeRandomString(10)
528- GeoReplicationEnabled := BoolToString(MakeRandomBool())
529- ExtendedProperties := []ExtendedProperty{{
530- Name: MakeRandomString(10),
531- Value: MakeRandomString(10)}}
532-
533- return &CreateStorageServiceInput{
534- XMLNS: XMLNS,
535- ServiceName: ServiceName,
536- Description: Description,
537- Label: Label,
538- AffinityGroup: AffinityGroup,
539- Location: Location,
540- GeoReplicationEnabled: GeoReplicationEnabled,
541- ExtendedProperties: ExtendedProperties}
542-}
543-
544-// rigRecordingDispatcher sets up a request dispatcher that records incoming
545-// requests by appending them to *record. It returns the result of whatever
546-// dispatcher was already active.
547-// If you also want the dispatcher to return a particular result, rig it for
548-// that result first (using one of the other rig...Dispatcher functions) and
549-// then chain the recording dispatcher in front of it.
550-func rigRecordingDispatcher(record *[]*x509Request) {
551- previousDispatcher := _X509Dispatcher
552- _X509Dispatcher = func(session *x509Session, request *x509Request) (*x509Response, error) {
553- *record = append(*record, request)
554- return previousDispatcher(session, request)
555- }
556-}
557-
558-// rigFixedResponseDispatcher sets up a request dispatcher that always returns
559-// a prepared response.
560-func rigFixedResponseDispatcher(response *x509Response) {
561- _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
562- return response, nil
563- }
564-}
565-
566-// rigFailingDispatcher sets up a request dispatcher that returns a given
567-// error.
568-func rigFailingDispatcher(failure error) {
569- _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
570- return nil, failure
571- }
572-}
573-
574-type DispatcherResponse struct {
575- response *x509Response
576- errorObject error
577-}
578-
579-// rigPreparedResponseDispatcher sets up a request dispatcher that returns,
580-// for each consecutive request, the next of a series of prepared responses.
581-func rigPreparedResponseDispatcher(responses []DispatcherResponse) {
582- index := 0
583- _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
584- response := responses[index]
585- index += 1
586- return response.response, response.errorObject
587- }
588-}
589-
590-// setUpDispatcher sets up a request dispatcher that:
591-// - records requests
592-// - returns empty responses
593-func setUpDispatcher(operationID string) *[]*x509Request {
594- header := http.Header{}
595- header.Set("X-Ms-Request-Id", operationID)
596- fixedResponse := x509Response{
597- StatusCode: http.StatusOK,
598- Body: []byte{},
599- Header: header,
600- }
601- rigFixedResponseDispatcher(&fixedResponse)
602- recordedRequests := make([]*x509Request, 0)
603- rigRecordingDispatcher(&recordedRequests)
604- return &recordedRequests
605-}
606-
607-func init() {
608- // Staggeringly, rand will produce the same numbers from the start of
609- // program invocation unless you seed it ...
610- rand.Seed(int64(time.Now().Nanosecond()))
611+ return fmt.Sprintf("%t", v)
612+}
613+
614+// StringToBool parses a string containing a boolean (case-insensitive).
615+func StringToBool(v string) (b bool) {
616+ items, err := fmt.Sscanf(v, "%t", &b)
617+ if err != nil || items != 1 {
618+ panic(fmt.Errorf("can't convert '%s' to a bool", v))
619+ }
620+ return
621 }
622
623=== added file 'testhelpers_x509dispatch.go'
624--- testhelpers_x509dispatch.go 1970-01-01 00:00:00 +0000
625+++ testhelpers_x509dispatch.go 2013-04-24 10:18:32 +0000
626@@ -0,0 +1,74 @@
627+// Copyright 2013 Canonical Ltd. This software is licensed under the
628+// GNU Lesser General Public License version 3 (see the file COPYING).
629+//
630+// Helpers for testing with x509 requests. These help inject fake responses
631+// into the x509 request dispatcher.
632+
633+package gwacl
634+
635+import (
636+ "net/http"
637+)
638+
639+// rigRecordingDispatcher sets up a request dispatcher that records incoming
640+// requests by appending them to *record. It returns the result of whatever
641+// dispatcher was already active.
642+// If you also want the dispatcher to return a particular result, rig it for
643+// that result first (using one of the other rig...Dispatcher functions) and
644+// then chain the recording dispatcher in front of it.
645+func rigRecordingDispatcher(record *[]*x509Request) {
646+ previousDispatcher := _X509Dispatcher
647+ _X509Dispatcher = func(session *x509Session, request *x509Request) (*x509Response, error) {
648+ *record = append(*record, request)
649+ return previousDispatcher(session, request)
650+ }
651+}
652+
653+// rigFixedResponseDispatcher sets up a request dispatcher that always returns
654+// a prepared response.
655+func rigFixedResponseDispatcher(response *x509Response) {
656+ _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
657+ return response, nil
658+ }
659+}
660+
661+// rigFailingDispatcher sets up a request dispatcher that returns a given
662+// error.
663+func rigFailingDispatcher(failure error) {
664+ _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
665+ return nil, failure
666+ }
667+}
668+
669+type DispatcherResponse struct {
670+ response *x509Response
671+ errorObject error
672+}
673+
674+// rigPreparedResponseDispatcher sets up a request dispatcher that returns,
675+// for each consecutive request, the next of a series of prepared responses.
676+func rigPreparedResponseDispatcher(responses []DispatcherResponse) {
677+ index := 0
678+ _X509Dispatcher = func(*x509Session, *x509Request) (*x509Response, error) {
679+ response := responses[index]
680+ index += 1
681+ return response.response, response.errorObject
682+ }
683+}
684+
685+// setUpDispatcher sets up a request dispatcher that:
686+// - records requests
687+// - returns empty responses
688+func setUpDispatcher(operationID string) *[]*x509Request {
689+ header := http.Header{}
690+ header.Set("X-Ms-Request-Id", operationID)
691+ fixedResponse := x509Response{
692+ StatusCode: http.StatusOK,
693+ Body: []byte{},
694+ Header: header,
695+ }
696+ rigFixedResponseDispatcher(&fixedResponse)
697+ recordedRequests := make([]*x509Request, 0)
698+ rigRecordingDispatcher(&recordedRequests)
699+ return &recordedRequests
700+}

Subscribers

People subscribed via source and target branches