diff --git a/store/account_test.go b/store/account_test.go index db568dc..7170acb 100644 --- a/store/account_test.go +++ b/store/account_test.go @@ -20,17 +20,21 @@ func expectAccountMatch( seed auth.ClientSaltSeed, expectedVerifyTokenString *auth.VerifyTokenString, approxVerifyExpiration *time.Time, + approxCreated time.Time, + approxUpdated time.Time, ) { var key auth.KDFKey var salt auth.ServerSalt var email auth.Email var verifyExpiration *time.Time var verifyTokenString *auth.VerifyTokenString + var created time.Time + var updated time.Time err := s.db.QueryRow( - `SELECT key, server_salt, email, verify_token, verify_expiration from accounts WHERE normalized_email=? AND client_salt_seed=?`, + `SELECT key, server_salt, email, verify_token, verify_expiration, created, updated from accounts WHERE normalized_email=? AND client_salt_seed=?`, normEmail, seed, - ).Scan(&key, &salt, &email, &verifyTokenString, &verifyExpiration) + ).Scan(&key, &salt, &email, &verifyTokenString, &verifyExpiration, &created, &updated) if err != nil { t.Fatalf("Error finding account for: %s %s - %+v", normEmail, password, err) } @@ -73,14 +77,32 @@ func expectAccountMatch( if time.Second < expDiff || expDiff < -time.Second { t.Fatalf( "Verify expiration not as expected. Want approximately: %s Got: %s", - verifyExpiration, approxVerifyExpiration, + verifyExpiration, ) } } if approxVerifyExpiration == nil && verifyExpiration != nil { t.Fatalf("Expected verify expiration to be nil. Got: %+v", verifyExpiration) } + + expDiff := approxCreated.Sub(created) + if time.Second*2 < expDiff || expDiff < -time.Second*2 { + t.Fatalf( + "Created timestamp not as expected. Want approximately: %s Got: %s", + approxCreated, + created, + ) + } + + expDiff = approxUpdated.Sub(updated) + if time.Second*2 < expDiff || expDiff < -time.Second*2 { + t.Fatalf( + "Updated timestamp not as expected. Want approximately: %s Got: %s", + approxUpdated, + updated, + ) + } } func expectAccountNotExists(t *testing.T, s *Store, normEmail auth.NormalizedEmail) { @@ -119,7 +141,7 @@ func TestStoreCreateAccount(t *testing.T) { } // Get and confirm the account we just put in - expectAccountMatch(t, &s, normEmail, email, password, seed, nil, nil) + expectAccountMatch(t, &s, normEmail, email, password, seed, nil, nil, time.Now().UTC(), time.Now().UTC()) newPassword := auth.Password("xyz") @@ -138,7 +160,7 @@ func TestStoreCreateAccount(t *testing.T) { } // Get the email and same *first* password we successfully put in - expectAccountMatch(t, &s, normEmail, email, password, seed, nil, nil) + expectAccountMatch(t, &s, normEmail, email, password, seed, nil, nil, time.Now().UTC(), time.Now().UTC()) } // Test that I can use CreateAccount twice for different emails with no veriy token @@ -165,8 +187,8 @@ func TestStoreCreateAccountTwoVerifiedSucceed(t *testing.T) { } // Get and confirm the accounts we just put in - expectAccountMatch(t, &s, normEmail1, email1, password1, seed1, nil, nil) - expectAccountMatch(t, &s, normEmail2, email2, password2, seed2, nil, nil) + expectAccountMatch(t, &s, normEmail1, email1, password1, seed1, nil, nil, time.Now().UTC(), time.Now().UTC()) + expectAccountMatch(t, &s, normEmail2, email2, password2, seed2, nil, nil, time.Now().UTC(), time.Now().UTC()) } // Test that I cannot use CreateAccount twice with the same verify token, but @@ -204,8 +226,8 @@ func TestStoreCreateAccountTwoSameVerfiyTokenFail(t *testing.T) { // Get and confirm the accounts we just put in approxVerifyExpiration := time.Now().Add(time.Hour * 24 * 2).UTC() - expectAccountMatch(t, &s, normEmail1, email1, password1, seed1, &verifyToken1, &approxVerifyExpiration) - expectAccountMatch(t, &s, normEmail2, email2, password2, seed2, &verifyToken2, &approxVerifyExpiration) + expectAccountMatch(t, &s, normEmail1, email1, password1, seed1, &verifyToken1, &approxVerifyExpiration, time.Now().UTC(), time.Now().UTC()) + expectAccountMatch(t, &s, normEmail2, email2, password2, seed2, &verifyToken2, &approxVerifyExpiration, time.Now().UTC(), time.Now().UTC()) } // Try CreateAccount with a verification string, thus unverified @@ -224,7 +246,7 @@ func TestStoreCreateAccountUnverified(t *testing.T) { // Get and confirm the account we just put in approxVerifyExpiration := time.Now().Add(time.Hour * 24 * 2).UTC() - expectAccountMatch(t, &s, normEmail, email, password, seed, &verifyToken, &approxVerifyExpiration) + expectAccountMatch(t, &s, normEmail, email, password, seed, &verifyToken, &approxVerifyExpiration, time.Now().UTC(), time.Now().UTC()) } // Test GetUserId for nonexisting email @@ -380,12 +402,12 @@ func TestUpdateVerifyTokenStringSuccess(t *testing.T) { if err := s.UpdateVerifyTokenString(lowerEmail, verifyTokenString2); err != nil { t.Fatalf("Unexpected error in UpdateVerifyTokenString: err: %+v", err) } - expectAccountMatch(t, &s, normEmail, email, password, createdSeed, &verifyTokenString2, &approxVerifyExpiration) + expectAccountMatch(t, &s, normEmail, email, password, createdSeed, &verifyTokenString2, &approxVerifyExpiration, time.Now().UTC(), time.Now().UTC()) if err := s.UpdateVerifyTokenString(upperEmail, verifyTokenString3); err != nil { t.Fatalf("Unexpected error in UpdateVerifyTokenString: err: %+v", err) } - expectAccountMatch(t, &s, normEmail, email, password, createdSeed, &verifyTokenString3, &approxVerifyExpiration) + expectAccountMatch(t, &s, normEmail, email, password, createdSeed, &verifyTokenString3, &approxVerifyExpiration, time.Now().UTC(), time.Now().UTC()) } // Test UpdateVerifyTokenString for nonexisting email @@ -428,7 +450,7 @@ func TestUpdateVerifyAccountSuccess(t *testing.T) { if err := s.VerifyAccount(verifyTokenString); err != nil { t.Fatalf("Unexpected error in VerifyAccount: err: %+v", err) } - expectAccountMatch(t, &s, normEmail, email, password, createdSeed, nil, nil) + expectAccountMatch(t, &s, normEmail, email, password, createdSeed, nil, nil, time.Now().UTC(), time.Now().UTC()) } // Test VerifyAccount for nonexisting token diff --git a/store/password_test.go b/store/password_test.go index ad67a2b..268dfa6 100644 --- a/store/password_test.go +++ b/store/password_test.go @@ -28,11 +28,11 @@ func TestStoreChangePasswordSuccess(t *testing.T) { } _, err = s.db.Exec( - "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)", + "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac, updated) VALUES(?,?,?,?,datetime('now'))", userId, "my-enc-wallet", 1, "my-hmac", ) if err != nil { - t.Fatalf("Error creating test wallet") + t.Fatalf("Error creating test wallet: %s", err.Error()) } newPassword := oldPassword + auth.Password("_new") @@ -47,8 +47,8 @@ func TestStoreChangePasswordSuccess(t *testing.T) { t.Errorf("ChangePasswordWithWallet (lower case email): unexpected error: %+v", err) } - expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, nil, nil) - expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac) + expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, nil, nil, time.Now().UTC(), time.Now().UTC()) + expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac, time.Now().UTC()) expectTokenNotExists(t, &s, token) newNewPassword := newPassword + auth.Password("_new") @@ -63,7 +63,7 @@ func TestStoreChangePasswordSuccess(t *testing.T) { t.Errorf("ChangePasswordWithWallet (upper case email): unexpected error: %+v", err) } - expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, nil, nil) + expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, nil, nil, time.Now().UTC(), time.Now().UTC()) } func TestStoreChangePasswordErrors(t *testing.T) { @@ -152,11 +152,11 @@ func TestStoreChangePasswordErrors(t *testing.T) { if tc.hasWallet { _, err := s.db.Exec( - "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)", + "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac, updated) VALUES(?,?,?,?,datetime('now'))", userId, oldEncryptedWallet, oldSequence, oldHmac, ) if err != nil { - t.Fatalf("Error creating test wallet") + t.Fatalf("Error creating test wallet: %s", err.Error()) } } @@ -173,9 +173,9 @@ func TestStoreChangePasswordErrors(t *testing.T) { // This tests the transaction rollbacks in particular, given the errors // that are at a couple different stages of the txn, triggered by these // tests. - expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration) + expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration, time.Now().UTC(), time.Now().UTC()) if tc.hasWallet { - expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac) + expectWalletExists(t, &s, userId, oldEncryptedWallet, oldSequence, oldHmac, time.Now().UTC()) } else { expectWalletNotExists(t, &s, userId) } @@ -208,7 +208,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) { t.Errorf("ChangePasswordNoWallet (lower case email): unexpected error: %+v", err) } - expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, nil, nil) + expectAccountMatch(t, &s, email.Normalize(), email, newPassword, newSeed, nil, nil, time.Now().UTC(), time.Now().UTC()) expectWalletNotExists(t, &s, userId) expectTokenNotExists(t, &s, token) @@ -221,7 +221,7 @@ func TestStoreChangePasswordNoWalletSuccess(t *testing.T) { t.Errorf("ChangePasswordNoWallet (upper case email): unexpected error: %+v", err) } - expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, nil, nil) + expectAccountMatch(t, &s, email.Normalize(), email, newNewPassword, newNewSeed, nil, nil, time.Now().UTC(), time.Now().UTC()) } func TestStoreChangePasswordNoWalletErrors(t *testing.T) { @@ -295,11 +295,11 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { if tc.hasWallet { _, err := s.db.Exec( - "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)", + "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac, updated) VALUES(?,?,?,?,datetime('now'))", userId, encryptedWallet, sequence, hmac, ) if err != nil { - t.Fatalf("Error creating test wallet") + t.Fatalf("Error creating test wallet: %s", err.Error()) } } @@ -316,9 +316,9 @@ func TestStoreChangePasswordNoWalletErrors(t *testing.T) { // deleted. This tests the transaction rollbacks in particular, given the // errors that are at a couple different stages of the txn, triggered by // these tests. - expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration) + expectAccountMatch(t, &s, email.Normalize(), email, oldPassword, oldSeed, tc.verifyToken, tc.verifyExpiration, time.Now().UTC(), time.Now().UTC()) if tc.hasWallet { - expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac) + expectWalletExists(t, &s, userId, encryptedWallet, sequence, hmac, time.Now().UTC()) } else { expectWalletNotExists(t, &s, userId) } diff --git a/store/store.go b/store/store.go index 0245c93..8f9af4d 100644 --- a/store/store.go +++ b/store/store.go @@ -111,6 +111,8 @@ func (s *Store) Migrate() error { encrypted_wallet TEXT NOT NULL, sequence INTEGER NOT NULL, hmac TEXT NOT NULL, + updated DATETIME NOT NULL, + PRIMARY KEY (user_id) FOREIGN KEY (user_id) REFERENCES accounts(user_id) CHECK ( @@ -134,6 +136,8 @@ func (s *Store) Migrate() error { verify_expiration DATETIME, user_id INTEGER PRIMARY KEY AUTOINCREMENT, + created DATETIME DEFAULT (DATETIME('now')), + updated DATETIME NOT NULL, CHECK ( email <> '' AND normalized_email <> '' AND @@ -281,7 +285,7 @@ func (s *Store) insertFirstWallet( // The database will enforce that this will not be set if this user already // has a wallet. _, err = s.db.Exec( - "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac) VALUES(?,?,?,?)", + "INSERT INTO wallets (user_id, encrypted_wallet, sequence, hmac, updated) VALUES(?,?,?,?, datetime('now'))", userId, encryptedWallet, 1, hmac, ) @@ -310,7 +314,7 @@ func (s *Store) updateWalletToSequence( // This way, if two clients attempt to update at the same time, it will return // an error for the second one. res, err := s.db.Exec( - "UPDATE wallets SET encrypted_wallet=?, sequence=?, hmac=? WHERE user_id=? AND sequence=?", + "UPDATE wallets SET encrypted_wallet=?, sequence=?, hmac=?, updated=datetime('now') WHERE user_id=? AND sequence=?", encryptedWallet, sequence, hmac, userId, sequence-1, ) if err != nil { @@ -403,7 +407,7 @@ func (s *Store) CreateAccount(email auth.Email, password auth.Password, seed aut // userId auto-increments _, err = s.db.Exec( - "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration) VALUES(?,?,?,?,?,?,?)", + "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration, updated) VALUES(?,?,?,?,?,?,?, datetime('now'))", email.Normalize(), email, key, salt, seed, verifyToken, verifyExpiration, ) var sqliteErr sqlite3.Error @@ -426,7 +430,7 @@ func (s *Store) UpdateVerifyTokenString(email auth.Email, verifyTokenString auth expiration := time.Now().UTC().Add(VerifyTokenLifespan) res, err := s.db.Exec( - `UPDATE accounts SET verify_token=?, verify_expiration=? WHERE normalized_email=? and verify_token is not null`, + `UPDATE accounts SET verify_token=?, verify_expiration=?, updated=datetime('now') WHERE normalized_email=? and verify_token is not null`, verifyTokenString, expiration, email.Normalize(), ) if err != nil { @@ -457,7 +461,7 @@ func (s *Store) UpdateVerifyTokenString(email auth.Email, verifyTokenString auth func (s *Store) VerifyAccount(verifyTokenString auth.VerifyTokenString) (err error) { res, err := s.db.Exec( - "UPDATE accounts SET verify_token=null, verify_expiration=null WHERE verify_token=?", + "UPDATE accounts SET verify_token=null, verify_expiration=null, updated=datetime('now') WHERE verify_token=?", verifyTokenString, ) if err != nil { @@ -585,7 +589,7 @@ func (s *Store) changePassword( } res, err := tx.Exec( - "UPDATE accounts SET key=?, server_salt=?, client_salt_seed=? WHERE user_id=?", + "UPDATE accounts SET key=?, server_salt=?, client_salt_seed=?, updated=datetime('now') WHERE user_id=?", newKey, newSalt, clientSaltSeed, userId, ) if err != nil { @@ -605,7 +609,7 @@ func (s *Store) changePassword( // With a wallet expected: update it. res, err = tx.Exec( - `UPDATE wallets SET encrypted_wallet=?, sequence=?, hmac=? + `UPDATE wallets SET encrypted_wallet=?, sequence=?, hmac=?, updated=datetime('now') WHERE user_id=? AND sequence=?`, encryptedWallet, sequence, hmac, userId, sequence-1, ) diff --git a/store/store_test.go b/store/store_test.go index 6f29eec..924cc24 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -51,7 +51,7 @@ func makeTestUser( seed = auth.ClientSaltSeed("abcd1234abcd1234") rows, err := s.db.Query( - "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration) values(?,?,?,?,?,?,?) returning user_id", + "INSERT INTO accounts (normalized_email, email, key, server_salt, client_salt_seed, verify_token, verify_expiration, updated) values(?,?,?,?,?,?,?, datetime('now')) returning user_id", normEmail, email, key, salt, seed, verifyToken, verifyExpiration, ) if err != nil { diff --git a/store/wallet_test.go b/store/wallet_test.go index 8cd8790..e319b20 100644 --- a/store/wallet_test.go +++ b/store/wallet_test.go @@ -3,6 +3,7 @@ package store import ( "errors" "testing" + "time" "github.com/mattn/go-sqlite3" @@ -17,9 +18,10 @@ func expectWalletExists( expectedEncryptedWallet wallet.EncryptedWallet, expectedSequence wallet.Sequence, expectedHmac wallet.WalletHmac, + approxUpdated time.Time, ) { rows, err := s.db.Query( - "SELECT encrypted_wallet, sequence, hmac FROM wallets WHERE user_id=?", userId) + "SELECT encrypted_wallet, sequence, hmac, updated FROM wallets WHERE user_id=?", userId) if err != nil { t.Fatalf("Error finding wallet for user_id=%d: %+v", userId, err) } @@ -28,6 +30,7 @@ func expectWalletExists( var encryptedWallet wallet.EncryptedWallet var sequence wallet.Sequence var hmac wallet.WalletHmac + var updated time.Time for rows.Next() { @@ -35,6 +38,7 @@ func expectWalletExists( &encryptedWallet, &sequence, &hmac, + &updated, ) if err != nil { @@ -45,6 +49,15 @@ func expectWalletExists( t.Fatalf("Unexpected values for wallet: encrypted wallet: %+v sequence: %+v hmac: %+v err: %+v", encryptedWallet, sequence, hmac, err) } + expDiff := approxUpdated.Sub(updated) + if time.Second*2 < expDiff || expDiff < -time.Second*2 { + t.Fatalf( + "Updated timestamp not as expected. Want approximately: %s Got: %s", + approxUpdated, + updated, + ) + } + return // found a match, we're good } t.Fatalf("Expected wallet for user_id=%d: %+v", userId, err) @@ -97,7 +110,7 @@ func TestStoreInsertWallet(t *testing.T) { } // Get a wallet, have the values we put in with a sequence of 1 - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet"), wallet.Sequence(1), wallet.WalletHmac("my-hmac")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet"), wallet.Sequence(1), wallet.WalletHmac("my-hmac"), time.Now().UTC()) // Put in a first wallet for a second time, have an error for trying if err := s.insertFirstWallet(userId, wallet.EncryptedWallet("my-enc-wallet-2"), wallet.WalletHmac("my-hmac-2")); err != ErrDuplicateWallet { @@ -105,7 +118,7 @@ func TestStoreInsertWallet(t *testing.T) { } // Get the same *first* wallet we successfully put in - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet"), wallet.Sequence(1), wallet.WalletHmac("my-hmac")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet"), wallet.Sequence(1), wallet.WalletHmac("my-hmac"), time.Now().UTC()) } // Test updateWalletToSequence, using insertFirstWallet as a helper @@ -139,7 +152,7 @@ func TestStoreUpdateWallet(t *testing.T) { } // Get the same wallet we initially *inserted*, since it didn't update - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a"), time.Now().UTC()) // Update the wallet successfully, with the right sequence if err := s.updateWalletToSequence(userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b")); err != nil { @@ -147,7 +160,7 @@ func TestStoreUpdateWallet(t *testing.T) { } // Get a wallet, have the values we put in - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b"), time.Now().UTC()) // Update the wallet again successfully if err := s.updateWalletToSequence(userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c")); err != nil { @@ -155,7 +168,7 @@ func TestStoreUpdateWallet(t *testing.T) { } // Get a wallet, have the values we put in - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c"), time.Now().UTC()) } // NOTE - the "behind the scenes" comments give a view of what we're expecting @@ -186,33 +199,33 @@ func TestStoreSetWallet(t *testing.T) { if err := s.SetWallet(userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a")); err != nil { t.Fatalf("Unexpected error in SetWallet: %+v", err) } - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a"), time.Now().UTC()) // Sequence 1 - fails - out of sequence (behind the scenes, tries to insert but there's something there already) if err := s.SetWallet(userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-b")); err != ErrWrongSequence { t.Fatalf(`SetWallet err: wanted "%+v", got "%+v"`, ErrWrongSequence, err) } // Expect the *first* wallet to still be there - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a"), time.Now().UTC()) // Sequence 3 - fails - out of sequence (behind the scenes: tries via update, which is appropriate here) if err := s.SetWallet(userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-b")); err != ErrWrongSequence { t.Fatalf(`SetWallet err: wanted "%+v", got "%+v"`, ErrWrongSequence, err) } // Expect the *first* wallet to still be there - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-a"), wallet.Sequence(1), wallet.WalletHmac("my-hmac-a"), time.Now().UTC()) // Sequence 2 - succeeds - (behind the scenes, does an update. Tests successful update-after-insert) if err := s.SetWallet(userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b")); err != nil { t.Fatalf("Unexpected error in SetWallet: %+v", err) } - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-b"), wallet.Sequence(2), wallet.WalletHmac("my-hmac-b"), time.Now().UTC()) // Sequence 3 - succeeds - (behind the scenes, does an update. Tests successful update-after-update. Maybe gratuitous?) if err := s.SetWallet(userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c")); err != nil { t.Fatalf("Unexpected error in SetWallet: %+v", err) } - expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c")) + expectWalletExists(t, &s, userId, wallet.EncryptedWallet("my-enc-wallet-c"), wallet.Sequence(3), wallet.WalletHmac("my-hmac-c"), time.Now().UTC()) } // Pretty simple, only two cases: wallet is there or it's not.