fix(tracker-web): replace broken webpack alias with transpilePackages — build now succeeds
fix(config): mock Azure SDK in keyvault tests — eliminates timeouts, 26/26 pass in <1s
This commit is contained in:
parent
062d87a93a
commit
7210464019
@ -21,6 +21,13 @@ const securityHeaders = [
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
...(process.env.VERCEL ? {} : { output: 'standalone' }),
|
||||
transpilePackages: [
|
||||
'@bytelyst/api-client',
|
||||
'@bytelyst/errors',
|
||||
'@bytelyst/config',
|
||||
'@bytelyst/react-auth',
|
||||
'@bytelyst/telemetry-client',
|
||||
],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
@ -29,23 +36,6 @@ const nextConfig: NextConfig = {
|
||||
},
|
||||
];
|
||||
},
|
||||
webpack: config => {
|
||||
// Handle file: references for @bytelyst packages
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('path');
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
'@bytelyst/api-client': path.resolve(
|
||||
__dirname,
|
||||
'../../learning_ai_common_plat/packages/api-client/dist/index.js'
|
||||
),
|
||||
'@bytelyst/errors': path.resolve(
|
||||
__dirname,
|
||||
'../../learning_ai_common_plat/packages/errors/dist/index.js'
|
||||
),
|
||||
};
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@ -6,6 +6,22 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { resolveKeyVaultSecrets, LYSNR_SECRETS } from '../keyvault.js';
|
||||
import type { SecretMapping } from '../keyvault.js';
|
||||
|
||||
// Mock Azure SDK dynamic imports to prevent test timeouts
|
||||
const { mockGetSecret } = vi.hoisted(() => {
|
||||
const mockGetSecret = vi.fn();
|
||||
return { mockGetSecret };
|
||||
});
|
||||
|
||||
vi.mock('@azure/identity', () => ({
|
||||
DefaultAzureCredential: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@azure/keyvault-secrets', () => ({
|
||||
SecretClient: vi.fn().mockImplementation(() => ({
|
||||
getSecret: mockGetSecret,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('resolveKeyVaultSecrets', () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
@ -14,17 +30,16 @@ describe('resolveKeyVaultSecrets', () => {
|
||||
delete process.env.AZURE_KEYVAULT_URL;
|
||||
delete process.env.TEST_SECRET_A;
|
||||
delete process.env.TEST_SECRET_B;
|
||||
mockGetSecret.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
vi.restoreAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('skips entirely when AZURE_KEYVAULT_URL is not set', async () => {
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' },
|
||||
];
|
||||
const secrets: SecretMapping[] = [{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' }];
|
||||
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
|
||||
@ -36,9 +51,7 @@ describe('resolveKeyVaultSecrets', () => {
|
||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||
process.env.TEST_SECRET_A = 'already-set';
|
||||
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'test-secret-a', envVar: 'TEST_SECRET_A' },
|
||||
];
|
||||
const secrets: SecretMapping[] = [{ kvName: 'test-secret-a', envVar: 'TEST_SECRET_A' }];
|
||||
|
||||
// Should not attempt KV call since all secrets are present
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
@ -47,16 +60,14 @@ describe('resolveKeyVaultSecrets', () => {
|
||||
});
|
||||
|
||||
it('accepts custom vaultUrl via opts', async () => {
|
||||
// With no AZURE_KEYVAULT_URL but custom vaultUrl, it should attempt resolution
|
||||
// This will fail with import error in test env (no @azure/identity), which is expected
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' },
|
||||
];
|
||||
mockGetSecret.mockResolvedValue({ value: 'resolved-value' });
|
||||
|
||||
// Should not throw — gracefully handles missing @azure/identity
|
||||
await expect(
|
||||
resolveKeyVaultSecrets(secrets, { vaultUrl: 'https://kv-test.vault.azure.net' })
|
||||
).resolves.not.toThrow();
|
||||
const secrets: SecretMapping[] = [{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' }];
|
||||
|
||||
await resolveKeyVaultSecrets(secrets, { vaultUrl: 'https://kv-test.vault.azure.net' });
|
||||
|
||||
expect(process.env.TEST_SECRET_A).toBe('resolved-value');
|
||||
expect(mockGetSecret).toHaveBeenCalledWith('test-secret');
|
||||
});
|
||||
|
||||
it('handles empty secrets array', async () => {
|
||||
@ -65,45 +76,59 @@ describe('resolveKeyVaultSecrets', () => {
|
||||
await expect(resolveKeyVaultSecrets([])).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('gracefully handles import failures (no @azure/identity installed)', async () => {
|
||||
it('resolves multiple missing secrets from Key Vault', async () => {
|
||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' },
|
||||
];
|
||||
|
||||
// In test env, @azure/identity likely isn't available
|
||||
// resolveKeyVaultSecrets should catch and warn, not throw
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
|
||||
// Either warn was called (no Azure SDK) or the env var remains unset
|
||||
// Both are acceptable — the function should not throw
|
||||
expect(process.env.TEST_SECRET_A).toBeUndefined();
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('filters to only missing secrets', async () => {
|
||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||
process.env.TEST_SECRET_A = 'present';
|
||||
// TEST_SECRET_B is missing
|
||||
mockGetSecret
|
||||
.mockResolvedValueOnce({ value: 'secret-a-val' })
|
||||
.mockResolvedValueOnce({ value: 'secret-b-val' });
|
||||
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'secret-a', envVar: 'TEST_SECRET_A' },
|
||||
{ kvName: 'secret-b', envVar: 'TEST_SECRET_B' },
|
||||
];
|
||||
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
|
||||
expect(process.env.TEST_SECRET_A).toBe('secret-a-val');
|
||||
expect(process.env.TEST_SECRET_B).toBe('secret-b-val');
|
||||
});
|
||||
|
||||
it('warns but does not throw when getSecret fails', async () => {
|
||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||
mockGetSecret.mockRejectedValue(new Error('SecretNotFound'));
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
const secrets: SecretMapping[] = [{ kvName: 'bad-secret', envVar: 'TEST_SECRET_A' }];
|
||||
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
|
||||
// TEST_SECRET_A should remain unchanged
|
||||
expect(process.env.TEST_SECRET_A).toBe('present');
|
||||
expect(process.env.TEST_SECRET_A).toBeUndefined();
|
||||
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('1/1 secrets failed'));
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('filters to only missing secrets — skips already-present', async () => {
|
||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||
process.env.TEST_SECRET_A = 'present';
|
||||
mockGetSecret.mockResolvedValue({ value: 'from-kv' });
|
||||
|
||||
const secrets: SecretMapping[] = [
|
||||
{ kvName: 'secret-a', envVar: 'TEST_SECRET_A' },
|
||||
{ kvName: 'secret-b', envVar: 'TEST_SECRET_B' },
|
||||
];
|
||||
|
||||
await resolveKeyVaultSecrets(secrets);
|
||||
|
||||
// TEST_SECRET_A should remain unchanged (already present)
|
||||
expect(process.env.TEST_SECRET_A).toBe('present');
|
||||
// TEST_SECRET_B should be resolved from KV
|
||||
expect(process.env.TEST_SECRET_B).toBe('from-kv');
|
||||
// getSecret should only be called for the missing secret
|
||||
expect(mockGetSecret).toHaveBeenCalledTimes(1);
|
||||
expect(mockGetSecret).toHaveBeenCalledWith('secret-b');
|
||||
});
|
||||
});
|
||||
|
||||
describe('LYSNR_SECRETS', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user