Files
sentryagent-idp/tests/unit/services/MarketplaceService.test.ts
SentryAgent.ai Developer 7441c9f298 fix(vv): resolve all 6 V&V issues — field trial unblocked
All findings from the inaugural LeadValidator audit resolved and
confirmed. Release gate: PASS.

VV_ISSUE_002 (BLOCKER): 15 OpenAPI specs verified present covering
all 20 route groups (46 endpoints documented in docs/openapi/)

VV_ISSUE_003 (MAJOR): Remove any types from src/db/pool.ts —
replaced pool.query shim with unknown[] + Object.defineProperty,
zero any types, eslint-disable suppressions removed

VV_ISSUE_004 (MAJOR): Remove raw Pool from ScaffoldController and
HealthDetailedController — injected AgentRepository/CredentialRepository
and DbProbe interface respectively; added CredentialRepository.findActiveClientId()

VV_ISSUE_005 (MAJOR): Add unit tests for 5 untested services —
ComplianceStatusStore, EventPublisher, MarketplaceService,
OIDCTrustPolicyService, UsageService

VV_ISSUE_006 (MAJOR): Add integration tests for 7 missing route
groups — analytics, billing, tiers, webhooks, marketplace,
oidc-trust-policies, oidc-token-exchange

VV_ISSUE_001 (MINOR): Create missing design.md and tasks.md in 4
OpenSpec archives — all archives now complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 04:52:47 +00:00

118 lines
4.0 KiB
TypeScript

/**
* Unit tests for src/services/MarketplaceService.ts
*/
import { MarketplaceService } from '../../../src/services/MarketplaceService';
import { AgentRepository } from '../../../src/repositories/AgentRepository';
import { AgentNotFoundError } from '../../../src/utils/errors';
import { IAgent, IMarketplaceFilters } from '../../../src/types/index';
jest.mock('../../../src/repositories/AgentRepository');
const MockAgentRepo = AgentRepository as jest.MockedClass<typeof AgentRepository>;
const BASE_FILTERS: IMarketplaceFilters = { page: 1, limit: 10 };
function makeAgent(overrides: Partial<IAgent> = {}): IAgent {
return {
agentId: 'agent-001',
organizationId: 'org-001',
email: 'agent@example.com',
agentType: 'screener',
version: 'v1.0.0',
capabilities: ['resume:read'],
owner: 'test-team',
deploymentEnv: 'production',
status: 'active',
isPublic: true,
createdAt: new Date('2026-01-01'),
updatedAt: new Date('2026-01-02'),
did: null,
didCreatedAt: null,
...overrides,
};
}
describe('MarketplaceService', () => {
let service: MarketplaceService;
let agentRepo: jest.Mocked<AgentRepository>;
beforeEach(() => {
jest.clearAllMocks();
agentRepo = new MockAgentRepo({} as never) as jest.Mocked<AgentRepository>;
service = new MarketplaceService(agentRepo);
});
describe('listPublicAgents()', () => {
it('should return mapped agent cards', async () => {
const agent = makeAgent();
agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });
const result = await service.listPublicAgents(BASE_FILTERS);
expect(result.data).toHaveLength(1);
expect(result.data[0].agentId).toBe('agent-001');
expect(result.total).toBe(1);
expect(result.page).toBe(1);
expect(result.limit).toBe(10);
});
it('should strip private fields (email, organizationId) from cards', async () => {
const agent = makeAgent();
agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });
const result = await service.listPublicAgents(BASE_FILTERS);
const card = result.data[0] as Record<string, unknown>;
expect(card['email']).toBeUndefined();
expect(card['organizationId']).toBeUndefined();
});
it('should include a minimal DID document when agent has a DID', async () => {
const agent = makeAgent({ did: 'did:web:sentryagent.ai:agents:agent-001' });
agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });
const result = await service.listPublicAgents(BASE_FILTERS);
expect(result.data[0].didDocument).not.toBeNull();
expect(result.data[0].didDocument?.id).toBe('did:web:sentryagent.ai:agents:agent-001');
});
it('should return null DID document when agent has no DID', async () => {
const agent = makeAgent({ did: null });
agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [agent], total: 1 });
const result = await service.listPublicAgents(BASE_FILTERS);
expect(result.data[0].didDocument).toBeNull();
});
it('should return empty data array when no public agents exist', async () => {
agentRepo.findPublicAgents = jest.fn().mockResolvedValue({ agents: [], total: 0 });
const result = await service.listPublicAgents(BASE_FILTERS);
expect(result.data).toHaveLength(0);
expect(result.total).toBe(0);
});
});
describe('getPublicAgent()', () => {
it('should return a card for a public agent', async () => {
const agent = makeAgent();
agentRepo.findPublicById = jest.fn().mockResolvedValue(agent);
const card = await service.getPublicAgent('agent-001');
expect(card.agentId).toBe('agent-001');
expect(card.owner).toBe('test-team');
});
it('should throw AgentNotFoundError when agent is not found', async () => {
agentRepo.findPublicById = jest.fn().mockResolvedValue(null);
await expect(service.getPublicAgent('nonexistent')).rejects.toThrow(AgentNotFoundError);
});
});
});