Skip to main content
Glama
ghostServiceImproved.tiers.test.js10.1 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createMockContextLogger } from '../../__tests__/helpers/mockLogger.js'; import { mockDotenv } from '../../__tests__/helpers/testUtils.js'; // Mock the Ghost Admin API with tiers support vi.mock('@tryghost/admin-api', () => ({ default: vi.fn(function () { return { posts: { add: vi.fn(), browse: vi.fn(), read: vi.fn(), edit: vi.fn(), delete: vi.fn(), }, pages: { add: vi.fn(), browse: vi.fn(), read: vi.fn(), edit: vi.fn(), delete: vi.fn(), }, tags: { add: vi.fn(), browse: vi.fn(), read: vi.fn(), edit: vi.fn(), delete: vi.fn(), }, members: { add: vi.fn(), browse: vi.fn(), read: vi.fn(), edit: vi.fn(), delete: vi.fn(), }, tiers: { add: vi.fn(), browse: vi.fn(), read: vi.fn(), edit: vi.fn(), delete: vi.fn(), }, site: { read: vi.fn(), }, images: { upload: vi.fn(), }, }; }), })); // Mock dotenv vi.mock('dotenv', () => mockDotenv()); // Mock logger vi.mock('../../utils/logger.js', () => ({ createContextLogger: createMockContextLogger(), })); // Mock fs for validateImagePath vi.mock('fs/promises', () => ({ default: { access: vi.fn(), }, })); // Import after setting up mocks import { createTier, updateTier, deleteTier, getTiers, getTier, api, } from '../ghostServiceImproved.js'; describe('ghostServiceImproved - Tiers', () => { beforeEach(() => { // Reset all mocks before each test vi.clearAllMocks(); }); describe('createTier', () => { it('should create a tier with required fields', async () => { const tierData = { name: 'Premium', currency: 'USD', }; const mockCreatedTier = { id: 'tier-1', name: 'Premium', currency: 'USD', type: 'paid', active: true, }; api.tiers.add.mockResolvedValue(mockCreatedTier); const result = await createTier(tierData); expect(api.tiers.add).toHaveBeenCalledWith( expect.objectContaining({ name: 'Premium', currency: 'USD', }), expect.any(Object) ); expect(result).toEqual(mockCreatedTier); }); it('should create a tier with all optional fields', async () => { const tierData = { name: 'Premium Membership', currency: 'USD', description: 'Access to premium content', monthly_price: 999, yearly_price: 9999, benefits: ['Ad-free experience', 'Exclusive content'], welcome_page_url: 'https://example.com/welcome', }; const mockCreatedTier = { id: 'tier-2', ...tierData, type: 'paid', active: true, }; api.tiers.add.mockResolvedValue(mockCreatedTier); const result = await createTier(tierData); expect(api.tiers.add).toHaveBeenCalledWith( expect.objectContaining(tierData), expect.any(Object) ); expect(result).toEqual(mockCreatedTier); }); it('should throw ValidationError when name is missing', async () => { await expect( createTier({ currency: 'USD', }) ).rejects.toThrow('Tier validation failed'); }); it('should throw ValidationError when currency is missing', async () => { await expect( createTier({ name: 'Premium', }) ).rejects.toThrow('Tier validation failed'); }); it('should throw ValidationError when currency is invalid', async () => { await expect( createTier({ name: 'Premium', currency: 'us', }) ).rejects.toThrow('Tier validation failed'); }); }); describe('getTiers', () => { it('should get all tiers with default options', async () => { const mockTiers = [ { id: 'tier-1', name: 'Free', type: 'free', active: true, }, { id: 'tier-2', name: 'Premium', type: 'paid', active: true, }, ]; api.tiers.browse.mockResolvedValue(mockTiers); const result = await getTiers(); expect(api.tiers.browse).toHaveBeenCalledWith( expect.objectContaining({ limit: 15, }), expect.any(Object) ); expect(result).toEqual(mockTiers); }); it('should get tiers with custom limit', async () => { const mockTiers = [ { id: 'tier-1', name: 'Free', type: 'free', active: true, }, ]; api.tiers.browse.mockResolvedValue(mockTiers); const result = await getTiers({ limit: 5 }); expect(api.tiers.browse).toHaveBeenCalledWith( expect.objectContaining({ limit: 5, }), expect.any(Object) ); expect(result).toEqual(mockTiers); }); it('should get tiers with filter', async () => { const mockTiers = [ { id: 'tier-2', name: 'Premium', type: 'paid', active: true, }, ]; api.tiers.browse.mockResolvedValue(mockTiers); const result = await getTiers({ filter: 'type:paid' }); expect(api.tiers.browse).toHaveBeenCalledWith( expect.objectContaining({ filter: 'type:paid', }), expect.any(Object) ); expect(result).toEqual(mockTiers); }); it('should return empty array when no tiers found', async () => { api.tiers.browse.mockResolvedValue([]); const result = await getTiers(); expect(result).toEqual([]); }); it('should throw ValidationError for invalid limit', async () => { await expect(getTiers({ limit: 0 })).rejects.toThrow('Tier query validation failed'); await expect(getTiers({ limit: 101 })).rejects.toThrow('Tier query validation failed'); }); }); describe('getTier', () => { it('should get a single tier by ID', async () => { const mockTier = { id: 'tier-1', name: 'Premium', currency: 'USD', type: 'paid', active: true, }; api.tiers.read.mockResolvedValue(mockTier); const result = await getTier('tier-1'); expect(api.tiers.read).toHaveBeenCalledWith( expect.objectContaining({ id: 'tier-1', }), expect.objectContaining({ id: 'tier-1', }) ); expect(result).toEqual(mockTier); }); it('should throw ValidationError when ID is missing', async () => { await expect(getTier()).rejects.toThrow('Tier ID is required'); }); it('should throw ValidationError when ID is empty string', async () => { await expect(getTier('')).rejects.toThrow('Tier ID is required'); }); it('should throw NotFoundError when tier does not exist', async () => { const mockError = new Error('Tier not found'); mockError.response = { status: 404 }; api.tiers.read.mockRejectedValue(mockError); await expect(getTier('nonexistent-id')).rejects.toThrow(); }); }); describe('updateTier', () => { it('should update a tier', async () => { const existingTier = { id: 'tier-1', name: 'Premium', currency: 'USD', monthly_price: 999, updated_at: '2024-01-01T00:00:00.000Z', }; const updateData = { name: 'Premium Plus', monthly_price: 1299, }; const mockUpdatedTier = { ...existingTier, ...updateData, }; api.tiers.read.mockResolvedValue(existingTier); api.tiers.edit.mockResolvedValue(mockUpdatedTier); const result = await updateTier('tier-1', updateData); expect(api.tiers.read).toHaveBeenCalledWith( expect.objectContaining({ id: 'tier-1' }), expect.objectContaining({ id: 'tier-1' }) ); expect(api.tiers.edit).toHaveBeenCalledWith( expect.objectContaining({ ...existingTier, ...updateData, }), expect.objectContaining({ id: 'tier-1', }) ); expect(result).toEqual(mockUpdatedTier); }); it('should throw ValidationError when ID is missing', async () => { await expect(updateTier('', { name: 'Updated' })).rejects.toThrow( 'Tier ID is required for update' ); }); it('should throw ValidationError for invalid update data', async () => { await expect(updateTier('tier-1', { monthly_price: -100 })).rejects.toThrow( 'Tier validation failed' ); }); it('should throw NotFoundError when tier does not exist', async () => { const mockError = new Error('Tier not found'); mockError.response = { status: 404 }; api.tiers.read.mockRejectedValue(mockError); await expect(updateTier('nonexistent-id', { name: 'Updated' })).rejects.toThrow(); }); }); describe('deleteTier', () => { it('should delete a tier', async () => { api.tiers.delete.mockResolvedValue({ success: true }); const result = await deleteTier('tier-1'); expect(api.tiers.delete).toHaveBeenCalledWith('tier-1', expect.any(Object)); expect(result).toEqual({ success: true }); }); it('should throw ValidationError when ID is missing', async () => { await expect(deleteTier()).rejects.toThrow('Tier ID is required for deletion'); }); it('should throw ValidationError when ID is empty string', async () => { await expect(deleteTier('')).rejects.toThrow('Tier ID is required for deletion'); }); it('should throw NotFoundError when tier does not exist', async () => { const mockError = new Error('Tier not found'); mockError.response = { status: 404 }; api.tiers.delete.mockRejectedValue(mockError); await expect(deleteTier('nonexistent-id')).rejects.toThrow(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jgardner04/Ghost-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server