github.com/status-im/status-go@v1.1.0/services/wallet/walletconnect/database_test.go (about)

     1  package walletconnect
     2  
     3  import (
     4  	"strconv"
     5  	"testing"
     6  
     7  	"database/sql"
     8  
     9  	"github.com/status-im/status-go/services/wallet/common"
    10  
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  type urlOverride *string
    15  type timestampOverride *int64
    16  
    17  // testSession will override defaults for the fields that are not null
    18  type testSession struct {
    19  	url          urlOverride
    20  	created      timestampOverride
    21  	expiry       timestampOverride
    22  	disconnected *bool
    23  	testChains   *bool
    24  }
    25  
    26  const testDappUrl = "https://test.url/"
    27  
    28  // generateTestData generates alternative disconnected and active sessions starting with the active one
    29  // timestamps start with 1234567890 and increase by 1 for each session
    30  // all sessions will share the same two pairing sessions (roll over after index 1)
    31  // testChains is false if not overridden
    32  func generateTestData(sessions []testSession) []DBSession {
    33  	res := make([]DBSession, len(sessions))
    34  	pairingIdx := 0
    35  	for i := 0; i < len(res); i++ {
    36  		strI := strconv.Itoa(i)
    37  		if i%2 == 0 {
    38  			pairingIdx++
    39  		}
    40  		pairingIdxStr := strconv.Itoa(pairingIdx)
    41  
    42  		s := sessions[i]
    43  
    44  		url := testDappUrl + strI
    45  		if s.url != nil {
    46  			url = *s.url
    47  		}
    48  
    49  		createdTimestamp := 1234567890 + int64(i)
    50  		if s.created != nil {
    51  			createdTimestamp = *s.created
    52  		}
    53  
    54  		expiryTimestamp := createdTimestamp + 1000 + int64(i)
    55  		if s.expiry != nil {
    56  			expiryTimestamp = *s.expiry
    57  		}
    58  
    59  		disconnected := (i % 2) != 0
    60  		if s.disconnected != nil {
    61  			disconnected = *s.disconnected
    62  		}
    63  
    64  		testChains := false
    65  		if s.testChains != nil {
    66  			testChains = *s.testChains
    67  		}
    68  
    69  		res[i] = DBSession{
    70  			Topic:            Topic(strI + "aaaaaa1234567890"),
    71  			Disconnected:     disconnected,
    72  			SessionJSON:      "{}",
    73  			Expiry:           expiryTimestamp,
    74  			CreatedTimestamp: createdTimestamp,
    75  			PairingTopic:     Topic(pairingIdxStr + "bbbbbb1234567890"),
    76  			TestChains:       testChains,
    77  			DBDApp: DBDApp{
    78  				URL:     url,
    79  				Name:    "TestApp" + strI,
    80  				IconURL: "https://test.icon" + strI,
    81  			},
    82  		}
    83  	}
    84  	return res
    85  }
    86  
    87  func insertTestData(t *testing.T, db *sql.DB, entries []DBSession) {
    88  	for _, entry := range entries {
    89  		err := UpsertSession(db, entry)
    90  		require.NoError(t, err)
    91  	}
    92  }
    93  
    94  func TestInsertUpdateAndGetSession(t *testing.T) {
    95  	db, close := SetupTestDB(t)
    96  	defer close()
    97  
    98  	entry := generateTestData(make([]testSession, 1))[0]
    99  	err := UpsertSession(db, entry)
   100  	require.NoError(t, err)
   101  
   102  	retrievedSession, err := GetSessionByTopic(db, entry.Topic)
   103  	require.NoError(t, err)
   104  
   105  	require.Equal(t, entry, *retrievedSession)
   106  
   107  	updatedEntry := entry
   108  	updatedEntry.Disconnected = true
   109  	updatedEntry.Expiry = 1111111111
   110  	err = UpsertSession(db, updatedEntry)
   111  
   112  	require.NoError(t, err)
   113  
   114  	retrievedSession, err = GetSessionByTopic(db, updatedEntry.Topic)
   115  	require.NoError(t, err)
   116  
   117  	require.Equal(t, updatedEntry, *retrievedSession)
   118  }
   119  
   120  func TestInsertAndGetSessionsByPairingTopic(t *testing.T) {
   121  	db, close := SetupTestDB(t)
   122  	defer close()
   123  
   124  	generatedSessions := generateTestData(make([]testSession, 4))
   125  	for _, session := range generatedSessions {
   126  		err := UpsertSession(db, session)
   127  		require.NoError(t, err)
   128  	}
   129  
   130  	retrievedSessions, err := GetSessionsByPairingTopic(db, generatedSessions[2].Topic)
   131  	require.NoError(t, err)
   132  	require.Equal(t, 0, len(retrievedSessions))
   133  
   134  	retrievedSessions, err = GetSessionsByPairingTopic(db, generatedSessions[2].PairingTopic)
   135  	require.NoError(t, err)
   136  	require.Equal(t, 2, len(retrievedSessions))
   137  
   138  	for i := 2; i < 4; i++ {
   139  		found := false
   140  		for _, session := range retrievedSessions {
   141  			if session.Topic == generatedSessions[i].Topic {
   142  				found = true
   143  				require.Equal(t, generatedSessions[i], session)
   144  			}
   145  		}
   146  		require.True(t, found)
   147  	}
   148  }
   149  
   150  func TestGet(t *testing.T) {
   151  	db, close := SetupTestDB(t)
   152  	defer close()
   153  
   154  	entries := generateTestData(make([]testSession, 3))
   155  	insertTestData(t, db, entries)
   156  
   157  	retrievedSession, err := GetSessionByTopic(db, entries[1].Topic)
   158  	require.NoError(t, err)
   159  
   160  	require.Equal(t, entries[1], *retrievedSession)
   161  
   162  	err = DeleteSession(db, entries[1].Topic)
   163  	require.NoError(t, err)
   164  
   165  	deletedSession, err := GetSessionByTopic(db, entries[1].Topic)
   166  	require.ErrorIs(t, err, sql.ErrNoRows)
   167  	require.Nil(t, deletedSession)
   168  }
   169  
   170  func TestGetActiveSessions(t *testing.T) {
   171  	db, close := SetupTestDB(t)
   172  	defer close()
   173  
   174  	// insert two disconnected and three active sessions
   175  	entries := generateTestData(make([]testSession, 5))
   176  	insertTestData(t, db, entries)
   177  
   178  	activeSessions, err := GetActiveSessions(db, entries[2].Expiry)
   179  	require.NoError(t, err)
   180  
   181  	require.Equal(t, 2, len(activeSessions))
   182  	// Expect newest on top
   183  	require.Equal(t, entries[4], activeSessions[0])
   184  	require.Equal(t, entries[2], activeSessions[1])
   185  }
   186  
   187  func TestDeleteSession(t *testing.T) {
   188  	db, close := SetupTestDB(t)
   189  	defer close()
   190  
   191  	entries := generateTestData(make([]testSession, 3))
   192  	insertTestData(t, db, entries)
   193  
   194  	err := DeleteSession(db, entries[1].Topic)
   195  	require.NoError(t, err)
   196  
   197  	sessions, err := GetSessions(db)
   198  	require.NoError(t, err)
   199  	require.Equal(t, 2, len(sessions))
   200  
   201  	require.Equal(t, entries[0], sessions[1])
   202  	require.Equal(t, entries[2], sessions[0])
   203  
   204  	err = DeleteSession(db, entries[0].Topic)
   205  	require.NoError(t, err)
   206  	err = DeleteSession(db, entries[2].Topic)
   207  	require.NoError(t, err)
   208  
   209  	sessions, err = GetSessions(db)
   210  	require.NoError(t, err)
   211  	require.Equal(t, 0, len(sessions))
   212  }
   213  
   214  // urlFor prepares a value to be used in testSession
   215  func urlFor(i int) urlOverride {
   216  	return common.NewAndSet(testDappUrl + strconv.Itoa(i))
   217  }
   218  
   219  // at prepares a value to be used in testSession
   220  func at(i int) timestampOverride {
   221  	return common.NewAndSet(int64(i))
   222  }
   223  
   224  // TestGetActiveDapps_JoinWorksAsExpected also validates that GetActiveDapps returns the dapps in the order of the last first time added
   225  func TestGetActiveDapps_JoinWorksAsExpected(t *testing.T) {
   226  	db, close := SetupTestDB(t)
   227  	defer close()
   228  
   229  	not := common.NewAndSet(false)
   230  	// The first creation date is 1, 2, 3 but the last name update is, respectively, 1, 4, 5
   231  	entries := generateTestData([]testSession{
   232  		{url: urlFor(1), created: at(1), disconnected: not},
   233  		{url: urlFor(1), created: at(2), disconnected: not},
   234  		{url: urlFor(2), created: at(3), disconnected: not},
   235  		{url: urlFor(3), created: at(4), disconnected: not},
   236  		{url: urlFor(2), created: at(5), disconnected: not},
   237  		{url: urlFor(3), created: at(6), disconnected: not},
   238  	})
   239  	insertTestData(t, db, entries)
   240  
   241  	getTestnet := false
   242  	validAtTimestamp := entries[0].Expiry
   243  	dapps, err := GetActiveDapps(db, validAtTimestamp, getTestnet)
   244  	require.NoError(t, err)
   245  	require.Equal(t, 3, len(dapps))
   246  
   247  	require.Equal(t, 3, len(dapps))
   248  	require.Equal(t, entries[5].Name, dapps[0].Name)
   249  	require.Equal(t, entries[4].Name, dapps[1].Name)
   250  	require.Equal(t, entries[1].Name, dapps[2].Name)
   251  }
   252  
   253  // TestGetActiveDapps_ActiveWorksAsExpected tests the combination of disconnected and expired sessions
   254  func TestGetActiveDapps_ActiveWorksAsExpected(t *testing.T) {
   255  	db, close := SetupTestDB(t)
   256  	defer close()
   257  
   258  	not := common.NewAndSet(false)
   259  	yes := common.NewAndSet(true)
   260  	timeNow := 4
   261  	entries := generateTestData([]testSession{
   262  		{url: urlFor(1), expiry: at(timeNow - 3), disconnected: not},
   263  		{url: urlFor(1), expiry: at(timeNow - 2), disconnected: yes},
   264  		{url: urlFor(2), expiry: at(timeNow - 2), disconnected: not},
   265  		{url: urlFor(3), expiry: at(timeNow - 1), disconnected: yes},
   266  		// ----- timeNow
   267  		{url: urlFor(3), expiry: at(timeNow + 1), disconnected: not},
   268  		{url: urlFor(4), expiry: at(timeNow + 1), disconnected: yes},
   269  		{url: urlFor(4), expiry: at(timeNow + 2), disconnected: not},
   270  		{url: urlFor(5), expiry: at(timeNow + 2), disconnected: yes},
   271  		{url: urlFor(6), expiry: at(timeNow + 3), disconnected: not},
   272  	})
   273  	insertTestData(t, db, entries)
   274  
   275  	getTestnet := false
   276  	dapps, err := GetActiveDapps(db, int64(timeNow), getTestnet)
   277  	require.NoError(t, err)
   278  	require.Equal(t, 3, len(dapps))
   279  }
   280  
   281  // TestGetActiveDapps_TestChainsWorksAsExpected tests the combination of disconnected and expired sessions
   282  func TestGetActiveDapps_TestChainsWorksAsExpected(t *testing.T) {
   283  	db, close := SetupTestDB(t)
   284  	defer close()
   285  
   286  	not := common.NewAndSet(false)
   287  	yes := common.NewAndSet(true)
   288  	timeNow := 4
   289  	entries := generateTestData([]testSession{
   290  		{url: urlFor(1), testChains: not, expiry: at(timeNow - 3), disconnected: not},
   291  		{url: urlFor(2), testChains: yes, expiry: at(timeNow - 2), disconnected: not},
   292  		{url: urlFor(2), testChains: not, expiry: at(timeNow - 1), disconnected: not},
   293  		// ----- timeNow
   294  		{url: urlFor(3), testChains: not, expiry: at(timeNow + 1), disconnected: not},
   295  		{url: urlFor(4), testChains: not, expiry: at(timeNow + 2), disconnected: not},
   296  		{url: urlFor(4), testChains: yes, expiry: at(timeNow + 3), disconnected: not},
   297  		{url: urlFor(5), testChains: yes, expiry: at(timeNow + 4), disconnected: not},
   298  	})
   299  	insertTestData(t, db, entries)
   300  
   301  	getTestnet := true
   302  	dapps, err := GetActiveDapps(db, int64(timeNow), getTestnet)
   303  	require.NoError(t, err)
   304  	require.Equal(t, 2, len(dapps))
   305  }
   306  
   307  // TestGetDapps_EmptyDB tests that an empty database will return an empty list
   308  func TestGetDapps_EmptyDB(t *testing.T) {
   309  	db, close := SetupTestDB(t)
   310  	defer close()
   311  
   312  	entries := generateTestData([]testSession{})
   313  	insertTestData(t, db, entries)
   314  
   315  	getTestnet := false
   316  	validAtTimestamp := int64(0)
   317  	dapps, err := GetActiveDapps(db, validAtTimestamp, getTestnet)
   318  	require.NoError(t, err)
   319  	require.Equal(t, 0, len(dapps))
   320  }
   321  
   322  // TestGetDapps_OrphanDapps tests that missing session will place the dapp at the end
   323  func TestGetDapps_OrphanDapps(t *testing.T) {
   324  	db, close := SetupTestDB(t)
   325  	defer close()
   326  
   327  	not := common.NewAndSet(false)
   328  	entries := generateTestData([]testSession{
   329  		{url: urlFor(1), disconnected: not},
   330  		{url: urlFor(2), disconnected: not},
   331  		{url: urlFor(2), disconnected: not},
   332  	})
   333  	insertTestData(t, db, entries)
   334  
   335  	err := DeleteSession(db, entries[1].Topic)
   336  	require.NoError(t, err)
   337  	err = DeleteSession(db, entries[2].Topic)
   338  	require.NoError(t, err)
   339  
   340  	getTestnet := false
   341  	validAtTimestamp := entries[0].Expiry
   342  	dapps, err := GetActiveDapps(db, validAtTimestamp, getTestnet)
   343  	require.NoError(t, err)
   344  	// The orphan dapp is not considered active
   345  	require.Equal(t, 1, len(dapps))
   346  	require.Equal(t, entries[0].Name, dapps[0].Name)
   347  }
   348  
   349  func TestDisconnectSession(t *testing.T) {
   350  	db, close := SetupTestDB(t)
   351  	defer close()
   352  
   353  	not := common.NewAndSet(false)
   354  	entries := generateTestData([]testSession{
   355  		{url: urlFor(1), disconnected: not},
   356  		{url: urlFor(2), disconnected: not},
   357  		{url: urlFor(2), disconnected: not},
   358  	})
   359  	insertTestData(t, db, entries)
   360  
   361  	activeSessions, err := GetActiveSessions(db, 0)
   362  	require.NoError(t, err)
   363  	require.Equal(t, 3, len(activeSessions))
   364  
   365  	getTestnet := false
   366  	validAtTimestamp := entries[0].Expiry
   367  	dapps, err := GetActiveDapps(db, validAtTimestamp, getTestnet)
   368  	require.NoError(t, err)
   369  	require.Equal(t, 2, len(dapps))
   370  
   371  	err = DisconnectSession(db, entries[1].Topic)
   372  	require.NoError(t, err)
   373  
   374  	activeSessions, err = GetActiveSessions(db, 0)
   375  	require.NoError(t, err)
   376  	require.Equal(t, 2, len(activeSessions))
   377  
   378  	err = DisconnectSession(db, entries[2].Topic)
   379  	require.NoError(t, err)
   380  
   381  	activeSessions, err = GetActiveSessions(db, 0)
   382  	require.NoError(t, err)
   383  	require.Equal(t, 1, len(activeSessions))
   384  
   385  	dapps, err = GetActiveDapps(db, validAtTimestamp, getTestnet)
   386  	require.NoError(t, err)
   387  	require.Equal(t, 1, len(dapps))
   388  	require.Equal(t, entries[0].Name, dapps[0].Name)
   389  }