@@ -235,6 +235,69 @@ describe("jobsApiApp", () => {
235235 expect ( res . body . source ) . toBe ( "valkey_global_index" ) ;
236236 } ) ;
237237
238+ it ( "GET /jobs/search aplica filtros de level, location e type" , async ( ) => {
239+ mocks . cacheGetJobsByIds . mockResolvedValue ( [
240+ {
241+ id : "id-1" ,
242+ title : "Desenvolvedor Node.js Júnior" ,
243+ company : "ACME" ,
244+ location : "Brasil - Remoto" ,
245+ } ,
246+ {
247+ id : "id-2" ,
248+ title : "Desenvolvedor Node.js Sênior" ,
249+ company : "Globo" ,
250+ location : "Estados Unidos - Remoto" ,
251+ } ,
252+ {
253+ id : "id-3" ,
254+ title : "Desenvolvedor Node.js Pleno" ,
255+ company : "Globex" ,
256+ location : "Brasil - Hybrid Remote" ,
257+ } ,
258+ ] ) ;
259+
260+ const app = createJobsApiApp ( ) ;
261+ const res = await request ( app )
262+ . get ( "/jobs/search" )
263+ . query ( {
264+ keywords : "Node.js" ,
265+ level : "Júnior" ,
266+ location : "Brasil" ,
267+ type : "Remoto" ,
268+ } )
269+ . expect ( 200 ) ;
270+
271+ expect ( res . body . jobs ) . toEqual ( [ expect . objectContaining ( { id : "id-1" } ) ] ) ;
272+ expect ( res . body . total ) . toBe ( 1 ) ;
273+ } ) ;
274+
275+ it ( "GET /jobs/search diferencia modelo híbrido de remoto" , async ( ) => {
276+ mocks . cacheGetJobsByIds . mockResolvedValue ( [
277+ {
278+ id : "id-1" ,
279+ title : "Desenvolvedor Node.js" ,
280+ company : "ACME" ,
281+ location : "Brasil - Remote" ,
282+ } ,
283+ {
284+ id : "id-2" ,
285+ title : "Desenvolvedor Node.js" ,
286+ company : "Globex" ,
287+ location : "Brasil - Hybrid Remote" ,
288+ } ,
289+ ] ) ;
290+
291+ const app = createJobsApiApp ( ) ;
292+ const res = await request ( app )
293+ . get ( "/jobs/search" )
294+ . query ( { keywords : "Node.js" , type : "Híbrido" } )
295+ . expect ( 200 ) ;
296+
297+ expect ( res . body . jobs ) . toEqual ( [ expect . objectContaining ( { id : "id-2" } ) ] ) ;
298+ expect ( res . body . total ) . toBe ( 1 ) ;
299+ } ) ;
300+
238301 it ( "GET /jobs/search retorna paginação correta" , async ( ) => {
239302 mocks . parsePagination . mockReturnValue ( { page : 2 , limit : 10 } ) ;
240303 mocks . paginate . mockReturnValue ( {
@@ -288,6 +351,71 @@ describe("jobsApiApp", () => {
288351 expect ( res . body . message ) . toBe ( "Erro ao recuperar vagas em memória." ) ;
289352 } ) ;
290353
354+ it ( "GET /jobs/search trata filtros em array e aplica o primeiro valor" , async ( ) => {
355+ mocks . cacheGetJobsByIds . mockResolvedValue ( [
356+ {
357+ id : "id-array" ,
358+ title : "Desenvolvedor Node.js Júnior" ,
359+ company : "ACME" ,
360+ location : "Brasil - Remoto" ,
361+ } ,
362+ ] ) ;
363+
364+ const app = createJobsApiApp ( ) ;
365+ const res = await request ( app )
366+ . get ( "/jobs/search" )
367+ . query ( {
368+ keywords : "Node.js" ,
369+ level : [ "Júnior" , "Sênior" ] ,
370+ location : [ "Brasil" , "Portugal" ] ,
371+ type : [ "Remoto" , "Híbrido" ] ,
372+ } )
373+ . expect ( 200 ) ;
374+
375+ expect ( res . body . jobs ) . toEqual ( [ expect . objectContaining ( { id : "id-array" } ) ] ) ;
376+ expect ( res . body . total ) . toBe ( 1 ) ;
377+ } ) ;
378+
379+ it ( "GET /jobs/search identifica vaga sênior presencial" , async ( ) => {
380+ mocks . cacheGetJobsByIds . mockResolvedValue ( [
381+ {
382+ id : "id-senior" ,
383+ title : "Tech Lead Sênior" ,
384+ company : "ACME" ,
385+ location : "São Paulo - Onsite" ,
386+ } ,
387+ ] ) ;
388+
389+ const app = createJobsApiApp ( ) ;
390+ const res = await request ( app )
391+ . get ( "/jobs/search" )
392+ . query ( { keywords : "Tech Lead" , type : "Presencial" } )
393+ . expect ( 200 ) ;
394+
395+ expect ( res . body . jobs ) . toEqual ( [ expect . objectContaining ( { id : "id-senior" } ) ] ) ;
396+ expect ( res . body . total ) . toBe ( 1 ) ;
397+ } ) ;
398+
399+ it ( "GET /jobs/search usa o fallback de tipo presencial quando não há pistas de remoto" , async ( ) => {
400+ mocks . cacheGetJobsByIds . mockResolvedValue ( [
401+ {
402+ id : "id-fallback" ,
403+ title : "Backend Developer" ,
404+ company : "ACME" ,
405+ location : "Brasil" ,
406+ } ,
407+ ] ) ;
408+
409+ const app = createJobsApiApp ( ) ;
410+ const res = await request ( app )
411+ . get ( "/jobs/search" )
412+ . query ( { keywords : "Backend" , type : "Presencial" } )
413+ . expect ( 200 ) ;
414+
415+ expect ( res . body . jobs ) . toEqual ( [ expect . objectContaining ( { id : "id-fallback" } ) ] ) ;
416+ expect ( res . body . total ) . toBe ( 1 ) ;
417+ } ) ;
418+
291419 // ── keywords ──────────────────────────────────────────────────────────
292420
293421 it ( "GET /keywords retorna keywords do banco" , async ( ) => {
0 commit comments