This file is indexed.

/usr/share/gocode/src/github.com/go-chef/chef/http_test.go is in golang-github-go-chef-chef-dev 0.0.1+git20161023.60.deb8c38-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
package chef

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	. "github.com/ctdk/chefcrypto"
	. "github.com/smartystreets/goconvey/convey"
	"io"
	"math/big"
	"net/http"
	"net/http/httptest"
	"regexp"
	"strconv"
	"strings"
	"testing"
)

var (
	testRequiredHeaders = []string{
		"X-Ops-Timestamp",
		"X-Ops-UserId",
		"X-Ops-Sign",
		"X-Ops-Content-Hash",
		"X-Ops-Authorization-1",
	}

	mux    *http.ServeMux
	server *httptest.Server
	client *Client
)

const (
	userid     = "tester"
	requestURL = "http://localhost:80"
	// Generated from
	// openssl genrsa -out privkey.pem 2048
	// perl -pe 's/\n/\\n/g' privkey.pem
	privateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAx12nDxxOwSPHRSJEDz67a0folBqElzlu2oGMiUTS+dqtj3FU
h5lJc1MjcprRVxcDVwhsSSo9948XEkk39IdblUCLohucqNMzOnIcdZn8zblN7Cnp
W03UwRM0iWX1HuwHnGvm6PKeqKGqplyIXYO0qlDWCzC+VaxFTwOUk31MfOHJQn4y
fTrfuE7h3FTElLBu065SFp3dPICIEmWCl9DadnxbnZ8ASxYQ9xG7hmZduDgjNW5l
3x6/EFkpym+//D6AbWDcVJ1ovCsJL3CfH/NZC3ekeJ/aEeLxP/vaCSH1VYC5VsYK
5Qg7SIa6Nth3+RZz1hYOoBJulEzwljznwoZYRQIDAQABAoIBADPQol+qAsnty5er
PTcdHcbXLJp5feZz1dzSeL0gdxja/erfEJIhg9aGUBs0I55X69VN6h7l7K8PsHZf
MzzJhUL4QJJETOYP5iuVhtIF0I+DTr5Hck/5nYcEv83KAvgjbiL4ZE486IF5awnL
2OE9HtJ5KfhEleNcX7MWgiIHGb8G1jCqu/tH0GI8Z4cNgUrXMbczGwfbN/5Wc0zo
Dtpe0Tec/Fd0DLFwRiAuheakPjlVWb7AGMDX4TyzCXfMpS1ul2jk6nGFk77uQozF
PQUawCRp+mVS4qecgq/WqfTZZbBlW2L18/kpafvsxG8kJ7OREtrb0SloZNFHEc2Q
70GbgKECgYEA6c/eOrI3Uour1gKezEBFmFKFH6YS/NZNpcSG5PcoqF6AVJwXg574
Qy6RatC47e92be2TT1Oyplntj4vkZ3REv81yfz/tuXmtG0AylH7REbxubxAgYmUT
18wUAL4s3TST2AlK4R29KwBadwUAJeOLNW+Rc4xht1galsqQRb4pUzkCgYEA2kj2
vUhKAB7QFCPST45/5q+AATut8WeHnI+t1UaiZoK41Jre8TwlYqUgcJ16Q0H6KIbJ
jlEZAu0IsJxjQxkD4oJgv8n5PFXdc14HcSQ512FmgCGNwtDY/AT7SQP3kOj0Rydg
N02uuRb/55NJ07Bh+yTQNGA+M5SSnUyaRPIAMW0CgYBgVU7grDDzB60C/g1jZk/G
VKmYwposJjfTxsc1a0gLJvSE59MgXc04EOXFNr4a+oC3Bh2dn4SJ2Z9xd1fh8Bur
UwCLwVE3DBTwl2C/ogiN4C83/1L4d2DXlrPfInvloBYR+rIpUlFweDLNuve2pKvk
llU9YGeaXOiHnGoY8iKgsQKBgQDZKMOHtZYhHoZlsul0ylCGAEz5bRT0V8n7QJlw
12+TSjN1F4n6Npr+00Y9ov1SUh38GXQFiLq4RXZitYKu6wEJZCm6Q8YXd1jzgDUp
IyAEHNsrV7Y/fSSRPKd9kVvGp2r2Kr825aqQasg16zsERbKEdrBHmwPmrsVZhi7n
rlXw1QKBgQDBOyUJKQOgDE2u9EHybhCIbfowyIE22qn9a3WjQgfxFJ+aAL9Bg124
fJIEzz43fJ91fe5lTOgyMF5TtU5ClAOPGtlWnXU0e5j3L4LjbcqzEbeyxvP3sn1z
dYkX7NdNQ5E6tcJZuJCGq0HxIAQeKPf3x9DRKzMnLply6BEzyuAC4g==
-----END RSA PRIVATE KEY-----
`
	// Generated from
	// openssl rsa -in privkey.pem -pubout -out pubkey.pem
	// perl -pe 's/\n/\\n/g' pubkey.pem
	publicKey = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx12nDxxOwSPHRSJEDz67
a0folBqElzlu2oGMiUTS+dqtj3FUh5lJc1MjcprRVxcDVwhsSSo9948XEkk39Idb
lUCLohucqNMzOnIcdZn8zblN7CnpW03UwRM0iWX1HuwHnGvm6PKeqKGqplyIXYO0
qlDWCzC+VaxFTwOUk31MfOHJQn4yfTrfuE7h3FTElLBu065SFp3dPICIEmWCl9Da
dnxbnZ8ASxYQ9xG7hmZduDgjNW5l3x6/EFkpym+//D6AbWDcVJ1ovCsJL3CfH/NZ
C3ekeJ/aEeLxP/vaCSH1VYC5VsYK5Qg7SIa6Nth3+RZz1hYOoBJulEzwljznwoZY
RQIDAQAB
-----END PUBLIC KEY-----
`
	// Generated from
	// openssl dsaparam -out dsaparam.pem 2048
	// openssl gendsa  -out privkey.pem dsaparam.pem
	// perl -pe 's/\n/\\n/g' privkey.pem
	badPrivateKey = `
-----BEGIN DSA PRIVATE KEY-----
MIIDVgIBAAKCAQEApv0SsaKRWyn0IrbI6i547c/gldLQ3vB5xoSuTkVOvmD3HfuE
EVPKMS+XKlhgHOJy677zYNKUOIR78vfDVr1M89w19NSic81UwGGaOkrjQWOkoHaA
BS4046AzYKWqHWQNn9dm7WdQlbMBcBv9u+J6EqlzstPwWVaRdbAzyPtwQZRF5WfC
OcrQr8XpXbKsPh55FzfvFpu4KEKTY+8ynLz9uDNW2iAxj9NtRlUHQNqKQvjQsr/8
4pVrEBh+CnzNrmPXQIbyxV0y8WukAo3I3ZXK5nsUcJhFoVCRx4aBlp9W96mYZ7OE
dPCkFsoVhUNFo0jlJhMPODR1NXy77c4v1Kh6xwIhAJwFm6CQBOWJxZdGo2luqExE
acUG9Hkr2qd0yccgs2tFAoIBAQCQJCwASD7X9l7nZyZvJpXMe6YreGaP3VbbHCz8
GHs1P5exOausfJXa9gRLx2qDW0sa1ZyFUDnd2Dt810tgAhY143lufNoV3a4IRHpS
Fm8jjDRMyBQ/BrLBBXgpwiZ9LHBuUSeoRKY0BdyRsULmcq2OaBq9J38NUblWSe2R
NjQ45X6SGgUdHy3CrQtLjCA9l8+VPg3l05IBbXIhVSllP5AUmMG4T9x6M7NHEoSr
c7ewKSJNvc1C8+G66Kfz8xcChKcKC2z1YzvxrlcDHF+BBLw1Ppp+yMBfhQDWIZfe
6tpiKEEyWoyi4GkzQ+vooFIriaaL+Nnggh+iJ7BEUByHBaHnAoIBAFUxSB3bpbbp
Vna0HN6b+svuTCFhYi9AcmI1dcyEFKycUvZjP/X07HvX2yrL8aGxMJgF6RzPob/F
+SZar3u9Fd8DUYLxis6/B5d/ih7GnfPdChrDOJM1nwlferTGHXd1TBDzugpAovCe
JAjXiPsGmcCi9RNyoGib/FgniT7IKA7s3yJAzYSeW3wtLToSNGFJHn+TzFDBuWV4
KH70bpEV84JIzWo0ejKzgMBQ0Zrjcsm4lGBtzaBqGSvOrlIVFuSWFYUxrSTTxthQ
/JYz4ch8+HsQC/0HBuJ48yALDCVKsWq4Y21LRRJIOC25DfjwEYWWaKNGlDDsJA1m
Y5WF0OX+ABcCIEXhrzI1NddyFwLnfDCQ+sy6HT8/xLKXfaipd2rpn3gL
-----END DSA PRIVATE KEY-----
`
)

// Gave up trying to implement this myself
// nopCloser came from https://groups.google.com/d/msg/golang-nuts/J-Y4LtdGNSw/wDSYbHWIKj0J
// yay for sharing
// nopCloser creates a io.ReadCloser to satisfy the request.Body input
type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

func setup() {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)
	client, _ = NewClient(&Config{
		Name:    userid,
		Key:     privateKey,
		BaseURL: server.URL,
	})
}

func teardown() {
	server.Close()
}

func createServer() *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(checkHeader))
}

// publicKeyFromString parses an RSA public key from a string
func publicKeyFromString(key []byte) (*rsa.PublicKey, error) {
	block, _ := pem.Decode(key)
	if block == nil {
		return nil, fmt.Errorf("block size invalid for '%s'", string(key))
	}
	rsaKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return rsaKey.(*rsa.PublicKey), nil
}

func makeAuthConfig() (*AuthConfig, error) {
	pk, err := PrivateKeyFromString([]byte(privateKey))
	if err != nil {
		return nil, err
	}

	ac := &AuthConfig{
		PrivateKey: pk,
		ClientName: userid,
	}
	return ac, nil
}

func TestAuthConfig(t *testing.T) {
	_, err := makeAuthConfig()
	if err != nil {
		t.Error("Failed to create AuthConfig struct from privatekeys and stuff", err)
	}
}

func TestBase64BlockEncodeNoLimit(t *testing.T) {
	ac, _ := makeAuthConfig()
	var content string
	for _, key := range []string{"header1", "header2", "header3"} {
		content += fmt.Sprintf("%s:blahblahblah\n", key)
	}
	content = strings.TrimSuffix(content, "\n")

	signature, _ := GenerateSignature(ac.PrivateKey, content)
	Base64BlockEncode(signature, 0)
}

func TestSignRequestBadSignature(t *testing.T) {
	ac, err := makeAuthConfig()
	request, err := http.NewRequest("GET", requestURL, nil)
	ac.PrivateKey.PublicKey.N = big.NewInt(23234728432324)

	err = ac.SignRequest(request)
	if err == nil {
		t.Fatal("failed to generate failed signature")
	}
}

func TestSignRequestNoBody(t *testing.T) {
	setup()
	defer teardown()
	ac, err := makeAuthConfig()
	request, err := client.NewRequest("GET", requestURL, nil)

	err = ac.SignRequest(request)
	if err != nil {
		t.Fatal("failed to generate RequestHeaders")
	}
	count := 0
	for _, requiredHeader := range testRequiredHeaders {
		for header := range request.Header {
			if strings.ToLower(requiredHeader) == strings.ToLower(header) {
				count++
				break
			}
		}
	}
	if count != len(testRequiredHeaders) {
		t.Error("apiRequestHeaders didn't return all of testRequiredHeaders")
	}
}

func TestSignRequestBody(t *testing.T) {
	ac, err := makeAuthConfig()
	if err != nil {
		t.Fatal(err)
	}
	setup()
	defer teardown()

	// Gave up trying to implement this myself
	// nopCloser came from https://groups.google.com/d/msg/golang-nuts/J-Y4LtdGNSw/wDSYbHWIKj0J
	// yay for sharing
	requestBody := strings.NewReader("somecoolbodytext")
	request, err := client.NewRequest("GET", requestURL, requestBody)

	err = ac.SignRequest(request)
	if err != nil {
		t.Fatal("failed to generate RequestHeaders")
	}
	count := 0
	for _, requiredHeader := range testRequiredHeaders {
		for header := range request.Header {
			if strings.ToLower(requiredHeader) == strings.ToLower(header) {
				count++
				break
			}
		}
	}
	if count != len(testRequiredHeaders) {
		t.Error("apiRequestHeaders didn't return all of testRequiredHeaders")
	}
}

// <3 goiardi
// Test our headers as goiardi would
// https://github.com/ctdk/goiardi/blob/master/authentication/authentication.go
// func checkHeader(user_id string, r *http.Request) string {
func checkHeader(rw http.ResponseWriter, req *http.Request) {
	user_id := req.Header.Get("X-OPS-USERID")
	// Since we don't have a real client or user to check against,
	// we'll just verify that input user = output user
	// user, err := actor.GetReqUser(user_id)
	// if err != nil {
	if user_id != userid {
		fmt.Fprintf(rw, "Failed to authenticate as %s", user_id)
	}

	contentHash := req.Header.Get("X-OPS-CONTENT-HASH")
	if contentHash == "" {
		fmt.Fprintf(rw, "no content hash provided")
	}

	authTimestamp := req.Header.Get("x-ops-timestamp")
	if authTimestamp == "" {
		fmt.Fprintf(rw, "no timestamp header provided")
	}
	// TODO: Will want to implement this later
	//  else {
	//  // check the time stamp w/ allowed slew
	//  tok, terr := checkTimeStamp(authTimestamp, config.Config.TimeSlewDur)
	//  if !tok {
	//    return terr
	//  }
	// }

	// Eventually this may be put to some sort of use, but for now just
	// make sure that it's there. Presumably eventually it would be used to
	// use algorithms other than sha1 for hashing the body, or using a
	// different version of the header signing algorithm.
	xopssign := req.Header.Get("x-ops-sign")
	var apiVer string
	var hashChk []string
	if xopssign == "" {
		fmt.Fprintf(rw, "missing X-Ops-Sign header")
	} else {
		re := regexp.MustCompile(`version=(\d+\.\d+)`)
		shaRe := regexp.MustCompile(`algorithm=(\w+)`)
		if verChk := re.FindStringSubmatch(xopssign); verChk != nil {
			apiVer = verChk[1]
			if apiVer != "1.0" && apiVer != "1.1" {
				fmt.Fprintf(rw, "Bad version number '%s' in X-Ops-Header", apiVer)

			}
		} else {
			fmt.Fprintf(rw, "malformed version in X-Ops-Header")
		}

		// if algorithm is missing, it uses sha1. Of course, no other
		// hashing algorithm is supported yet...
		if hashChk = shaRe.FindStringSubmatch(xopssign); hashChk != nil {
			if hashChk[1] != "sha1" {
				fmt.Fprintf(rw, "Unsupported hashing algorithm '%s' specified in X-Ops-Header", hashChk[1])
			}
		}
	}

	signedHeaders, sherr := assembleSignedHeader(req)
	if sherr != nil {
		fmt.Fprintf(rw, sherr.Error())
	}

	_, err := HeaderDecrypt(publicKey, signedHeaders)
	if err != nil {
		fmt.Fprintf(rw, "unexpected header decryption error '%s'", err)
	}
}

func TestRequest(t *testing.T) {
	ac, err := makeAuthConfig()
	server := createServer()
	defer server.Close()
	setup()
	defer teardown()

	request, err := client.NewRequest("GET", server.URL, nil)

	err = ac.SignRequest(request)
	if err != nil {
		t.Fatal("failed to generate RequestHeaders")
	}

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Non 200 return code: " + response.Status)
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(response.Body)
	bodyStr := buf.String()

	if bodyStr != "" {
		t.Error(bodyStr)
	}

}

func TestRequestToEndpoint(t *testing.T) {
	ac, err := makeAuthConfig()
	server := createServer()
	defer server.Close()

	requestBody := strings.NewReader("somecoolbodytext")
	request, err := client.NewRequest("GET", server.URL+"/clients", requestBody)

	err = ac.SignRequest(request)
	if err != nil {
		t.Fatal("failed to generate RequestHeaders")
	}

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		t.Error(err)
	}

	if response.StatusCode != 200 {
		t.Error("Non 200 return code: " + response.Status)
	}

	buf := new(bytes.Buffer)
	buf.ReadFrom(response.Body)
	bodyStr := buf.String()

	if bodyStr != "" {
		t.Error(bodyStr)
	}
}

// More Goiardi <3
func assembleSignedHeader(r *http.Request) (string, error) {
	sHeadStore := make(map[int]string)
	authHeader := regexp.MustCompile(`(?i)^X-Ops-Authorization-(\d+)`)
	for k := range r.Header {
		if c := authHeader.FindStringSubmatch(k); c != nil {
			/* Have to put it into a map first, then sort, in case
			 * the headers don't come out in the right order */
			// skipping this error because we shouldn't even be
			// able to get here with something that won't be an
			// integer. Famous last words, I'm sure.
			i, _ := strconv.Atoi(c[1])
			sHeadStore[i] = r.Header.Get(k)
		}
	}
	if len(sHeadStore) == 0 {
		return "", errors.New("no authentication headers found")
	}

	sH := make([]string, len(sHeadStore))
	sHlimit := len(sH)
	for k, v := range sHeadStore {
		if k > sHlimit {
			return "", errors.New("malformed authentication headers")
		}
		sH[k-1] = v
	}
	signedHeaders := strings.Join(sH, "")

	return signedHeaders, nil
}

func assembleHeaderToCheck(r *http.Request) string {

	// To validate the signature it seems to be very particular
	// Would like to use this loop to generate the content
	// But it causes validation to fail.. so we do it explicitly

	// authHeader := regexp.MustCompile(`(?i)^X-Ops-Authorization-(\d+)`)
	// acceptEncoding := regexp.MustCompile(`(?i)^Accept-Encoding`)
	// userAgent := regexp.MustCompile(`(?i)^User-Agent`)
	//
	// var content string
	// for key, value := range r.Header {
	//  if !authHeader.MatchString(key) && !acceptEncoding.MatchString(key) && !userAgent.MatchString(key) {
	//    content += fmt.Sprintf("%s:%s\n", key, value)
	//  }
	// }
	// return content
	var content string
	content += fmt.Sprintf("%s:%s\n", "Method", r.Header.Get("Method"))
	content += fmt.Sprintf("%s:%s\n", "Hashed Path", r.Header.Get("Hashed Path"))
	content += fmt.Sprintf("%s:%s\n", "Accept", r.Header.Get("Accept"))
	content += fmt.Sprintf("%s:%s\n", "X-Chef-Version", r.Header.Get("X-Chef-Version"))
	content += fmt.Sprintf("%s:%s\n", "X-Ops-Timestamp", r.Header.Get("X-Ops-Timestamp"))
	content += fmt.Sprintf("%s:%s\n", "X-Ops-Userid", r.Header.Get("X-Ops-Userid"))
	content += fmt.Sprintf("%s:%s\n", "X-Ops-Sign", r.Header.Get("X-Ops-Sign"))
	content += fmt.Sprintf("%s:%s\n", "X-Ops-Content-Hash", r.Header.Get("X-Ops-Content-Hash"))
	return content
}

func TestGenerateHash(t *testing.T) {
	input, output := HashStr("hi"), "witfkXg0JglCjW9RssWvTAveakI="

	Convey("correctly hashes a given input string", t, func() {
		So(input, ShouldEqual, output)
	})
}

// BUG(fujin): @bradbeam: this doesn't make sense to me.
func TestGenerateSignatureError(t *testing.T) {
	ac, _ := makeAuthConfig()

	// BUG(fujin): what about the 'hi' string is not meant to be signable?
	sig, err := GenerateSignature(ac.PrivateKey, "hi")

	Convey("sig should be empty?", t, func() {
		So(sig, ShouldNotBeEmpty)
	})

	Convey("errors for an unknown reason to fujin", t, func() {
		So(err, ShouldBeNil)
	})
}

func TestRequestError(t *testing.T) {
	ac, err := makeAuthConfig()
	if err != nil {
		t.Fatal(err)
	}

	// Gave up trying to implement this myself
	// nopCloser came from https://groups.google.com/d/msg/golang-nuts/J-Y4LtdGNSw/wDSYbHWIKj0J
	// yay for sharing
	requestBody := nopCloser{bytes.NewBufferString("somecoolbodytext")}
	request, err := http.NewRequest("GET", requestURL, requestBody)

	err = ac.SignRequest(request)

	// BUG(fujin): This should actually error not bubble nil?
	Convey("should not sign a request with missing required information", t, func() {
		So(err, ShouldBeNil)
	})
}

func TestNewClient(t *testing.T) {
	cfg := &Config{Name: "testclient", Key: privateKey, SkipSSL: false}
	c, err := NewClient(cfg)
	if err != nil {
		t.Error("Couldn't make a valid client...\n", err)
	}
	// simple validation on the created client
	if c.Auth.ClientName != "testclient" {
		t.Error("unexpected client name: ", c.Auth.ClientName)
	}

	// Bad PEM should be an error
	cfg = &Config{Name: "blah", Key: "not a key", SkipSSL: false}
	c, err = NewClient(cfg)
	if err == nil {
		t.Error("Built a client from a bad key string")
	}

	// Not a proper key should be an error
	cfg = &Config{Name: "blah", Key: badPrivateKey, SkipSSL: false}
	c, err = NewClient(cfg)
	if err == nil {
		t.Error("Built a client from a bad key string")
	}
}

func TestNewRequest(t *testing.T) {
	var err error
	server := createServer()
	cfg := &Config{Name: "testclient", Key: privateKey, SkipSSL: false}
	c, _ := NewClient(cfg)
	defer server.Close()

	request, err := c.NewRequest("GET", server.URL, nil)
	if err != nil {
		t.Error("HRRRM! we tried to make a request but it failed :`( ", err)
	}

	resp, err := c.Do(request, nil)
	if resp.StatusCode != 200 {
		t.Error("Non 200 return code: ", resp.Status)
	}

	// This should fail because we've got an invalid URI
	_, err = c.NewRequest("GET", "%gh&%ij", nil)
	if err == nil {
		t.Error("This terrible request thing should fail and it didn't")
	}

	// This should fail because there is no TOODLES! method :D
	request, err = c.NewRequest("TOODLES!", "", nil)
	_, err = c.Do(request, nil)
	if err == nil {
		t.Error("This terrible request thing should fail and it didn't")
	}
}

func TestDo_badjson(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/hashrocket", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, " pigthrusters => 100% ")
	})

	stupidData := struct{}{}
	request, err := client.NewRequest("GET", "hashrocket", nil)
	_, err = client.Do(request, &stupidData)
	if err == nil {
		t.Error(err)
	}

}