github.com/condensat/bank-core@v0.1.0/database/query/cryptoaddress_test.go (about)

     1  // Copyright 2020 Condensat Tech. All rights reserved.
     2  // Use of this source code is governed by a MIT
     3  // license that can be found in the LICENSE file.
     4  
     5  package query
     6  
     7  import (
     8  	"reflect"
     9  	"sort"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/condensat/bank-core/database"
    14  	"github.com/condensat/bank-core/database/model"
    15  	"github.com/condensat/bank-core/database/query/tests"
    16  )
    17  
    18  func Test_cryptoAddressColumnNames(t *testing.T) {
    19  	t.Parallel()
    20  
    21  	fields := tests.GetSortedTypeFileds(reflect.TypeOf(model.CryptoAddress{}))
    22  	names := cryptoAddressColumnNames()
    23  	sort.Strings(names)
    24  
    25  	if !reflect.DeepEqual(names, fields) {
    26  		t.Errorf("columnsNames() = %v, want %v", names, fields)
    27  	}
    28  }
    29  
    30  func TestAddOrUpdateCryptoAddress(t *testing.T) {
    31  	const databaseName = "TestAddOrUpdateCryptoAddress"
    32  	t.Parallel()
    33  
    34  	db := tests.Setup(databaseName, CryptoAddressModel())
    35  	defer tests.Teardown(db, databaseName)
    36  
    37  	const chain = model.String("chain1")
    38  
    39  	// create db entry for duplicate test
    40  	existingPublicAddress := model.String("bar bar")
    41  	_, err := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 100, PublicAddress: existingPublicAddress, Chain: chain})
    42  	if err != nil {
    43  		t.Errorf("failed to AddOrUpdateCryptoAddress for duplicate tests")
    44  	}
    45  
    46  	type args struct {
    47  		address model.CryptoAddress
    48  	}
    49  	tests := []struct {
    50  		name    string
    51  		args    args
    52  		want    model.CryptoAddress
    53  		wantErr bool
    54  	}{
    55  		{"default", args{}, model.CryptoAddress{}, true},
    56  
    57  		{"invalidAccountID", args{model.CryptoAddress{PublicAddress: "foo"}}, model.CryptoAddress{}, true},
    58  		{"invalidPublicAddress", args{model.CryptoAddress{AccountID: 42}}, model.CryptoAddress{}, true},
    59  
    60  		{"InvalidDuplicates", args{model.CryptoAddress{AccountID: 101, PublicAddress: existingPublicAddress, Chain: chain}}, model.CryptoAddress{}, true},
    61  		{"invalidChain", args{model.CryptoAddress{AccountID: 1337, PublicAddress: "foo"}}, model.CryptoAddress{}, true},
    62  		{"invalidAllChain", args{model.CryptoAddress{AccountID: 1337, PublicAddress: "foo", Chain: AllChains}}, model.CryptoAddress{}, true},
    63  
    64  		{"valid", args{model.CryptoAddress{AccountID: 1337, PublicAddress: "foo", Chain: chain}}, model.CryptoAddress{AccountID: 1337, PublicAddress: "foo", Chain: chain}, false},
    65  		{"validMultiple", args{model.CryptoAddress{AccountID: 1337, PublicAddress: "bar", Chain: chain}}, model.CryptoAddress{AccountID: 1337, PublicAddress: "bar", Chain: chain}, false},
    66  	}
    67  	for _, tt := range tests {
    68  		tt := tt // capture range variable
    69  		t.Run(tt.name, func(t *testing.T) {
    70  			got, err := AddOrUpdateCryptoAddress(db, tt.args.address)
    71  			if (err != nil) != tt.wantErr {
    72  				t.Errorf("AddOrUpdateCryptoAddress() error = %v, wantErr %v", err, tt.wantErr)
    73  				return
    74  			}
    75  			if tt.wantErr {
    76  				return
    77  			}
    78  
    79  			// skip update tests of no entry was created
    80  			if got.AccountID == 0 {
    81  				return
    82  			}
    83  
    84  			if got.CreationDate == nil || got.CreationDate.IsZero() {
    85  				t.Errorf("Invalid CreationDate: %v", got.CreationDate)
    86  				return
    87  			}
    88  
    89  			{
    90  				want := cloneCryptoAddress(tt.want)
    91  				want.ID = got.ID
    92  				want.CreationDate = got.CreationDate // set CreationDate for DeepEqual
    93  
    94  				if !reflect.DeepEqual(got, want) {
    95  					t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", got, want)
    96  				}
    97  			}
    98  
    99  			ref, _ := GetCryptoAddress(db, got.ID)
   100  			checkCryptoAddressUpdate(t, db, ref)
   101  		})
   102  	}
   103  }
   104  
   105  func TestGetCryptoAddress(t *testing.T) {
   106  	const databaseName = "TestGetCryptoAddress"
   107  	t.Parallel()
   108  
   109  	db := tests.Setup(databaseName, CryptoAddressModel())
   110  	defer tests.Teardown(db, databaseName)
   111  
   112  	const chain = model.String("chain1")
   113  
   114  	accountID := model.AccountID(42)
   115  	ref1, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref1", Chain: chain})
   116  
   117  	type args struct {
   118  		ID model.CryptoAddressID
   119  	}
   120  	tests := []struct {
   121  		name    string
   122  		args    args
   123  		want    model.CryptoAddress
   124  		wantErr bool
   125  	}{
   126  		{"empty", args{}, model.CryptoAddress{}, true},
   127  		{"notFound", args{42}, model.CryptoAddress{}, true},
   128  		{"ref1", args{ref1.ID}, cloneCryptoAddress(ref1), false},
   129  	}
   130  	for _, tt := range tests {
   131  		tt := tt // capture range variable
   132  		t.Run(tt.name, func(t *testing.T) {
   133  			got, err := GetCryptoAddress(db, tt.args.ID)
   134  			if (err != nil) != tt.wantErr {
   135  				t.Errorf("GetCryptoAddress() error = %v, wantErr %v", err, tt.wantErr)
   136  				return
   137  			}
   138  			if !reflect.DeepEqual(got, tt.want) {
   139  				t.Errorf("GetCryptoAddress() = %v, want %v", got, tt.want)
   140  			}
   141  		})
   142  	}
   143  }
   144  
   145  func TestGetCryptoAddressWithPublicAddress(t *testing.T) {
   146  	const databaseName = "TestGetCryptoAddressWithPublicAddress"
   147  	t.Parallel()
   148  
   149  	db := tests.Setup(databaseName, CryptoAddressModel())
   150  	defer tests.Teardown(db, databaseName)
   151  
   152  	const chain = model.String("chain1")
   153  	const pubAddr = model.String("ref1")
   154  
   155  	accountID := model.AccountID(42)
   156  	ref1, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: pubAddr, Chain: chain})
   157  
   158  	type args struct {
   159  		publicAddress model.String
   160  	}
   161  	tests := []struct {
   162  		name    string
   163  		args    args
   164  		want    model.CryptoAddress
   165  		wantErr bool
   166  	}{
   167  		{"empty", args{}, model.CryptoAddress{}, true},
   168  		{"notFound", args{"not-present"}, model.CryptoAddress{}, true},
   169  		{"ref1", args{pubAddr}, cloneCryptoAddress(ref1), false},
   170  	}
   171  	for _, tt := range tests {
   172  		tt := tt // capture range variable
   173  		t.Run(tt.name, func(t *testing.T) {
   174  			got, err := GetCryptoAddressWithPublicAddress(db, tt.args.publicAddress)
   175  			if (err != nil) != tt.wantErr {
   176  				t.Errorf("GetCryptoAddressWithPublicAddress() error = %v, wantErr %v", err, tt.wantErr)
   177  				return
   178  			}
   179  			if !reflect.DeepEqual(got, tt.want) {
   180  				t.Errorf("GetCryptoAddressWithPublicAddress() = %v, want %v", got, tt.want)
   181  			}
   182  		})
   183  	}
   184  }
   185  
   186  func TestLastAccountCryptoAddress(t *testing.T) {
   187  	const databaseName = "TestLastAccountCryptoAddress"
   188  	t.Parallel()
   189  
   190  	db := tests.Setup(databaseName, CryptoAddressModel())
   191  	defer tests.Teardown(db, databaseName)
   192  
   193  	const chain = model.String("chain1")
   194  
   195  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 42, PublicAddress: "ref1", Chain: chain})
   196  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 42, PublicAddress: "ref2", Chain: chain})
   197  	lastRef, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 42, PublicAddress: "ref3", Chain: chain})
   198  
   199  	type args struct {
   200  		accountID model.AccountID
   201  	}
   202  	tests := []struct {
   203  		name    string
   204  		args    args
   205  		want    model.CryptoAddress
   206  		wantErr bool
   207  	}{
   208  		{"Default", args{}, model.CryptoAddress{}, true},
   209  		{"NotFound", args{1337}, model.CryptoAddress{}, false},
   210  
   211  		{"Valid", args{lastRef.AccountID}, lastRef, false},
   212  	}
   213  	for _, tt := range tests {
   214  		tt := tt // capture range variable
   215  		t.Run(tt.name, func(t *testing.T) {
   216  			got, err := LastAccountCryptoAddress(db, tt.args.accountID)
   217  			if (err != nil) != tt.wantErr {
   218  				t.Errorf("LastAccountCryptoAddress() error = %v, wantErr %v", err, tt.wantErr)
   219  				return
   220  			}
   221  
   222  			want := cloneCryptoAddress(tt.want)
   223  
   224  			if !reflect.DeepEqual(got, want) {
   225  				t.Errorf("LastAccountCryptoAddress() = %v, want %v", got, want)
   226  			}
   227  		})
   228  	}
   229  }
   230  
   231  func TestAllAccountCryptoAddresses(t *testing.T) {
   232  	const databaseName = "TestAllAccountCryptoAddresses"
   233  	t.Parallel()
   234  
   235  	db := tests.Setup(databaseName, CryptoAddressModel())
   236  	defer tests.Teardown(db, databaseName)
   237  
   238  	const chain = model.String("chain1")
   239  
   240  	accountID := model.AccountID(42)
   241  	ref1, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref1", Chain: chain})
   242  	ref2, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref2", Chain: chain})
   243  	ref3, _ := AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref3", Chain: chain})
   244  	allRefs := []model.CryptoAddress{
   245  		ref1,
   246  		ref2,
   247  		ref3,
   248  	}
   249  
   250  	type args struct {
   251  		accountID model.AccountID
   252  	}
   253  	tests := []struct {
   254  		name    string
   255  		args    args
   256  		want    []model.CryptoAddress
   257  		wantErr bool
   258  	}{
   259  		{"Default", args{}, nil, true},
   260  		{"NotFound", args{1337}, nil, false},
   261  
   262  		{"Valid", args{accountID}, allRefs, false},
   263  	}
   264  	for _, tt := range tests {
   265  		tt := tt // capture range variable
   266  		t.Run(tt.name, func(t *testing.T) {
   267  			got, err := AllAccountCryptoAddresses(db, tt.args.accountID)
   268  			if (err != nil) != tt.wantErr {
   269  				t.Errorf("LastAccountCryptoAddress() error = %v, wantErr %v", err, tt.wantErr)
   270  				return
   271  			}
   272  
   273  			if !reflect.DeepEqual(got, tt.want) {
   274  				t.Errorf("LastAccountCryptoAddress() = %v, want %v", got, tt.want)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func TestAllUnusedAccountCryptoAddresses(t *testing.T) {
   281  	const databaseName = "TestAllUnusedAccountCryptoAddresses"
   282  	t.Parallel()
   283  
   284  	db := tests.Setup(databaseName, CryptoAddressModel())
   285  	defer tests.Teardown(db, databaseName)
   286  
   287  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 42, PublicAddress: "ref1", Chain: "chain1", FirstBlockId: 424242})
   288  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 42, PublicAddress: "ref2", Chain: "chain1", FirstBlockId: 0})
   289  
   290  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 1337, PublicAddress: "ref3", Chain: "chain2", FirstBlockId: 1})
   291  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 1337, PublicAddress: "ref4", Chain: "chain2", FirstBlockId: 0})
   292  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: 1337, PublicAddress: "ref5", Chain: "chain2", FirstBlockId: 0})
   293  
   294  	type args struct {
   295  		accountID model.AccountID
   296  	}
   297  	tests := []struct {
   298  		name    string
   299  		args    args
   300  		want    int
   301  		wantErr bool
   302  	}{
   303  		{"default", args{0}, 0, true},
   304  
   305  		{"account42", args{42}, 1, false},
   306  		{"account1337", args{1337}, 2, false},
   307  	}
   308  	for _, tt := range tests {
   309  		tt := tt // capture range variable
   310  		t.Run(tt.name, func(t *testing.T) {
   311  			got, err := AllUnusedAccountCryptoAddresses(db, tt.args.accountID)
   312  			if (err != nil) != tt.wantErr {
   313  				t.Errorf("AllUnusedAccountCryptoAddresses() error = %v, wantErr %v", err, tt.wantErr)
   314  				return
   315  			}
   316  			if len(got) != tt.want {
   317  				t.Errorf("AllUnusedAccountCryptoAddresses() = %v, want %v", len(got), tt.want)
   318  			}
   319  		})
   320  	}
   321  }
   322  
   323  func TestAllUnusedCryptoAddresses(t *testing.T) {
   324  	const databaseName = "TestAllUnusedCryptoAddresses"
   325  	t.Parallel()
   326  
   327  	db := tests.Setup(databaseName, CryptoAddressModel())
   328  	defer tests.Teardown(db, databaseName)
   329  
   330  	accountID := model.AccountID(42)
   331  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref1", Chain: "chain1", FirstBlockId: 424242})
   332  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref2", Chain: "chain1", FirstBlockId: 0})
   333  
   334  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref3", Chain: "chain2", FirstBlockId: 1})
   335  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref4", Chain: "chain2", FirstBlockId: 0})
   336  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref5", Chain: "chain2", FirstBlockId: 0})
   337  
   338  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref7", Chain: "chain3", FirstBlockId: 0})
   339  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref8", Chain: "chain3", FirstBlockId: 0})
   340  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref9", Chain: "chain3", FirstBlockId: 0})
   341  
   342  	type args struct {
   343  		chain model.String
   344  	}
   345  	tests := []struct {
   346  		name    string
   347  		args    args
   348  		want    int
   349  		wantErr bool
   350  	}{
   351  		{"default", args{""}, 0, true},
   352  		{"allchains", args{"*"}, 6, false},
   353  
   354  		{"chain1", args{"chain1"}, 1, false},
   355  		{"chain2", args{"chain2"}, 2, false},
   356  		{"chain3", args{"chain3"}, 3, false},
   357  	}
   358  	for _, tt := range tests {
   359  		tt := tt // capture range variable
   360  		t.Run(tt.name, func(t *testing.T) {
   361  			got, err := AllUnusedCryptoAddresses(db, tt.args.chain)
   362  			if (err != nil) != tt.wantErr {
   363  				t.Errorf("AllUnusedCryptoAddresses() error = %v, wantErr %v", err, tt.wantErr)
   364  				return
   365  			}
   366  			if len(got) != tt.want {
   367  				t.Errorf("AllUnusedCryptoAddresses() = %v, want %v", len(got), tt.want)
   368  			}
   369  		})
   370  	}
   371  }
   372  
   373  func TestAllMempoolCryptoAddresses(t *testing.T) {
   374  	const databaseName = "TestAllMempoolCryptoAddresses"
   375  	t.Parallel()
   376  
   377  	db := tests.Setup(databaseName, CryptoAddressModel())
   378  	defer tests.Teardown(db, databaseName)
   379  
   380  	accountID := model.AccountID(42)
   381  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref1", Chain: "chain1", FirstBlockId: 424242})
   382  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref2", Chain: "chain1", FirstBlockId: 0})
   383  
   384  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref3", Chain: "chain2", FirstBlockId: 1})
   385  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref4", Chain: "chain2", FirstBlockId: 0})
   386  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref5", Chain: "chain2", FirstBlockId: 0})
   387  
   388  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref7", Chain: "chain3", FirstBlockId: 0})
   389  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref8", Chain: "chain3", FirstBlockId: 0})
   390  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref9", Chain: "chain3", FirstBlockId: 0})
   391  
   392  	type args struct {
   393  		chain model.String
   394  	}
   395  	tests := []struct {
   396  		name    string
   397  		args    args
   398  		want    int
   399  		wantErr bool
   400  	}{
   401  		{"default", args{""}, 0, true},
   402  		{"allchains", args{"*"}, 1, false},
   403  
   404  		{"chain1", args{"chain1"}, 0, false},
   405  		{"chain2", args{"chain2"}, 1, false},
   406  		{"chain3", args{"chain3"}, 0, false},
   407  	}
   408  	for _, tt := range tests {
   409  		tt := tt // capture range variable
   410  		t.Run(tt.name, func(t *testing.T) {
   411  			got, err := AllMempoolCryptoAddresses(db, tt.args.chain)
   412  			if (err != nil) != tt.wantErr {
   413  				t.Errorf("AllUnusedCryptoAddresses() error = %v, wantErr %v", err, tt.wantErr)
   414  				return
   415  			}
   416  			if len(got) != tt.want {
   417  				t.Errorf("AllUnusedCryptoAddresses() = %v, want %v", len(got), tt.want)
   418  			}
   419  		})
   420  	}
   421  }
   422  
   423  func TestAllUnconfirmedCryptoAddresses(t *testing.T) {
   424  	const databaseName = "TestAllUnconfirmedCryptoAddresses"
   425  	t.Parallel()
   426  
   427  	db := tests.Setup(databaseName, CryptoAddressModel())
   428  	defer tests.Teardown(db, databaseName)
   429  
   430  	accountID := model.AccountID(42)
   431  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref1", Chain: "chain1", FirstBlockId: 424242})
   432  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref2", Chain: "chain1", FirstBlockId: 0})
   433  
   434  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref3", Chain: "chain2", FirstBlockId: 1337})
   435  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref4", Chain: "chain2", FirstBlockId: 1})
   436  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref5", Chain: "chain2", FirstBlockId: 0})
   437  
   438  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref7", Chain: "chain3", FirstBlockId: 1337})
   439  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref8", Chain: "chain3", FirstBlockId: 424242})
   440  	_, _ = AddOrUpdateCryptoAddress(db, model.CryptoAddress{AccountID: accountID, PublicAddress: "ref9", Chain: "chain3", FirstBlockId: 0})
   441  
   442  	type args struct {
   443  		chain      model.String
   444  		afterBlock model.BlockID
   445  	}
   446  	tests := []struct {
   447  		name    string
   448  		args    args
   449  		want    int
   450  		wantErr bool
   451  	}{
   452  		{"default", args{"", 424242}, 0, true},
   453  
   454  		// all chains
   455  		{"allchainsStep1A", args{"*", 1336}, 4, false},
   456  		{"allchainsStep1E", args{"*", 1337}, 4, false},
   457  		{"allchainsStep1B", args{"*", 1338}, 2, false},
   458  
   459  		{"allchainsStep2A", args{"*", 424241}, 2, false},
   460  		{"allchainsStep2E", args{"*", 424242}, 2, false},
   461  		{"allchainsStep3B", args{"*", 424243}, 0, false},
   462  
   463  		// chain 1
   464  		{"chain1Step1A", args{"chain1", 1336}, 1, false},
   465  		{"chain1Step1E", args{"chain1", 1337}, 1, false},
   466  		{"chain1Step1B", args{"chain1", 1338}, 1, false},
   467  
   468  		{"chain1Step2A", args{"chain1", 424241}, 1, false},
   469  		{"chain1Step2E", args{"chain1", 424242}, 1, false},
   470  		{"chain1Step2B", args{"chain1", 424243}, 0, false},
   471  
   472  		// chain 2
   473  		{"chain2Step1A", args{"chain2", 1336}, 1, false},
   474  		{"chain2Step1E", args{"chain2", 1337}, 1, false},
   475  		{"chain2Step1B", args{"chain2", 1338}, 0, false},
   476  
   477  		{"chain2Step2A", args{"chain2", 424241}, 0, false},
   478  		{"chain2Step2E", args{"chain2", 424242}, 0, false},
   479  		{"chain2Step2B", args{"chain2", 424243}, 0, false},
   480  
   481  		// chain 3
   482  		{"chain3Step1A", args{"chain3", 1336}, 2, false},
   483  		{"chain3Step1E", args{"chain3", 1337}, 2, false},
   484  		{"chain3Step1B", args{"chain3", 1338}, 1, false},
   485  
   486  		{"chain3Step2A", args{"chain3", 424241}, 1, false},
   487  		{"chain3Step2E", args{"chain3", 424242}, 1, false},
   488  		{"chain3Step2B", args{"chain3", 424243}, 0, false},
   489  	}
   490  	for _, tt := range tests {
   491  		tt := tt // capture range variable
   492  		t.Run(tt.name, func(t *testing.T) {
   493  			got, err := AllUnconfirmedCryptoAddresses(db, tt.args.chain, tt.args.afterBlock)
   494  			if (err != nil) != tt.wantErr {
   495  				t.Errorf("AllUnusedCryptoAddresses() error = %v, wantErr %v", err, tt.wantErr)
   496  				return
   497  			}
   498  			if len(got) != tt.want {
   499  				t.Errorf("AllUnusedCryptoAddresses() = %v, want %v", len(got), tt.want)
   500  			}
   501  		})
   502  	}
   503  }
   504  
   505  func checkCryptoAddressUpdate(t *testing.T, db database.Context, ref model.CryptoAddress) {
   506  	// fetch from db
   507  	{
   508  		list, err := AllAccountCryptoAddresses(db, ref.AccountID)
   509  		if err != nil {
   510  			t.Errorf("GetCryptoAddressByAccountID() error= %v", err)
   511  		}
   512  		if ok, got := containsCryptoAddress(list, ref); ok {
   513  			if !reflect.DeepEqual(got, ref) {
   514  				t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", got, ref)
   515  			}
   516  		}
   517  	}
   518  
   519  	// do not change CreationDate
   520  	{
   521  		want, _ := GetCryptoAddress(db, ref.ID)
   522  		cpy, _ := GetCryptoAddress(db, ref.ID)
   523  
   524  		timestamp := time.Now().UTC().Truncate(time.Second).Add(3 * time.Second)
   525  		cpy.CreationDate = &timestamp
   526  
   527  		update, err := AddOrUpdateCryptoAddress(db, cpy)
   528  		if err != nil {
   529  			t.Errorf("AddOrUpdateCryptoAddress() error= %v", err)
   530  		}
   531  		if !reflect.DeepEqual(update, want) {
   532  			t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", update, want)
   533  		}
   534  
   535  		check, _ := GetCryptoAddress(db, ref.ID)
   536  		if !reflect.DeepEqual(check, update) {
   537  			t.Errorf("CreationDate change stored = %+v, want %+v", check, update)
   538  		}
   539  	}
   540  
   541  	// change PublicAddress
   542  	{
   543  		want, _ := GetCryptoAddress(db, ref.ID)
   544  		cpy, _ := GetCryptoAddress(db, ref.ID)
   545  
   546  		cpy.PublicAddress = model.String(tests.RandSeq(4))
   547  
   548  		update, err := AddOrUpdateCryptoAddress(db, cpy)
   549  		if err != nil {
   550  			t.Errorf("AddOrUpdateCryptoAddress() error= %v", err)
   551  		}
   552  		if !reflect.DeepEqual(update, want) {
   553  			t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", update, want)
   554  		}
   555  
   556  		check, _ := GetCryptoAddress(db, ref.ID)
   557  		if !reflect.DeepEqual(check, update) {
   558  			t.Errorf("PublicAddress change not stored = %+v, want %+v", check, update)
   559  		}
   560  	}
   561  
   562  	// do not revert PublicAddress to empty
   563  	{
   564  		want := model.CryptoAddress{}
   565  		cpy, _ := GetCryptoAddress(db, ref.ID)
   566  
   567  		cpy.PublicAddress = ""
   568  
   569  		update, err := AddOrUpdateCryptoAddress(db, cpy)
   570  		if err == nil {
   571  			t.Errorf("AddOrUpdateCryptoAddress() should fails")
   572  		}
   573  		if !reflect.DeepEqual(update, want) {
   574  			t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", update, want)
   575  		}
   576  	}
   577  
   578  	// Mempool
   579  	{
   580  		want, _ := GetCryptoAddress(db, ref.ID)
   581  		cpy, _ := GetCryptoAddress(db, ref.ID)
   582  
   583  		want.FirstBlockId = 1
   584  		cpy.FirstBlockId = want.FirstBlockId
   585  
   586  		update, err := AddOrUpdateCryptoAddress(db, cpy)
   587  		if err != nil {
   588  			t.Errorf("AddOrUpdateCryptoAddress() error= %v", err)
   589  		}
   590  		if !reflect.DeepEqual(update, want) {
   591  			t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", update, want)
   592  		}
   593  
   594  		if !update.IsUsed() {
   595  			t.Errorf("Updated CryptoAddress should be in use: %+v, want %+v", update, want)
   596  		}
   597  
   598  		store, _ := GetCryptoAddress(db, ref.ID)
   599  		if !reflect.DeepEqual(store, update) {
   600  			t.Errorf("Mempool change not stored = %+v, want %+v", store, update)
   601  		}
   602  	}
   603  
   604  	// Mined
   605  	{
   606  		want, _ := GetCryptoAddress(db, ref.ID)
   607  		cpy, _ := GetCryptoAddress(db, ref.ID)
   608  
   609  		want.FirstBlockId = 424242
   610  		cpy.FirstBlockId = want.FirstBlockId
   611  
   612  		update, err := AddOrUpdateCryptoAddress(db, cpy)
   613  		if err != nil {
   614  			t.Errorf("AddOrUpdateCryptoAddress() error= %v", err)
   615  		}
   616  		if !reflect.DeepEqual(update, want) {
   617  			t.Errorf("AddOrUpdateCryptoAddress() = %+v, want %+v", update, want)
   618  		}
   619  
   620  		if !update.IsUsed() {
   621  			t.Errorf("Updated CryptoAddress should be in use: %+v, want %+v", update, want)
   622  		}
   623  
   624  		if update.Confirmations(424242) != 1 {
   625  			t.Errorf("Failed to update FirstBlockId: %+v, want %+v", update, want)
   626  		}
   627  
   628  		store, _ := GetCryptoAddress(db, update.ID)
   629  		if !reflect.DeepEqual(store, update) {
   630  			t.Errorf("Mined change not stored = %+v, want %+v", store, update)
   631  		}
   632  	}
   633  
   634  	// reset to reference state
   635  	_, err := AddOrUpdateCryptoAddress(db, ref)
   636  	if err != nil {
   637  		t.Errorf("Failed to reset to referecnce state() error= %v", err)
   638  	}
   639  }
   640  
   641  func cloneCryptoAddress(address model.CryptoAddress) model.CryptoAddress {
   642  	result := address
   643  
   644  	if address.CreationDate != nil {
   645  		creationDate := *address.CreationDate
   646  		result.CreationDate = &creationDate
   647  	}
   648  	return result
   649  }
   650  
   651  func containsCryptoAddress(list []model.CryptoAddress, item model.CryptoAddress) (bool, model.CryptoAddress) {
   652  	for _, address := range list {
   653  		if address.ID == item.ID {
   654  			return true, address
   655  		}
   656  	}
   657  	return false, model.CryptoAddress{}
   658  }