import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { searchPackages } from '../../src/tools/search-packages.js';
import { mavenCentralApi } from '../../src/services/maven-central-api.js';
// Mock the services
vi.mock('../../src/services/maven-central-api.js', () => ({
mavenCentralApi: {
searchPackages: vi.fn(),
}
}));
describe('searchPackages integration', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should search packages successfully', async () => {
const mockSearchResponse = {
response: {
numFound: 2,
docs: [
{
id: 'org.springframework:spring-core',
g: 'org.springframework',
a: 'spring-core',
latestVersion: '6.0.0',
repositoryId: 'central',
p: 'jar',
timestamp: 1234567890000,
versionCount: 150,
ec: ['-sources.jar', '-javadoc.jar', '.jar', '.pom']
},
{
id: 'org.springframework:spring-context',
g: 'org.springframework',
a: 'spring-context',
latestVersion: '6.0.0',
repositoryId: 'central',
p: 'jar',
timestamp: 1234567880000,
versionCount: 145
}
]
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'spring core',
limit: 20,
offset: 0,
quality: 0.5,
popularity: 0.3
});
expect(result).toEqual({
query: 'spring core',
total: 2,
packages: [
{
package_name: 'org.springframework:spring-core',
group_id: 'org.springframework',
artifact_id: 'spring-core',
latest_version: '6.0.0',
description: undefined,
homepage: undefined,
repository: {
type: 'maven',
url: 'https://repo1.maven.org/maven2/org/springframework/spring-core'
},
stats: {
version_count: 150,
last_updated: '2009-02-13T23:31:30.000Z'
}
},
{
package_name: 'org.springframework:spring-context',
group_id: 'org.springframework',
artifact_id: 'spring-context',
latest_version: '6.0.0',
description: undefined,
homepage: undefined,
repository: {
type: 'maven',
url: 'https://repo1.maven.org/maven2/org/springframework/spring-context'
},
stats: {
version_count: 145,
last_updated: '2009-02-13T23:31:20.000Z'
}
}
]
});
expect(mavenCentralApi.searchPackages).toHaveBeenCalledWith('spring core', 20, 0);
});
it('should handle empty search results', async () => {
const mockSearchResponse = {
response: {
numFound: 0,
docs: []
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'nonexistent-package-xyz'
});
expect(result).toEqual({
query: 'nonexistent-package-xyz',
total: 0,
packages: []
});
});
it('should use default parameters', async () => {
const mockSearchResponse = {
response: {
numFound: 1,
docs: [
{
id: 'test:test',
g: 'test',
a: 'test',
latestVersion: '1.0.0'
}
]
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'test'
});
expect(result.packages).toHaveLength(1);
expect(mavenCentralApi.searchPackages).toHaveBeenCalledWith('test', 20, 0);
});
it('should handle custom limit and offset', async () => {
const mockSearchResponse = {
response: {
numFound: 100,
docs: Array.from({ length: 50 }, (_, i) => ({
id: `test:test${i}`,
g: 'test',
a: `test${i}`,
latestVersion: '1.0.0'
}))
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'test',
limit: 50,
offset: 10
});
expect(result.total).toBe(100);
expect(result.packages).toHaveLength(50);
expect(mavenCentralApi.searchPackages).toHaveBeenCalledWith('test', 50, 10);
});
it('should validate parameters', async () => {
await expect(searchPackages({
query: ''
})).rejects.toThrow();
await expect(searchPackages({
query: 'test',
limit: -1
})).rejects.toThrow();
await expect(searchPackages({
query: 'test',
offset: -1
})).rejects.toThrow();
await expect(searchPackages({
query: 'test',
quality: 1.5
})).rejects.toThrow();
await expect(searchPackages({
query: 'test',
popularity: -0.1
})).rejects.toThrow();
});
it('should handle packages with minimal information', async () => {
const mockSearchResponse = {
response: {
numFound: 1,
docs: [
{
id: 'minimal:package',
g: 'minimal',
a: 'package'
// Missing most optional fields
}
]
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'minimal'
});
expect(result.packages[0]).toEqual({
package_name: 'minimal:package',
group_id: 'minimal',
artifact_id: 'package',
latest_version: undefined,
description: undefined,
homepage: undefined,
repository: {
type: 'maven',
url: 'https://repo1.maven.org/maven2/minimal/package'
},
stats: {
version_count: undefined,
last_updated: undefined
}
});
});
it('should handle malformed package IDs gracefully', async () => {
const mockSearchResponse = {
response: {
numFound: 2,
docs: [
{
id: 'valid:package',
g: 'valid',
a: 'package',
latestVersion: '1.0.0'
},
{
id: 'invalid-id-format',
g: 'invalid',
a: 'package',
latestVersion: '1.0.0'
}
]
}
};
vi.mocked(mavenCentralApi.searchPackages).mockResolvedValue(mockSearchResponse);
const result = await searchPackages({
query: 'package'
});
// Should still include packages with malformed IDs if they have g and a fields
expect(result.packages).toHaveLength(2);
expect(result.packages[0].package_name).toBe('valid:package');
expect(result.packages[1].package_name).toBe('invalid:package');
});
});