From cb275759bb73f9d657fb07bafab8971d1395a6a9 Mon Sep 17 00:00:00 2001 From: 3byss Date: Fri, 20 Mar 2026 16:26:30 -0600 Subject: [PATCH] Fixed types and created tests --- lib/llm/grok.mjs | 6 ++-- test/llm-grok.test.mjs | 79 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 test/llm-grok.test.mjs diff --git a/lib/llm/grok.mjs b/lib/llm/grok.mjs index 8fc4267..8c8dc41 100644 --- a/lib/llm/grok.mjs +++ b/lib/llm/grok.mjs @@ -10,15 +10,15 @@ export class GrokProvider extends LLMProvider { this.model = config.model || 'grok-3'; } - getConfigured() { + get isConfigured() { return !!this.apiKey; } async complete(systemPrompt, userMessage, opts = {}) { const res = await fetch('https://api.x.ai/v1/chat/completions', { - method: POST, + method: 'POST', headers: { - 'Content-type': 'application/json', + 'Content-Type': 'application/json', Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ diff --git a/test/llm-grok.test.mjs b/test/llm-grok.test.mjs new file mode 100644 index 0000000..59a648a --- /dev/null +++ b/test/llm-grok.test.mjs @@ -0,0 +1,79 @@ +// Grok provider — unit tests +// Uses Node.js built-in test runner (node:test) — no extra dependencies + +import { describe, it, mock, beforeEach } from 'node:test'; +import assert from 'node:assert/strict'; +import { GrokProvider } from '../lib/llm/grok.mjs'; +import { createLLMProvider } from '../lib/llm/index.mjs'; + +// ─── Unit Tests ─── + +describe('GrokProvider', () => { + it('should set defaults correctly', () => { + const provider = new GrokProvider({ apiKey: 'sk-test' }); + assert.equal(provider.name, 'grok'); + assert.equal(provider.model, 'grok-3'); + assert.equal(provider.isConfigured, true); + }); + + it('should accept custom model', () => { + const provider = new GrokProvider({ apiKey: 'sk-test', model: 'grok-2' }); + assert.equal(provider.model, 'grok-2'); + }); + + it('should report not configured without API key', () => { + const provider = new GrokProvider({}); + assert.equal(provider.isConfigured, false); + }); + + it('should throw on API error', async () => { + const provider = new GrokProvider({ apiKey: 'sk-test' }); + const originalFetch = globalThis.fetch; + globalThis.fetch = mock.fn(() => + Promise.resolve({ ok: false, status: 401, text: () => Promise.resolve('Unauthorized') }) + ); + try { + await assert.rejects( + () => provider.complete('system', 'user'), + (err) => { + assert.match(err.message, /Grok API 401/); + return true; + } + ); + } finally { + globalThis.fetch = originalFetch; + } + }); + + it('should parse successful response', async () => { + const provider = new GrokProvider({ apiKey: 'sk-test' }); + const mockResponse = { + choices: [{ message: { content: 'Hello world' } }], + usage: { prompt_tokens: 10, completion_tokens: 5 }, + model: 'grok-3' + }; + const originalFetch = globalThis.fetch; + globalThis.fetch = mock.fn(() => + Promise.resolve({ ok: true, json: () => Promise.resolve(mockResponse) }) + ); + try { + const result = await provider.complete('system', 'user'); + assert.equal(result.text, 'Hello world'); + assert.equal(result.usage.inputTokens, 10); + assert.equal(result.usage.outputTokens, 5); + assert.equal(result.model, 'grok-3'); + } finally { + globalThis.fetch = originalFetch; + } + }); +}); + +// ─── Factory Tests ─── + +describe('createLLMProvider', () => { + it('should create Grok provider', () => { + const provider = createLLMProvider({ provider: 'grok', apiKey: 'sk-test' }); + assert.ok(provider instanceof GrokProvider); + assert.equal(provider.isConfigured, true); + }); +}); \ No newline at end of file