@@ -232,3 +232,275 @@ describe('ibr tool context7 (T-0005)', () => {
232232 expect ( result . stdout + result . stderr ) . toMatch ( / q u e s t i o n | r e q u i r e d p a r a m / i) ;
233233 } , 10000 ) ;
234234} ) ;
235+
236+ // ─── ebay (T-0016) ───────────────────────────────────────────────────────────
237+
238+ describe ( 'ibr tool ebay (T-0016)' , ( ) => {
239+ let ai , web ;
240+
241+ beforeAll ( async ( ) => {
242+ web = await startStaticServer ( ) ;
243+ ai = await startFromCassette ( 'tool-ebay' , { SERVER_URL : web . baseUrl } ) ;
244+ } , 15000 ) ;
245+
246+ afterAll ( async ( ) => {
247+ await ai . close ( ) ;
248+ await web . close ( ) ;
249+ } ) ;
250+
251+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
252+ const result = await runIbr (
253+ [ 'tool' , 'ebay' , '--param' , 'query=iphone 15 pro' ] ,
254+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
255+ ) ;
256+ expect ( result . code ) . toBe ( 0 ) ;
257+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
258+ } , 30000 ) ;
259+
260+ it ( 'query is required — missing → non-zero exit before browser' , async ( ) => {
261+ const result = await runIbr (
262+ [ 'tool' , 'ebay' ] ,
263+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
264+ ) ;
265+ expect ( result . code ) . not . toBe ( 0 ) ;
266+ expect ( result . stdout + result . stderr ) . toMatch ( / q u e r y | r e q u i r e d p a r a m / i) ;
267+ } , 10000 ) ;
268+ } ) ;
269+
270+ // ─── npm (T-0017) ────────────────────────────────────────────────────────────
271+
272+ describe ( 'ibr tool npm (T-0017)' , ( ) => {
273+ let ai , web ;
274+
275+ beforeAll ( async ( ) => {
276+ web = await startStaticServer ( ) ;
277+ ai = await startFromCassette ( 'tool-npm' , { SERVER_URL : web . baseUrl } ) ;
278+ } , 15000 ) ;
279+
280+ afterAll ( async ( ) => {
281+ await ai . close ( ) ;
282+ await web . close ( ) ;
283+ } ) ;
284+
285+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
286+ const result = await runIbr (
287+ [ 'tool' , 'npm' , '--param' , 'package=react' ] ,
288+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
289+ ) ;
290+ expect ( result . code ) . toBe ( 0 ) ;
291+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
292+ } , 30000 ) ;
293+
294+ it ( 'package is required — missing → non-zero exit before browser' , async ( ) => {
295+ const result = await runIbr (
296+ [ 'tool' , 'npm' ] ,
297+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
298+ ) ;
299+ expect ( result . code ) . not . toBe ( 0 ) ;
300+ expect ( result . stdout + result . stderr ) . toMatch ( / p a c k a g e | r e q u i r e d p a r a m / i) ;
301+ } , 10000 ) ;
302+ } ) ;
303+
304+ // ─── reddit (T-0020) ─────────────────────────────────────────────────────────
305+
306+ describe ( 'ibr tool reddit (T-0020)' , ( ) => {
307+ let ai , web ;
308+
309+ beforeAll ( async ( ) => {
310+ web = await startStaticServer ( ) ;
311+ ai = await startFromCassette ( 'tool-reddit' , { SERVER_URL : web . baseUrl } ) ;
312+ } , 15000 ) ;
313+
314+ afterAll ( async ( ) => {
315+ await ai . close ( ) ;
316+ await web . close ( ) ;
317+ } ) ;
318+
319+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
320+ const result = await runIbr (
321+ [ 'tool' , 'reddit' , '--param' , 'query=rust async' ] ,
322+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
323+ ) ;
324+ expect ( result . code ) . toBe ( 0 ) ;
325+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
326+ } , 30000 ) ;
327+
328+ it ( 'query is required — missing → non-zero exit before browser' , async ( ) => {
329+ const result = await runIbr (
330+ [ 'tool' , 'reddit' ] ,
331+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
332+ ) ;
333+ expect ( result . code ) . not . toBe ( 0 ) ;
334+ expect ( result . stdout + result . stderr ) . toMatch ( / q u e r y | r e q u i r e d p a r a m / i) ;
335+ } , 10000 ) ;
336+ } ) ;
337+
338+ // ─── hackernews (T-0021) ─────────────────────────────────────────────────────
339+
340+ describe ( 'ibr tool hackernews (T-0021)' , ( ) => {
341+ let ai , web ;
342+
343+ beforeAll ( async ( ) => {
344+ web = await startStaticServer ( ) ;
345+ ai = await startFromCassette ( 'tool-hackernews' , { SERVER_URL : web . baseUrl } ) ;
346+ } , 15000 ) ;
347+
348+ afterAll ( async ( ) => {
349+ await ai . close ( ) ;
350+ await web . close ( ) ;
351+ } ) ;
352+
353+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
354+ const result = await runIbr (
355+ [ 'tool' , 'hackernews' , '--param' , 'query=rust async' ] ,
356+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
357+ ) ;
358+ expect ( result . code ) . toBe ( 0 ) ;
359+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
360+ } , 30000 ) ;
361+
362+ it ( 'query is required — missing → non-zero exit before browser' , async ( ) => {
363+ const result = await runIbr (
364+ [ 'tool' , 'hackernews' ] ,
365+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
366+ ) ;
367+ expect ( result . code ) . not . toBe ( 0 ) ;
368+ expect ( result . stdout + result . stderr ) . toMatch ( / q u e r y | r e q u i r e d p a r a m / i) ;
369+ } , 10000 ) ;
370+ } ) ;
371+
372+ // ─── yahoo-finance (T-0022) ───────────────────────────────────────────────────
373+
374+ describe ( 'ibr tool yahoo-finance (T-0022)' , ( ) => {
375+ let ai , web ;
376+
377+ beforeAll ( async ( ) => {
378+ web = await startStaticServer ( ) ;
379+ ai = await startFromCassette ( 'tool-yahoo-finance' , { SERVER_URL : web . baseUrl } ) ;
380+ } , 15000 ) ;
381+
382+ afterAll ( async ( ) => {
383+ await ai . close ( ) ;
384+ await web . close ( ) ;
385+ } ) ;
386+
387+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
388+ const result = await runIbr (
389+ [ 'tool' , 'yahoo-finance' , '--param' , 'ticker=AAPL' ] ,
390+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
391+ ) ;
392+ expect ( result . code ) . toBe ( 0 ) ;
393+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
394+ } , 30000 ) ;
395+
396+ it ( 'ticker is required — missing → non-zero exit before browser' , async ( ) => {
397+ const result = await runIbr (
398+ [ 'tool' , 'yahoo-finance' ] ,
399+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
400+ ) ;
401+ expect ( result . code ) . not . toBe ( 0 ) ;
402+ expect ( result . stdout + result . stderr ) . toMatch ( / t i c k e r | r e q u i r e d p a r a m / i) ;
403+ } , 10000 ) ;
404+ } ) ;
405+
406+ // ─── dockerhub (T-0023) ──────────────────────────────────────────────────────
407+
408+ describe ( 'ibr tool dockerhub (T-0023)' , ( ) => {
409+ let ai , web ;
410+
411+ beforeAll ( async ( ) => {
412+ web = await startStaticServer ( ) ;
413+ ai = await startFromCassette ( 'tool-dockerhub' , { SERVER_URL : web . baseUrl } ) ;
414+ } , 15000 ) ;
415+
416+ afterAll ( async ( ) => {
417+ await ai . close ( ) ;
418+ await web . close ( ) ;
419+ } ) ;
420+
421+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
422+ const result = await runIbr (
423+ [ 'tool' , 'dockerhub' , '--param' , 'image=nginx' ] ,
424+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
425+ ) ;
426+ expect ( result . code ) . toBe ( 0 ) ;
427+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
428+ } , 30000 ) ;
429+
430+ it ( 'image is required — missing → non-zero exit before browser' , async ( ) => {
431+ const result = await runIbr (
432+ [ 'tool' , 'dockerhub' ] ,
433+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
434+ ) ;
435+ expect ( result . code ) . not . toBe ( 0 ) ;
436+ expect ( result . stdout + result . stderr ) . toMatch ( / i m a g e | r e q u i r e d p a r a m / i) ;
437+ } , 10000 ) ;
438+ } ) ;
439+
440+ // ─── wikipedia (T-0014) ───────────────────────────────────────────────────────
441+
442+ describe ( 'ibr tool wikipedia (T-0014)' , ( ) => {
443+ let ai , web ;
444+
445+ beforeAll ( async ( ) => {
446+ web = await startStaticServer ( ) ;
447+ ai = await startFromCassette ( 'tool-wikipedia' , { SERVER_URL : web . baseUrl } ) ;
448+ } , 15000 ) ;
449+
450+ afterAll ( async ( ) => {
451+ await ai . close ( ) ;
452+ await web . close ( ) ;
453+ } ) ;
454+
455+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
456+ const result = await runIbr (
457+ [ 'tool' , 'wikipedia' , '--param' , 'topic=Playwright' ] ,
458+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
459+ ) ;
460+ expect ( result . code ) . toBe ( 0 ) ;
461+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
462+ } , 30000 ) ;
463+
464+ it ( 'topic is required — missing → non-zero exit before browser' , async ( ) => {
465+ const result = await runIbr (
466+ [ 'tool' , 'wikipedia' ] ,
467+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
468+ ) ;
469+ expect ( result . code ) . not . toBe ( 0 ) ;
470+ expect ( result . stdout + result . stderr ) . toMatch ( / t o p i c | r e q u i r e d p a r a m / i) ;
471+ } , 10000 ) ;
472+ } ) ;
473+
474+ // ─── amazon (T-0015) ─────────────────────────────────────────────────────────
475+
476+ describe ( 'ibr tool amazon (T-0015)' , ( ) => {
477+ let ai , web ;
478+
479+ beforeAll ( async ( ) => {
480+ web = await startStaticServer ( ) ;
481+ ai = await startFromCassette ( 'tool-amazon' , { SERVER_URL : web . baseUrl } ) ;
482+ } , 15000 ) ;
483+
484+ afterAll ( async ( ) => {
485+ await ai . close ( ) ;
486+ await web . close ( ) ;
487+ } ) ;
488+
489+ it ( 'exits 0 and completes extraction without error' , async ( ) => {
490+ const result = await runIbr (
491+ [ 'tool' , 'amazon' , '--param' , 'query=noise cancelling headphones' ] ,
492+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
493+ ) ;
494+ expect ( result . code ) . toBe ( 0 ) ;
495+ expect ( result . stdout + result . stderr ) . toMatch ( / T a s k e x e c u t i o n c o m p l e t e d / i) ;
496+ } , 30000 ) ;
497+
498+ it ( 'query is required — missing → non-zero exit before browser' , async ( ) => {
499+ const result = await runIbr (
500+ [ 'tool' , 'amazon' ] ,
501+ { ...BASE_ENV , OPENAI_BASE_URL : ai . baseUrl } ,
502+ ) ;
503+ expect ( result . code ) . not . toBe ( 0 ) ;
504+ expect ( result . stdout + result . stderr ) . toMatch ( / q u e r y | r e q u i r e d p a r a m / i) ;
505+ } , 10000 ) ;
506+ } ) ;
0 commit comments