ai-cant-even
Version:
A satirical AI-powered utility that's confidently wrong about basic math operations
404 lines (403 loc) • 19.1 kB
JavaScript
;
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');
}));
});
});