|
1 | 1 | import { StreamableHTTPClientTransport, StreamableHTTPReconnectionOptions } from "./streamableHttp.js";
|
2 | 2 | import { OAuthClientProvider, UnauthorizedError } from "./auth.js";
|
3 |
| -import { JSONRPCMessage } from "../types.js"; |
| 3 | +import { JSONRPCMessage, LATEST_PROTOCOL_VERSION } from "../types.js"; |
| 4 | +import { OAuthTokens } from "src/shared/auth.js"; |
4 | 5 |
|
5 | 6 |
|
6 | 7 | describe("StreamableHTTPClientTransport", () => {
|
@@ -517,19 +518,106 @@ describe("StreamableHTTPClientTransport", () => {
|
517 | 518 | id: "test-id"
|
518 | 519 | };
|
519 | 520 |
|
| 521 | + const clientInfo = { |
| 522 | + "issuer": "http://localhost:1234", |
| 523 | + "authorization_endpoint": "http://localhost:1234/authorize", |
| 524 | + "token_endpoint": "http://localhost:1234/token", |
| 525 | + "revocation_endpoint": "http://localhost:1234/revoke", |
| 526 | + "scopes_supported": [ |
| 527 | + 'wow', |
| 528 | + ], |
| 529 | + "grant_types_supported": [ |
| 530 | + "authorization_code", |
| 531 | + "refresh_token" |
| 532 | + ], |
| 533 | + "token_endpoint_auth_methods_supported": [ |
| 534 | + "client_secret_basic", |
| 535 | + "client_secret_post" |
| 536 | + ], |
| 537 | + "code_challenge_methods_supported": [ |
| 538 | + "S256" |
| 539 | + ], |
| 540 | + "registration_endpoint": "http://localhost:1234/register", |
| 541 | + "response_types_supported": [ |
| 542 | + "code" |
| 543 | + ], |
| 544 | + "response_modes_supported": [ |
| 545 | + "query", |
| 546 | + "fragment" |
| 547 | + ] |
| 548 | + }; |
| 549 | + |
520 | 550 | (global.fetch as jest.Mock)
|
521 |
| - .mockResolvedValueOnce({ |
522 |
| - ok: false, |
| 551 | + .mockResolvedValueOnce(new Response("{}", { |
523 | 552 | status: 401,
|
524 |
| - statusText: "Unauthorized", |
525 | 553 | headers: new Headers()
|
| 554 | + })) |
| 555 | + .mockImplementationOnce(async (url: URL | string, init?: RequestInit) => { |
| 556 | + return new Response(JSON.stringify(clientInfo), { |
| 557 | + status: 200, |
| 558 | + headers: new Headers({ 'content-type': 'application/json' }) |
| 559 | + }) |
526 | 560 | })
|
527 |
| - .mockResolvedValue({ |
528 |
| - ok: false, |
529 |
| - status: 404 |
530 |
| - }); |
| 561 | + .mockImplementationOnce(async (url: URL | string, init?: RequestInit) => { |
| 562 | + return new Response(JSON.stringify(clientInfo), { |
| 563 | + status: 200, |
| 564 | + headers: new Headers({ 'content-type': 'application/json' }) |
| 565 | + }) |
| 566 | + }) |
| 567 | + .mockImplementationOnce(async (url: URL | string, init?: RequestInit) => { |
| 568 | + expect(init).toBeDefined() |
| 569 | + expect(init!.body).toBeDefined() |
| 570 | + expect(new URLSearchParams(init!.body! as string).get('code')).toBe('any code') |
| 571 | + return new Response(JSON.stringify({ |
| 572 | + "access_token": "anything", |
| 573 | + "token_type": "Bearer", |
| 574 | + "expires_at": new Date(Date.now() + 5000), |
| 575 | + "scope": "anything", |
| 576 | + "refresh_token": "something else" |
| 577 | + }), { |
| 578 | + status: 200, |
| 579 | + headers: new Headers({ 'content-type': 'application/json' }) |
| 580 | + }) |
| 581 | + }) |
| 582 | + .mockImplementationOnce(async (url: URL | string, init?: RequestInit) => { |
| 583 | + expect(init).toBeDefined() |
| 584 | + expect(init!.body).toBeDefined() |
| 585 | + expect(init!.headers).toBeDefined() |
| 586 | + expect(new Headers(init!.headers).get('authorization')).toBe(`Bearer anything`) |
| 587 | + const body = JSON.parse(init!.body! as string) |
| 588 | + return new Response(JSON.stringify({ |
| 589 | + jsonrpc: '2.0', |
| 590 | + id: body.id, |
| 591 | + result: { |
| 592 | + protocolVersion: LATEST_PROTOCOL_VERSION, |
| 593 | + capabilities: {}, |
| 594 | + serverInfo: { |
| 595 | + name: "test", |
| 596 | + version: "1.0", |
| 597 | + }, |
| 598 | + }, |
| 599 | + }), { |
| 600 | + status: 200, |
| 601 | + headers: new Headers({ 'content-type': 'application/json' }) |
| 602 | + }) |
| 603 | + }) |
| 604 | + |
| 605 | + |
| 606 | + let tokens: OAuthTokens |
| 607 | + mockAuthProvider.tokens = jest.fn(() => { |
| 608 | + return tokens! |
| 609 | + }) |
| 610 | + mockAuthProvider.saveTokens = jest.fn((t: OAuthTokens) => { |
| 611 | + tokens = t |
| 612 | + }) |
| 613 | + |
| 614 | + mockAuthProvider.redirectToAuthorization = jest.fn(async (redirectUrl: URL) => { |
| 615 | + await transport.finishAuth('any code') |
| 616 | + }) |
531 | 617 |
|
532 |
| - await expect(transport.send(message)).rejects.toThrow(UnauthorizedError); |
533 |
| - expect(mockAuthProvider.redirectToAuthorization.mock.calls).toHaveLength(1); |
| 618 | + await transport.send(message) |
| 619 | + expect(mockAuthProvider.redirectToAuthorization.mock.calls.length).toBe(1) |
| 620 | + expect(mockAuthProvider.saveTokens.mock.calls.length).toBe(1) |
| 621 | + expect(mockAuthProvider.tokens.mock.calls.length).toBe(3) |
534 | 622 | });
|
535 | 623 | });
|
0 commit comments