@@ -345,6 +345,71 @@ describe('getIdentitySegments single segment evaluation', () => {
345345 } ) ;
346346} ) ;
347347
348+ describe ( 'traitsMatchSegmentCondition with $.identity.traits.* properties' , ( ) => {
349+ const mockContext : EvaluationContext = {
350+ environment : { key : 'env' , name : 'test' } ,
351+ identity : {
352+ key : 'user' ,
353+ identifier : 'user@example.com' ,
354+ traits : {
355+ age : 25 ,
356+ tamaño : 'grande' ,
357+ サイズ : 'medium' ,
358+ '[$the.size$]' : 'small'
359+ }
360+ } ,
361+ segments : { } ,
362+ features : { }
363+ } ;
364+
365+ test . each ( [
366+ // dot notation – normal trait name
367+ [ { property : '$.identity.traits.age' , operator : 'EQUAL' , value : '25' } , true ] ,
368+ [ { property : '$.identity.traits.age' , operator : 'EQUAL' , value : '30' } , false ] ,
369+ // dot notation – unicode trait name
370+ [ { property : '$.identity.traits.tamaño' , operator : 'EQUAL' , value : 'grande' } , true ] ,
371+ [ { property : '$.identity.traits.サイズ' , operator : 'EQUAL' , value : 'medium' } , true ] ,
372+ // bracket notation – special characters in trait name that break jsonpath-plus
373+ [
374+ { property : "$.identity.traits['[$the.size$]']" , operator : 'EQUAL' , value : 'small' } ,
375+ true
376+ ] ,
377+ [
378+ { property : "$.identity.traits['[$the.size$]']" , operator : 'EQUAL' , value : 'large' } ,
379+ false
380+ ] ,
381+ // non-existent trait
382+ [ { property : '$.identity.traits.nonexistent' , operator : 'EQUAL' , value : 'any' } , false ] ,
383+ // IS_SET / IS_NOT_SET
384+ [ { property : '$.identity.traits.age' , operator : 'IS_SET' , value : null } , true ] ,
385+ [ { property : '$.identity.traits.nonexistent' , operator : 'IS_SET' , value : null } , false ] ,
386+ [ { property : '$.identity.traits.nonexistent' , operator : 'IS_NOT_SET' , value : null } , true ] ,
387+ [ { property : '$.identity.traits.age' , operator : 'IS_NOT_SET' , value : null } , false ] ,
388+ // IN operator
389+ [
390+ {
391+ property : '$.identity.traits.tamaño' ,
392+ operator : CONDITION_OPERATORS . IN ,
393+ value : [ 'grande' , 'pequeño' ]
394+ } ,
395+ true
396+ ] ,
397+ [
398+ {
399+ property : '$.identity.traits.tamaño' ,
400+ operator : CONDITION_OPERATORS . IN ,
401+ value : [ 'pequeño' ]
402+ } ,
403+ false
404+ ]
405+ ] as Array < [ SegmentCondition | InSegmentCondition , boolean ] > ) (
406+ 'evaluates %j to %s' ,
407+ ( condition , expected ) => {
408+ expect ( traitsMatchSegmentCondition ( condition , 'seg' , mockContext ) ) . toBe ( expected ) ;
409+ }
410+ ) ;
411+ } ) ;
412+
348413describe ( 'getContextValue' , ( ) => {
349414 const mockContext : EvaluationContext = {
350415 environment : {
@@ -354,6 +419,7 @@ describe('getContextValue', () => {
354419 identity : {
355420 key : 'user-123' ,
356421 identifier : 'user@example.com'
422+ // intentionally no traits – tests below confirm paths that require traits return undefined
357423 } ,
358424 segments : { } ,
359425 features : { }
@@ -371,7 +437,7 @@ describe('getContextValue', () => {
371437
372438 // Undefined or invalid cases
373439 test . each ( [
374- [ '$.identity.traits.user_type' , 'unsupported nested path ' ] ,
440+ [ '$.identity.traits.user_type' , 'no traits in context ' ] ,
375441 [ 'identity.identifier' , 'missing $ prefix' ] ,
376442 [ '$.invalid.path' , 'completely invalid path' ] ,
377443 [ '$.identity.nonexistent' , 'valid structure but missing property' ] ,
0 commit comments