@@ -8,8 +8,10 @@ package cache
88
99import (
1010 "context"
11+ "errors"
1112 "sync"
1213 "testing"
14+ "time"
1315
1416 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/metrics/disabled"
1517 "github.com/hyperledger-labs/fabric-token-sdk/token/driver"
@@ -18,6 +20,7 @@ import (
1820 "github.com/test-go/testify/require"
1921)
2022
23+ // TestIdentityCache verifies basic cache functionality and identity retrieval.
2124func TestIdentityCache (t * testing.T ) {
2225 c := NewIdentityCache (func (context.Context , []byte ) (* idriver.IdentityDescriptor , error ) {
2326 return & idriver.IdentityDescriptor {
@@ -36,6 +39,7 @@ func TestIdentityCache(t *testing.T) {
3639 assert .Equal (t , []byte ("audit" ), identityDescriptor .AuditInfo )
3740}
3841
42+ // TestIdentityCacheForRace tests concurrent cache access for thread-safety.
3943func TestIdentityCacheForRace (t * testing.T ) {
4044 c := NewIdentityCache (func (context.Context , []byte ) (* idriver.IdentityDescriptor , error ) {
4145 return & idriver.IdentityDescriptor {
@@ -61,3 +65,132 @@ func TestIdentityCacheForRace(t *testing.T) {
6165 }
6266 wg .Wait ()
6367}
68+
69+ // TestFetchIdentityFromBackend verifies backend fetch when audit info doesn't match.
70+ func TestFetchIdentityFromBackend (t * testing.T ) {
71+ expectedIdentity := & idriver.IdentityDescriptor {
72+ Identity : []byte ("backend identity" ),
73+ AuditInfo : []byte ("backend audit" ),
74+ }
75+
76+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
77+ return expectedIdentity , nil
78+ }, 10 , []byte ("cache audit" ), NewMetrics (& disabled.Provider {}))
79+
80+ // Call with different audit info to trigger backend fetch
81+ identityDescriptor , err := c .Identity (context .Background (), []byte ("different audit" ))
82+ require .NoError (t , err )
83+ assert .Equal (t , expectedIdentity .Identity , identityDescriptor .Identity )
84+ assert .Equal (t , expectedIdentity .AuditInfo , identityDescriptor .AuditInfo )
85+ }
86+
87+ // TestFetchIdentityFromBackendError verifies error propagation from backend failures.
88+ func TestFetchIdentityFromBackendError (t * testing.T ) {
89+ expectedErr := errors .New ("backend error" )
90+
91+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
92+ return nil , expectedErr
93+ }, 10 , []byte ("cache audit" ), NewMetrics (& disabled.Provider {}))
94+
95+ // Call with different audit info to trigger backend fetch
96+ _ , err := c .Identity (context .Background (), []byte ("different audit" ))
97+ require .Error (t , err )
98+ assert .Equal (t , expectedErr , err )
99+ }
100+
101+ // TestFetchIdentityFromCacheTimeout verifies on-demand generation after cache timeout.
102+ func TestFetchIdentityFromCacheTimeout (t * testing.T ) {
103+ callCount := make (chan struct {}, 10 )
104+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
105+ callCount <- struct {}{}
106+ // Simulate slow backend - not strictly needed for the test
107+ // time.Sleep(10 * time.Millisecond)
108+ return & idriver.IdentityDescriptor {
109+ Identity : []byte ("timeout identity" ),
110+ AuditInfo : []byte ("timeout audit" ),
111+ }, nil
112+ }, 0 , nil , NewMetrics (& disabled.Provider {})) // cache size 0 to force timeout
113+
114+ // Set short timeout to trigger timeout path
115+ c .cacheTimeout = 1 * time .Millisecond
116+
117+ identityDescriptor , err := c .Identity (context .Background (), nil )
118+ require .NoError (t , err )
119+ assert .Equal (t , driver .Identity ([]byte ("timeout identity" )), identityDescriptor .Identity )
120+ assert .Equal (t , []byte ("timeout audit" ), identityDescriptor .AuditInfo )
121+ assert .Len (t , callCount , 1 )
122+ }
123+
124+ // TestFetchIdentityFromCacheTimeoutError verifies error handling after cache timeout.
125+ func TestFetchIdentityFromCacheTimeoutError (t * testing.T ) {
126+ expectedErr := errors .New ("timeout backend error" )
127+
128+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
129+ return nil , expectedErr
130+ }, 0 , nil , NewMetrics (& disabled.Provider {}))
131+
132+ // Set short timeout to trigger timeout path
133+ c .cacheTimeout = 1 * time .Millisecond
134+
135+ _ , err := c .Identity (context .Background (), nil )
136+ require .Error (t , err )
137+ assert .Equal (t , expectedErr , err )
138+ }
139+
140+ // TestProvisionIdentitiesError verifies provisioning retries after errors.
141+ func TestProvisionIdentitiesError (t * testing.T ) {
142+ callCount := make (chan struct {}, 100 )
143+ maxCalls := 3
144+
145+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
146+ // Fail 3 times then succeed
147+ callCount <- struct {}{} // send once per call
148+ if len (callCount ) <= maxCalls {
149+ return nil , errors .New ("provision error" )
150+ }
151+
152+ return & idriver.IdentityDescriptor {
153+ Identity : []byte ("success identity" ),
154+ AuditInfo : []byte ("success audit" ),
155+ }, nil
156+ }, 10 , nil , NewMetrics (& disabled.Provider {}))
157+
158+ // Trigger provisioning
159+ _ , err := c .Identity (context .Background (), nil )
160+ require .NoError (t , err )
161+
162+ // Wait a bit for provisioning to attempt multiple times
163+ time .Sleep (50 * time .Millisecond )
164+
165+ // Verify that provisioning continued after errors
166+ assert .Greater (t , len (callCount ), maxCalls )
167+ }
168+
169+ // TestFetchIdentityFromCacheNilEntry verifies backend fallback for nil cache entries.
170+ func TestFetchIdentityFromCacheNilEntry (t * testing.T ) {
171+ backendCalled := make (chan struct {}, 1 )
172+
173+ c := NewIdentityCache (func (ctx context.Context , auditInfo []byte ) (* idriver.IdentityDescriptor , error ) {
174+ backendCalled <- struct {}{}
175+
176+ return & idriver.IdentityDescriptor {
177+ Identity : []byte ("backend fallback" ),
178+ AuditInfo : []byte ("backend audit" ),
179+ }, nil
180+ }, 10 , nil , NewMetrics (& disabled.Provider {}))
181+
182+ // Send nil to cache to test nil handling
183+ c .cache <- nil
184+
185+ identityDescriptor , err := c .Identity (context .Background (), nil )
186+ require .NoError (t , err )
187+ assert .Eventually (t , func () bool {
188+ select {
189+ case <- backendCalled :
190+ return true
191+ default :
192+ return false
193+ }
194+ }, time .Second , 10 * time .Millisecond )
195+ assert .Equal (t , driver .Identity ([]byte ("backend fallback" )), identityDescriptor .Identity )
196+ }
0 commit comments