diff --git a/ContinuousTests/ContinuousTest.cs b/ContinuousTests/ContinuousTest.cs index 1d4fb23..cdbf6a8 100644 --- a/ContinuousTests/ContinuousTest.cs +++ b/ContinuousTests/ContinuousTest.cs @@ -1,6 +1,7 @@ using DistTestCore; using DistTestCore.Codex; using Logging; +using Utils; namespace ContinuousTests { @@ -27,6 +28,15 @@ namespace ContinuousTests Log = log; FileManager = fileManager; Configuration = configuration; + + if (nodes != null) + { + NodeRunner = new NodeRunner(Nodes.ToList().PickOneRandom(), configuration, TimeSet, Log, CustomK8sNamespace, EthereumAccountIndex); + } + else + { + NodeRunner = null!; + } } public CodexNode[] Nodes { get; private set; } = null!; @@ -34,6 +44,7 @@ namespace ContinuousTests public IFileManager FileManager { get; private set; } = null!; public Configuration Configuration { get; private set; } = null!; public virtual ITimeSet TimeSet { get { return new DefaultTimeSet(); } } + public NodeRunner NodeRunner { get; private set; } = null!; public abstract int RequiredNumberOfNodes { get; } public abstract TimeSpan RunTestEvery { get; } diff --git a/ContinuousTests/NodeRunner.cs b/ContinuousTests/NodeRunner.cs new file mode 100644 index 0000000..5663817 --- /dev/null +++ b/ContinuousTests/NodeRunner.cs @@ -0,0 +1,106 @@ +using DistTestCore.Codex; +using DistTestCore.Marketplace; +using DistTestCore; +using KubernetesWorkflow; +using NUnit.Framework; +using Logging; + +namespace ContinuousTests +{ + public class NodeRunner + { + private readonly CodexNode bootstrapNode; + private readonly Configuration config; + private readonly ITimeSet timeSet; + private readonly BaseLog log; + private readonly string customNamespace; + private readonly int ethereumAccountIndex; + + public NodeRunner(CodexNode bootstrapNode, Configuration config, ITimeSet timeSet, BaseLog log, string customNamespace, int ethereumAccountIndex) + { + this.bootstrapNode = bootstrapNode; + this.config = config; + this.timeSet = timeSet; + this.log = log; + this.customNamespace = customNamespace; + this.ethereumAccountIndex = ethereumAccountIndex; + } + + public void RunNode(Action operation) + { + RunNode(operation, 0.TestTokens()); + } + + public void RunNode(Action operation, TestToken mintTestTokens) + { + var (workflowCreator, lifecycle) = CreateFacilities(); + var flow = workflowCreator.CreateWorkflow(); + + try + { + var debugInfo = bootstrapNode.GetDebugInfo(); + Assert.That(!string.IsNullOrEmpty(debugInfo.spr)); + + var startupConfig = new StartupConfig(); + var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Trace); + codexStartConfig.MarketplaceConfig = new MarketplaceInitialConfig(0.Eth(), 0.TestTokens(), false); + codexStartConfig.MarketplaceConfig.AccountIndexOverride = ethereumAccountIndex; + codexStartConfig.BootstrapSpr = debugInfo.spr; + startupConfig.Add(codexStartConfig); + startupConfig.Add(config.CodexDeployment.GethStartResult); + var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig); + + var account = config.CodexDeployment.GethStartResult.CompanionNode.Accounts[ethereumAccountIndex]; + + var marketplaceNetwork = config.CodexDeployment.GethStartResult.MarketplaceNetwork; + if (mintTestTokens.Amount > 0) + { + var tokenAddress = marketplaceNetwork.Marketplace.TokenAddress; + var interaction = marketplaceNetwork.Bootstrap.StartInteraction(lifecycle); + interaction.MintTestTokens(new[] { account.Account }, mintTestTokens.Amount, tokenAddress); + } + + var container = rc.Containers[0]; + var codexAccess = new CodexAccess(lifecycle, container); + var marketAccess = new MarketplaceAccess(lifecycle, marketplaceNetwork, account, codexAccess); + + operation(codexAccess, marketAccess); + } + finally + { + flow.DeleteTestResources(); + } + } + + private (WorkflowCreator, TestLifecycle) CreateFacilities() + { + var kubeConfig = GetKubeConfig(config.KubeConfigFile); + var lifecycleConfig = new DistTestCore.Configuration + ( + kubeConfigFile: kubeConfig, + logPath: "null", + logDebug: false, + dataFilesPath: config.LogPath, + codexLogLevel: CodexLogLevel.Debug, + runnerLocation: TestRunnerLocation.ExternalToCluster + ); + + var kubeFlowConfig = new KubernetesWorkflow.Configuration( + k8sNamespacePrefix: customNamespace, + kubeConfigFile: kubeConfig, + operationTimeout: timeSet.K8sOperationTimeout(), + retryDelay: timeSet.WaitForK8sServiceDelay()); + + var workflowCreator = new WorkflowCreator(log, kubeFlowConfig, testNamespacePostfix: string.Empty); + var lifecycle = new TestLifecycle(new NullLog(), lifecycleConfig, timeSet, workflowCreator); + + return (workflowCreator, lifecycle); + } + + private static string? GetKubeConfig(string kubeConfigFile) + { + if (string.IsNullOrEmpty(kubeConfigFile) || kubeConfigFile.ToLowerInvariant() == "null") return null; + return kubeConfigFile; + } + } +} diff --git a/ContinuousTests/Tests/MarketplaceTest.cs b/ContinuousTests/Tests/MarketplaceTest.cs index 5728068..7dd1a26 100644 --- a/ContinuousTests/Tests/MarketplaceTest.cs +++ b/ContinuousTests/Tests/MarketplaceTest.cs @@ -1,8 +1,5 @@ using DistTestCore; using DistTestCore.Codex; -using DistTestCore.Marketplace; -using KubernetesWorkflow; -using Logging; using Newtonsoft.Json; using NUnit.Framework; @@ -33,40 +30,12 @@ namespace ContinuousTests.Tests file = FileManager.GenerateTestFile(fileSize); - var (workflowCreator, lifecycle) = CreateFacilities(); - var flow = workflowCreator.CreateWorkflow(); - - try + NodeRunner.RunNode((codexAccess, marketplaceAccess) => { - var debugInfo = Nodes[0].GetDebugInfo(); - Assert.That(!string.IsNullOrEmpty(debugInfo.spr)); - - var startupConfig = new StartupConfig(); - var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Trace); - 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); - - var account = Configuration.CodexDeployment.GethStartResult.CompanionNode.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 myNodeInfo = codexAccess.Node.GetDebugInfo(); - - var marketAccess = new MarketplaceAccess(lifecycle, marketplaceNetwork, account, codexAccess); - cid = UploadFile(codexAccess.Node, file); Assert.That(cid, Is.Not.Null); - purchaseId = marketAccess.RequestStorage( + purchaseId = marketplaceAccess.RequestStorage( contentId: cid!, pricePerSlotPerSecond: pricePerSlotPerSecond, requiredCollateral: 100.TestTokens(), @@ -74,103 +43,50 @@ namespace ContinuousTests.Tests proofProbability: 10, duration: contractDuration); - Log($"PurchaseId: '{purchaseId}'"); Assert.That(!string.IsNullOrEmpty(purchaseId)); - var lastState = ""; - var waitStart = DateTime.UtcNow; - var filesizeInMb = fileSize.SizeInBytes / 1024; - var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); - while (lastState != "started") - { - var purchaseStatus = codexAccess.Node.GetPurchaseStatus(purchaseId); - if (purchaseStatus != null && purchaseStatus.state != lastState) - { - lastState = purchaseStatus.state; - } - - Thread.Sleep(2000); - - if (lastState == "errored") - { - Assert.Fail("Contract start failed: " + JsonConvert.SerializeObject(purchaseStatus)); - } - - if (DateTime.UtcNow - waitStart > maxWaitTime) - { - Assert.Fail($"Contract was not picked up within {maxWaitTime.TotalSeconds} seconds timeout: " + JsonConvert.SerializeObject(purchaseStatus)); - } - } - } - finally - { - flow.DeleteTestResources(); - } + WaitForContractToStart(codexAccess, purchaseId); + }); } [TestMoment(t: MinuteFive * 2)] public void StoredDataIsAvailableAfterThreeDays() { - var (workflowCreator, lifecycle) = CreateFacilities(); - var flow = workflowCreator.CreateWorkflow(); - - try + NodeRunner.RunNode((codexAccess, marketplaceAccess) => { - 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); - var container = rc.Containers[0]; - var codexAccess = new CodexAccess(lifecycle, container); - var result = DownloadContent(codexAccess.Node, cid!); file.AssertIsEqual(result); - } - finally + }); + } + + private void WaitForContractToStart(CodexAccess codexAccess, string purchaseId) + { + var lastState = ""; + var waitStart = DateTime.UtcNow; + var filesizeInMb = fileSize.SizeInBytes / 1024; + var maxWaitTime = TimeSpan.FromSeconds(filesizeInMb * 10.0); + + while (lastState != "started") { - flow.DeleteTestResources(); + var purchaseStatus = codexAccess.Node.GetPurchaseStatus(purchaseId); + if (purchaseStatus != null && purchaseStatus.state != lastState) + { + lastState = purchaseStatus.state; + } + + Thread.Sleep(2000); + + if (lastState == "errored") + { + Assert.Fail("Contract start failed: " + JsonConvert.SerializeObject(purchaseStatus)); + } + + if (DateTime.UtcNow - waitStart > maxWaitTime) + { + Assert.Fail($"Contract was not picked up within {maxWaitTime.TotalSeconds} seconds timeout: " + JsonConvert.SerializeObject(purchaseStatus)); + } } } - - private (WorkflowCreator, TestLifecycle) CreateFacilities() - { - var kubeConfig = GetKubeConfig(Configuration.KubeConfigFile); - var lifecycleConfig = new DistTestCore.Configuration - ( - kubeConfigFile: kubeConfig, - logPath: "null", - logDebug: false, - dataFilesPath: Configuration.LogPath, - codexLogLevel: CodexLogLevel.Debug, - runnerLocation: TestRunnerLocation.ExternalToCluster - ); - - var kubeFlowConfig = new KubernetesWorkflow.Configuration( - k8sNamespacePrefix: MarketplaceTestNamespace, - kubeConfigFile: kubeConfig, - operationTimeout: TimeSet.K8sOperationTimeout(), - retryDelay: TimeSet.WaitForK8sServiceDelay()); - - var workflowCreator = new WorkflowCreator(base.Log, kubeFlowConfig, testNamespacePostfix: string.Empty); - var lifecycle = new TestLifecycle(new NullLog(), lifecycleConfig, TimeSet, workflowCreator); - - return (workflowCreator, lifecycle); - } - - private string? GetKubeConfig(string kubeConfigFile) - { - if (string.IsNullOrEmpty(kubeConfigFile) || kubeConfigFile.ToLowerInvariant() == "null") return null; - return kubeConfigFile; - } - - private new void Log(string msg) - { - base.Log.Log(msg); - } } }