ai-cant-even
Version:
A satirical AI-powered utility that's confidently wrong about basic math operations
494 lines (408 loc) • 15.1 kB
text/typescript
import { AiCantEven, aiCantEven, Confidence, Logic, Provider } from '../index';
import { AiSdkClient } from '../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 AiCantEven();
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should create instance with custom configuration', () => {
const config = {
confidence: Confidence.SMUG,
logic: Logic.PSEUDOMATH,
provider: Provider.ANTHROPIC,
apiKey: 'test-key',
model: 'claude-3-sonnet',
};
const ai = new AiCantEven(config);
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should initialize with partial configuration', () => {
const ai = new AiCantEven({ confidence: Confidence.SNARKY });
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should handle empty configuration object', () => {
const ai = new AiCantEven({});
expect(ai).toBeInstanceOf(AiCantEven);
});
});
describe('Fluent API Methods', () => {
let ai: AiCantEven;
beforeEach(() => {
ai = new AiCantEven();
});
it('should support withConfidence method chaining', () => {
const result = ai.withConfidence(Confidence.SMUG);
expect(result).toBe(ai);
});
it('should support withLogic method chaining', () => {
const result = ai.withLogic(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(Confidence.OVERTHINK)
.withLogic(Logic.NONSEQUITUR)
.withModel('claude-3-haiku')
.withApiKey('test-key');
expect(result).toBe(ai);
});
});
describe('Math Operation Methods - Without API Key (Fallback)', () => {
let ai: AiCantEven;
beforeEach(() => {
ai = new AiCantEven();
});
it('should return fallback response for isEven', async () => {
const response = await ai.isEven(4);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isOdd', async () => {
const response = await ai.isOdd(3);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isGreaterThan', async () => {
const response = await ai.isGreaterThan(10, 5);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isLessThan', async () => {
const response = await ai.isLessThan(3, 8);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for areEqual', async () => {
const response = await ai.areEqual(5, 5);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isPrime', async () => {
const response = await ai.isPrime(17);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isPositive', async () => {
const response = await ai.isPositive(-3);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isDivisibleBy', async () => {
const response = await ai.isDivisibleBy(15, 3);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isNumber', async () => {
const response = await ai.isNumber('42');
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
it('should return fallback response for isInteger', async () => {
const response = await ai.isInteger(3.14);
expect(typeof response).toBe('string');
expect(response.length).toBeGreaterThan(0);
});
});
describe('Math Operation Methods - With API Key', () => {
let ai: AiCantEven;
const { generateText } = require('ai');
beforeEach(() => {
ai = new AiCantEven({
provider: Provider.ANTHROPIC,
apiKey: 'test-key',
});
generateText.mockResolvedValue({ text: 'Mocked AI response' });
});
it('should call AI SDK for isEven with API key', async () => {
const response = await ai.isEven(4);
expect(generateText).toHaveBeenCalled();
expect(response).toBe('Mocked AI response');
});
it('should call AI SDK for operations with comparison values', async () => {
await 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', async () => {
await ai.withConfidence(Confidence.SMUG).isEven(4);
await 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', async () => {
await ai.withModel('custom-model').isEven(4);
expect(generateText).toHaveBeenCalled();
});
});
describe('Error Handling', () => {
let ai: AiCantEven;
const { generateText } = require('ai');
beforeEach(() => {
ai = new AiCantEven({
provider: Provider.ANTHROPIC,
apiKey: 'test-key',
});
});
it('should handle API errors gracefully', async () => {
generateText.mockRejectedValue(new Error('API Error'));
const response = await 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', async () => {
generateText.mockRejectedValue(new Error('Network error'));
const response = await 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 = aiCantEven();
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should create instance with provider and API key', () => {
const ai = aiCantEven({
provider: Provider.ANTHROPIC,
apiKey: 'test-key',
});
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should use ANTHROPIC_API_KEY environment variable', () => {
process.env.ANTHROPIC_API_KEY = 'env-anthropic-key';
const ai = aiCantEven();
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should use OPENAI_API_KEY environment variable', () => {
process.env.OPENAI_API_KEY = 'env-openai-key';
const ai = aiCantEven();
expect(ai).toBeInstanceOf(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 = aiCantEven();
expect(ai).toBeInstanceOf(AiCantEven);
});
it('should override environment variables with explicit config', () => {
process.env.ANTHROPIC_API_KEY = 'env-key';
const ai = aiCantEven({
provider: Provider.OPENAI,
apiKey: 'explicit-key',
});
expect(ai).toBeInstanceOf(AiCantEven);
});
});
describe('Type Exports', () => {
it('should export Confidence enum', () => {
expect(Confidence.OVERWHELMED).toBe('OVERWHELMED');
expect(Confidence.OVERTHINK).toBe('OVERTHINK');
expect(Confidence.SMUG).toBe('SMUG');
expect(Confidence.SNARKY).toBe('SNARKY');
});
it('should export Logic enum', () => {
expect(Logic.SIMPLE).toBe('SIMPLE');
expect(Logic.NONSEQUITUR).toBe('NONSEQUITUR');
expect(Logic.PSEUDOMATH).toBe('PSEUDOMATH');
expect(Logic.VISUAL).toBe('VISUAL');
});
it('should export Provider enum', () => {
expect(Provider.ANTHROPIC).toBe('anthropic');
expect(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 AiSdkClient(Provider.ANTHROPIC, 'test-key');
expect(createAnthropic).toHaveBeenCalledWith({
apiKey: 'test-key',
});
});
it('should initialize with OpenAI provider', () => {
new AiSdkClient(Provider.OPENAI, 'test-key');
expect(createOpenAI).toHaveBeenCalledWith({
apiKey: 'test-key',
});
});
it('should initialize with custom endpoint', () => {
new AiSdkClient(
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 AiSdkClient(
Provider.ANTHROPIC,
'test-key',
undefined,
'claude-3-opus'
);
expect(createAnthropic).toHaveBeenCalled();
});
it('should throw error for unsupported provider', () => {
expect(() => {
new AiSdkClient('unsupported' as Provider, 'test-key');
}).toThrow('Unsupported provider: unsupported');
});
});
describe('generateResponse', () => {
let client: AiSdkClient;
beforeEach(() => {
client = new AiSdkClient(Provider.ANTHROPIC, 'test-key');
});
it('should generate response successfully', async () => {
generateText.mockResolvedValue({ text: 'AI response' });
const params = {
confidence: Confidence.SMUG,
logic: Logic.SIMPLE,
operation: 'isEven',
value: 4,
};
const response = await 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', async () => {
generateText.mockResolvedValue({ text: 'Comparison response' });
const params = {
confidence: Confidence.OVERTHINK,
logic: Logic.VISUAL,
operation: 'isGreaterThan',
value: 10,
comparisonValue: 5,
};
await 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', async () => {
generateText.mockRejectedValue(new Error('API Error'));
const params = {
confidence: Confidence.SNARKY,
logic: Logic.PSEUDOMATH,
operation: 'isPrime',
value: 17,
};
const response = await 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', async () => {
generateText.mockResolvedValue({ text: 'Response' });
const params = {
confidence: Confidence.OVERWHELMED,
logic: Logic.NONSEQUITUR,
operation: 'isOdd',
value: 7,
};
await 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', async () => {
generateText.mockResolvedValue({ text: 'Unknown operation response' });
const params = {
confidence: Confidence.SMUG,
logic: Logic.SIMPLE,
operation: 'unknownOperation',
value: 42,
};
await 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', async () => {
generateText.mockResolvedValue({ text: ' Trimmed response ' });
const params = {
confidence: Confidence.SNARKY,
logic: Logic.VISUAL,
operation: 'isInteger',
value: 3.14,
};
const response = await client.generateResponse(params);
expect(response.text).toBe('Trimmed response');
});
});
});