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 = {
|
const nextConfig: NextConfig = {
|
||||||
...(process.env.VERCEL ? {} : { output: 'standalone' }),
|
...(process.env.VERCEL ? {} : { output: 'standalone' }),
|
||||||
|
transpilePackages: [
|
||||||
|
'@bytelyst/api-client',
|
||||||
|
'@bytelyst/errors',
|
||||||
|
'@bytelyst/config',
|
||||||
|
'@bytelyst/react-auth',
|
||||||
|
'@bytelyst/telemetry-client',
|
||||||
|
],
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
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;
|
export default nextConfig;
|
||||||
|
|||||||
@ -6,6 +6,22 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|||||||
import { resolveKeyVaultSecrets, LYSNR_SECRETS } from '../keyvault.js';
|
import { resolveKeyVaultSecrets, LYSNR_SECRETS } from '../keyvault.js';
|
||||||
import type { SecretMapping } 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', () => {
|
describe('resolveKeyVaultSecrets', () => {
|
||||||
const originalEnv = { ...process.env };
|
const originalEnv = { ...process.env };
|
||||||
|
|
||||||
@ -14,17 +30,16 @@ describe('resolveKeyVaultSecrets', () => {
|
|||||||
delete process.env.AZURE_KEYVAULT_URL;
|
delete process.env.AZURE_KEYVAULT_URL;
|
||||||
delete process.env.TEST_SECRET_A;
|
delete process.env.TEST_SECRET_A;
|
||||||
delete process.env.TEST_SECRET_B;
|
delete process.env.TEST_SECRET_B;
|
||||||
|
mockGetSecret.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
process.env = { ...originalEnv };
|
process.env = { ...originalEnv };
|
||||||
vi.restoreAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('skips entirely when AZURE_KEYVAULT_URL is not set', async () => {
|
it('skips entirely when AZURE_KEYVAULT_URL is not set', async () => {
|
||||||
const secrets: SecretMapping[] = [
|
const secrets: SecretMapping[] = [{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' }];
|
||||||
{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' },
|
|
||||||
];
|
|
||||||
|
|
||||||
await resolveKeyVaultSecrets(secrets);
|
await resolveKeyVaultSecrets(secrets);
|
||||||
|
|
||||||
@ -36,9 +51,7 @@ describe('resolveKeyVaultSecrets', () => {
|
|||||||
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||||
process.env.TEST_SECRET_A = 'already-set';
|
process.env.TEST_SECRET_A = 'already-set';
|
||||||
|
|
||||||
const secrets: SecretMapping[] = [
|
const secrets: SecretMapping[] = [{ kvName: 'test-secret-a', envVar: 'TEST_SECRET_A' }];
|
||||||
{ kvName: 'test-secret-a', envVar: 'TEST_SECRET_A' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Should not attempt KV call since all secrets are present
|
// Should not attempt KV call since all secrets are present
|
||||||
await resolveKeyVaultSecrets(secrets);
|
await resolveKeyVaultSecrets(secrets);
|
||||||
@ -47,16 +60,14 @@ describe('resolveKeyVaultSecrets', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('accepts custom vaultUrl via opts', async () => {
|
it('accepts custom vaultUrl via opts', async () => {
|
||||||
// With no AZURE_KEYVAULT_URL but custom vaultUrl, it should attempt resolution
|
mockGetSecret.mockResolvedValue({ value: 'resolved-value' });
|
||||||
// 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' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Should not throw — gracefully handles missing @azure/identity
|
const secrets: SecretMapping[] = [{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' }];
|
||||||
await expect(
|
|
||||||
resolveKeyVaultSecrets(secrets, { vaultUrl: 'https://kv-test.vault.azure.net' })
|
await resolveKeyVaultSecrets(secrets, { vaultUrl: 'https://kv-test.vault.azure.net' });
|
||||||
).resolves.not.toThrow();
|
|
||||||
|
expect(process.env.TEST_SECRET_A).toBe('resolved-value');
|
||||||
|
expect(mockGetSecret).toHaveBeenCalledWith('test-secret');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles empty secrets array', async () => {
|
it('handles empty secrets array', async () => {
|
||||||
@ -65,45 +76,59 @@ describe('resolveKeyVaultSecrets', () => {
|
|||||||
await expect(resolveKeyVaultSecrets([])).resolves.not.toThrow();
|
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';
|
process.env.AZURE_KEYVAULT_URL = 'https://kv-test.vault.azure.net';
|
||||||
|
mockGetSecret
|
||||||
const secrets: SecretMapping[] = [
|
.mockResolvedValueOnce({ value: 'secret-a-val' })
|
||||||
{ kvName: 'test-secret', envVar: 'TEST_SECRET_A' },
|
.mockResolvedValueOnce({ value: 'secret-b-val' });
|
||||||
];
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
const secrets: SecretMapping[] = [
|
const secrets: SecretMapping[] = [
|
||||||
{ kvName: 'secret-a', envVar: 'TEST_SECRET_A' },
|
{ kvName: 'secret-a', envVar: 'TEST_SECRET_A' },
|
||||||
{ kvName: 'secret-b', envVar: 'TEST_SECRET_B' },
|
{ 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 warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||||
|
|
||||||
|
const secrets: SecretMapping[] = [{ kvName: 'bad-secret', envVar: 'TEST_SECRET_A' }];
|
||||||
|
|
||||||
await resolveKeyVaultSecrets(secrets);
|
await resolveKeyVaultSecrets(secrets);
|
||||||
|
|
||||||
// TEST_SECRET_A should remain unchanged
|
expect(process.env.TEST_SECRET_A).toBeUndefined();
|
||||||
expect(process.env.TEST_SECRET_A).toBe('present');
|
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('1/1 secrets failed'));
|
||||||
|
|
||||||
warnSpy.mockRestore();
|
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', () => {
|
describe('LYSNR_SECRETS', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user