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  }