← back to Wine Finder Next
__tests__/integration/api-marketplace.test.ts
247 lines
// Integration Tests for Marketplace API
import { NextRequest } from 'next/server';
import { GET, POST } from '@/app/api/membership/marketplace/route';
jest.mock('@/lib/membershipDatabase');
jest.mock('@/lib/rateLimit');
jest.mock('@/lib/auth');
jest.mock('@/lib/securityMonitoring');
import { marketplaceDb } from '@/lib/membershipDatabase';
import { checkRateLimit, getClientIdentifier } from '@/lib/rateLimit';
import { logAudit } from '@/lib/auth';
import { analyzeRequest } from '@/lib/securityMonitoring';
describe('Marketplace API Integration Tests', () => {
beforeEach(() => {
jest.clearAllMocks();
(checkRateLimit as jest.Mock).mockReturnValue({
success: true,
limit: 100,
remaining: 99,
reset: Date.now() + 60000,
});
(getClientIdentifier as jest.Mock).mockReturnValue('127.0.0.1');
(logAudit as jest.Mock).mockReturnValue(undefined);
(analyzeRequest as jest.Mock).mockReturnValue({ safe: true, threats: [] });
});
describe('GET /api/membership/marketplace', () => {
it('should return all listings', async () => {
const mockListings = [
{
id: 'listing_1',
listingType: 'membership',
itemId: 'unit_1',
bottleId: 'bottle_1',
sellerId: 'user_1',
sellerName: 'Test Seller',
price: 15.50,
currency: 'USD',
status: 'active',
listedAt: new Date(),
},
];
(marketplaceDb.getAll as jest.Mock).mockReturnValue(mockListings);
const request = new NextRequest('http://localhost:3000/api/membership/marketplace');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
expect(data.data).toHaveLength(1);
expect(data.data[0].price).toBe(15.50);
});
it('should filter by active listings', async () => {
const mockActiveListings = [
{
id: 'listing_active',
listingType: 'membership',
itemId: 'unit_1',
bottleId: 'bottle_1',
sellerId: 'user_1',
sellerName: 'Seller',
price: 10,
currency: 'USD',
status: 'active',
listedAt: new Date(),
},
];
(marketplaceDb.getActive as jest.Mock).mockReturnValue(mockActiveListings);
const request = new NextRequest('http://localhost:3000/api/membership/marketplace?active=true');
const response = await GET(request);
const data = await response.json();
expect(marketplaceDb.getActive).toHaveBeenCalled();
expect(data.data.every((l: any) => l.status === 'active')).toBe(true);
});
it('should filter by seller ID', async () => {
const sellerId = 'user_123';
const mockSellerListings = [
{
id: 'listing_seller',
listingType: 'membership',
itemId: 'unit_1',
bottleId: 'bottle_1',
sellerId,
sellerName: 'Seller',
price: 10,
currency: 'USD',
status: 'active',
listedAt: new Date(),
},
];
(marketplaceDb.getBySellerId as jest.Mock).mockReturnValue(mockSellerListings);
const request = new NextRequest(`http://localhost:3000/api/membership/marketplace?sellerId=${sellerId}`);
const response = await GET(request);
const data = await response.json();
expect(marketplaceDb.getBySellerId).toHaveBeenCalledWith(sellerId);
expect(data.success).toBe(true);
});
it('should validate seller ID format', async () => {
const invalidSellerId = '<script>alert(1)</script>';
const request = new NextRequest(`http://localhost:3000/api/membership/marketplace?sellerId=${invalidSellerId}`);
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.success).toBe(false);
expect(data.error).toContain('Invalid seller ID format');
});
});
describe('POST /api/membership/marketplace', () => {
it('should create a new listing', async () => {
const newListing = {
id: 'listing_new',
listingType: 'membership',
itemId: 'unit_123',
bottleId: 'bottle_456',
sellerId: 'user_1',
sellerName: 'Test Seller',
price: 20.00,
currency: 'USD',
status: 'active',
listedAt: new Date(),
};
(marketplaceDb.create as jest.Mock).mockReturnValue(newListing);
const requestBody = {
listingType: 'membership',
itemId: 'unit_123',
bottleId: 'bottle_456',
sellerId: 'user_1',
sellerName: 'Test Seller',
price: 20.00,
currency: 'USD',
status: 'active',
};
const request = new NextRequest('http://localhost:3000/api/membership/marketplace', {
method: 'POST',
body: JSON.stringify(requestBody),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
expect(data.data.price).toBe(20.00);
expect(data.message).toBe('Listing created successfully');
});
it('should validate seller ID on POST', async () => {
const requestBody = {
sellerId: 'invalid_id',
price: 10,
};
const request = new NextRequest('http://localhost:3000/api/membership/marketplace', {
method: 'POST',
body: JSON.stringify(requestBody),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.success).toBe(false);
expect(data.error).toContain('Invalid seller ID format');
});
it('should validate price format', async () => {
const requestBody = {
sellerId: 'user_1',
price: -10,
};
const request = new NextRequest('http://localhost:3000/api/membership/marketplace', {
method: 'POST',
body: JSON.stringify(requestBody),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.success).toBe(false);
expect(data.error).toContain('Invalid price format');
});
it('should log marketplace creation audit', async () => {
const newListing = {
id: 'listing_audit',
listingType: 'membership',
itemId: 'unit_1',
bottleId: 'bottle_1',
sellerId: 'user_1',
sellerName: 'Seller',
price: 15,
currency: 'USD',
status: 'active',
listedAt: new Date(),
};
(marketplaceDb.create as jest.Mock).mockReturnValue(newListing);
const request = new NextRequest('http://localhost:3000/api/membership/marketplace', {
method: 'POST',
body: JSON.stringify({
sellerId: 'user_1',
price: 15,
}),
});
await POST(request);
expect(logAudit).toHaveBeenCalledWith({
action: 'MARKETPLACE_CREATE',
resource: `marketplace/${newListing.id}`,
ipAddress: '127.0.0.1',
userAgent: expect.any(String),
success: true,
metadata: {
listingId: newListing.id,
sellerId: newListing.sellerId,
price: newListing.price,
},
});
});
});
});