gitlab.com/SkynetLabs/skyd@v1.6.9/cmd/skyc/accountingcmd_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "strconv" 7 "strings" 8 "testing" 9 "time" 10 11 "gitlab.com/NebulousLabs/fastrand" 12 "gitlab.com/SkynetLabs/skyd/skymodules" 13 "go.sia.tech/siad/types" 14 ) 15 16 // randomAcountingInfo is a helper that generates random accounting information 17 func randomAcountingInfo(num int) []skymodules.AccountingInfo { 18 var ais []skymodules.AccountingInfo 19 for i := 0; i < num; i++ { 20 ais = append(ais, skymodules.AccountingInfo{ 21 Wallet: skymodules.WalletAccounting{ 22 ConfirmedSiacoinBalance: types.NewCurrency64(fastrand.Uint64n(1000)), 23 ConfirmedSiafundBalance: types.NewCurrency64(fastrand.Uint64n(1000)), 24 }, 25 Renter: skymodules.RenterAccounting{ 26 UnspentUnallocated: types.NewCurrency64(fastrand.Uint64n(1000)), 27 WithheldFunds: types.NewCurrency64(fastrand.Uint64n(1000)), 28 }, 29 Timestamp: time.Now().Unix(), 30 }) 31 } 32 return ais 33 } 34 35 // TestWriteAccountingCSV tests the writeAccountingCSV function 36 func TestWriteAccountingCSV(t *testing.T) { 37 t.Parallel() 38 39 // Create buffer 40 var buf bytes.Buffer 41 42 // Create accounting information 43 ais := randomAcountingInfo(5) 44 45 // Write the information to the csv file. 46 err := writeAccountingCSV(ais, &buf) 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 // Read the written data from the file. 52 bytes := buf.Bytes() 53 54 // Generated expected 55 headerStr := strings.Join(accountingHeaders, ",") 56 expected := fmt.Sprintf("%v\n", headerStr) 57 for _, ai := range ais { 58 timeStr := strconv.FormatInt(ai.Timestamp, 10) 59 scStr := ai.Wallet.ConfirmedSiacoinBalance.String() 60 sfStr := ai.Wallet.ConfirmedSiafundBalance.String() 61 usStr := ai.Renter.UnspentUnallocated.String() 62 whStr := ai.Renter.WithheldFunds.String() 63 data := []string{timeStr, scStr, sfStr, usStr, whStr} 64 dataStr := strings.Join(data, ",") 65 expected = fmt.Sprintf("%v%v\n", expected, dataStr) 66 } 67 68 // Check bytes vs expected 69 if expected != string(bytes) { 70 t.Log("actual", string(bytes)) 71 t.Log("expected", expected) 72 t.Fatal("unexpected") 73 } 74 } 75 76 // TestAverageSCPriceAndTxnValue verifies the functionality of 77 // averageSCPriceAndTxnValue 78 func TestAverageSCPriceAndTxnValue(t *testing.T) { 79 now := time.Now() 80 before := now.AddDate(-1, 0, 0) 81 after := now.AddDate(1, 0, 0) 82 var tests = []struct { 83 name string 84 scPrices []SCPrice 85 funds types.Currency 86 timestamp time.Time 87 expectedSCPAverage string 88 expectAvgPrice float64 89 }{ 90 {"nil scpPrices", nil, types.SiacoinPrecision, now, "", 0}, 91 {"zero funds", nil, types.ZeroCurrency, now, "0", 0}, 92 {"after time window", []SCPrice{ 93 {Average: 1.00, 94 EndTime: before, 95 StartTime: before, 96 }, 97 }, types.SiacoinPrecision, now, "", 0}, 98 {"before time window", []SCPrice{ 99 {Average: 1.00, 100 EndTime: after, 101 StartTime: after, 102 }, 103 }, types.SiacoinPrecision, now, "", 0}, 104 {"happy case", []SCPrice{ 105 {Average: 1.00, 106 EndTime: after, 107 StartTime: before, 108 }, 109 }, types.SiacoinPrecision, now, "1", 1}, 110 {"2 decimal places", []SCPrice{ 111 {Average: 1.01, 112 EndTime: after, 113 StartTime: before, 114 }, 115 }, types.SiacoinPrecision, now, "1.01", 1.01}, 116 {"less than 1", []SCPrice{ 117 {Average: 0.01, 118 EndTime: after, 119 StartTime: before, 120 }, 121 }, types.SiacoinPrecision, now, "0.01", 0.01}, 122 {"less than 0.01", []SCPrice{ 123 {Average: 0.0001, 124 EndTime: after, 125 StartTime: before, 126 }, 127 }, types.SiacoinPrecision, now, "0.0001", 0.0001}, 128 } 129 for _, test := range tests { 130 scpAverage, avgPrice := averageSCPriceAndTxnValue(test.scPrices, test.funds, test.timestamp) 131 if scpAverage != test.expectedSCPAverage { 132 t.Fatal(test.name, "wrong scpAverage", scpAverage, test.expectedSCPAverage) 133 } 134 if avgPrice != test.expectAvgPrice { 135 t.Fatal(test.name, "wrong avgPrice", avgPrice, test.expectAvgPrice) 136 } 137 } 138 } 139 140 // TestBlockAndTime tests the components of the blockAndTime function 141 func TestBlockAndTime(t *testing.T) { 142 t.Run("Estimate", testBlockAndTimeEstiamte) 143 t.Run("Exact", testBlockAndTimeExact) 144 } 145 146 // testBlockAndTimeEstiamte verifies the functionality of blockAndTimeEstimate 147 func testBlockAndTimeEstiamte(t *testing.T) { 148 // Define today as end of February 149 today := time.Date(2022, time.March, 0, 0, 0, 0, 0, time.UTC) 150 151 // twoMonths is the rough number of seconds in 2 months. Using this as 152 // the refHeight since the blockFrequency for testing is 1 sec. 153 twoMonths := types.BlockHeight(1440 * 60 * 60) 154 155 // Test start of month 156 startBlock, startTime := blockAndTimeEstimate(today, time.January, twoMonths, true) 157 158 // Get the expected Data 159 date := time.Date(today.Year(), time.January, 1, 0, 0, 0, 0, today.Location()) 160 161 // Check Block 162 secondsSinceDate := uint64(today.Sub(date).Seconds()) 163 blocksSinceStart := types.BlockHeight(secondsSinceDate) / types.BlockFrequency 164 blockHeight := twoMonths - blocksSinceStart 165 if blockHeight != startBlock { 166 t.Fatalf("Unexpected block; expected %v, got %v", blockHeight, startBlock) 167 } 168 169 // Check Time 170 expectedTime := date.Unix() 171 if expectedTime != startTime { 172 t.Fatalf("Unexpected time; expected %v, got %v", expectedTime, startTime) 173 } 174 175 // Test end of month 176 endBlock, endTime := blockAndTimeEstimate(today, time.January, twoMonths, false) 177 178 // Get the expected Data 179 day := skymodules.DaysInMonth(time.January, today.Year()) 180 date = time.Date(today.Year(), time.January, day, 23, 59, 59, 0, today.Location()) 181 182 // Check Block 183 secondsSinceDate = uint64(today.Sub(date).Seconds()) 184 blocksSinceStart = types.BlockHeight(secondsSinceDate) / types.BlockFrequency 185 blockHeight = twoMonths - blocksSinceStart 186 if blockHeight != endBlock { 187 t.Fatalf("Unexpected block; expected %v, got %v", blockHeight, endBlock) 188 } 189 190 // Check Time 191 expectedTime = date.Unix() 192 if expectedTime != endTime { 193 t.Fatalf("Unexpected time; expected %v, got %v", expectedTime, endTime) 194 } 195 } 196 197 // testBlockAndTimeExact verifies the functionality of blockAndTimeExact 198 func testBlockAndTimeExact(t *testing.T) { 199 if testing.Short() { 200 t.SkipNow() 201 } 202 t.Parallel() 203 204 // Create a test node/client for this test group 205 groupDir := skycTestDir(t.Name()) 206 n, err := newTestNode(groupDir) 207 if err != nil { 208 t.Fatal(err) 209 } 210 defer func() { 211 if err := n.Close(); err != nil { 212 t.Fatal(err) 213 } 214 }() 215 216 // Get a block and timeStamp 217 equalHeight, err := n.BlockHeight() 218 if err != nil { 219 t.Fatal(err) 220 } 221 cbg, err := n.ConsensusBlocksHeightGet(equalHeight) 222 if err != nil { 223 t.Fatal(err) 224 } 225 equalTime := cbg.Timestamp 226 227 // Sleep to ensure significant different between last block time stamp for testing and NDFs 228 time.Sleep(2 * time.Second) 229 230 // Grab a timestamp 231 beforeTime := time.Now().Unix() 232 233 // Sleep to ensure large gap between blocks for timestamp testing 234 time.Sleep(2 * time.Second) 235 236 // Mine some blocks so there are blocks after the timestamp 237 for i := 0; i < 5; i++ { 238 err = n.MineBlock() 239 if err != nil { 240 t.Fatal(err) 241 } 242 // Sleep in between blocks to ensure different timestamps 243 time.Sleep(time.Second) 244 } 245 246 var tests = []struct { 247 startingBlockHeight types.BlockHeight 248 expectedBlockHeight types.BlockHeight 249 dateTime int64 250 start bool 251 }{ 252 // Test Cases 253 254 // Looking for start block, current block timestamp is before dateTime 255 {equalHeight, equalHeight + 1, beforeTime, true}, 256 // Looking for start block, current block timestamp is after dateTime 257 {equalHeight + 1, equalHeight + 1, beforeTime, true}, 258 // Looking for start block, current block timestamp is equal to dateTime 259 {equalHeight, equalHeight, int64(equalTime), true}, 260 // Looking for end block, current block timestamp is before dateTime 261 {equalHeight, equalHeight, beforeTime, false}, 262 // Looking for end block, current block timestamp is after dateTime 263 {equalHeight + 1, equalHeight, beforeTime, false}, 264 // Looking for end block, current block timestamp is equal to dateTime 265 {equalHeight, equalHeight, int64(equalTime), false}, 266 } 267 for i, test := range tests { 268 blockHeight, dateTimeReturn := blockAndTimeExact(n.Client, test.startingBlockHeight, test.dateTime, test.start) 269 if blockHeight != test.expectedBlockHeight { 270 t.Errorf("%v: Expected BH: %v, Actual BH: %v", i, test.expectedBlockHeight, blockHeight) 271 } 272 if dateTimeReturn != test.dateTime { 273 t.Fatal("dateTime shouldn't change", dateTimeReturn, test.dateTime) 274 } 275 } 276 }