Skip to content

Commit 597f5de

Browse files
committed
Add unit tests for DecodeToken, httpClient, and profile decoding
1 parent 7013cdf commit 597f5de

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

ims/config_test.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
package ims
1212

1313
import (
14+
"encoding/base64"
1415
"math/rand"
16+
"net/http"
1517
"testing"
1618
"time"
1719
)
@@ -396,6 +398,162 @@ func searchString(s, substr string) bool {
396398
return false
397399
}
398400

401+
func TestDecodeToken(t *testing.T) {
402+
// Build a valid JWT with known header and payload.
403+
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"HS256"}`))
404+
payload := base64.RawURLEncoding.EncodeToString([]byte(`{"sub":"1234567890"}`))
405+
validJWT := header + "." + payload + ".signature"
406+
407+
tests := []struct {
408+
name string
409+
token string
410+
wantHeader string
411+
wantPayload string
412+
wantErr string
413+
}{
414+
{name: "valid JWT", token: validJWT, wantHeader: `{"alg":"HS256"}`, wantPayload: `{"sub":"1234567890"}`},
415+
{name: "empty token", token: "", wantErr: "missing token parameter"},
416+
{name: "no dots", token: "nodots", wantErr: "not composed by 3 parts"},
417+
{name: "one dot", token: "a.b", wantErr: "not composed by 3 parts"},
418+
{name: "four parts", token: "a.b.c.d", wantErr: "not composed by 3 parts"},
419+
{name: "invalid base64 header", token: "!!!." + payload + ".sig", wantErr: "error decoding token header"},
420+
{name: "invalid base64 payload", token: header + ".!!!.sig", wantErr: "error decoding token payload"},
421+
}
422+
for _, tt := range tests {
423+
t.Run(tt.name, func(t *testing.T) {
424+
c := Config{Token: tt.token}
425+
result, err := c.DecodeToken()
426+
assertError(t, err, tt.wantErr)
427+
if err == nil {
428+
if result.Header != tt.wantHeader {
429+
t.Errorf("Header = %q, want %q", result.Header, tt.wantHeader)
430+
}
431+
if result.Payload != tt.wantPayload {
432+
t.Errorf("Payload = %q, want %q", result.Payload, tt.wantPayload)
433+
}
434+
}
435+
})
436+
}
437+
}
438+
439+
func TestHttpClient(t *testing.T) {
440+
tests := []struct {
441+
name string
442+
config Config
443+
wantErr string
444+
wantProxy bool
445+
wantInsecure bool
446+
}{
447+
{
448+
name: "default client",
449+
config: Config{Timeout: 30},
450+
},
451+
{
452+
name: "with proxy",
453+
config: Config{Timeout: 30, ProxyURL: "http://proxy.example.com:8080"},
454+
wantProxy: true,
455+
},
456+
{
457+
name: "with proxy and ignore TLS",
458+
config: Config{Timeout: 30, ProxyURL: "http://proxy.example.com:8080", ProxyIgnoreTLS: true},
459+
wantProxy: true,
460+
wantInsecure: true,
461+
},
462+
{
463+
name: "malformed proxy URL",
464+
config: Config{ProxyURL: "://bad"},
465+
wantErr: "malformed",
466+
},
467+
{
468+
name: "timeout is respected",
469+
config: Config{Timeout: 60},
470+
},
471+
}
472+
for _, tt := range tests {
473+
t.Run(tt.name, func(t *testing.T) {
474+
client, err := tt.config.httpClient()
475+
assertError(t, err, tt.wantErr)
476+
if err != nil {
477+
return
478+
}
479+
expectedTimeout := time.Duration(tt.config.Timeout) * time.Second
480+
if client.Timeout != expectedTimeout {
481+
t.Errorf("Timeout = %v, want %v", client.Timeout, expectedTimeout)
482+
}
483+
if tt.wantProxy {
484+
transport, ok := client.Transport.(*http.Transport)
485+
if !ok || transport.Proxy == nil {
486+
t.Error("expected proxy to be configured")
487+
}
488+
if tt.wantInsecure {
489+
if transport.TLSClientConfig == nil || !transport.TLSClientConfig.InsecureSkipVerify {
490+
t.Error("expected InsecureSkipVerify to be true")
491+
}
492+
}
493+
}
494+
})
495+
}
496+
}
497+
498+
func TestNewIMSClient(t *testing.T) {
499+
tests := []struct {
500+
name string
501+
config Config
502+
wantErr string
503+
}{
504+
{name: "valid config", config: Config{URL: "https://ims.example.com", Timeout: 30}},
505+
{name: "malformed proxy", config: Config{URL: "https://ims.example.com", ProxyURL: "://bad"}, wantErr: "malformed"},
506+
}
507+
for _, tt := range tests {
508+
t.Run(tt.name, func(t *testing.T) {
509+
_, err := tt.config.newIMSClient()
510+
assertError(t, err, tt.wantErr)
511+
})
512+
}
513+
}
514+
515+
func TestDecodeProfile(t *testing.T) {
516+
tests := []struct {
517+
name string
518+
input string
519+
wantErr string
520+
}{
521+
{name: "simple profile", input: `{"name":"John","email":"john@example.com"}`},
522+
{name: "profile with unrelated fulfillable_data", input: `{"serviceCode":"other","fulfillable_data":"test"}`},
523+
{name: "invalid JSON", input: `not json`, wantErr: "error parsing profile JSON"},
524+
}
525+
for _, tt := range tests {
526+
t.Run(tt.name, func(t *testing.T) {
527+
_, err := decodeProfile([]byte(tt.input))
528+
assertError(t, err, tt.wantErr)
529+
})
530+
}
531+
}
532+
533+
func TestFindFulfillableData(t *testing.T) {
534+
// Verify that findFulfillableData doesn't panic on various data structures.
535+
tests := []struct {
536+
name string
537+
input interface{}
538+
}{
539+
{name: "nil", input: nil},
540+
{name: "string", input: "hello"},
541+
{name: "number", input: 42.0},
542+
{name: "empty map", input: map[string]interface{}{}},
543+
{name: "empty slice", input: []interface{}{}},
544+
{name: "nested map", input: map[string]interface{}{"a": map[string]interface{}{"b": 1}}},
545+
{name: "fulfillable_data with wrong serviceCode", input: map[string]interface{}{"serviceCode": "unknown", "fulfillable_data": "test"}},
546+
{name: "fulfillable_data non-string value", input: map[string]interface{}{"serviceCode": "dma_media_library", "fulfillable_data": 123}},
547+
{name: "nested array", input: []interface{}{map[string]interface{}{"key": "value"}}},
548+
}
549+
for _, tt := range tests {
550+
t.Run(tt.name, func(t *testing.T) {
551+
// Should not panic.
552+
findFulfillableData(tt.input)
553+
})
554+
}
555+
}
556+
399557
// randomString generates a string of the given length with arbitrary bytes.
400558
func randomString(rng *rand.Rand, length int) string {
401559
b := make([]byte, length)

0 commit comments

Comments
 (0)