import { test } from 'node:test'; import assert from 'node:assert/strict'; import { dusunmeAyari, zaiChat, gecici429, zaiAktifIstek } from '../server/zai.mjs'; import { analyzeSpec, routeFor } from '../server/ai.mjs'; import { tedCevir, harfOnar } from '../server/ted-ceviri.mjs'; const ZAI_URL = 'https://api.z.ai/api/coding/paas/v4/chat/completions'; const ok = (content) => new Response(JSON.stringify({ choices: [{ message: { content } }] }), { status: 200 }); const hata429 = (code) => new Response(JSON.stringify({ error: { code, message: 'x' } }), { status: 429 }); function ortam(vars) { const eski = {}; for (const [k, v] of Object.entries(vars)) { eski[k] = process.env[k]; if (v === undefined) delete process.env[k]; else process.env[k] = v; } return () => { for (const [k, v] of Object.entries(eski)) { if (v === undefined) delete process.env[k]; else process.env[k] = v; } }; } const TEMEL = { ZAI_API_KEY: 'synthetic-test-only', ZAI_BASE_URL: ZAI_URL, ZAI_RETRY_BASE_MS: '0', ZAI_MAX_CONCURRENCY: '2', OPENROUTER_API_KEY: undefined, GEMINI_API_KEY: undefined }; test('düşünme ayarı modele göre: glm-5 reasoning_effort low, glm-4 thinking kapalı', () => { const geri = ortam({ ZAI_REASONING_EFFORT: undefined }); try { assert.deepEqual(dusunmeAyari('glm-5.3'), { reasoning_effort: 'low' }); assert.deepEqual(dusunmeAyari('glm-5.3-flash'), { reasoning_effort: 'low' }); assert.deepEqual(dusunmeAyari('glm-5.3', null), {}); assert.deepEqual(dusunmeAyari('glm-4.7-flash'), { thinking: { type: 'disabled' } }); } finally { geri(); } }); test('geçici 429 aynı modelle yeniden denenir, bakiye hatası (1113) hemen fırlatılır', async () => { const geri = ortam(TEMEL); const orijinal = globalThis.fetch; try { const govdeler = []; globalThis.fetch = async (url, { body }) => { govdeler.push(JSON.parse(body)); return govdeler.length === 1 ? hata429('1302') : ok('{"a":1}'); }; const sonuc = await zaiChat('glm-5.3-flash', [{ role: 'user', content: 'x' }], { retries429: 2 }); assert.equal(sonuc, '{"a":1}'); assert.equal(govdeler.length, 2); assert.equal(govdeler[1].model, 'glm-5.3-flash'); assert.equal(govdeler[1].reasoning_effort, 'low'); assert.deepEqual(govdeler[1].response_format, { type: 'json_object' }); let cagri = 0; globalThis.fetch = async () => { cagri++; return hata429('1113'); }; await assert.rejects(zaiChat('glm-5.3', [{ role: 'user', content: 'x' }], { retries429: 3 }), /1113/); assert.equal(cagri, 1); assert.equal(gecici429('{"error":{"code":"1302"}}'), true); } finally { globalThis.fetch = orijinal; geri(); } }); test('eşzamanlı Z.ai isteği sınırı süreç genelinde 2 ile sınırlı', async () => { const geri = ortam(TEMEL); const orijinal = globalThis.fetch; try { let acik = 0, tepe = 0; globalThis.fetch = async () => { acik++; tepe = Math.max(tepe, acik); await new Promise((r) => setTimeout(r, 20)); acik--; return ok('{}'); }; await Promise.all([1, 2, 3, 4].map(() => zaiChat('glm-5.3-flash', [{ role: 'user', content: 'x' }]))); assert.equal(tepe, 2); assert.equal(zaiAktifIstek(), 0); } finally { globalThis.fetch = orijinal; geri(); } }); test('analyzeSpec Z.ai anahtarı varken GLM 5.3\'ü doğrudan Z.ai\'den çağırır', async () => { const geri = ortam(TEMEL); const orijinal = globalThis.fetch; try { assert.deepEqual(routeFor('zai'), { yol: 'direct', key: 'synthetic-test-only', model: 'glm-5.3' }); const istekler = []; globalThis.fetch = async (url, { body }) => { istekler.push({ url, body: JSON.parse(body) }); return ok(JSON.stringify({ summary: 'özet', qualificationCriteria: [], criticalRequirements: [], risksAndPenalties: [], bidAnalysis: {} })); }; const out = await analyzeSpec({ text: 'Sentetik şartname metni', chain: ['zai'] }); assert.equal(istekler.length, 1); assert.equal(istekler[0].url, ZAI_URL); assert.equal(istekler[0].body.model, 'glm-5.3'); assert.equal(istekler[0].body.reasoning_effort, 'low'); assert.equal(out.model.provider, 'Z.ai'); assert.equal(out.model.yol, 'direct'); assert.equal(out.result.summary, 'özet'); } finally { globalThis.fetch = orijinal; geri(); } }); test('TED çevirisi OpenRouter anahtarı olmadan önce Z.ai GLM 5.3 ile çalışır', async () => { const geri = ortam(TEMEL); const orijinal = globalThis.fetch; try { const istekler = []; globalThis.fetch = async (url, { body }) => { const b = JSON.parse(body); istekler.push({ url, model: b.model }); const ilanlar = JSON.parse(b.messages[1].content).ilanlar; return ok(JSON.stringify({ ceviriler: ilanlar.map((i) => ({ id: i.id, baslik: `TR ${i.baslik}`, aciklama: '' })) })); }; const tenders = [{ id: 't1', baslik: 'Austria – Works' }, { id: 't2', baslik: 'Poland – Supplies' }]; const bekleyen = await tedCevir(tenders, { blockMs: 2000 }); assert.equal(bekleyen, 0); assert.equal(istekler.length, 1); assert.equal(istekler[0].url, ZAI_URL); assert.equal(istekler[0].model, 'glm-5.3'); assert.equal(tenders[0].baslikTr, 'TR Austria – Works'); assert.equal(tenders[1].ceviriKaynagi, 'model'); } finally { globalThis.fetch = orijinal; geri(); } }); test('harfOnar kelime içi büyük İ/I bozulmasını düzeltir, kısaltmalara dokunmaz', () => { assert.equal(harfOnar('Romanya – Elektrİklİ otobüsler – Elektrİklİ otobüs ve şarj İstasyonlarİ temİNİ'), 'Romanya – Elektrikli otobüsler – Elektrikli otobüs ve şarj İstasyonlari temini'); assert.equal(harfOnar('Denİz Habİtatlarının ve NATURA 2000 Alanlarının Tespİtİ'), 'Deniz Habitatlarının ve NATURA 2000 Alanlarının Tespiti'); assert.equal(harfOnar('İstanbul MEB İl Müdürlüğü'), 'İstanbul MEB İl Müdürlüğü'); assert.equal(harfOnar(null), ''); });