UNPKG

ai-cant-even

Version:

A satirical AI-powered utility that's confidently wrong about basic math operations

404 lines (403 loc) 19.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const index_1 = require("../index"); const ai_sdk_client_1 = require("../ai-sdk-client"); // Mock the AI SDK modules jest.mock('@ai-sdk/anthropic', () => ({ createAnthropic: jest.fn(() => jest.fn(() => 'mocked-anthropic-model')), })); jest.mock('@ai-sdk/openai', () => ({ createOpenAI: jest.fn(() => jest.fn(() => 'mocked-openai-model')), })); jest.mock('ai', () => ({ generateText: jest.fn(), })); // Mock console.error to avoid noise in tests const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(() => { }); describe('AiCantEven', () => { beforeEach(() => { jest.clearAllMocks(); delete process.env.ANTHROPIC_API_KEY; delete process.env.OPENAI_API_KEY; }); afterAll(() => { mockConsoleError.mockRestore(); }); describe('Constructor and Configuration', () => { it('should create instance with default configuration', () => { const ai = new index_1.AiCantEven(); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should create instance with custom configuration', () => { const config = { confidence: index_1.Confidence.SMUG, logic: index_1.Logic.PSEUDOMATH, provider: index_1.Provider.ANTHROPIC, apiKey: 'test-key', model: 'claude-3-sonnet', }; const ai = new index_1.AiCantEven(config); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should initialize with partial configuration', () => { const ai = new index_1.AiCantEven({ confidence: index_1.Confidence.SNARKY }); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should handle empty configuration object', () => { const ai = new index_1.AiCantEven({}); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); }); describe('Fluent API Methods', () => { let ai; beforeEach(() => { ai = new index_1.AiCantEven(); }); it('should support withConfidence method chaining', () => { const result = ai.withConfidence(index_1.Confidence.SMUG); expect(result).toBe(ai); }); it('should support withLogic method chaining', () => { const result = ai.withLogic(index_1.Logic.VISUAL); expect(result).toBe(ai); }); it('should support withModel method chaining', () => { const result = ai.withModel('gpt-4'); expect(result).toBe(ai); }); it('should support withApiKey method chaining', () => { const result = ai.withApiKey('test-key'); expect(result).toBe(ai); }); it('should support withApiKey with endpoint', () => { const result = ai.withApiKey('test-key', 'https://custom-endpoint.com'); expect(result).toBe(ai); }); it('should support method chaining combination', () => { const result = ai .withConfidence(index_1.Confidence.OVERTHINK) .withLogic(index_1.Logic.NONSEQUITUR) .withModel('claude-3-haiku') .withApiKey('test-key'); expect(result).toBe(ai); }); }); describe('Math Operation Methods - Without API Key (Fallback)', () => { let ai; beforeEach(() => { ai = new index_1.AiCantEven(); }); it('should return fallback response for isEven', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isEven(4); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isOdd', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isOdd(3); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isGreaterThan', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isGreaterThan(10, 5); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isLessThan', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isLessThan(3, 8); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for areEqual', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.areEqual(5, 5); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isPrime', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isPrime(17); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isPositive', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isPositive(-3); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isDivisibleBy', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isDivisibleBy(15, 3); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isNumber', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isNumber('42'); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); it('should return fallback response for isInteger', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isInteger(3.14); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); })); }); describe('Math Operation Methods - With API Key', () => { let ai; const { generateText } = require('ai'); beforeEach(() => { ai = new index_1.AiCantEven({ provider: index_1.Provider.ANTHROPIC, apiKey: 'test-key', }); generateText.mockResolvedValue({ text: 'Mocked AI response' }); }); it('should call AI SDK for isEven with API key', () => __awaiter(void 0, void 0, void 0, function* () { const response = yield ai.isEven(4); expect(generateText).toHaveBeenCalled(); expect(response).toBe('Mocked AI response'); })); it('should call AI SDK for operations with comparison values', () => __awaiter(void 0, void 0, void 0, function* () { yield ai.isGreaterThan(10, 5); expect(generateText).toHaveBeenCalledWith(expect.objectContaining({ model: 'mocked-anthropic-model', messages: expect.arrayContaining([ expect.objectContaining({ role: 'system' }), expect.objectContaining({ role: 'user', content: expect.stringContaining('Is 10 greater than 5?') }), ]), })); })); it('should reset temporary settings after each operation', () => __awaiter(void 0, void 0, void 0, function* () { yield ai.withConfidence(index_1.Confidence.SMUG).isEven(4); yield ai.isOdd(3); expect(generateText).toHaveBeenCalledTimes(2); const [firstCall, secondCall] = generateText.mock.calls; expect(firstCall[0].messages[1].content).toContain('SMUG'); expect(secondCall[0].messages[1].content).toContain('OVERWHELMED'); })); it('should use temporary model for single operation', () => __awaiter(void 0, void 0, void 0, function* () { yield ai.withModel('custom-model').isEven(4); expect(generateText).toHaveBeenCalled(); })); }); describe('Error Handling', () => { let ai; const { generateText } = require('ai'); beforeEach(() => { ai = new index_1.AiCantEven({ provider: index_1.Provider.ANTHROPIC, apiKey: 'test-key', }); }); it('should handle API errors gracefully', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockRejectedValue(new Error('API Error')); const response = yield ai.isEven(4); expect(typeof response).toBe('string'); expect(response.length).toBeGreaterThan(0); expect(mockConsoleError).toHaveBeenCalledWith('Error calling anthropic API:', expect.any(Error)); })); it('should return fallback response on API failure', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockRejectedValue(new Error('Network error')); const response = yield ai.isPrime(17); expect(response).toMatch(/can't even|Error 418|neural networks|existential|Math\.exe/); })); }); describe('aiCantEven Factory Function', () => { it('should create instance with no config', () => { const ai = (0, index_1.aiCantEven)(); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should create instance with provider and API key', () => { const ai = (0, index_1.aiCantEven)({ provider: index_1.Provider.ANTHROPIC, apiKey: 'test-key', }); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should use ANTHROPIC_API_KEY environment variable', () => { process.env.ANTHROPIC_API_KEY = 'env-anthropic-key'; const ai = (0, index_1.aiCantEven)(); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should use OPENAI_API_KEY environment variable', () => { process.env.OPENAI_API_KEY = 'env-openai-key'; const ai = (0, index_1.aiCantEven)(); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should prioritize ANTHROPIC_API_KEY over OPENAI_API_KEY', () => { process.env.ANTHROPIC_API_KEY = 'env-anthropic-key'; process.env.OPENAI_API_KEY = 'env-openai-key'; const ai = (0, index_1.aiCantEven)(); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); it('should override environment variables with explicit config', () => { process.env.ANTHROPIC_API_KEY = 'env-key'; const ai = (0, index_1.aiCantEven)({ provider: index_1.Provider.OPENAI, apiKey: 'explicit-key', }); expect(ai).toBeInstanceOf(index_1.AiCantEven); }); }); describe('Type Exports', () => { it('should export Confidence enum', () => { expect(index_1.Confidence.OVERWHELMED).toBe('OVERWHELMED'); expect(index_1.Confidence.OVERTHINK).toBe('OVERTHINK'); expect(index_1.Confidence.SMUG).toBe('SMUG'); expect(index_1.Confidence.SNARKY).toBe('SNARKY'); }); it('should export Logic enum', () => { expect(index_1.Logic.SIMPLE).toBe('SIMPLE'); expect(index_1.Logic.NONSEQUITUR).toBe('NONSEQUITUR'); expect(index_1.Logic.PSEUDOMATH).toBe('PSEUDOMATH'); expect(index_1.Logic.VISUAL).toBe('VISUAL'); }); it('should export Provider enum', () => { expect(index_1.Provider.ANTHROPIC).toBe('anthropic'); expect(index_1.Provider.OPENAI).toBe('openai'); }); }); }); describe('AiSdkClient', () => { const { generateText } = require('ai'); const { createAnthropic } = require('@ai-sdk/anthropic'); const { createOpenAI } = require('@ai-sdk/openai'); beforeEach(() => { jest.clearAllMocks(); }); describe('Constructor', () => { it('should initialize with Anthropic provider', () => { new ai_sdk_client_1.AiSdkClient(index_1.Provider.ANTHROPIC, 'test-key'); expect(createAnthropic).toHaveBeenCalledWith({ apiKey: 'test-key', }); }); it('should initialize with OpenAI provider', () => { new ai_sdk_client_1.AiSdkClient(index_1.Provider.OPENAI, 'test-key'); expect(createOpenAI).toHaveBeenCalledWith({ apiKey: 'test-key', }); }); it('should initialize with custom endpoint', () => { new ai_sdk_client_1.AiSdkClient(index_1.Provider.ANTHROPIC, 'test-key', 'https://custom-endpoint.com'); expect(createAnthropic).toHaveBeenCalledWith({ apiKey: 'test-key', baseURL: 'https://custom-endpoint.com', }); }); it('should initialize with custom model', () => { new ai_sdk_client_1.AiSdkClient(index_1.Provider.ANTHROPIC, 'test-key', undefined, 'claude-3-opus'); expect(createAnthropic).toHaveBeenCalled(); }); it('should throw error for unsupported provider', () => { expect(() => { new ai_sdk_client_1.AiSdkClient('unsupported', 'test-key'); }).toThrow('Unsupported provider: unsupported'); }); }); describe('generateResponse', () => { let client; beforeEach(() => { client = new ai_sdk_client_1.AiSdkClient(index_1.Provider.ANTHROPIC, 'test-key'); }); it('should generate response successfully', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockResolvedValue({ text: 'AI response' }); const params = { confidence: index_1.Confidence.SMUG, logic: index_1.Logic.SIMPLE, operation: 'isEven', value: 4, }; const response = yield client.generateResponse(params); expect(generateText).toHaveBeenCalledWith({ model: 'mocked-anthropic-model', messages: [ { role: 'system', content: expect.stringContaining('You are an AI') }, { role: 'user', content: expect.stringContaining('Is 4 even?') }, ], temperature: 0.9, maxTokens: 100, }); expect(response).toEqual({ text: 'AI response', success: true, }); })); it('should handle comparison operations', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockResolvedValue({ text: 'Comparison response' }); const params = { confidence: index_1.Confidence.OVERTHINK, logic: index_1.Logic.VISUAL, operation: 'isGreaterThan', value: 10, comparisonValue: 5, }; yield client.generateResponse(params); expect(generateText).toHaveBeenCalledWith(expect.objectContaining({ messages: expect.arrayContaining([ expect.objectContaining({ role: 'user', content: expect.stringContaining('Is 10 greater than 5?'), }), ]), })); })); it('should handle API errors with fallback', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockRejectedValue(new Error('API Error')); const params = { confidence: index_1.Confidence.SNARKY, logic: index_1.Logic.PSEUDOMATH, operation: 'isPrime', value: 17, }; const response = yield client.generateResponse(params); expect(response.success).toBe(false); expect(typeof response.text).toBe('string'); expect(response.text.length).toBeGreaterThan(0); })); it('should include confidence and logic in prompt', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockResolvedValue({ text: 'Response' }); const params = { confidence: index_1.Confidence.OVERWHELMED, logic: index_1.Logic.NONSEQUITUR, operation: 'isOdd', value: 7, }; yield client.generateResponse(params); const userMessage = generateText.mock.calls[0][0].messages[1].content; expect(userMessage).toContain('OVERWHELMED'); expect(userMessage).toContain('NONSEQUITUR'); })); it('should handle unknown operations', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockResolvedValue({ text: 'Unknown operation response' }); const params = { confidence: index_1.Confidence.SMUG, logic: index_1.Logic.SIMPLE, operation: 'unknownOperation', value: 42, }; yield client.generateResponse(params); const userMessage = generateText.mock.calls[0][0].messages[1].content; expect(userMessage).toContain('What can you tell me about 42?'); })); it('should trim response text', () => __awaiter(void 0, void 0, void 0, function* () { generateText.mockResolvedValue({ text: ' Trimmed response ' }); const params = { confidence: index_1.Confidence.SNARKY, logic: index_1.Logic.VISUAL, operation: 'isInteger', value: 3.14, }; const response = yield client.generateResponse(params); expect(response.text).toBe('Trimmed response'); })); }); });