2023-06-26 13:37:16 +00:00
|
|
|
|
using DistTestCore;
|
|
|
|
|
using DistTestCore.Codex;
|
|
|
|
|
using DistTestCore.Marketplace;
|
|
|
|
|
using KubernetesWorkflow;
|
|
|
|
|
using Logging;
|
2023-06-27 13:28:00 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2023-06-26 13:37:16 +00:00
|
|
|
|
using NUnit.Framework;
|
2023-06-23 09:38:30 +00:00
|
|
|
|
|
|
|
|
|
namespace ContinuousTests.Tests
|
|
|
|
|
{
|
2023-06-26 13:37:16 +00:00
|
|
|
|
public class MarketplaceTest : ContinuousTest
|
|
|
|
|
{
|
|
|
|
|
public override int RequiredNumberOfNodes => 1;
|
|
|
|
|
public override TimeSpan RunTestEvery => TimeSpan.FromDays(4);
|
2023-06-26 14:00:16 +00:00
|
|
|
|
public override TestFailMode TestFailMode => TestFailMode.StopAfterFirstFailure;
|
2023-06-26 13:37:16 +00:00
|
|
|
|
|
|
|
|
|
public const int EthereumAccountIndex = 200; // TODO: Check against all other account indices of all other tests.
|
2023-06-27 13:28:00 +00:00
|
|
|
|
public const string MarketplaceTestNamespace = "codex-continuous-marketplace"; // prevent clashes too
|
2023-06-26 13:37:16 +00:00
|
|
|
|
|
2023-06-27 13:28:00 +00:00
|
|
|
|
private readonly uint numberOfSlots = 3;
|
|
|
|
|
private readonly ByteSize fileSize = 10.MB();
|
|
|
|
|
private readonly TestToken pricePerSlotPerSecond = 10.TestTokens();
|
2023-06-26 13:37:16 +00:00
|
|
|
|
|
|
|
|
|
private TestFile file = null!;
|
|
|
|
|
private ContentId? cid;
|
|
|
|
|
private string purchaseId = string.Empty;
|
|
|
|
|
|
|
|
|
|
[TestMoment(t: Zero)]
|
|
|
|
|
public void NodePostsStorageRequest()
|
|
|
|
|
{
|
|
|
|
|
var contractDuration = TimeSpan.FromDays(3) + TimeSpan.FromHours(1);
|
|
|
|
|
decimal totalDurationSeconds = Convert.ToDecimal(contractDuration.TotalSeconds);
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var expectedTotalCost = numberOfSlots * pricePerSlotPerSecond.Amount * (totalDurationSeconds + 1);
|
|
|
|
|
Log.Log("expected total cost: " + expectedTotalCost);
|
2023-06-26 13:37:16 +00:00
|
|
|
|
|
|
|
|
|
file = FileManager.GenerateTestFile(fileSize);
|
|
|
|
|
|
|
|
|
|
var (workflowCreator, lifecycle) = CreateFacilities();
|
|
|
|
|
var flow = workflowCreator.CreateWorkflow();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var debugInfo = Nodes[0].GetDebugInfo();
|
|
|
|
|
Assert.That(!string.IsNullOrEmpty(debugInfo.spr));
|
|
|
|
|
|
|
|
|
|
var startupConfig = new StartupConfig();
|
|
|
|
|
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
|
|
|
|
codexStartConfig.MarketplaceConfig = new MarketplaceInitialConfig(0.Eth(), 0.TestTokens(), false);
|
|
|
|
|
codexStartConfig.MarketplaceConfig.AccountIndexOverride = EthereumAccountIndex;
|
|
|
|
|
codexStartConfig.BootstrapSpr = debugInfo.spr;
|
|
|
|
|
startupConfig.Add(codexStartConfig);
|
|
|
|
|
startupConfig.Add(Configuration.CodexDeployment.GethStartResult);
|
|
|
|
|
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
|
|
|
|
|
2023-06-26 13:37:16 +00:00
|
|
|
|
var account = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Bootstrap.AllAccounts.Accounts[EthereumAccountIndex];
|
|
|
|
|
var tokenAddress = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Marketplace.TokenAddress;
|
|
|
|
|
|
|
|
|
|
var interaction = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Bootstrap.StartInteraction(lifecycle);
|
|
|
|
|
interaction.MintTestTokens(new[] { account.Account }, expectedTotalCost, tokenAddress);
|
|
|
|
|
|
|
|
|
|
var container = rc.Containers[0];
|
|
|
|
|
var marketplaceNetwork = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork;
|
|
|
|
|
var codexAccess = new CodexAccess(lifecycle, container);
|
|
|
|
|
var marketAccess = new MarketplaceAccess(lifecycle, marketplaceNetwork, account, codexAccess);
|
|
|
|
|
|
|
|
|
|
cid = UploadFile(codexAccess.Node, file);
|
|
|
|
|
Assert.That(cid, Is.Not.Null);
|
|
|
|
|
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var balance = marketAccess.GetBalance();
|
|
|
|
|
Log.Log("Account: " + account.Account);
|
|
|
|
|
Log.Log("Balance: " + balance);
|
2023-06-26 13:37:16 +00:00
|
|
|
|
|
|
|
|
|
purchaseId = marketAccess.RequestStorage(
|
|
|
|
|
contentId: cid!,
|
2023-06-27 13:28:00 +00:00
|
|
|
|
pricePerSlotPerSecond: pricePerSlotPerSecond,
|
2023-06-26 13:37:16 +00:00
|
|
|
|
requiredCollateral: 100.TestTokens(),
|
2023-06-27 13:28:00 +00:00
|
|
|
|
minRequiredNumberOfNodes: numberOfSlots,
|
2023-06-26 13:37:16 +00:00
|
|
|
|
proofProbability: 10,
|
|
|
|
|
duration: contractDuration);
|
|
|
|
|
|
2023-06-27 13:28:00 +00:00
|
|
|
|
Log.Log($"PurchaseId: '{purchaseId}'");
|
2023-06-26 13:37:16 +00:00
|
|
|
|
Assert.That(!string.IsNullOrEmpty(purchaseId));
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2023-06-27 13:28:00 +00:00
|
|
|
|
flow.DeleteTestResources();
|
2023-06-26 13:37:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TestMoment(t: DayThree)]
|
|
|
|
|
public void StoredDataIsAvailableAfterThreeDays()
|
|
|
|
|
{
|
|
|
|
|
var (workflowCreator, lifecycle) = CreateFacilities();
|
|
|
|
|
var flow = workflowCreator.CreateWorkflow();
|
2023-06-27 13:28:00 +00:00
|
|
|
|
|
2023-06-26 13:37:16 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var debugInfo = Nodes[0].GetDebugInfo();
|
|
|
|
|
Assert.That(!string.IsNullOrEmpty(debugInfo.spr));
|
|
|
|
|
|
|
|
|
|
var startupConfig = new StartupConfig();
|
|
|
|
|
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
|
|
|
|
codexStartConfig.BootstrapSpr = debugInfo.spr;
|
|
|
|
|
startupConfig.Add(codexStartConfig);
|
|
|
|
|
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
2023-06-26 13:37:16 +00:00
|
|
|
|
var container = rc.Containers[0];
|
|
|
|
|
var codexAccess = new CodexAccess(lifecycle, container);
|
|
|
|
|
|
|
|
|
|
var result = DownloadContent(codexAccess.Node, cid!);
|
|
|
|
|
|
|
|
|
|
file.AssertIsEqual(result);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
2023-06-27 13:28:00 +00:00
|
|
|
|
flow.DeleteTestResources();
|
2023-06-26 13:37:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private (WorkflowCreator, TestLifecycle) CreateFacilities()
|
|
|
|
|
{
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var kubeConfig = GetKubeConfig(Configuration.KubeConfigFile);
|
2023-06-26 13:37:16 +00:00
|
|
|
|
var lifecycleConfig = new DistTestCore.Configuration
|
|
|
|
|
(
|
2023-06-27 13:28:00 +00:00
|
|
|
|
kubeConfigFile: kubeConfig,
|
2023-06-26 13:37:16 +00:00
|
|
|
|
logPath: "null",
|
2023-06-27 13:28:00 +00:00
|
|
|
|
logDebug: false,
|
|
|
|
|
dataFilesPath: Configuration.LogPath,
|
2023-06-26 13:37:16 +00:00
|
|
|
|
codexLogLevel: CodexLogLevel.Debug,
|
2023-06-27 13:28:00 +00:00
|
|
|
|
runnerLocation: TestRunnerLocation.ExternalToCluster
|
2023-06-26 13:37:16 +00:00
|
|
|
|
);
|
|
|
|
|
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var kubeFlowConfig = new KubernetesWorkflow.Configuration(
|
2023-06-26 13:37:16 +00:00
|
|
|
|
k8sNamespacePrefix: MarketplaceTestNamespace,
|
2023-06-27 13:28:00 +00:00
|
|
|
|
kubeConfigFile: kubeConfig,
|
2023-06-26 13:37:16 +00:00
|
|
|
|
operationTimeout: TimeSet.K8sOperationTimeout(),
|
|
|
|
|
retryDelay: TimeSet.WaitForK8sServiceDelay());
|
|
|
|
|
|
2023-06-27 13:28:00 +00:00
|
|
|
|
var workflowCreator = new WorkflowCreator(Log, kubeFlowConfig, testNamespacePostfix: string.Empty);
|
2023-06-26 13:37:16 +00:00
|
|
|
|
var lifecycle = new TestLifecycle(new NullLog(), lifecycleConfig, TimeSet, workflowCreator);
|
|
|
|
|
|
|
|
|
|
return (workflowCreator, lifecycle);
|
|
|
|
|
}
|
2023-06-27 13:28:00 +00:00
|
|
|
|
|
|
|
|
|
private string? GetKubeConfig(string kubeConfigFile)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(kubeConfigFile) || kubeConfigFile.ToLowerInvariant() == "null") return null;
|
|
|
|
|
return kubeConfigFile;
|
|
|
|
|
}
|
2023-06-26 13:37:16 +00:00
|
|
|
|
}
|
2023-06-23 09:38:30 +00:00
|
|
|
|
}
|