import { describe, it, expect, vi, beforeEach } from 'vitest';
import { analyzeDependencies } from '../src/analyzer';
import axios from 'axios';
import { Dependency } from '../src/types';
vi.mock('axios');
const mockedAxios = axios as any;
describe('Integration Logic (Analyzer)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should handle mixed results: OSV vuln, Socket risk, and API errors', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const dependencies: Dependency[] = [
{ name: 'vuln-pkg', version: '1.0.0' },
{ name: 'risk-pkg', version: '2.0.0' },
{ name: 'safe-pkg', version: '3.0.0' },
{ name: 'error-pkg', version: '4.0.0' }, // Simulates API error
{ name: 'unknown-pkg', version: '5.0.0' } // Simulates 404
];
// Mock OSV Batch Response
mockedAxios.post.mockResolvedValueOnce({
data: {
results: [
{ vulns: [{ id: 'CVE-2023-0000', summary: 'Bad Vuln', database_specific: { severity: 'HIGH' } }] }, // vuln-pkg
null, // risk-pkg
null, // safe-pkg
null, // error-pkg
null // unknown-pkg
]
}
});
// Mock Socket.dev Responses (Sequential calls via mapConcurrent)
// We need to mock the behavior of axios.get for specific URLs
mockedAxios.get.mockImplementation((url: string) => {
if (url.includes('vuln-pkg')) return Promise.resolve({ data: { name: 'vuln-pkg', version: '1.0.0' } });
if (url.includes('risk-pkg')) return Promise.resolve({
data: {
name: 'risk-pkg',
version: '2.0.0',
hasInstallScript: true,
score: { supplyChain: 50 }
}
});
if (url.includes('safe-pkg')) return Promise.resolve({ data: { name: 'safe-pkg', version: '3.0.0', score: { supplyChain: 100 } } });
if (url.includes('error-pkg')) return Promise.reject(new Error('Network Timeout'));
if (url.includes('unknown-pkg')) {
const err: any = new Error('Not Found');
err.response = { status: 404 };
err.isAxiosError = true;
return Promise.reject(err);
}
return Promise.resolve({ data: {} });
});
// Helper to mock isAxiosError since we are mocking the module
mockedAxios.isAxiosError = (payload: any) => payload?.isAxiosError === true;
const results = await analyzeDependencies(dependencies);
consoleWarnSpy.mockRestore();
consoleErrorSpy.mockRestore();
// Check vuln-pkg
const vulnReport = results.find(r => r.package === 'vuln-pkg');
expect(vulnReport).toBeDefined();
expect(vulnReport?.vulnerabilities).toHaveLength(1);
expect(vulnReport?.vulnerabilities[0].id).toBe('CVE-2023-0000');
expect(vulnReport?.vulnerabilities[0].source).toBe('OSV');
expect(vulnReport?.vulnerabilities[0].url).toBe('https://osv.dev/vulnerability/CVE-2023-0000');
// Check risk-pkg
const riskReport = results.find(r => r.package === 'risk-pkg');
expect(riskReport).toBeDefined();
expect(riskReport?.risks.some(r => r.type === 'install_script')).toBe(true);
expect(riskReport?.risks.some(r => r.type === 'low_reputation')).toBe(true);
expect(riskReport?.risks[0].source).toBe('Socket');
expect(riskReport?.risks[0].url).toBe('https://socket.dev/npm/package/risk-pkg/overview/2.0.0');
// Check safe-pkg (should not be in results)
const safeReport = results.find(r => r.package === 'safe-pkg');
expect(safeReport).toBeUndefined();
// Check error-pkg (should be safe because we fallback to safe on error, but logic shouldn't crash)
const errorReport = results.find(r => r.package === 'error-pkg');
expect(errorReport).toBeUndefined();
// Check unknown-pkg (should be safe because 404 = not found/safe)
const unknownReport = results.find(r => r.package === 'unknown-pkg');
expect(unknownReport).toBeUndefined();
});
});