diff --git a/apps/vs-code-designer/src/app/utils/codeless/__test__/getAuthorizationToken.test.ts b/apps/vs-code-designer/src/app/utils/codeless/__test__/getAuthorizationToken.test.ts new file mode 100644 index 00000000000..5cb4f0521d9 --- /dev/null +++ b/apps/vs-code-designer/src/app/utils/codeless/__test__/getAuthorizationToken.test.ts @@ -0,0 +1,143 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { getAuthorizationToken, getAuthorizationTokenFromNode, getCloudHost } from '../getAuthorizationToken'; + +// The module-level mock for '@microsoft/vscode-azext-azureauth/out/src/getSessionFromVSCode' +// is aliased via vitest.config.ts to '__mocks__/vscode-azext-azureauth.ts'. +// We import and spy on it to control return values per test. +import * as azureAuth from '@microsoft/vscode-azext-azureauth/out/src/getSessionFromVSCode'; +import * as vscode from 'vscode'; + +vi.mock('@microsoft/vscode-azext-azureauth', () => ({ + getConfiguredAzureEnv: vi.fn(() => ({ + managementEndpointUrl: 'https://management.azure.com', + })), +})); + +describe('getAuthorizationToken', () => { + beforeEach(() => { + vi.restoreAllMocks(); + // Mock vscode.workspace.getConfiguration to return a config with get() + vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ + get: vi.fn(() => false), + } as any); + }); + + it('should return a Bearer token when session has an accessToken', async () => { + vi.spyOn(azureAuth, 'getSessionFromVSCode').mockResolvedValue({ + accessToken: 'test-token-123', + id: 'session-1', + account: { id: 'account-1', label: 'Test' }, + scopes: [], + } as any); + + const token = await getAuthorizationToken('test-tenant'); + expect(token).toBe('Bearer test-token-123'); + }); + + it('should throw when session returns no accessToken', async () => { + vi.spyOn(azureAuth, 'getSessionFromVSCode').mockResolvedValue({ + id: 'session-1', + account: { id: 'account-1', label: 'Test' }, + scopes: [], + } as any); + + await expect(getAuthorizationToken()).rejects.toThrow('No access token available'); + }); + + it('should propagate errors when session acquisition fails', async () => { + vi.spyOn(azureAuth, 'getSessionFromVSCode').mockRejectedValue(new Error('Auth session expired')); + + await expect(getAuthorizationToken()).rejects.toThrow('Auth session expired'); + }); + + it('should pass tenantId to getSessionFromVSCode', async () => { + const spy = vi.spyOn(azureAuth, 'getSessionFromVSCode').mockResolvedValue({ + accessToken: 'tenant-token', + id: 'session-1', + account: { id: 'account-1', label: 'Test' }, + scopes: [], + } as any); + + await getAuthorizationToken('specific-tenant-id'); + expect(spy).toHaveBeenCalledWith(undefined, 'specific-tenant-id', expect.any(Object)); + }); +}); + +describe('getAuthorizationTokenFromNode', () => { + beforeEach(() => { + vi.restoreAllMocks(); + vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({ + get: vi.fn(() => false), + } as any); + }); + + it('should throw when node is null/undefined', async () => { + await expect(getAuthorizationTokenFromNode(null as any)).rejects.toThrow(); + }); + + it('should throw when node has no subscription', async () => { + const node = {} as any; + await expect(getAuthorizationTokenFromNode(node)).rejects.toThrow(); + }); + + it('should return Bearer token from node subscription credentials', async () => { + const node = { + subscription: { + tenantId: 'tenant-1', + credentials: { + getToken: vi.fn().mockResolvedValue({ token: 'node-token-abc' }), + }, + }, + } as any; + + const token = await getAuthorizationTokenFromNode(node); + expect(token).toBe('Bearer node-token-abc'); + }); + + it('should fall back to getAuthorizationToken when credentials.getToken returns null', async () => { + vi.spyOn(azureAuth, 'getSessionFromVSCode').mockResolvedValue({ + accessToken: 'fallback-token', + id: 'session-1', + account: { id: 'account-1', label: 'Test' }, + scopes: [], + } as any); + + const node = { + subscription: { + tenantId: 'tenant-1', + credentials: { + getToken: vi.fn().mockResolvedValue(null), + }, + }, + } as any; + + const token = await getAuthorizationTokenFromNode(node); + expect(token).toBe('Bearer fallback-token'); + }); + + it('should fall back to getAuthorizationToken when no credentials exist', async () => { + vi.spyOn(azureAuth, 'getSessionFromVSCode').mockResolvedValue({ + accessToken: 'fallback-token-2', + id: 'session-1', + account: { id: 'account-1', label: 'Test' }, + scopes: [], + } as any); + + const node = { + subscription: { + tenantId: 'tenant-2', + credentials: undefined, + }, + } as any; + + const token = await getAuthorizationTokenFromNode(node); + expect(token).toBe('Bearer fallback-token-2'); + }); +}); + +describe('getCloudHost', () => { + it('should return the managementEndpointUrl from configured environment', async () => { + const host = await getCloudHost(); + expect(host).toBe('https://management.azure.com'); + }); +}); diff --git a/apps/vs-code-designer/src/app/utils/codeless/getAuthorizationToken.ts b/apps/vs-code-designer/src/app/utils/codeless/getAuthorizationToken.ts index f581c4ad3a5..bebac41bee1 100644 --- a/apps/vs-code-designer/src/app/utils/codeless/getAuthorizationToken.ts +++ b/apps/vs-code-designer/src/app/utils/codeless/getAuthorizationToken.ts @@ -18,7 +18,10 @@ export async function getAuthData(tenantId?: string): Promise { const authData = await getAuthData(tenantId); - return `Bearer ${authData?.accessToken}`; + if (!authData?.accessToken) { + throw new Error(localize('noAccessToken', 'No access token available. Please sign in to Azure.')); + } + return `Bearer ${authData.accessToken}`; } /**