From 2a4ba8f4ed7f7e4b532dd1b5cfa2a3f64f22d91b Mon Sep 17 00:00:00 2001 From: Oleg Proskurin Date: Thu, 9 Oct 2025 23:37:17 +0700 Subject: [PATCH] feat: cover Enhancemet service by tests --- .../PromptEnhancementService.test.ts | 164 ++++++++++++++++++ .../__tests__/agents.test.ts | 65 +++++++ .../promptEnhancement/__tests__/utils.test.ts | 90 ++++++++++ .../__tests__/validators.test.ts | 43 +++++ 4 files changed, 362 insertions(+) create mode 100644 apps/api-service/src/services/promptEnhancement/__tests__/PromptEnhancementService.test.ts create mode 100644 apps/api-service/src/services/promptEnhancement/__tests__/agents.test.ts create mode 100644 apps/api-service/src/services/promptEnhancement/__tests__/utils.test.ts create mode 100644 apps/api-service/src/services/promptEnhancement/__tests__/validators.test.ts diff --git a/apps/api-service/src/services/promptEnhancement/__tests__/PromptEnhancementService.test.ts b/apps/api-service/src/services/promptEnhancement/__tests__/PromptEnhancementService.test.ts new file mode 100644 index 0000000..ce1a7a4 --- /dev/null +++ b/apps/api-service/src/services/promptEnhancement/__tests__/PromptEnhancementService.test.ts @@ -0,0 +1,164 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { PromptEnhancementService } from '../PromptEnhancementService'; +import type { AgentResult } from '../types'; + +// Mock the agents module +vi.mock('../agents', () => ({ + getAgent: vi.fn(() => ({ + templateType: 'general', + enhance: vi.fn(), + })), +})); + +describe('PromptEnhancementService', () => { + let service: PromptEnhancementService; + const mockApiKey = 'test-gemini-api-key'; + + beforeEach(() => { + vi.clearAllMocks(); + service = new PromptEnhancementService(mockApiKey); + }); + + describe('constructor', () => { + it('should throw error when API key is missing', () => { + expect(() => new PromptEnhancementService('')).toThrow('Gemini API key is required'); + }); + + it('should create instance with valid API key', () => { + expect(service).toBeInstanceOf(PromptEnhancementService); + }); + }); + + describe('enhancePrompt', () => { + it('should reject empty prompts', async () => { + const result = await service.enhancePrompt(''); + expect(result.success).toBe(false); + expect(result.error).toBe('Prompt cannot be empty'); + }); + + it('should reject prompts exceeding 5000 characters', async () => { + const longPrompt = 'a'.repeat(5001); + const result = await service.enhancePrompt(longPrompt); + expect(result.success).toBe(false); + expect(result.error).toContain('exceeds maximum length'); + }); + + it('should successfully enhance valid prompt', async () => { + const { getAgent } = await import('../agents'); + const mockAgent = { + templateType: 'general', + enhance: vi.fn().mockResolvedValue({ + success: true, + enhancedPrompt: 'A beautiful sunset over the ocean with vibrant colors', + appliedTemplate: 'general', + enhancements: ['Added detailed descriptions'], + } as AgentResult), + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + const result = await service.enhancePrompt('sunset'); + expect(result.success).toBe(true); + expect(result.enhancedPrompt).toBe('A beautiful sunset over the ocean with vibrant colors'); + expect(result.appliedTemplate).toBe('general'); + }); + + it('should handle agent enhancement failure', async () => { + const { getAgent } = await import('../agents'); + const mockAgent = { + templateType: 'general', + enhance: vi.fn().mockResolvedValue({ + success: false, + error: 'Enhancement failed', + enhancements: [], + } as AgentResult), + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + const result = await service.enhancePrompt('sunset'); + expect(result.success).toBe(false); + expect(result.error).toBe('Enhancement failed'); + }); + + it('should truncate enhanced prompt exceeding 2000 characters', async () => { + const { getAgent } = await import('../agents'); + const longEnhancedPrompt = 'enhanced '.repeat(300); // > 2000 chars + const mockAgent = { + templateType: 'general', + enhance: vi.fn().mockResolvedValue({ + success: true, + enhancedPrompt: longEnhancedPrompt, + appliedTemplate: 'general', + enhancements: [], + } as AgentResult), + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + const result = await service.enhancePrompt('sunset'); + expect(result.success).toBe(true); + expect(result.enhancedPrompt?.length).toBe(2000); + }); + + it('should pass options to agent', async () => { + const { getAgent } = await import('../agents'); + const mockEnhance = vi.fn().mockResolvedValue({ + success: true, + enhancedPrompt: 'Enhanced', + appliedTemplate: 'photorealistic', + enhancements: [], + } as AgentResult); + const mockAgent = { + templateType: 'photorealistic', + enhance: mockEnhance, + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + await service.enhancePrompt('sunset', { + template: 'photorealistic', + tags: ['landscape', 'nature'], + }); + + expect(mockEnhance).toHaveBeenCalledWith('sunset', { + template: 'photorealistic', + tags: ['landscape', 'nature'], + }); + }); + + it('should include detected language in result', async () => { + const { getAgent } = await import('../agents'); + const mockAgent = { + templateType: 'general', + enhance: vi.fn().mockResolvedValue({ + success: true, + enhancedPrompt: 'Enhanced prompt', + detectedLanguage: 'Spanish', + appliedTemplate: 'general', + enhancements: [], + } as AgentResult), + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + const result = await service.enhancePrompt('hermosa puesta de sol'); + expect(result.success).toBe(true); + expect(result.detectedLanguage).toBe('Spanish'); + }); + + it('should handle exceptions gracefully', async () => { + const { getAgent } = await import('../agents'); + const mockAgent = { + templateType: 'general', + enhance: vi.fn().mockRejectedValue(new Error('Network error')), + }; + vi.mocked(getAgent).mockReturnValue(mockAgent); + + const result = await service.enhancePrompt('sunset'); + expect(result.success).toBe(false); + expect(result.error).toBe('Network error'); + }); + + it('should return original prompt in all results', async () => { + const originalPrompt = 'test prompt'; + const result = await service.enhancePrompt(originalPrompt); + expect(result.originalPrompt).toBe(originalPrompt); + }); + }); +}); diff --git a/apps/api-service/src/services/promptEnhancement/__tests__/agents.test.ts b/apps/api-service/src/services/promptEnhancement/__tests__/agents.test.ts new file mode 100644 index 0000000..60bbf72 --- /dev/null +++ b/apps/api-service/src/services/promptEnhancement/__tests__/agents.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect } from 'vitest'; +import { getAgent } from '../agents'; +import { + GeneralAgent, + PhotorealisticAgent, + IllustrationAgent, + MinimalistAgent, + StickerAgent, + ProductAgent, + ComicAgent, +} from '../agents'; + +describe('getAgent', () => { + const mockApiKey = 'test-api-key'; + + it('should return GeneralAgent when no template provided', () => { + const agent = getAgent(mockApiKey); + expect(agent).toBeInstanceOf(GeneralAgent); + }); + + it('should return PhotorealisticAgent for photorealistic template', () => { + const agent = getAgent(mockApiKey, 'photorealistic'); + expect(agent).toBeInstanceOf(PhotorealisticAgent); + }); + + it('should return IllustrationAgent for illustration template', () => { + const agent = getAgent(mockApiKey, 'illustration'); + expect(agent).toBeInstanceOf(IllustrationAgent); + }); + + it('should return MinimalistAgent for minimalist template', () => { + const agent = getAgent(mockApiKey, 'minimalist'); + expect(agent).toBeInstanceOf(MinimalistAgent); + }); + + it('should return StickerAgent for sticker template', () => { + const agent = getAgent(mockApiKey, 'sticker'); + expect(agent).toBeInstanceOf(StickerAgent); + }); + + it('should return ProductAgent for product template', () => { + const agent = getAgent(mockApiKey, 'product'); + expect(agent).toBeInstanceOf(ProductAgent); + }); + + it('should return ComicAgent for comic template', () => { + const agent = getAgent(mockApiKey, 'comic'); + expect(agent).toBeInstanceOf(ComicAgent); + }); + + it('should return GeneralAgent for general template', () => { + const agent = getAgent(mockApiKey, 'general'); + expect(agent).toBeInstanceOf(GeneralAgent); + }); + + it('should fallback to GeneralAgent for unknown template', () => { + const agent = getAgent(mockApiKey, 'unknown-template' as any); + expect(agent).toBeInstanceOf(GeneralAgent); + }); + + it('should return agent with correct templateType', () => { + const agent = getAgent(mockApiKey, 'photorealistic'); + expect(agent.templateType).toBe('photorealistic'); + }); +}); diff --git a/apps/api-service/src/services/promptEnhancement/__tests__/utils.test.ts b/apps/api-service/src/services/promptEnhancement/__tests__/utils.test.ts new file mode 100644 index 0000000..e8ef4b6 --- /dev/null +++ b/apps/api-service/src/services/promptEnhancement/__tests__/utils.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect } from 'vitest'; +import { detectLanguage, detectEnhancements } from '../utils'; + +describe('detectLanguage', () => { + it('should detect English', () => { + expect(detectLanguage('a sunset over the ocean')).toBe('English'); + }); + + it('should detect Chinese', () => { + expect(detectLanguage('美丽的日落')).toBe('Chinese'); + }); + + it('should detect Japanese', () => { + expect(detectLanguage('こんにちは')).toBe('Japanese'); + }); + + it('should detect Korean', () => { + expect(detectLanguage('아름다운 일몰')).toBe('Korean'); + }); + + it('should detect Romance Language', () => { + expect(detectLanguage('hermosa puesta de sól')).toBe('Romance Language'); + expect(detectLanguage('beau coucher de soléil')).toBe('Romance Language'); + }); + + it('should detect Russian', () => { + expect(detectLanguage('красивый закат')).toBe('Russian'); + }); + + it('should detect Greek', () => { + expect(detectLanguage('όμορφο ηλιοβασίλεμα')).toBe('Greek'); + }); + + it('should detect Arabic', () => { + expect(detectLanguage('غروب جميل')).toBe('Arabic'); + }); + + it('should detect Hebrew', () => { + expect(detectLanguage('שקיעה יפה')).toBe('Hebrew'); + }); + + it('should default to English for unknown scripts', () => { + expect(detectLanguage('123')).toBe('English'); + }); +}); + +describe('detectEnhancements', () => { + it('should detect added detailed descriptions', () => { + const original = 'sunset'; + const enhanced = 'A breathtaking sunset over the ocean with vibrant orange and pink hues'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements).toContain('Added detailed descriptions'); + }); + + it('should detect photography terminology', () => { + const original = 'sunset'; + const enhanced = 'A photorealistic sunset shot with 50mm lens'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements).toContain('Applied photography terminology'); + }); + + it('should detect lighting descriptions', () => { + const original = 'room'; + const enhanced = 'A room with soft lighting and warm illuminated corners'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements).toContain('Enhanced lighting description'); + }); + + it('should detect texture details', () => { + const original = 'wall'; + const enhanced = 'A wall with rough texture and weathered surface'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements).toContain('Added texture details'); + }); + + it('should detect multiple enhancements', () => { + const original = 'sunset'; + const enhanced = + 'A photorealistic sunset with dramatic lighting, beautiful texture in the clouds, and soft illuminated sky'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements.length).toBeGreaterThan(1); + }); + + it('should return empty array when no enhancements detected', () => { + const original = 'sunset'; + const enhanced = 'sunset'; + const enhancements = detectEnhancements(original, enhanced); + expect(enhancements).toEqual([]); + }); +}); diff --git a/apps/api-service/src/services/promptEnhancement/__tests__/validators.test.ts b/apps/api-service/src/services/promptEnhancement/__tests__/validators.test.ts new file mode 100644 index 0000000..e40dce5 --- /dev/null +++ b/apps/api-service/src/services/promptEnhancement/__tests__/validators.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect } from 'vitest'; +import { validatePromptLength } from '../validators'; + +describe('validatePromptLength', () => { + it('should reject empty prompts', () => { + const result = validatePromptLength(''); + expect(result.valid).toBe(false); + expect(result.error).toBe('Prompt cannot be empty'); + }); + + it('should reject whitespace-only prompts', () => { + const result = validatePromptLength(' '); + expect(result.valid).toBe(false); + expect(result.error).toBe('Prompt cannot be empty'); + }); + + it('should accept valid prompt within max length', () => { + const result = validatePromptLength('a sunset over the ocean', 2000); + expect(result.valid).toBe(true); + expect(result.error).toBeUndefined(); + }); + + it('should reject prompts exceeding max length', () => { + const longPrompt = 'a'.repeat(2001); + const result = validatePromptLength(longPrompt, 2000); + expect(result.valid).toBe(false); + expect(result.error).toContain('exceeds maximum length'); + expect(result.error).toContain('2000'); + }); + + it('should use custom max length', () => { + const prompt = 'a'.repeat(150); + const result = validatePromptLength(prompt, 100); + expect(result.valid).toBe(false); + expect(result.error).toContain('100'); + }); + + it('should accept prompt at exact max length', () => { + const prompt = 'a'.repeat(100); + const result = validatePromptLength(prompt, 100); + expect(result.valid).toBe(true); + }); +});