github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/wallet/sender_test.go (about) 1 package wallet 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/keybase/client/go/chat/globals" 10 "github.com/keybase/client/go/externalstest" 11 "github.com/keybase/client/go/protocol/chat1" 12 "github.com/keybase/client/go/protocol/gregor1" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/client/go/protocol/stellar1" 15 "github.com/stretchr/testify/require" 16 17 "github.com/keybase/client/go/chat/types" 18 "github.com/keybase/client/go/libkb" 19 ) 20 21 type mockUIDMapper struct { 22 libkb.UIDMapper 23 usernames map[string]string 24 } 25 26 func newMockUIDMapper() *mockUIDMapper { 27 return &mockUIDMapper{ 28 usernames: make(map[string]string), 29 } 30 } 31 32 func (m *mockUIDMapper) addUser(uid gregor1.UID, username string) { 33 m.usernames[uid.String()] = username 34 } 35 36 func (m *mockUIDMapper) getUser(uid gregor1.UID) string { 37 return m.usernames[uid.String()] 38 } 39 40 func (m *mockUIDMapper) MapUIDsToUsernamePackages(ctx context.Context, g libkb.UIDMapperContext, 41 uids []keybase1.UID, fullNameFreshness, networkTimeBudget time.Duration, 42 forceNetworkForFullNames bool) (res []libkb.UsernamePackage, err error) { 43 for _, uid := range uids { 44 res = append(res, libkb.UsernamePackage{ 45 NormalizedUsername: libkb.NewNormalizedUsername(m.getUser(uid.ToBytes())), 46 }) 47 } 48 return res, nil 49 } 50 51 type mockParticipantsSource struct { 52 types.ParticipantSource 53 partsFn func() []gregor1.UID 54 } 55 56 func (m *mockParticipantsSource) Get(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID, 57 dataSource types.InboxSourceDataSourceTyp) ([]gregor1.UID, error) { 58 return m.partsFn(), nil 59 } 60 61 type mockInboxSource struct { 62 types.InboxSource 63 membersTypFn func() chat1.ConversationMembersType 64 tlfNameFn func() string 65 } 66 67 func (m *mockInboxSource) ReadUnverified(ctx context.Context, uid gregor1.UID, 68 dataSource types.InboxSourceDataSourceTyp, query *chat1.GetInboxQuery) (types.Inbox, error) { 69 return types.Inbox{ 70 ConvsUnverified: []types.RemoteConversation{{ 71 Conv: chat1.Conversation{ 72 Metadata: chat1.ConversationMetadata{ 73 ConversationID: query.ConvIDs[0], 74 MembersType: m.membersTypFn(), 75 }, 76 }, 77 LocalMetadata: &types.RemoteConversationMetadata{ 78 Name: m.tlfNameFn(), 79 }, 80 }}, 81 }, nil 82 } 83 84 type mockStellar struct { 85 libkb.Stellar 86 miniFn func([]libkb.MiniChatPayment) ([]libkb.MiniChatPaymentResult, error) 87 } 88 89 func (m *mockStellar) SendMiniChatPayments(mctx libkb.MetaContext, convID chat1.ConversationID, payments []libkb.MiniChatPayment) (res []libkb.MiniChatPaymentResult, err error) { 90 return m.miniFn(payments) 91 } 92 93 func (m *mockStellar) KnownCurrencyCodeInstant(context.Context, string) (bool, bool) { 94 return false, false 95 } 96 97 func TestStellarSender(t *testing.T) { 98 tc := externalstest.SetupTest(t, "stellarsender", 0) 99 defer tc.Cleanup() 100 101 mikeUID := gregor1.UID([]byte{0, 1}) 102 patrickUID := gregor1.UID([]byte{0, 2}) 103 maxUID := gregor1.UID([]byte{0, 4}) 104 convID := chat1.ConversationID([]byte{0, 3}) 105 ms := mockStellar{} 106 mi := mockInboxSource{} 107 mp := mockParticipantsSource{} 108 mu := newMockUIDMapper() 109 mu.addUser(mikeUID, "mikem") 110 mu.addUser(patrickUID, "patrick") 111 mu.addUser(maxUID, "max") 112 tc.G.SetStellar(&ms) 113 tc.G.SetUIDMapper(mu) 114 g := globals.NewContext(tc.G, &globals.ChatContext{ 115 InboxSource: &mi, 116 ParticipantsSource: &mp, 117 }) 118 sender := NewSender(g) 119 successFn := func(incErr error, uids ...gregor1.UID) func(payments []libkb.MiniChatPayment) ([]libkb.MiniChatPaymentResult, error) { 120 return func(payments []libkb.MiniChatPayment) (res []libkb.MiniChatPaymentResult, err error) { 121 for index, p := range payments { 122 require.Equal(t, p.Username.String(), mu.getUser(uids[index])) 123 res = append(res, libkb.MiniChatPaymentResult{ 124 Username: p.Username, 125 Error: incErr, 126 PaymentID: stellar1.PaymentID("MIKE"), 127 }) 128 } 129 return res, nil 130 } 131 } 132 nativeFn := func() chat1.ConversationMembersType { 133 return chat1.ConversationMembersType_IMPTEAMNATIVE 134 } 135 teamFn := func() chat1.ConversationMembersType { 136 return chat1.ConversationMembersType_TEAM 137 } 138 mikePatrickFn := func() []gregor1.UID { 139 return []gregor1.UID{mikeUID, patrickUID} 140 } 141 mikePatrickNameFn := func() string { 142 return "mikem,patrick" 143 } 144 allFn := func() []gregor1.UID { 145 return []gregor1.UID{mikeUID, patrickUID, maxUID} 146 } 147 allNameFn := func() string { 148 return "mikem,patrick,max" 149 } 150 teamNameFn := func() string { 151 return "team" 152 } 153 type paymentRes struct { 154 resultTyp chat1.TextPaymentResultTyp 155 text string 156 } 157 testCase := func(body string, expected []paymentRes, senderUID gregor1.UID, 158 partsFn func() []gregor1.UID, typFn func() chat1.ConversationMembersType, 159 miniFn func([]libkb.MiniChatPayment) ([]libkb.MiniChatPaymentResult, error), 160 nameFn func() string) { 161 mi.membersTypFn = typFn 162 mi.tlfNameFn = nameFn 163 mp.partsFn = partsFn 164 ms.miniFn = miniFn 165 parsedPayments := sender.ParsePayments(context.TODO(), senderUID, convID, body, nil) 166 res, err := sender.SendPayments(context.TODO(), convID, parsedPayments) 167 require.NoError(t, err) 168 require.Equal(t, len(expected), len(res)) 169 for index, r := range expected { 170 require.Equal(t, r.text, res[index].PaymentText) 171 typ, err := res[0].Result.ResultTyp() 172 require.NoError(t, err) 173 require.Equal(t, r.resultTyp, typ) 174 } 175 } 176 177 t.Logf("imp team") 178 testCase("+1XLM", []paymentRes{{ 179 resultTyp: chat1.TextPaymentResultTyp_SENT, 180 text: "+1XLM", 181 }}, mikeUID, mikePatrickFn, nativeFn, successFn(nil, patrickUID), mikePatrickNameFn) 182 testCase("+1XLM", []paymentRes{{ 183 resultTyp: chat1.TextPaymentResultTyp_ERROR, 184 text: "+1XLM", 185 }}, mikeUID, mikePatrickFn, nativeFn, successFn(errors.New("NOOOO"), patrickUID), mikePatrickNameFn) 186 testCase("+1XLM", nil, mikeUID, allFn, nativeFn, successFn(nil), allNameFn) 187 testCase("+1XLM@patrick", []paymentRes{{ 188 resultTyp: chat1.TextPaymentResultTyp_SENT, 189 text: "+1XLM@patrick", 190 }}, mikeUID, allFn, nativeFn, successFn(nil, patrickUID), allNameFn) 191 testCase("+1XLM@patrick and also +10USD@max", []paymentRes{{ 192 resultTyp: chat1.TextPaymentResultTyp_SENT, 193 text: "+1XLM@patrick", 194 }, { 195 resultTyp: chat1.TextPaymentResultTyp_SENT, 196 text: "+10USD@max", 197 }}, mikeUID, allFn, nativeFn, successFn(nil, patrickUID, maxUID), allNameFn) 198 199 t.Logf("team successes") 200 testCase("+1XLM", nil, mikeUID, mikePatrickFn, teamFn, successFn(nil), teamNameFn) 201 testCase("+1XLM@patrick", []paymentRes{{ 202 resultTyp: chat1.TextPaymentResultTyp_SENT, 203 text: "+1XLM@patrick", 204 }}, mikeUID, mikePatrickFn, teamFn, successFn(nil, patrickUID), teamNameFn) 205 testCase("+1XLM@patrick and also +10USD@max", []paymentRes{{ 206 resultTyp: chat1.TextPaymentResultTyp_SENT, 207 text: "+1XLM@patrick", 208 }, { 209 resultTyp: chat1.TextPaymentResultTyp_SENT, 210 text: "+10USD@max", 211 }}, mikeUID, allFn, teamFn, successFn(nil, patrickUID, maxUID), teamNameFn) 212 testCase("+1XLM@patrick and also +10USD@max, and +10cad@karenm", []paymentRes{{ 213 resultTyp: chat1.TextPaymentResultTyp_SENT, 214 text: "+1XLM@patrick", 215 }, { 216 resultTyp: chat1.TextPaymentResultTyp_SENT, 217 text: "+10USD@max", 218 }}, mikeUID, allFn, teamFn, successFn(nil, patrickUID, maxUID), teamNameFn) 219 220 }