test: testing slot expiry
This commit is contained in:
parent
bd9edd3931
commit
8ca9feb9e2
|
@ -5,7 +5,7 @@ namespace DistTestCore.Codex
|
|||
{
|
||||
public class CodexContainerRecipe : ContainerRecipeFactory
|
||||
{
|
||||
public const string DefaultDockerImage = "codexstorage/nim-codex:sha-5141d85";
|
||||
public const string DefaultDockerImage = "codexstorage/nim-codex:latest-dist-tests";
|
||||
public const string MetricsPortTag = "metrics_port";
|
||||
public const string DiscoveryPortTag = "discovery-port";
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ namespace DistTestCore.Helpers
|
|||
{
|
||||
try
|
||||
{
|
||||
var c = constraint.Resolve();
|
||||
Time.WaitUntil(() => c.ApplyTo(actual()).IsSuccess);
|
||||
Time.WaitUntil(() => constraint.Resolve().ApplyTo(actual()).IsSuccess);
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using Newtonsoft.Json;
|
|||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
using Utils;
|
||||
|
||||
namespace DistTestCore.Marketplace
|
||||
|
@ -12,7 +13,7 @@ namespace DistTestCore.Marketplace
|
|||
public interface IMarketplaceAccess
|
||||
{
|
||||
string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration);
|
||||
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
|
||||
StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null);
|
||||
void AssertThatBalance(IResolveConstraint constraint, string message = "");
|
||||
TestToken GetBalance();
|
||||
}
|
||||
|
@ -32,17 +33,18 @@ namespace DistTestCore.Marketplace
|
|||
this.codexAccess = codexAccess;
|
||||
}
|
||||
|
||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null)
|
||||
{
|
||||
var expiryTimestamp = expiry is null ? null : ((DateTimeOffset)expiry).ToUnixTimeSeconds().ToString();
|
||||
var request = new CodexSalesRequestStorageRequest
|
||||
{
|
||||
duration = ToDecInt(duration.TotalSeconds),
|
||||
proofProbability = ToDecInt(proofProbability),
|
||||
reward = ToDecInt(pricePerSlotPerSecond),
|
||||
collateral = ToDecInt(requiredCollateral),
|
||||
expiry = null,
|
||||
expiry = expiryTimestamp,
|
||||
nodes = minRequiredNumberOfNodes,
|
||||
tolerance = null,
|
||||
tolerance = tolerance,
|
||||
};
|
||||
|
||||
Log($"Requesting storage for: {contentId.Id}... (" +
|
||||
|
@ -50,7 +52,9 @@ namespace DistTestCore.Marketplace
|
|||
$"requiredCollateral: {requiredCollateral}, " +
|
||||
$"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " +
|
||||
$"proofProbability: {proofProbability}, " +
|
||||
$"duration: {Time.FormatDuration(duration)})");
|
||||
$"duration: {Time.FormatDuration(duration)}, " +
|
||||
$"expiry: {expiry:yyyy-MM-dd HH:mm:ss}, " +
|
||||
$"tolerance: {tolerance})");
|
||||
|
||||
var response = codexAccess.RequestStorage(request, contentId.Id);
|
||||
|
||||
|
@ -123,7 +127,7 @@ namespace DistTestCore.Marketplace
|
|||
|
||||
public class MarketplaceUnavailable : IMarketplaceAccess
|
||||
{
|
||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
||||
public StoragePurchaseContract RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration, DateTime? expiry = null, uint? tolerance = null)
|
||||
{
|
||||
Unavailable();
|
||||
return null!;
|
||||
|
@ -187,6 +191,29 @@ namespace DistTestCore.Marketplace
|
|||
WaitForStorageContractState(timeout, "finished");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for contract to terminate. Which means that it reaches one of the following states: Finished, Cancelled, Failed
|
||||
/// </summary>
|
||||
public void WaitForStorageContractTerminated()
|
||||
{
|
||||
if (!contractStartUtc.HasValue)
|
||||
{
|
||||
WaitForStorageContractStarted();
|
||||
}
|
||||
var gracePeriod = TimeSpan.FromSeconds(10);
|
||||
var currentContractTime = DateTime.UtcNow - contractStartUtc!.Value;
|
||||
var timeout = (ContractDuration - currentContractTime) + gracePeriod;
|
||||
string[] terminatedStates = { "finished", "failed", "cancelled" };
|
||||
WaitForStorageContractState(timeout, terminatedStates);
|
||||
}
|
||||
|
||||
public void WaitForStorageContractFailed(TimeSpan timeout)
|
||||
{
|
||||
WaitForStorageContractState(timeout, "failed");
|
||||
contractStartUtc = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Wait for contract to start. Max timeout depends on contract filesize. Allows more time for larger files.
|
||||
/// </summary>
|
||||
|
@ -205,12 +232,18 @@ namespace DistTestCore.Marketplace
|
|||
}
|
||||
|
||||
private void WaitForStorageContractState(TimeSpan timeout, string desiredState)
|
||||
{
|
||||
string[] states = { desiredState };
|
||||
WaitForStorageContractState(timeout, states);
|
||||
}
|
||||
|
||||
private void WaitForStorageContractState(TimeSpan timeout, string[] desiredState)
|
||||
{
|
||||
var lastState = "";
|
||||
var waitStart = DateTime.UtcNow;
|
||||
|
||||
log.Log($"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{desiredState}'.");
|
||||
while (lastState != desiredState)
|
||||
log.Log($"Waiting for {Time.FormatDuration(timeout)} for contract '{PurchaseId}' to reach state '{string.Join("/", desiredState)}'.");
|
||||
while (!desiredState.Contains(lastState))
|
||||
{
|
||||
var purchaseStatus = codexAccess.GetPurchaseStatus(PurchaseId);
|
||||
var statusJson = JsonConvert.SerializeObject(purchaseStatus);
|
||||
|
@ -229,10 +262,10 @@ namespace DistTestCore.Marketplace
|
|||
|
||||
if (DateTime.UtcNow - waitStart > timeout)
|
||||
{
|
||||
Assert.Fail($"Contract did not reach '{desiredState}' within timeout. {statusJson}");
|
||||
Assert.Fail($"Contract did not reach '{string.Join("/", desiredState)}' within timeout. {statusJson}");
|
||||
}
|
||||
}
|
||||
log.Log($"Contract '{desiredState}'.");
|
||||
log.Log($"Contract '{string.Join("/", desiredState)}'.");
|
||||
}
|
||||
|
||||
public CodexStoragePurchase GetPurchaseStatus(string purchaseId)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using DistTestCore;
|
||||
using DistTestCore.Codex;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.BasicTests
|
||||
|
@ -46,6 +47,7 @@ namespace Tests.BasicTests
|
|||
var fileSize = 10.MB();
|
||||
|
||||
var seller = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
|
||||
.WithStorageQuota(11.GB())
|
||||
.EnableMarketplace(sellerInitialBalance));
|
||||
|
||||
|
@ -59,6 +61,7 @@ namespace Tests.BasicTests
|
|||
var testFile = GenerateTestFile(fileSize);
|
||||
|
||||
var buyer = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "purchasing", "node", "restapi")
|
||||
.WithBootstrapNode(seller)
|
||||
.EnableMarketplace(buyerInitialBalance));
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
using DistTestCore;
|
||||
using DistTestCore.Codex;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
namespace Tests.BasicTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SlotSelection : AutoBootstrapDistTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void RequestExpiresIfNotFilledAndMoneyAreReturned()
|
||||
{
|
||||
var sellerInitialBalance = 234.TestTokens();
|
||||
var buyerInitialBalance = 1000.TestTokens();
|
||||
var fileSize = 10.MB();
|
||||
|
||||
var seller1 = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
|
||||
.WithStorageQuota(50.MB())
|
||||
.EnableMarketplace(sellerInitialBalance)
|
||||
.WithName("seller1"));
|
||||
|
||||
seller1.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));
|
||||
|
||||
// The two availabilities are needed until https://github.com/codex-storage/nim-codex/pull/535 is merged
|
||||
seller1.Marketplace.MakeStorageAvailable(
|
||||
size: 11.MB(),
|
||||
minPricePerBytePerSecond: 1.TestTokens(),
|
||||
maxCollateral: 20.TestTokens(),
|
||||
maxDuration: TimeSpan.FromMinutes(3));
|
||||
seller1.Marketplace.MakeStorageAvailable(
|
||||
size: 11.MB(),
|
||||
minPricePerBytePerSecond: 1.TestTokens(),
|
||||
maxCollateral: 20.TestTokens(),
|
||||
maxDuration: TimeSpan.FromMinutes(3));
|
||||
|
||||
var seller2 = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
|
||||
.WithStorageQuota(50.MB())
|
||||
.EnableMarketplace(sellerInitialBalance)
|
||||
.WithName("seller2"));
|
||||
|
||||
seller2.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance));
|
||||
seller2.Marketplace.MakeStorageAvailable(
|
||||
size: 11.MB(),
|
||||
minPricePerBytePerSecond: 1.TestTokens(),
|
||||
maxCollateral: 20.TestTokens(),
|
||||
maxDuration: TimeSpan.FromMinutes(3));
|
||||
seller2.Marketplace.MakeStorageAvailable(
|
||||
size: 11.MB(),
|
||||
minPricePerBytePerSecond: 1.TestTokens(),
|
||||
maxCollateral: 20.TestTokens(),
|
||||
maxDuration: TimeSpan.FromMinutes(3));
|
||||
|
||||
var testFile = GenerateTestFile(fileSize);
|
||||
var buyer = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "purchasing", "node", "restapi")
|
||||
.EnableMarketplace(buyerInitialBalance)
|
||||
.WithName("buyer"));
|
||||
|
||||
buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance));
|
||||
|
||||
var contentId = buyer.UploadFile(testFile);
|
||||
var purchaseContract = buyer.Marketplace.RequestStorage(contentId,
|
||||
pricePerSlotPerSecond: 2.TestTokens(),
|
||||
requiredCollateral: 10.TestTokens(),
|
||||
minRequiredNumberOfNodes: 3,
|
||||
proofProbability: 5,
|
||||
duration: TimeSpan.FromMinutes(1),
|
||||
expiry: DateTime.Now.AddMinutes(2));
|
||||
|
||||
Time.Sleep(TimeSpan.FromSeconds(100));
|
||||
|
||||
seller1.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
|
||||
seller2.Marketplace.AssertThatBalance(Is.LessThan(sellerInitialBalance), "Collateral was not placed.");
|
||||
buyer.Marketplace.AssertThatBalance(Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage.");
|
||||
|
||||
purchaseContract.WaitForStorageContractFailed(TimeSpan.FromSeconds(120));
|
||||
|
||||
seller1.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance), "Seller was not returned collateral.");
|
||||
seller2.Marketplace.AssertThatBalance(Is.EqualTo(sellerInitialBalance), "Seller was not returned collateral.");
|
||||
buyer.Marketplace.AssertThatBalance(Is.EqualTo(buyerInitialBalance), "Buyer was not returned money for the request.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,13 +16,13 @@ namespace Tests.BasicTests
|
|||
|
||||
var seller = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
|
||||
.WithStorageQuota(20.GB())
|
||||
.WithStorageQuota(21.GB())
|
||||
.EnableMarketplace(sellerInitialBalance)
|
||||
.WithName("seller"));
|
||||
|
||||
var sellerWithFailures = SetupCodexNode(s => s
|
||||
.WithLogLevel(CodexLogLevel.Trace, "marketplace", "sales", "proving", "reservations")
|
||||
.WithStorageQuota(20.GB())
|
||||
.WithStorageQuota(21.GB())
|
||||
.WithBootstrapNode(seller)
|
||||
.WithSimulateProofFailures(2)
|
||||
.EnableMarketplace(sellerInitialBalance)
|
||||
|
|
Loading…
Reference in New Issue