1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 | /** Scripted-sequence tests for the multi-request convenience flows. */
import assert from 'node:assert/strict';
import { test } from 'node:test';
import * as auth from '../src/auth.js';
import { isConfigError, isJobFailedError, isTimeoutError, isUploadFailedError } from '../src/errors.js';
import * as ipfs from '../src/ipfs.js';
import * as jobs from '../src/jobs.js';
import { bodyJson, captureClient, caught, type Call } from './helpers.js';
const B = 'https://api.ird.ro';
const trail = (calls: readonly Call[]) => calls.map((c) => `${c.method} ${c.url.slice(B.length)}`);
test('job builders produce each typed payload', () => {
assert.deepEqual(jobs.domainAvailabilityBulk(['a.ro', 'b.ro']), {
type: 'domain-availability-bulk',
domains: ['a.ro', 'b.ro'],
});
assert.deepEqual(jobs.embedding('text'), { type: 'embedding', input: 'text' });
assert.deepEqual(jobs.embedding('text', { model: 'BAAI/bge-m3' }), {
type: 'embedding',
input: 'text',
model: 'BAAI/bge-m3',
});
assert.deepEqual(jobs.llmChat([{ role: 'user', content: 'hi' }], { max_tokens: 64 }), {
type: 'llm-chat',
messages: [{ role: 'user', content: 'hi' }],
max_tokens: 64,
});
assert.deepEqual(jobs.llmRawCompletion('Once', { temperature: 0.7 }), {
type: 'llm-raw-completion',
prompt: 'Once',
temperature: 0.7,
});
assert.deepEqual(jobs.nlpAnalyze('text', { language: 'ro' }), { type: 'nlp-analyze', input: 'text', language: 'ro' });
assert.deepEqual(jobs.nlpDetectLanguage('salut'), { type: 'nlp-detect-language', input: 'salut' });
});
test('multimodal helpers build OpenAI-style structures', async (t) => {
assert.deepEqual(jobs.textPart('describe this'), { type: 'text', text: 'describe this' });
const part = await jobs.imagePart(new TextEncoder().encode('fakepng'), 'image/png');
assert.equal(part.type, 'image_url');
assert.match(part.image_url.url, /^data:image\/png;base64,/);
assert.equal(part.image_url.url, `data:image/png;base64,${btoa('fakepng')}`);
const msg = jobs.userMessage('what is here?', await jobs.imagePart(new Uint8Array([120]), 'image/webp'));
assert.equal(msg.role, 'user');
assert.equal(msg.content.length, 2);
assert.equal(msg.content[0]!.type, 'text');
await t.test('the MIME type is sniffed from a path, or must be given', async () => {
const e = await caught(() => jobs.imagePart(new Uint8Array([1])));
assert.ok(isConfigError(e));
assert.match(e.message, /Cannot guess image MIME type/);
});
});
test('jobs.run', async (t) => {
await t.test('enqueue → poll through processing → fetch result', async () => {
const { client, calls } = captureClient({ apiKey: 'ird_k' }, [
{ status: 202, body: { job: { id: 'j1', status: 'queued' } } },
{ status: 200, body: { job: { id: 'j1', status: 'processing' } } },
{ status: 200, body: { job: { id: 'j1', status: 'succeeded' } } },
{ status: 200, body: { result: { results: [{ domain: 'a.ro', status: 'available' }] } } },
]);
const result = await jobs.run(client, jobs.domainAvailabilityBulk(['a.ro']));
assert.equal((result.result as { results: { status: string }[] }).results[0]!.status, 'available');
assert.deepEqual(trail(calls), ['POST /jobs', 'GET /jobs/j1', 'GET /jobs/j1', 'GET /jobs/j1/result']);
});
await t.test('a failed job throws jobFailed with the job', async () => {
const { client } = captureClient({ apiKey: 'ird_k' }, [
{ status: 202, body: { job: { id: 'j2', status: 'queued' } } },
{ status: 200, body: { job: { id: 'j2', status: 'failed', error: 'boom' } } },
]);
const e = await caught(() => jobs.run(client, jobs.embedding('x')));
assert.ok(isJobFailedError(e));
assert.equal((e.job as { id: string }).id, 'j2');
});
await t.test('a passed deadline throws timeout with the last response', async () => {
const { client } = captureClient({ apiKey: 'ird_k' }, [
{ status: 202, body: { job: { id: 'j3', status: 'queued' } } },
{ status: 200, body: { job: { id: 'j3', status: 'processing' } } },
]);
const e = await caught(() => jobs.run(client, jobs.embedding('x'), { timeoutMs: -1 }));
assert.ok(isTimeoutError(e));
assert.equal((e.last as { job: { status: string } }).job.status, 'processing');
});
});
test('ipfs.addFile', async (t) => {
await t.test('grant → raw upload to the grant URL → poll until completed', async () => {
const { client, calls } = captureClient({ apiKey: 'ird_k' }, [
{
status: 201,
body: { upload: { id: 'up1', pin_id: 'p1', url: 'https://node1.ird.ro/up/abc', token: 'grant-tok', status: 'issued' } },
},
{ status: 200, body: { ok: true } },
{ status: 200, body: { upload: { id: 'up1', status: 'issued' } } },
{ status: 200, body: { upload: { id: 'up1', pin_id: 'p1', status: 'completed', cid: 'bafy123' } } },
]);
const upload = await ipfs.addFile(client, new TextEncoder().encode('hello ipfs'), { name: 'hello.txt' });
assert.equal(upload.cid, 'bafy123');
assert.equal(upload.pin_id, 'p1');
const [start, put, poll1, poll2] = calls;
assert.match(start!.url, /\/ipfs\/uploads$/);
assert.deepEqual(bodyJson(start!), { name: 'hello.txt' });
assert.equal(put!.url, 'https://node1.ird.ro/up/abc');
assert.equal(put!.headers.Authorization, 'Bearer grant-tok');
assert.equal(put!.headers['Content-Type'], 'application/octet-stream');
assert.match(poll1!.url, /\/ipfs\/uploads\/up1$/);
assert.match(poll2!.url, /\/ipfs\/uploads\/up1$/);
});
await t.test('a failed upload throws uploadFailed', async () => {
const { client } = captureClient({ apiKey: 'ird_k' }, [
{ status: 201, body: { upload: { id: 'up2', url: 'https://n/u', token: 't', status: 'issued' } } },
{ status: 200, body: { ok: true } },
{ status: 200, body: { upload: { id: 'up2', status: 'failed' } } },
]);
const e = await caught(() => ipfs.addFile(client, new Uint8Array([1])));
assert.ok(isUploadFailedError(e));
assert.equal((e.upload as { status: string }).status, 'failed');
});
});
test('auth.deviceLogin: start → prompt → poll until approved → complete', async () => {
const { client, calls } = captureClient(
{},
[
{
status: 200,
body: {
device_code: 'dc',
user_code: 'AB-12',
verification_uri: 'https://ird.ro/device',
verification_uri_complete: 'https://ird.ro/device?code=AB-12',
interval: 1,
expires_in: 600,
},
},
{ status: 400, body: { error: { code: 'authorization-pending', message: 'authorization pending' } } },
{ status: 429, body: { error: { code: 'slow-down', message: 'slow down' } } },
{ status: 200, body: { approved: true } },
{ status: 200, body: { access_token: 'jwt-token', refresh_token: 'r', user: { email: 'x@y.ro' } } },
],
);
let prompted: string | undefined;
const tokens = await auth.deviceLogin(client, {
onPrompt: (start) => {
prompted = start.user_code;
},
confirm: () => 'CONF-99',
});
assert.equal(tokens.access_token, 'jwt-token');
assert.equal(prompted, 'AB-12');
const complete = calls.at(-1)!;
assert.match(complete.url, /\/auth\/device\/complete$/);
assert.equal((bodyJson(complete) as { confirmation_code: string }).confirmation_code, 'CONF-99');
assert.equal(calls[0]!.headers.Authorization, undefined, 'the device flow is anonymous');
});
|