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:
saravanakumardb1 2026-02-28 12:11:37 -08:00
parent 062d87a93a
commit 7210464019
2 changed files with 73 additions and 58 deletions

View File

@ -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;

View File

@ -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', () => {