@@ -539,4 +539,124 @@ void shouldCreateTimestampGtLtFiltersWithDateTimeObjects() {
539539 Filter ltInstant = Filter .timestamp ("last_updated" ).lt (instant );
540540 assertThat (ltInstant .build ()).isEqualTo ("@last_updated:[-inf (1742147139]" );
541541 }
542+
543+ // ========== Tag wildcard (tagLike) filter tests ==========
544+
545+ @ Test
546+ @ DisplayName ("Should create tag wildcard filter with prefix pattern" )
547+ void shouldCreateTagWildcardFilterWithPrefixPattern () {
548+ // When - basic prefix wildcard
549+ Filter filter = Filter .tagLike ("category" , "tech*" );
550+
551+ // Then - asterisk should NOT be escaped
552+ assertThat (filter .build ()).isEqualTo ("@category:{tech*}" );
553+ }
554+
555+ @ Test
556+ @ DisplayName ("Should create tag wildcard filter with suffix pattern" )
557+ void shouldCreateTagWildcardFilterWithSuffixPattern () {
558+ // When - suffix wildcard
559+ Filter filter = Filter .tagLike ("category" , "*tech" );
560+
561+ // Then
562+ assertThat (filter .build ()).isEqualTo ("@category:{*tech}" );
563+ }
564+
565+ @ Test
566+ @ DisplayName ("Should create tag wildcard filter with contains pattern" )
567+ void shouldCreateTagWildcardFilterWithContainsPattern () {
568+ // When - contains wildcard (asterisk on both sides)
569+ Filter filter = Filter .tagLike ("category" , "*tech*" );
570+
571+ // Then
572+ assertThat (filter .build ()).isEqualTo ("@category:{*tech*}" );
573+ }
574+
575+ @ Test
576+ @ DisplayName ("Should create tag wildcard filter with multiple patterns" )
577+ void shouldCreateTagWildcardFilterWithMultiplePatterns () {
578+ // When - multiple patterns
579+ Filter filter = Filter .tagLike ("category" , "tech*" , "*soft" );
580+
581+ // Then
582+ assertThat (filter .build ()).isEqualTo ("@category:{tech*|*soft}" );
583+ }
584+
585+ @ Test
586+ @ DisplayName ("Should escape special characters but preserve wildcards in tagLike" )
587+ void shouldEscapeSpecialCharsButPreserveWildcards () {
588+ // When - pattern with special char (hyphen) AND wildcard
589+ Filter filter = Filter .tagLike ("category" , "tech*-pro" );
590+
591+ // Then - hyphen should be escaped, asterisk should NOT be escaped
592+ assertThat (filter .build ()).isEqualTo ("@category:{tech*\\ -pro}" );
593+
594+ // When - pattern with space and wildcard
595+ Filter filterWithSpace = Filter .tagLike ("category" , "hello w*" );
596+
597+ // Then - space should be escaped, asterisk should NOT be escaped
598+ assertThat (filterWithSpace .build ()).isEqualTo ("@category:{hello\\ w*}" );
599+
600+ // When - pattern with special character ($) and wildcard
601+ Filter filterWithDollar = Filter .tagLike ("category" , "cat$*" );
602+
603+ // Then - $ should be escaped, asterisk should NOT be escaped
604+ assertThat (filterWithDollar .build ()).isEqualTo ("@category:{cat\\ $*}" );
605+ }
606+
607+ @ Test
608+ @ DisplayName ("Should return wildcard for empty tagLike patterns" )
609+ void shouldReturnWildcardForEmptyTagLikePatterns () {
610+ // Test empty string array
611+ Filter emptyFilter = Filter .tagLike ("category" );
612+ assertThat (emptyFilter .build ()).isEqualTo ("*" );
613+
614+ // Test null array
615+ Filter nullFilter = Filter .tagLike ("category" , (String []) null );
616+ assertThat (nullFilter .build ()).isEqualTo ("*" );
617+ }
618+
619+ @ Test
620+ @ DisplayName ("Tag filter should escape asterisk while tagLike preserves it" )
621+ void tagFilterShouldEscapeAsteriskWhileTagLikePreservesIt () {
622+ // When - using regular tag filter with asterisk
623+ Filter tagFilter = Filter .tag ("category" , "tech*" );
624+
625+ // Then - asterisk SHOULD be escaped in regular tag
626+ assertThat (tagFilter .build ()).isEqualTo ("@category:{tech\\ *}" );
627+
628+ // When - using tagLike filter with asterisk
629+ Filter tagLikeFilter = Filter .tagLike ("category" , "tech*" );
630+
631+ // Then - asterisk should NOT be escaped in tagLike
632+ assertThat (tagLikeFilter .build ()).isEqualTo ("@category:{tech*}" );
633+ }
634+
635+ @ Test
636+ @ DisplayName ("Should combine tagLike with exact tag filters" )
637+ void shouldCombineTagLikeWithExactTagFilters () {
638+ // Create filters with different operators
639+ Filter exactMatch = Filter .tag ("brand" , "nike" );
640+ Filter wildcardMatch = Filter .tagLike ("category" , "tech*" );
641+
642+ // Verify individual filters work correctly
643+ assertThat (exactMatch .build ()).isEqualTo ("@brand:{nike}" );
644+ assertThat (wildcardMatch .build ()).isEqualTo ("@category:{tech*}" );
645+
646+ // Combine with AND - wildcard should be preserved, exact match should not have unescaped *
647+ Filter combinedAnd = Filter .and (exactMatch , wildcardMatch );
648+ assertThat (combinedAnd .build ()).isEqualTo ("(@brand:{nike} @category:{tech*})" );
649+
650+ // Combine with OR
651+ Filter combinedOr = Filter .or (exactMatch , wildcardMatch );
652+ assertThat (combinedOr .build ()).isEqualTo ("(@brand:{nike} | @category:{tech*})" );
653+
654+ // Mix of exact, wildcard, and exact with * in value
655+ Filter exactWithAsterisk = Filter .tag ("status" , "active*" ); // * should be escaped
656+ Filter complexFilter = Filter .and (exactMatch , wildcardMatch , exactWithAsterisk );
657+ String result = complexFilter .build ();
658+ assertThat (result ).contains ("@brand:{nike}" );
659+ assertThat (result ).contains ("@category:{tech*}" ); // wildcard preserved
660+ assertThat (result ).contains ("@status:{active\\ *}" ); // asterisk escaped
661+ }
542662}
0 commit comments