@@ -362,5 +362,192 @@ describe('Security Solution - Health Diagnostic Queries - CircuitBreakingQueryEx
362362 done
363363 ) ;
364364 } ) ;
365+
366+ test ( 'should handle ILM API errors and assume serverless' , ( done ) => {
367+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' , 'warm' ] } ) ;
368+ const circuitBreaker = createMockCircuitBreaker ( true ) ;
369+
370+ const ilmError = new Error (
371+ 'no handler found for uri [/.alerts-security.alerts*/_ilm/explain?only_managed=false&filter_path=indices.*.phase] and method [GET]'
372+ ) ;
373+ mockEsClient . ilm . explainLifecycle . mockRejectedValue ( ilmError ) ;
374+ setupPointInTime ( mockEsClient ) ;
375+ mockEsClient . search . mockResolvedValue ( createMockSearchResponse ( [ ] ) ) ;
376+
377+ executeObservableTest (
378+ queryExecutor . search ( { query, circuitBreakers : [ circuitBreaker ] } ) ,
379+ ( ) => {
380+ expect ( mockEsClient . ilm . explainLifecycle ) . toHaveBeenCalledWith ( {
381+ index : 'test-index' ,
382+ only_managed : false ,
383+ filter_path : [ 'indices.*.phase' ] ,
384+ } ) ;
385+ expect ( mockEsClient . openPointInTime ) . toHaveBeenCalledWith ( {
386+ index : [ 'test-index' ] ,
387+ keep_alive : '1m' ,
388+ } ) ;
389+ done ( ) ;
390+ } ,
391+ done
392+ ) ;
393+ } ) ;
394+
395+ test ( 'should handle network errors during ILM checks' , ( done ) => {
396+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
397+ const circuitBreaker = createMockCircuitBreaker ( true ) ;
398+
399+ const networkError = new Error ( 'ECONNREFUSED' ) ;
400+ mockEsClient . ilm . explainLifecycle . mockRejectedValue ( networkError ) ;
401+ setupPointInTime ( mockEsClient ) ;
402+ mockEsClient . search . mockResolvedValue ( createMockSearchResponse ( [ ] ) ) ;
403+
404+ executeObservableTest (
405+ queryExecutor . search ( { query, circuitBreakers : [ circuitBreaker ] } ) ,
406+ ( ) => {
407+ expect ( mockEsClient . openPointInTime ) . toHaveBeenCalledWith ( {
408+ index : [ 'test-index' ] ,
409+ keep_alive : '1m' ,
410+ } ) ;
411+ done ( ) ;
412+ } ,
413+ done
414+ ) ;
415+ } ) ;
416+
417+ test ( 'should handle malformed ILM responses' , ( done ) => {
418+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
419+ const circuitBreaker = createMockCircuitBreaker ( true ) ;
420+
421+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( { } ) ;
422+ setupPointInTime ( mockEsClient ) ;
423+ mockEsClient . search . mockResolvedValue ( createMockSearchResponse ( [ ] ) ) ;
424+
425+ executeObservableTest (
426+ queryExecutor . search ( { query, circuitBreakers : [ circuitBreaker ] } ) ,
427+ ( ) => {
428+ expect ( mockEsClient . openPointInTime ) . toHaveBeenCalledWith ( {
429+ index : [ 'test-index' ] ,
430+ keep_alive : '1m' ,
431+ } ) ;
432+ done ( ) ;
433+ } ,
434+ done
435+ ) ;
436+ } ) ;
437+ } ) ;
438+
439+ describe ( 'indicesFor method' , ( ) => {
440+ test ( 'should return original index when no tiers are specified' , async ( ) => {
441+ const query = createMockQuery ( QueryType . DSL ) ;
442+ const result = await queryExecutor . indicesFor ( query ) ;
443+ expect ( result ) . toEqual ( [ 'test-index' ] ) ;
444+ expect ( mockEsClient . ilm . explainLifecycle ) . not . toHaveBeenCalled ( ) ;
445+ } ) ;
446+
447+ test ( 'should filter indices by tiers when ILM is available' , async ( ) => {
448+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' , 'warm' ] } ) ;
449+
450+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( {
451+ indices : {
452+ 'test-index-000001' : { phase : 'hot' } ,
453+ 'test-index-000002' : { phase : 'warm' } ,
454+ 'test-index-000003' : { phase : 'cold' } ,
455+ 'test-index-000004' : { phase : 'hot' } ,
456+ } ,
457+ } ) ;
458+
459+ const result = await queryExecutor . indicesFor ( query ) ;
460+ expect ( result ) . toEqual ( [ 'test-index-000001' , 'test-index-000002' , 'test-index-000004' ] ) ;
461+ expect ( mockEsClient . ilm . explainLifecycle ) . toHaveBeenCalledWith ( {
462+ index : 'test-index' ,
463+ only_managed : false ,
464+ filter_path : [ 'indices.*.phase' ] ,
465+ } ) ;
466+ } ) ;
467+
468+ test ( 'should handle serverless environment (undefined indices)' , async ( ) => {
469+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
470+
471+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( { indices : undefined } ) ;
472+
473+ const result = await queryExecutor . indicesFor ( query ) ;
474+ expect ( result ) . toEqual ( [ 'test-index' ] ) ;
475+ } ) ;
476+
477+ test ( 'should handle empty ILM response' , async ( ) => {
478+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
479+
480+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( { } ) ;
481+
482+ const result = await queryExecutor . indicesFor ( query ) ;
483+ expect ( result ) . toEqual ( [ 'test-index' ] ) ;
484+ } ) ;
485+
486+ test ( 'should handle indices without phase information' , async ( ) => {
487+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
488+
489+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( {
490+ indices : {
491+ 'test-index-000001' : { phase : 'hot' } ,
492+ 'test-index-000002' : { } ,
493+ 'test-index-000003' : { other_field : 'value' } ,
494+ } ,
495+ } ) ;
496+
497+ const result = await queryExecutor . indicesFor ( query ) ;
498+ expect ( result ) . toEqual ( [ 'test-index-000001' ] ) ;
499+ } ) ;
500+
501+ test ( 'should filter out indices not in specified tiers' , async ( ) => {
502+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
503+
504+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( {
505+ indices : {
506+ 'test-index-000001' : { phase : 'hot' } ,
507+ 'test-index-000002' : { phase : 'warm' } ,
508+ 'test-index-000003' : { phase : 'cold' } ,
509+ } ,
510+ } ) ;
511+
512+ const result = await queryExecutor . indicesFor ( query ) ;
513+ expect ( result ) . toEqual ( [ 'test-index-000001' ] ) ;
514+ } ) ;
515+
516+ test ( 'should handle ILM API errors by falling back to original index' , async ( ) => {
517+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
518+
519+ const serverlessError = new Error (
520+ 'no handler found for uri [/.alerts-security.alerts*/_ilm/explain?only_managed=false&filter_path=indices.*.phase] and method [GET]'
521+ ) ;
522+ mockEsClient . ilm . explainLifecycle . mockRejectedValue ( serverlessError ) ;
523+
524+ const result = await queryExecutor . indicesFor ( query ) ;
525+ expect ( result ) . toEqual ( [ 'test-index' ] ) ;
526+ } ) ;
527+
528+ test ( 'should handle authorization errors gracefully' , async ( ) => {
529+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'hot' ] } ) ;
530+
531+ const authError = new Error ( 'security_exception' ) ;
532+ mockEsClient . ilm . explainLifecycle . mockRejectedValue ( authError ) ;
533+
534+ const result = await queryExecutor . indicesFor ( query ) ;
535+ expect ( result ) . toEqual ( [ 'test-index' ] ) ;
536+ } ) ;
537+
538+ test ( 'should return empty array when no indices match tiers' , async ( ) => {
539+ const query = createMockQuery ( QueryType . DSL , { tiers : [ 'frozen' ] } ) ;
540+
541+ mockEsClient . ilm . explainLifecycle . mockResolvedValue ( {
542+ indices : {
543+ 'test-index-000001' : { phase : 'hot' } ,
544+ 'test-index-000002' : { phase : 'warm' } ,
545+ 'test-index-000003' : { phase : 'cold' } ,
546+ } ,
547+ } ) ;
548+
549+ const result = await queryExecutor . indicesFor ( query ) ;
550+ expect ( result ) . toEqual ( [ ] ) ;
551+ } ) ;
365552 } ) ;
366553} ) ;
0 commit comments