@@ -650,6 +650,25 @@ describe('per-option visibleWhen — a faulting predicate REFUSES the write (ADR
650650 expect ( entry . constraint . fault ) . toBe ( 'runtime: No such key: statsu' ) ;
651651 } ) ;
652652
653+ it . each ( [
654+ [ "current_user.positions.x == 'a'" , 'x' ] ,
655+ [ "ctx.user.organizationId.y == 'a'" , 'y' ] ,
656+ ] ) ( 'a read one hop BELOW a bound member (%s) faults on the write path and refuses' , ( source , key ) => {
657+ // The first member is bound, so the build's member verdict passes it; the
658+ // hop below it is a key the acting user's value does not carry.
659+ const entry = goldRefusal ( source , { } , { currentUser : AUTHED } ) ;
660+ expect ( entry . constraint . fault ) . toBe ( `runtime: No such key: ${ key } ` ) ;
661+ expect ( entry . constraint ) . not . toHaveProperty ( 'missingKey' ) ;
662+ expect ( entry . message ) . not . toContain ( 'does not declare' ) ;
663+ } ) ;
664+
665+ it ( 'control: `has()` over the same hop does not fault — it is a clean FALSE (`invalid_option`)' , ( ) => {
666+ const err = refusalOf ( ( ) =>
667+ evaluateValidationRules ( gated ( 'has(current_user.positions.x)' ) , { tier : 'gold' } , 'insert' , { currentUser : AUTHED } ) ,
668+ ) ;
669+ expect ( err . fields ) . toEqual ( [ expect . objectContaining ( { field : 'tier' , code : 'invalid_option' , value : 'gold' } ) ] ) ;
670+ } ) ;
671+
653672 it ( 'a read THROUGH a reference refuses with the repair that is true for it' , ( ) => {
654673 const entry = goldRefusal ( "record.account.tier == 'enterprise'" , { account : 'acc_1' } , { currentUser : AUTHED } ) ;
655674 expect ( entry . message ) . toContain ( "reads 'tier' through 'account', a reference to 'crm_account'" ) ;
0 commit comments