@@ -13,10 +13,24 @@ package boolpolicy
1313import (
1414 "testing"
1515
16+ tdriver "github.com/LFDT-Panurus/panurus/token/driver"
1617 "github.com/stretchr/testify/assert"
1718 "github.com/stretchr/testify/require"
1819)
1920
21+ // countingVerifier wraps a driver.Verifier and records how many times Verify
22+ // was invoked, so tests can assert per-index memoisation.
23+ type countingVerifier struct {
24+ inner tdriver.Verifier
25+ calls int
26+ }
27+
28+ func (c * countingVerifier ) Verify (msg , sig []byte ) error {
29+ c .calls ++
30+
31+ return c .inner .Verify (msg , sig )
32+ }
33+
2034// ---------------------------------------------------------------------------
2135// AND policy
2236// ---------------------------------------------------------------------------
@@ -178,3 +192,45 @@ func TestPolicyVerify_SlotCountTooMany(t *testing.T) {
178192 sig := buildPolicySig (t , "s0" , "s1" , "extra" )
179193 assert .Error (t , pv .Verify ([]byte (testMsg ), sig ))
180194}
195+
196+ // ---------------------------------------------------------------------------
197+ // Per-index memoisation within a single Verify call
198+ // ---------------------------------------------------------------------------
199+
200+ // TestPolicyVerify_Memoisation_RepeatedRefVerifiedOnce verifies that an index
201+ // referenced multiple times in a policy triggers the underlying verifier only
202+ // once per Verify call.
203+ func TestPolicyVerify_Memoisation_RepeatedRefVerifiedOnce (t * testing.T ) {
204+ stubs := makeVerifiers (testMsg , "s0" , "s1" )
205+ counter := & countingVerifier {inner : stubs [0 ]}
206+
207+ // "$0 AND ($0 OR $1)" references $0 twice.
208+ node , err := Parse ("$0 AND ($0 OR $1)" )
209+ require .NoError (t , err )
210+ pv := & PolicyVerifier {
211+ Policy : node ,
212+ Verifiers : []tdriver.Verifier {counter , stubs [1 ]},
213+ }
214+
215+ require .NoError (t , pv .Verify ([]byte (testMsg ), buildPolicySig (t , "s0" , "s1" )))
216+ assert .Equal (t , 1 , counter .calls , "verifier for $0 must be invoked exactly once" )
217+ }
218+
219+ // TestPolicyVerify_Memoisation_FailingRefVerifiedOnce verifies that a failing
220+ // index is also cached: a wrong signature referenced twice is verified once and
221+ // the cached failure is reused.
222+ func TestPolicyVerify_Memoisation_FailingRefVerifiedOnce (t * testing.T ) {
223+ stubs := makeVerifiers (testMsg , "s0" )
224+ counter := & countingVerifier {inner : stubs [0 ]}
225+
226+ // "$0 OR $0" references $0 twice; a wrong signature fails both times.
227+ node , err := Parse ("$0 OR $0" )
228+ require .NoError (t , err )
229+ pv := & PolicyVerifier {
230+ Policy : node ,
231+ Verifiers : []tdriver.Verifier {counter },
232+ }
233+
234+ require .Error (t , pv .Verify ([]byte (testMsg ), buildPolicySig (t , "wrong" )))
235+ assert .Equal (t , 1 , counter .calls , "failing verifier for $0 must be invoked exactly once" )
236+ }
0 commit comments