|
2 | 2 | * Unit tests for keyvault-checker service (ARM-based approach) |
3 | 3 | */ |
4 | 4 |
|
5 | | -import { describe, it, expect, vi, beforeEach } from 'vitest'; |
| 5 | +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
6 | 6 | import { |
7 | 7 | checkKeyVaultSecretAccess, |
8 | 8 | type TokenProviderFactory, |
9 | 9 | type KeyVaultCheckContext, |
10 | 10 | } from '../../../src/services/keyvault-checker.js'; |
| 11 | +import { USER_AGENT } from '../../../src/lib/user-agent.js'; |
11 | 12 |
|
12 | 13 | describe('checkKeyVaultSecretAccess', () => { |
13 | 14 | let mockGetToken: ReturnType<typeof vi.fn>; |
@@ -573,3 +574,62 @@ describe('checkKeyVaultSecretAccess', () => { |
573 | 574 | ).resolves.toBeUndefined(); |
574 | 575 | }); |
575 | 576 | }); |
| 577 | + |
| 578 | +describe('defaultArmRequest User-Agent header', () => { |
| 579 | + let fetchSpy: ReturnType<typeof vi.fn>; |
| 580 | + |
| 581 | + beforeEach(() => { |
| 582 | + fetchSpy = vi.fn(); |
| 583 | + vi.stubGlobal('fetch', fetchSpy); |
| 584 | + }); |
| 585 | + |
| 586 | + afterEach(() => { |
| 587 | + vi.restoreAllMocks(); |
| 588 | + vi.unstubAllGlobals(); |
| 589 | + }); |
| 590 | + |
| 591 | + it('should include User-Agent header when defaultArmRequest is used', async () => { |
| 592 | + // Arrange: mock all ARM responses so checkKeyVaultSecretAccess runs to completion |
| 593 | + const apimIdentity = { |
| 594 | + identity: { type: 'SystemAssigned', principalId: 'pid-1' }, |
| 595 | + }; |
| 596 | + const vaultList = { value: [{ id: '/subscriptions/sub-1/resourceGroups/rg-1/providers/Microsoft.KeyVault/vaults/myvault' }] }; |
| 597 | + const vaultDetail = { |
| 598 | + properties: { |
| 599 | + enableRbacAuthorization: false, |
| 600 | + accessPolicies: [ |
| 601 | + { |
| 602 | + objectId: 'pid-1', |
| 603 | + permissions: { secrets: ['get'] }, |
| 604 | + }, |
| 605 | + ], |
| 606 | + }, |
| 607 | + }; |
| 608 | + |
| 609 | + fetchSpy |
| 610 | + .mockResolvedValueOnce(new Response(JSON.stringify(apimIdentity), { status: 200, headers: { 'Content-Type': 'application/json' } })) |
| 611 | + .mockResolvedValueOnce(new Response(JSON.stringify(vaultList), { status: 200, headers: { 'Content-Type': 'application/json' } })) |
| 612 | + .mockResolvedValueOnce(new Response(JSON.stringify(vaultDetail), { status: 200, headers: { 'Content-Type': 'application/json' } })); |
| 613 | + |
| 614 | + const mockTokenProviderFactory: TokenProviderFactory = () => ({ |
| 615 | + getToken: vi.fn().mockResolvedValue({ token: 'fake-arm-token' }), |
| 616 | + }); |
| 617 | + |
| 618 | + await checkKeyVaultSecretAccess( |
| 619 | + 'https://myvault.vault.azure.net/secrets/my-secret', |
| 620 | + undefined, |
| 621 | + { subscriptionId: 'sub-1', resourceGroup: 'rg-1', serviceName: 'apim-1' }, |
| 622 | + mockTokenProviderFactory, |
| 623 | + // No armRequest override — uses defaultArmRequest which must include User-Agent |
| 624 | + ); |
| 625 | + |
| 626 | + // All three fetch calls should include the User-Agent header |
| 627 | + expect(fetchSpy.mock.calls.length).toBeGreaterThan(0); |
| 628 | + for (const call of fetchSpy.mock.calls) { |
| 629 | + const [_url, init] = call as [string, RequestInit]; |
| 630 | + const headers = new Headers(init?.headers); |
| 631 | + expect(headers.get('User-Agent')).toBe(USER_AGENT); |
| 632 | + expect(headers.get('User-Agent')).toMatch(/^apiops-cli\/\d+\.\d+\.\d+/); |
| 633 | + } |
| 634 | + }); |
| 635 | +}); |
0 commit comments