|
11 | 11 | package ims |
12 | 12 |
|
13 | 13 | import ( |
| 14 | + "encoding/base64" |
14 | 15 | "math/rand" |
| 16 | + "net/http" |
15 | 17 | "testing" |
16 | 18 | "time" |
17 | 19 | ) |
@@ -396,6 +398,162 @@ func searchString(s, substr string) bool { |
396 | 398 | return false |
397 | 399 | } |
398 | 400 |
|
| 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 | + |
399 | 557 | // randomString generates a string of the given length with arbitrary bytes. |
400 | 558 | func randomString(rng *rand.Rand, length int) string { |
401 | 559 | b := make([]byte, length) |
|
0 commit comments