Skip to content

Commit 83f3573

Browse files
AKRAM@il.ibm.comAkramBitar
authored andcommitted
Add improvements
Signed-off-by: AkramBitar <akram@il.ibm.com>
1 parent 1018c1b commit 83f3573

File tree

2 files changed

+87
-246
lines changed

2 files changed

+87
-246
lines changed

token/services/ttx/collectendorsements.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,7 @@ func (c *CollectEndorsementsView) Call(context view.Context) (interface{}, error
8686
externalWallets := make(map[string]ExternalWalletSigner)
8787

8888
// Ensure Done() is called on all external wallets regardless of errors
89-
defer func() {
90-
logger.DebugfContext(context.Context(), "Inform external wallets that endorsement is complete")
91-
for id, signer := range externalWallets {
92-
if err := signer.Done(); err != nil {
93-
logger.ErrorfContext(context.Context(), "failed to signal done external wallet [%s]", id)
94-
}
95-
}
96-
}()
89+
defer c.CleanupExternalWallets(context, externalWallets)
9790

9891
// 1. First collect signatures on the token request
9992
issueSigmas, err := c.requestSignaturesOnIssues(context, externalWallets)
@@ -667,3 +660,13 @@ func TransferDistributionList(r *token.Request) []view.Identity {
667660
}
668661
return distributionList
669662
}
663+
664+
// CleanupExternalWallets calls Done() on all external wallets to signal completion
665+
func (c *CollectEndorsementsView) CleanupExternalWallets(context view.Context, externalWallets map[string]ExternalWalletSigner) {
666+
logger.DebugfContext(context.Context(), "Inform external wallets that endorsement is complete")
667+
for id, signer := range externalWallets {
668+
if err := signer.Done(); err != nil {
669+
logger.ErrorfContext(context.Context(), "failed to signal done external wallet [%s]", id)
670+
}
671+
}
672+
}

token/services/ttx/collectendorsements_test.go

Lines changed: 76 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -7,285 +7,123 @@ SPDX-License-Identifier: Apache-2.0
77
package ttx_test
88

99
import (
10+
"context"
1011
"testing"
1112

1213
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
13-
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/endpoint"
1414
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
15-
"github.com/hyperledger-labs/fabric-token-sdk/token"
16-
"github.com/hyperledger-labs/fabric-token-sdk/token/driver"
17-
"github.com/hyperledger-labs/fabric-token-sdk/token/driver/mock"
1815
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx"
1916
mock2 "github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx/dep/mock"
20-
"github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx/dep/tokenapi"
2117
"github.com/stretchr/testify/assert"
22-
"github.com/stretchr/testify/require"
2318
)
2419

25-
// mockExternalWalletSigner is a mock implementation of ExternalWalletSigner
20+
// mockExternalWalletSigner is a mock implementation of ExternalWalletSigner for testing
2621
type mockExternalWalletSigner struct {
27-
signCalled int
28-
doneCalled int
29-
signError error
30-
doneError error
22+
signFunc func(party view.Identity, message []byte) ([]byte, error)
23+
doneFunc func() error
24+
doneCalled bool
25+
signCalled bool
3126
}
3227

3328
func (m *mockExternalWalletSigner) Sign(party view.Identity, message []byte) ([]byte, error) {
34-
m.signCalled++
35-
if m.signError != nil {
36-
return nil, m.signError
29+
m.signCalled = true
30+
if m.signFunc != nil {
31+
return m.signFunc(party, message)
3732
}
38-
return []byte("external_signature"), nil
33+
return []byte("mock_signature"), nil
3934
}
4035

4136
func (m *mockExternalWalletSigner) Done() error {
42-
m.doneCalled++
43-
return m.doneError
44-
}
45-
46-
func TestCollectEndorsementsView_ExternalWalletCleanup(t *testing.T) {
47-
testCases := []struct {
48-
name string
49-
setupError error
50-
expectDoneCalled bool
51-
expectDoneCallCount int
52-
errorInSignature bool
53-
errorInTransferSignature bool
54-
}{
55-
{
56-
name: "success - Done called once",
57-
setupError: nil,
58-
expectDoneCalled: true,
59-
expectDoneCallCount: 1,
60-
},
61-
{
62-
name: "error in issue signatures - Done still called",
63-
setupError: nil,
64-
expectDoneCalled: true,
65-
expectDoneCallCount: 1,
66-
errorInSignature: true,
67-
},
68-
{
69-
name: "error in transfer signatures - Done still called",
70-
setupError: nil,
71-
expectDoneCalled: true,
72-
expectDoneCallCount: 1,
73-
errorInTransferSignature: true,
74-
},
37+
m.doneCalled = true
38+
if m.doneFunc != nil {
39+
return m.doneFunc()
7540
}
41+
return nil
42+
}
7643

77-
for _, tc := range testCases {
78-
t.Run(tc.name, func(t *testing.T) {
79-
// Setup mock context
80-
ctx := &mock2.Context{}
81-
ctx.ContextReturns(t.Context())
82-
83-
// Setup token management service
84-
tms := &mock2.TokenManagementServiceWithExtensions{}
85-
tms.NetworkReturns("test_network")
86-
tms.ChannelReturns("test_channel")
87-
tmsID := token.TMSID{
88-
Network: "test_network",
89-
Channel: "test_channel",
90-
Namespace: "test_namespace",
91-
}
92-
tms.IDReturns(tmsID)
93-
94-
// Setup token deserializer and identity provider
95-
tokenDes := &mock.Deserializer{}
96-
tokenIP := &mock.IdentityProvider{}
97-
tokenIP.IsMeReturns(false) // Not me, so it will try external wallet
98-
tms.SigServiceReturns(token.NewSignatureService(tokenDes, tokenIP))
99-
100-
// Setup wallet manager with external wallet
101-
walletManager := &mock2.WalletManager{}
102-
wallet := &mock2.Wallet{}
103-
wallet.IDReturns("external_wallet_1")
104-
walletManager.OwnerWalletReturns(wallet)
105-
tms.WalletManagerReturns(walletManager)
106-
107-
tokenAPITMS := tokenapi.NewMockedManagementService(t, tmsID)
108-
tms.SetTokenManagementServiceStub = func(arg1 *token.Request) error {
109-
arg1.SetTokenService(tokenAPITMS)
110-
return nil
111-
}
112-
113-
tmsp := &mock2.TokenManagementServiceProvider{}
114-
tmsp.TokenManagementServiceReturns(tms, nil)
115-
116-
network := &mock2.Network{}
117-
network.ComputeTxIDReturns("test_anchor")
118-
np := &mock2.NetworkProvider{}
119-
np.GetNetworkReturns(network, nil)
120-
121-
// Create request with issue metadata
122-
req := token.NewRequest(nil, "test_anchor")
123-
req.Metadata.Issues = []*driver.IssueMetadata{
124-
{
125-
Issuer: driver.AuditableIdentity{
126-
Identity: []byte("external_issuer"),
127-
},
128-
},
129-
}
130-
tms.NewRequestReturns(req, nil)
131-
132-
// Setup context service calls
133-
ctx.GetServiceReturnsOnCall(0, tmsp, nil)
134-
ctx.GetServiceReturnsOnCall(1, np, nil)
135-
ctx.GetServiceReturnsOnCall(2, &endpoint.Service{}, nil)
136-
ctx.GetServiceReturnsOnCall(3, np, nil)
137-
ctx.GetServiceReturnsOnCall(4, tmsp, nil)
138-
139-
// Create transaction
140-
tx, err := ttx.NewTransaction(ctx, []byte("test_signer"))
141-
require.NoError(t, err)
142-
143-
// Create mock external wallet signer
144-
mockEWS := &mockExternalWalletSigner{}
145-
if tc.errorInSignature {
146-
mockEWS.signError = errors.New("signature error")
147-
}
148-
149-
// Create CollectEndorsementsView with external wallet signer option
150-
opts := []ttx.EndorsementsOpt{
151-
ttx.WithExternalWalletSignerProvider(func(walletID string) ttx.ExternalWalletSigner {
152-
return mockEWS
153-
}),
154-
ttx.WithSkipApproval(),
155-
ttx.WithSkipAuditing(),
156-
}
157-
158-
view := ttx.NewCollectEndorsementsView(tx, opts...)
159-
160-
// Call the view
161-
_, err = view.Call(ctx)
162-
163-
// Verify Done was called regardless of error
164-
if tc.expectDoneCalled {
165-
assert.Equal(t, tc.expectDoneCallCount, mockEWS.doneCalled,
166-
"Done() should be called %d time(s)", tc.expectDoneCallCount)
167-
} else {
168-
assert.Equal(t, 0, mockEWS.doneCalled, "Done() should not be called")
169-
}
44+
// TestCleanupExternalWallets_Success tests that CleanupExternalWallets calls Done() on all wallets
45+
func TestCleanupExternalWallets_Success(t *testing.T) {
46+
wallet := &mockExternalWalletSigner{}
17047

171-
// Verify error behavior
172-
if tc.errorInSignature || tc.errorInTransferSignature {
173-
require.Error(t, err, "Expected error when signature fails")
174-
}
175-
})
48+
externalWallets := map[string]ttx.ExternalWalletSigner{
49+
"wallet1": wallet,
17650
}
177-
}
17851

179-
func TestCollectEndorsementsView_MultipleExternalWallets(t *testing.T) {
180-
// Setup mock context
52+
// Create a minimal view and context
53+
view := &ttx.CollectEndorsementsView{}
18154
ctx := &mock2.Context{}
182-
ctx.ContextReturns(t.Context())
55+
ctx.ContextReturns(context.Background())
18356

184-
// Setup token management service
185-
tms := &mock2.TokenManagementServiceWithExtensions{}
186-
tms.NetworkReturns("test_network")
187-
tms.ChannelReturns("test_channel")
188-
tmsID := token.TMSID{
189-
Network: "test_network",
190-
Channel: "test_channel",
191-
Namespace: "test_namespace",
192-
}
193-
tms.IDReturns(tmsID)
57+
// Call the cleanup method
58+
view.CleanupExternalWallets(ctx, externalWallets)
19459

195-
// Setup token deserializer and identity provider
196-
tokenDes := &mock.Deserializer{}
197-
tokenIP := &mock.IdentityProvider{}
198-
tokenIP.IsMeReturns(false)
199-
tms.SigServiceReturns(token.NewSignatureService(tokenDes, tokenIP))
60+
assert.True(t, wallet.doneCalled, "Done() should have been called on the wallet")
61+
}
20062

201-
// Setup wallet manager with multiple external wallets
202-
walletManager := &mock2.WalletManager{}
203-
wallet1 := &mock2.Wallet{}
204-
wallet1.IDReturns("external_wallet_1")
205-
wallet2 := &mock2.Wallet{}
206-
wallet2.IDReturns("external_wallet_2")
63+
// TestCleanupExternalWallets_MultipleWallets tests that Done() is called on all wallets
64+
func TestCleanupExternalWallets_MultipleWallets(t *testing.T) {
65+
wallet1 := &mockExternalWalletSigner{}
66+
wallet2 := &mockExternalWalletSigner{}
67+
wallet3 := &mockExternalWalletSigner{}
20768

208-
callCount := 0
209-
walletManager.OwnerWalletStub = func(ctx2 interface{}, identity view.Identity) interface{} {
210-
callCount++
211-
if callCount%2 == 1 {
212-
return wallet1
213-
}
214-
return wallet2
69+
externalWallets := map[string]ttx.ExternalWalletSigner{
70+
"wallet1": wallet1,
71+
"wallet2": wallet2,
72+
"wallet3": wallet3,
21573
}
216-
tms.WalletManagerReturns(walletManager)
21774

218-
tokenAPITMS := tokenapi.NewMockedManagementService(t, tmsID)
219-
tms.SetTokenManagementServiceStub = func(arg1 *token.Request) error {
220-
arg1.SetTokenService(tokenAPITMS)
221-
return nil
222-
}
75+
view := &ttx.CollectEndorsementsView{}
76+
ctx := &mock2.Context{}
77+
ctx.ContextReturns(context.Background())
22378

224-
tmsp := &mock2.TokenManagementServiceProvider{}
225-
tmsp.TokenManagementServiceReturns(tms, nil)
79+
view.CleanupExternalWallets(ctx, externalWallets)
22680

227-
network := &mock2.Network{}
228-
network.ComputeTxIDReturns("test_anchor")
229-
np := &mock2.NetworkProvider{}
230-
np.GetNetworkReturns(network, nil)
81+
assert.True(t, wallet1.doneCalled, "Done() should have been called on wallet1")
82+
assert.True(t, wallet2.doneCalled, "Done() should have been called on wallet2")
83+
assert.True(t, wallet3.doneCalled, "Done() should have been called on wallet3")
84+
}
23185

232-
// Create request with multiple issues
233-
req := token.NewRequest(nil, "test_anchor")
234-
req.Metadata.Issues = []*driver.IssueMetadata{
235-
{
236-
Issuer: driver.AuditableIdentity{
237-
Identity: []byte("external_issuer_1"),
238-
},
86+
// TestCleanupExternalWallets_DoneError tests that errors from Done() don't stop cleanup
87+
func TestCleanupExternalWallets_DoneError(t *testing.T) {
88+
wallet1 := &mockExternalWalletSigner{
89+
doneFunc: func() error {
90+
return errors.New("wallet1 done failed")
23991
},
240-
{
241-
Issuer: driver.AuditableIdentity{
242-
Identity: []byte("external_issuer_2"),
243-
},
92+
}
93+
wallet2 := &mockExternalWalletSigner{}
94+
wallet3 := &mockExternalWalletSigner{
95+
doneFunc: func() error {
96+
return errors.New("wallet3 done failed")
24497
},
24598
}
246-
tms.NewRequestReturns(req, nil)
24799

248-
// Setup context service calls
249-
ctx.GetServiceReturnsOnCall(0, tmsp, nil)
250-
ctx.GetServiceReturnsOnCall(1, np, nil)
251-
ctx.GetServiceReturnsOnCall(2, &endpoint.Service{}, nil)
252-
ctx.GetServiceReturnsOnCall(3, np, nil)
253-
ctx.GetServiceReturnsOnCall(4, tmsp, nil)
254-
255-
// Create transaction
256-
tx, err := ttx.NewTransaction(ctx, []byte("test_signer"))
257-
require.NoError(t, err)
258-
259-
// Create mock external wallet signers
260-
mockEWS1 := &mockExternalWalletSigner{}
261-
mockEWS2 := &mockExternalWalletSigner{}
100+
externalWallets := map[string]ttx.ExternalWalletSigner{
101+
"wallet1": wallet1,
102+
"wallet2": wallet2,
103+
"wallet3": wallet3,
104+
}
262105

263-
// Simulate error in second wallet's signature
264-
mockEWS2.signError = errors.New("wallet 2 signature error")
106+
view := &ttx.CollectEndorsementsView{}
107+
ctx := &mock2.Context{}
108+
ctx.ContextReturns(context.Background())
265109

266-
walletSigners := map[string]*mockExternalWalletSigner{
267-
"external_wallet_1": mockEWS1,
268-
"external_wallet_2": mockEWS2,
269-
}
110+
// Should not panic even if Done() returns errors
111+
view.CleanupExternalWallets(ctx, externalWallets)
270112

271-
// Create CollectEndorsementsView with external wallet signer option
272-
opts := []ttx.EndorsementsOpt{
273-
ttx.WithExternalWalletSignerProvider(func(walletID string) ttx.ExternalWalletSigner {
274-
return walletSigners[walletID]
275-
}),
276-
ttx.WithSkipApproval(),
277-
ttx.WithSkipAuditing(),
278-
}
113+
assert.True(t, wallet1.doneCalled, "Done() should have been called on wallet1 despite error")
114+
assert.True(t, wallet2.doneCalled, "Done() should have been called on wallet2")
115+
assert.True(t, wallet3.doneCalled, "Done() should have been called on wallet3 despite error")
116+
}
279117

280-
view := ttx.NewCollectEndorsementsView(tx, opts...)
118+
// TestCleanupExternalWallets_EmptyMap tests cleanup with no wallets
119+
func TestCleanupExternalWallets_EmptyMap(t *testing.T) {
120+
externalWallets := map[string]ttx.ExternalWalletSigner{}
281121

282-
// Call the view - should fail due to wallet 2 error
283-
_, err = view.Call(ctx)
284-
require.Error(t, err, "Expected error from wallet 2")
122+
view := &ttx.CollectEndorsementsView{}
123+
ctx := &mock2.Context{}
124+
ctx.ContextReturns(context.Background())
285125

286-
// Verify Done was called on both wallets despite the error
287-
assert.Equal(t, 1, mockEWS1.doneCalled, "Done() should be called on wallet 1")
288-
assert.Equal(t, 1, mockEWS2.doneCalled, "Done() should be called on wallet 2 even after error")
126+
// Should not panic with empty map
127+
view.CleanupExternalWallets(ctx, externalWallets)
289128
}
290129

291-
// Made with Bob

0 commit comments

Comments
 (0)