mirror of
https://github.com/logos-storage/logos-storage-nim-cs-dist-tests.git
synced 2026-07-10 09:10:11 +00:00
first pass at adding framework tests that cover the release tests
This commit is contained in:
parent
e8b2ad014b
commit
b5e8a155dc
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,7 +1,8 @@
|
||||
.vs
|
||||
.vs
|
||||
obj
|
||||
bin
|
||||
.vscode
|
||||
Tools/AutoClient/datapath
|
||||
.editorconfig
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
dotnet-tools.json
|
||||
|
||||
31
ProjectPlugins/LogosStorageClient/ILogosStorageAccess.cs
Normal file
31
ProjectPlugins/LogosStorageClient/ILogosStorageAccess.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Logging;
|
||||
using Utils;
|
||||
|
||||
namespace LogosStorageClient
|
||||
{
|
||||
public interface ILogosStorageAccess
|
||||
{
|
||||
string GetName();
|
||||
string GetImageName();
|
||||
DateTime GetStartUtc();
|
||||
DebugInfo GetDebugInfo();
|
||||
void SetLogLevel(string logLevel);
|
||||
string GetSpr();
|
||||
DebugPeer GetDebugPeer(string peerId);
|
||||
void ConnectToPeer(string peerId, string[] peerMultiAddresses);
|
||||
string UploadFile(UploadInput uploadInput);
|
||||
Stream DownloadFile(string contentId);
|
||||
LocalDataset DownloadStreamless(ContentId cid);
|
||||
LocalDataset DownloadManifestOnly(ContentId cid);
|
||||
LocalDatasetList LocalFiles();
|
||||
LogosStorageSpace Space();
|
||||
Address GetDiscoveryEndpoint();
|
||||
Address GetApiEndpoint();
|
||||
Address GetListenEndpoint();
|
||||
Address? GetMetricsEndpoint();
|
||||
bool HasCrashed();
|
||||
void DeleteDataDirFolder();
|
||||
void Stop(bool waitTillStopped);
|
||||
IDownloadedLog DownloadLog(string additionalName = "");
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ using WebUtils;
|
||||
|
||||
namespace LogosStorageClient
|
||||
{
|
||||
public class LogosStorageAccess
|
||||
public class LogosStorageAccess : ILogosStorageAccess
|
||||
{
|
||||
public const string API_BASE_URL = "/api/storage/v1";
|
||||
|
||||
|
||||
@ -53,10 +53,10 @@ namespace LogosStorageClient
|
||||
private readonly TransferSpeeds transferSpeeds;
|
||||
private string peerId = string.Empty;
|
||||
private string nodeId = string.Empty;
|
||||
private readonly LogosStorageAccess logosStorageAccess;
|
||||
private readonly ILogosStorageAccess logosStorageAccess;
|
||||
private readonly IFileManager fileManager;
|
||||
|
||||
public StorageNode(ILog log, LogosStorageAccess logosStorageAccess, IFileManager fileManager, IStorageNodeHooks hooks)
|
||||
public StorageNode(ILog log, ILogosStorageAccess logosStorageAccess, IFileManager fileManager, IStorageNodeHooks hooks)
|
||||
{
|
||||
this.logosStorageAccess = logosStorageAccess;
|
||||
this.fileManager = fileManager;
|
||||
@ -226,13 +226,11 @@ namespace LogosStorageClient
|
||||
|
||||
public void ConnectToPeer(IStorageNode node)
|
||||
{
|
||||
var peer = (StorageNode)node;
|
||||
|
||||
Log($"Connecting to peer {peer.GetName()}...");
|
||||
Log($"Connecting to peer {node.GetName()}...");
|
||||
var peerInfo = node.GetDebugInfo();
|
||||
logosStorageAccess.ConnectToPeer(peerInfo.Id, GetPeerMultiAddresses(peer, peerInfo));
|
||||
logosStorageAccess.ConnectToPeer(peerInfo.Id, GetPeerMultiAddresses(node, peerInfo));
|
||||
|
||||
Log($"Successfully connected to peer {peer.GetName()}.");
|
||||
Log($"Successfully connected to peer {node.GetName()}.");
|
||||
}
|
||||
|
||||
public void DeleteDataDirFolder()
|
||||
@ -307,7 +305,7 @@ namespace LogosStorageClient
|
||||
log.AddStringReplace(LogosStorageUtils.ToShortId(nodeId), nodeName);
|
||||
}
|
||||
|
||||
private string[] GetPeerMultiAddresses(StorageNode peer, DebugInfo peerInfo)
|
||||
private string[] GetPeerMultiAddresses(IStorageNode peer, DebugInfo peerInfo)
|
||||
{
|
||||
var peerId = peer.GetDiscoveryEndpoint().Host
|
||||
.Replace("http://", "")
|
||||
|
||||
125
Tests/FrameworkTests/LogosStorageClient/HooksTests.cs
Normal file
125
Tests/FrameworkTests/LogosStorageClient/HooksTests.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
using LogosStorageClient.Hooks;
|
||||
using Utils;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class MuxingStorageNodeHooksTests
|
||||
{
|
||||
private Mock<IStorageNodeHooks> hookA = null!;
|
||||
private Mock<IStorageNodeHooks> hookB = null!;
|
||||
private MuxingStorageNodeHooks mux = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
hookA = new Mock<IStorageNodeHooks>();
|
||||
hookB = new Mock<IStorageNodeHooks>();
|
||||
mux = new MuxingStorageNodeHooks(new[] { hookA.Object, hookB.Object });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnNodeStarting_DispatchesToAllHooks()
|
||||
{
|
||||
var start = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
mux.OnNodeStarting(start, "image:tag");
|
||||
hookA.Verify(h => h.OnNodeStarting(start, "image:tag"), Times.Once);
|
||||
hookB.Verify(h => h.OnNodeStarting(start, "image:tag"), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnNodeStarted_DispatchesToAllHooks()
|
||||
{
|
||||
var mockNode = new Mock<IStorageNode>();
|
||||
mux.OnNodeStarted(mockNode.Object, "peerId-abc", "nodeId-xyz");
|
||||
hookA.Verify(h => h.OnNodeStarted(mockNode.Object, "peerId-abc", "nodeId-xyz"), Times.Once);
|
||||
hookB.Verify(h => h.OnNodeStarted(mockNode.Object, "peerId-abc", "nodeId-xyz"), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnNodeStopping_DispatchesToAllHooks()
|
||||
{
|
||||
mux.OnNodeStopping();
|
||||
hookA.Verify(h => h.OnNodeStopping(), Times.Once);
|
||||
hookB.Verify(h => h.OnNodeStopping(), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnFileUploading_DispatchesToAllHooks()
|
||||
{
|
||||
var size = new ByteSize(1024);
|
||||
mux.OnFileUploading("uid-1", size);
|
||||
hookA.Verify(h => h.OnFileUploading("uid-1", size), Times.Once);
|
||||
hookB.Verify(h => h.OnFileUploading("uid-1", size), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnFileUploaded_DispatchesToAllHooks()
|
||||
{
|
||||
var size = new ByteSize(1024);
|
||||
var cid = new ContentId("bafybeicid1");
|
||||
mux.OnFileUploaded("uid-1", size, cid);
|
||||
hookA.Verify(h => h.OnFileUploaded("uid-1", size, cid), Times.Once);
|
||||
hookB.Verify(h => h.OnFileUploaded("uid-1", size, cid), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnFileDownloading_DispatchesToAllHooks()
|
||||
{
|
||||
var cid = new ContentId("bafybeicid1");
|
||||
mux.OnFileDownloading(cid);
|
||||
hookA.Verify(h => h.OnFileDownloading(cid), Times.Once);
|
||||
hookB.Verify(h => h.OnFileDownloading(cid), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnFileDownloaded_DispatchesToAllHooks()
|
||||
{
|
||||
var size = new ByteSize(2048);
|
||||
var cid = new ContentId("bafybeicid2");
|
||||
mux.OnFileDownloaded(size, cid);
|
||||
hookA.Verify(h => h.OnFileDownloaded(size, cid), Times.Once);
|
||||
hookB.Verify(h => h.OnFileDownloaded(size, cid), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class LogosStorageHooksFactoryTests
|
||||
{
|
||||
[Test]
|
||||
public void CreateHooks_NoProviders_ReturnsDoNothingHooks()
|
||||
{
|
||||
var factory = new LogosStorageHooksFactory();
|
||||
var hooks = factory.CreateHooks("test-node");
|
||||
Assert.That(hooks, Is.InstanceOf<DoNothingLogosStorageHooks>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateHooks_OneProvider_ReturnsMuxingHooks()
|
||||
{
|
||||
var mockHooks = new Mock<IStorageNodeHooks>();
|
||||
var mockProvider = new Mock<ILogosStorageHooksProvider>();
|
||||
mockProvider.Setup(p => p.CreateHooks("my-node")).Returns(mockHooks.Object);
|
||||
|
||||
var factory = new LogosStorageHooksFactory();
|
||||
factory.Providers.Add(mockProvider.Object);
|
||||
|
||||
var result = factory.CreateHooks("my-node");
|
||||
Assert.That(result, Is.InstanceOf<MuxingStorageNodeHooks>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateHooks_MultipleProviders_ReturnsMuxingHooks()
|
||||
{
|
||||
var factory = new LogosStorageHooksFactory();
|
||||
factory.Providers.Add(new DoNothingHooksProvider());
|
||||
factory.Providers.Add(new DoNothingHooksProvider());
|
||||
|
||||
var result = factory.CreateHooks("my-node");
|
||||
Assert.That(result, Is.InstanceOf<MuxingStorageNodeHooks>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class LogosStorageLogLineTests
|
||||
{
|
||||
[Test]
|
||||
public void Parse_NullOrEmpty_ReturnsNull()
|
||||
{
|
||||
Assert.That(LogosStorageLogLine.Parse(string.Empty), Is.Null);
|
||||
Assert.That(LogosStorageLogLine.Parse(null!), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TooShort_ReturnsNull()
|
||||
{
|
||||
Assert.That(LogosStorageLogLine.Parse("INF 2024-01-01 00:00:00.000"), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_ValidLine_ReturnsCorrectFields()
|
||||
{
|
||||
// Real Nim log format: LVL yyyy-MM-dd HH:mm:ss.fff+00:00 message attrs
|
||||
// position 3 = space after level, position 33 = space before message
|
||||
var line = "INF 2024-03-15 12:34:56.789+00:00 Node started successfully topic=storage";
|
||||
|
||||
var result = LogosStorageLogLine.Parse(line);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.LogLevel, Is.EqualTo("INF"));
|
||||
Assert.That(result.TimestampUtc, Is.EqualTo(new DateTime(2024, 3, 15, 12, 34, 56, 789, DateTimeKind.Utc)));
|
||||
Assert.That(result.Message, Is.EqualTo("Node started successfully"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_SimpleAttribute_ParsedCorrectly()
|
||||
{
|
||||
var line = "ERR 2024-03-15 12:34:56.789+00:00 Upload failed reason=timeout";
|
||||
|
||||
var result = LogosStorageLogLine.Parse(line);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Attributes.ContainsKey("reason"), Is.True);
|
||||
Assert.That(result.Attributes["reason"], Is.EqualTo("timeout"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_QuotedAttributeValue_ParsedCorrectly()
|
||||
{
|
||||
var line = "WRN 2024-03-15 12:34:56.789+00:00 Slow download path=\"/data dir/file.bin\"";
|
||||
|
||||
var result = LogosStorageLogLine.Parse(line);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Attributes["path"], Is.EqualTo("/data dir/file.bin"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_MultipleAttributes_AllParsed()
|
||||
{
|
||||
var line = "DBG 2024-03-15 12:34:56.789+00:00 Peer discovered peerId=abc nodeId=xyz seen=true";
|
||||
|
||||
var result = LogosStorageLogLine.Parse(line);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Attributes["peerId"], Is.EqualTo("abc"));
|
||||
Assert.That(result.Attributes["nodeId"], Is.EqualTo("xyz"));
|
||||
Assert.That(result.Attributes["seen"], Is.EqualTo("true"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_InvalidTimestamp_ReturnsNull()
|
||||
{
|
||||
// Passes position-3 and position-33 space checks, but the 23-char timestamp
|
||||
// substring (positions 4-26) is not a parseable datetime → returns null.
|
||||
// 29 X's fill positions 4-32, space at 33, then message with attributes.
|
||||
var line = "INF XXXXXXXXXXXXXXXXXXXXXXXXXXXXX message key=value";
|
||||
Assert.That(LogosStorageLogLine.Parse(line), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("INF")]
|
||||
[TestCase("ERR")]
|
||||
[TestCase("WRN")]
|
||||
[TestCase("DBG")]
|
||||
public void Parse_LogLevel_IsPreserved(string level)
|
||||
{
|
||||
var line = $"{level} 2024-03-15 12:34:56.789+00:00 Message key=value";
|
||||
var result = LogosStorageLogLine.Parse(line);
|
||||
Assert.That(result?.LogLevel, Is.EqualTo(level));
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Tests/FrameworkTests/LogosStorageClient/MapperTests.cs
Normal file
134
Tests/FrameworkTests/LogosStorageClient/MapperTests.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
using StorageOpenApi;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class MapperTests
|
||||
{
|
||||
private Mapper mapper = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
mapper = new Mapper();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_DebugInfo_MapsAllFields()
|
||||
{
|
||||
var openApiDebugInfo = new StorageOpenApi.DebugInfo
|
||||
{
|
||||
Id = "peer-123",
|
||||
Spr = "spr-abc",
|
||||
Addrs = new List<string> { "/ip4/1.2.3.4/tcp/8080" },
|
||||
AnnounceAddresses = new List<string> { "/ip4/5.6.7.8/tcp/8080" },
|
||||
Storage = new StorageVersion { Version = "1.0.0", Revision = "abc123" },
|
||||
Table = new PeersTable
|
||||
{
|
||||
LocalNode = new Node { NodeId = "nodeId-1", PeerId = "peerId-1" },
|
||||
Nodes = new List<Node>()
|
||||
}
|
||||
};
|
||||
|
||||
var result = mapper.Map(openApiDebugInfo);
|
||||
|
||||
Assert.That(result.Id, Is.EqualTo("peer-123"));
|
||||
Assert.That(result.Spr, Is.EqualTo("spr-abc"));
|
||||
Assert.That(result.Addrs, Is.EqualTo(new[] { "/ip4/1.2.3.4/tcp/8080" }));
|
||||
Assert.That(result.AnnounceAddresses, Is.EqualTo(new[] { "/ip4/5.6.7.8/tcp/8080" }));
|
||||
Assert.That(result.Version.Version, Is.EqualTo("1.0.0"));
|
||||
Assert.That(result.Version.Revision, Is.EqualTo("abc123"));
|
||||
Assert.That(result.Table.LocalNode.NodeId, Is.EqualTo("nodeId-1"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_Space_MapsAllBytes()
|
||||
{
|
||||
var openApiSpace = new StorageOpenApi.Space
|
||||
{
|
||||
QuotaMaxBytes = 10_000_000,
|
||||
QuotaUsedBytes = 3_000_000,
|
||||
QuotaReservedBytes = 1_000_000,
|
||||
TotalBlocks = 42
|
||||
};
|
||||
|
||||
var result = mapper.Map(openApiSpace);
|
||||
|
||||
Assert.That(result.QuotaMaxBytes, Is.EqualTo(10_000_000));
|
||||
Assert.That(result.QuotaUsedBytes, Is.EqualTo(3_000_000));
|
||||
Assert.That(result.QuotaReservedBytes, Is.EqualTo(1_000_000));
|
||||
Assert.That(result.TotalBlocks, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_DataList_MapsContent()
|
||||
{
|
||||
var openApiDataList = new StorageOpenApi.DataList
|
||||
{
|
||||
Content = new List<DataItem>
|
||||
{
|
||||
new DataItem
|
||||
{
|
||||
Cid = "bafybeicid1",
|
||||
Manifest = new ManifestItem
|
||||
{
|
||||
TreeCid = "rootHashXYZ",
|
||||
DatasetSize = 1024,
|
||||
BlockSize = 512
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = mapper.Map(openApiDataList);
|
||||
|
||||
Assert.That(result.Content.Length, Is.EqualTo(1));
|
||||
Assert.That(result.Content[0].Cid.Id, Is.EqualTo("bafybeicid1"));
|
||||
Assert.That(result.Content[0].Manifest.RootHash, Is.EqualTo("rootHashXYZ"));
|
||||
Assert.That(result.Content[0].Manifest.DatasetSize.SizeInBytes, Is.EqualTo(1024));
|
||||
Assert.That(result.Content[0].Manifest.BlockSize.SizeInBytes, Is.EqualTo(512));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_DebugInfoTable_WithPeers_MapsNodes()
|
||||
{
|
||||
var openApiDebugInfo = new StorageOpenApi.DebugInfo
|
||||
{
|
||||
Id = "x",
|
||||
Spr = "x",
|
||||
Addrs = new List<string>(),
|
||||
AnnounceAddresses = new List<string>(),
|
||||
Storage = new StorageVersion { Version = "1", Revision = "r" },
|
||||
Table = new PeersTable
|
||||
{
|
||||
LocalNode = new Node { NodeId = "local-nodeId", PeerId = "local-peerId" },
|
||||
Nodes = new List<Node>
|
||||
{
|
||||
new Node { NodeId = "node-1", PeerId = "peer-1", Seen = true }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = mapper.Map(openApiDebugInfo);
|
||||
|
||||
Assert.That(result.Table.Nodes.Length, Is.EqualTo(1));
|
||||
Assert.That(result.Table.Nodes[0].PeerId, Is.EqualTo("peer-1"));
|
||||
Assert.That(result.Table.Nodes[0].Seen, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_DataList_EmptyContent_ReturnsEmptyArray()
|
||||
{
|
||||
var openApiDataList = new StorageOpenApi.DataList
|
||||
{
|
||||
Content = new List<DataItem>()
|
||||
};
|
||||
|
||||
var result = mapper.Map(openApiDataList);
|
||||
|
||||
Assert.That(result.Content, Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Tests/FrameworkTests/LogosStorageClient/StorageNodeTests.cs
Normal file
207
Tests/FrameworkTests/LogosStorageClient/StorageNodeTests.cs
Normal file
@ -0,0 +1,207 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
using LogosStorageClient.Hooks;
|
||||
using FileUtils;
|
||||
using Logging;
|
||||
using Utils;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class StorageNodeTests
|
||||
{
|
||||
private Mock<ILogosStorageAccess> mockAccess = null!;
|
||||
private Mock<IStorageNodeHooks> mockHooks = null!;
|
||||
private Mock<IFileManager> mockFileManager = null!;
|
||||
private StorageNode node = null!;
|
||||
private NullLog nullLog = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
mockAccess = new Mock<ILogosStorageAccess>();
|
||||
mockHooks = new Mock<IStorageNodeHooks>();
|
||||
mockFileManager = new Mock<IFileManager>();
|
||||
nullLog = new NullLog();
|
||||
|
||||
mockAccess.Setup(a => a.GetName()).Returns("test-node");
|
||||
mockAccess.Setup(a => a.GetStartUtc()).Returns(DateTime.UtcNow);
|
||||
mockAccess.Setup(a => a.GetImageName()).Returns("test-image");
|
||||
|
||||
node = new StorageNode(nullLog, mockAccess.Object, mockFileManager.Object, mockHooks.Object);
|
||||
}
|
||||
|
||||
// --- GetName ---
|
||||
|
||||
[Test]
|
||||
public void GetName_DelegatesToAccess()
|
||||
{
|
||||
Assert.That(node.GetName(), Is.EqualTo("test-node"));
|
||||
}
|
||||
|
||||
// --- UploadFile ---
|
||||
|
||||
[Test]
|
||||
public void UploadFile_ValidResponse_ReturnsCid()
|
||||
{
|
||||
using var tmpFile = new TempFile();
|
||||
File.WriteAllText(tmpFile.Path, "hello world");
|
||||
var trackedFile = new TrackedFile(nullLog, tmpFile.Path, "test-file");
|
||||
|
||||
mockAccess.Setup(a => a.UploadFile(It.IsAny<UploadInput>())).Returns("bafybeicid123");
|
||||
|
||||
var cid = node.UploadFile(trackedFile);
|
||||
|
||||
Assert.That(cid.Id, Is.EqualTo("bafybeicid123"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UploadFile_FiresOnFileUploadingAndOnFileUploaded()
|
||||
{
|
||||
using var tmpFile = new TempFile();
|
||||
File.WriteAllText(tmpFile.Path, "data");
|
||||
var trackedFile = new TrackedFile(nullLog, tmpFile.Path, "test-file");
|
||||
mockAccess.Setup(a => a.UploadFile(It.IsAny<UploadInput>())).Returns("bafybeicid999");
|
||||
|
||||
node.UploadFile(trackedFile);
|
||||
|
||||
mockHooks.Verify(h => h.OnFileUploading(It.IsAny<string>(), It.IsAny<ByteSize>()), Times.Once);
|
||||
mockHooks.Verify(h => h.OnFileUploaded(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<ByteSize>(),
|
||||
It.Is<ContentId>(c => c.Id == "bafybeicid999")), Times.Once);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UploadFile_EmptyResponse_Throws()
|
||||
{
|
||||
using var tmpFile = new TempFile();
|
||||
File.WriteAllText(tmpFile.Path, "data");
|
||||
var trackedFile = new TrackedFile(nullLog, tmpFile.Path, "test-file");
|
||||
mockAccess.Setup(a => a.UploadFile(It.IsAny<UploadInput>())).Returns(string.Empty);
|
||||
|
||||
Assert.Throws<Exception>(() => node.UploadFile(trackedFile));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UploadFile_StoreBlockFailure_Throws()
|
||||
{
|
||||
using var tmpFile = new TempFile();
|
||||
File.WriteAllText(tmpFile.Path, "data");
|
||||
var trackedFile = new TrackedFile(nullLog, tmpFile.Path, "test-file");
|
||||
mockAccess.Setup(a => a.UploadFile(It.IsAny<UploadInput>())).Returns("Unable to store block");
|
||||
|
||||
Assert.Throws<Exception>(() => node.UploadFile(trackedFile));
|
||||
}
|
||||
|
||||
// --- Stop ---
|
||||
|
||||
[Test]
|
||||
public void Stop_FiresOnNodeStoppingAndCallsAccessStop()
|
||||
{
|
||||
node.Stop(waitTillStopped: true);
|
||||
|
||||
mockHooks.Verify(h => h.OnNodeStopping(), Times.Once);
|
||||
mockAccess.Verify(a => a.Stop(true), Times.Once);
|
||||
}
|
||||
|
||||
// --- Initialize ---
|
||||
|
||||
[Test]
|
||||
public void Initialize_ValidDebugInfo_SetsVersion()
|
||||
{
|
||||
mockAccess.Setup(a => a.GetDebugInfo()).Returns(new DebugInfo
|
||||
{
|
||||
Id = "peer-abc",
|
||||
Table = new DebugInfoTable { LocalNode = new DebugInfoTableNode { NodeId = "node-xyz" } },
|
||||
Version = new DebugInfoVersion { Version = "1.0.0", Revision = "rev123" }
|
||||
});
|
||||
|
||||
node.Initialize();
|
||||
|
||||
Assert.That(node.Version.Version, Is.EqualTo("1.0.0"));
|
||||
Assert.That(node.Version.Revision, Is.EqualTo("rev123"));
|
||||
Assert.That(node.GetPeerId(), Is.EqualTo("peer-abc"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Initialize_FiresOnNodeStarted()
|
||||
{
|
||||
mockAccess.Setup(a => a.GetDebugInfo()).Returns(new DebugInfo
|
||||
{
|
||||
Id = "peer-abc",
|
||||
Table = new DebugInfoTable { LocalNode = new DebugInfoTableNode { NodeId = "node-xyz" } },
|
||||
Version = new DebugInfoVersion { Version = "1.0.0", Revision = "rev123" }
|
||||
});
|
||||
|
||||
node.Initialize();
|
||||
|
||||
mockHooks.Verify(h => h.OnNodeStarted(node, "peer-abc", "node-xyz"), Times.Once);
|
||||
}
|
||||
|
||||
// --- WaitUntilQuotaUsedIncreased ---
|
||||
|
||||
[Test]
|
||||
public void WaitUntilQuotaUsedIncreased_QuotaAlreadyMet_DoesNotThrow()
|
||||
{
|
||||
var startSpace = new LogosStorageSpace { QuotaUsedBytes = 0, QuotaMaxBytes = 1_000_000, QuotaReservedBytes = 0 };
|
||||
var afterSpace = new LogosStorageSpace { QuotaUsedBytes = 500, QuotaMaxBytes = 1_000_000, QuotaReservedBytes = 0 };
|
||||
mockAccess.Setup(a => a.Space()).Returns(afterSpace);
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
node.WaitUntilQuotaUsedIncreased(startSpace, new ByteSize(400), TimeSpan.FromSeconds(5)));
|
||||
}
|
||||
|
||||
// --- ConnectToPeer ---
|
||||
|
||||
[Test]
|
||||
public void ConnectToPeer_ReplacesZeroIPWithPeerDiscoveryHost()
|
||||
{
|
||||
var mockPeerNode = new Mock<IStorageNode>();
|
||||
mockPeerNode.Setup(n => n.GetName()).Returns("peer-node");
|
||||
mockPeerNode.Setup(n => n.GetDiscoveryEndpoint())
|
||||
.Returns(new Address("peer-node", "http://10.0.0.2", 1234));
|
||||
mockPeerNode.Setup(n => n.GetDebugInfo(It.IsAny<bool>())).Returns(new DebugInfo
|
||||
{
|
||||
Id = "peer-id-xyz",
|
||||
Addrs = new[] { "/ip4/0.0.0.0/tcp/8080", "/ip4/0.0.0.0/tcp/9090" },
|
||||
Table = new DebugInfoTable { LocalNode = new DebugInfoTableNode() },
|
||||
Version = new DebugInfoVersion { Version = "1.0", Revision = "r1" }
|
||||
});
|
||||
|
||||
node.ConnectToPeer(mockPeerNode.Object);
|
||||
|
||||
mockAccess.Verify(a => a.ConnectToPeer(
|
||||
"peer-id-xyz",
|
||||
It.Is<string[]>(addrs =>
|
||||
addrs[0] == "/ip4/10.0.0.2/tcp/8080" &&
|
||||
addrs[1] == "/ip4/10.0.0.2/tcp/9090")),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
// --- TransferSpeeds ---
|
||||
|
||||
[Test]
|
||||
public void TransferSpeeds_InitiallyEmpty()
|
||||
{
|
||||
Assert.That(node.TransferSpeeds.GetUploadSpeed(), Is.Null);
|
||||
Assert.That(node.TransferSpeeds.GetDownloadSpeed(), Is.Null);
|
||||
}
|
||||
|
||||
// --- HasCrashed / Space / GetPeerId ---
|
||||
|
||||
[Test]
|
||||
public void HasCrashed_DelegatesToAccess()
|
||||
{
|
||||
mockAccess.Setup(a => a.HasCrashed()).Returns(true);
|
||||
Assert.That(node.HasCrashed(), Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
internal class TempFile : IDisposable
|
||||
{
|
||||
public string Path { get; } = System.IO.Path.GetTempFileName();
|
||||
public void Dispose() => File.Delete(Path);
|
||||
}
|
||||
}
|
||||
105
Tests/FrameworkTests/LogosStorageClient/StorageTypesTests.cs
Normal file
105
Tests/FrameworkTests/LogosStorageClient/StorageTypesTests.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class DebugInfoVersionTests
|
||||
{
|
||||
[Test]
|
||||
public void IsValid_BothEmpty_ReturnsFalse()
|
||||
{
|
||||
var v = new DebugInfoVersion();
|
||||
Assert.That(v.IsValid(), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsValid_VersionEmptyRevisionSet_ReturnsFalse()
|
||||
{
|
||||
var v = new DebugInfoVersion { Version = string.Empty, Revision = "abc123" };
|
||||
Assert.That(v.IsValid(), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsValid_RevisionEmptyVersionSet_ReturnsFalse()
|
||||
{
|
||||
var v = new DebugInfoVersion { Version = "1.0.0", Revision = string.Empty };
|
||||
Assert.That(v.IsValid(), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsValid_BothSet_ReturnsTrue()
|
||||
{
|
||||
var v = new DebugInfoVersion { Version = "1.0.0", Revision = "abc123" };
|
||||
Assert.That(v.IsValid(), Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class LogosStorageSpaceTests
|
||||
{
|
||||
[Test]
|
||||
public void FreeBytes_IsQuotaMax_MinusUsedAndReserved()
|
||||
{
|
||||
var space = new LogosStorageSpace
|
||||
{
|
||||
QuotaMaxBytes = 1000,
|
||||
QuotaUsedBytes = 300,
|
||||
QuotaReservedBytes = 100
|
||||
};
|
||||
Assert.That(space.FreeBytes, Is.EqualTo(600));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FreeBytes_NothingUsed_EqualToMax()
|
||||
{
|
||||
var space = new LogosStorageSpace { QuotaMaxBytes = 5000, QuotaUsedBytes = 0, QuotaReservedBytes = 0 };
|
||||
Assert.That(space.FreeBytes, Is.EqualTo(5000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FreeBytes_ReservedAloneReducesFreeSpace()
|
||||
{
|
||||
var space = new LogosStorageSpace { QuotaMaxBytes = 1000, QuotaUsedBytes = 0, QuotaReservedBytes = 400 };
|
||||
Assert.That(space.FreeBytes, Is.EqualTo(600));
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class LogosStorageUtilsTests
|
||||
{
|
||||
[Test]
|
||||
public void ToShortId_ShortString_ReturnedUnchanged()
|
||||
{
|
||||
Assert.That(LogosStorageUtils.ToShortId("abc"), Is.EqualTo("abc"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToShortId_ExactlyTenChars_ReturnedUnchanged()
|
||||
{
|
||||
Assert.That(LogosStorageUtils.ToShortId("abcde12345"), Is.EqualTo("abcde12345"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToShortId_LongString_ReturnsFirst3StarLast6()
|
||||
{
|
||||
// "abcde12345678xyz" (16 chars): id[..3]="abc", id[^6..]="678xyz"
|
||||
var result = LogosStorageUtils.ToShortId("abcde12345678xyz");
|
||||
Assert.That(result, Is.EqualTo("abc*678xyz"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToNodeIdShortId_ShortString_ReturnedUnchanged()
|
||||
{
|
||||
Assert.That(LogosStorageUtils.ToNodeIdShortId("abc"), Is.EqualTo("abc"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToNodeIdShortId_LongString_ReturnsFirst2StarLast6()
|
||||
{
|
||||
// "abcde12345678xyz" (16 chars): id[..2]="ab", id[^6..]="678xyz"
|
||||
var result = LogosStorageUtils.ToNodeIdShortId("abcde12345678xyz");
|
||||
Assert.That(result, Is.EqualTo("ab*678xyz"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
using NUnit.Framework;
|
||||
using LogosStorageClient;
|
||||
using Utils;
|
||||
|
||||
namespace FrameworkTests.LogosStorageClient
|
||||
{
|
||||
[TestFixture]
|
||||
public class TransferSpeedsTests
|
||||
{
|
||||
private TransferSpeeds speeds;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
speeds = new TransferSpeeds();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUploadSpeed_NoSamples_ReturnsNull()
|
||||
{
|
||||
Assert.That(speeds.GetUploadSpeed(), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetDownloadSpeed_NoSamples_ReturnsNull()
|
||||
{
|
||||
Assert.That(speeds.GetDownloadSpeed(), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUploadSpeed_OneSample_ReturnsCorrectSpeed()
|
||||
{
|
||||
// 1000 bytes in 2 seconds = 500 bytes/sec
|
||||
speeds.AddUploadSample(new ByteSize(1000), TimeSpan.FromSeconds(2));
|
||||
Assert.That(speeds.GetUploadSpeed()!.SizeInBytes, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetDownloadSpeed_OneSample_ReturnsCorrectSpeed()
|
||||
{
|
||||
// 3000 bytes in 3 seconds = 1000 bytes/sec
|
||||
speeds.AddDownloadSample(new ByteSize(3000), TimeSpan.FromSeconds(3));
|
||||
Assert.That(speeds.GetDownloadSpeed()!.SizeInBytes, Is.EqualTo(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetUploadSpeed_MultipleSamples_ReturnsAverage()
|
||||
{
|
||||
// 1000/1s = 1000, 3000/3s = 1000 → avg = 1000
|
||||
speeds.AddUploadSample(new ByteSize(1000), TimeSpan.FromSeconds(1));
|
||||
speeds.AddUploadSample(new ByteSize(3000), TimeSpan.FromSeconds(3));
|
||||
Assert.That(speeds.GetUploadSpeed()!.SizeInBytes, Is.EqualTo(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UploadAndDownload_Independent()
|
||||
{
|
||||
speeds.AddUploadSample(new ByteSize(2000), TimeSpan.FromSeconds(1));
|
||||
speeds.AddDownloadSample(new ByteSize(500), TimeSpan.FromSeconds(1));
|
||||
|
||||
Assert.That(speeds.GetUploadSpeed()!.SizeInBytes, Is.EqualTo(2000));
|
||||
Assert.That(speeds.GetDownloadSpeed()!.SizeInBytes, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Combine_WithNull_ReturnsSelf()
|
||||
{
|
||||
speeds.AddUploadSample(new ByteSize(1000), TimeSpan.FromSeconds(1));
|
||||
var combined = speeds.Combine(null);
|
||||
Assert.That(combined.GetUploadSpeed()!.SizeInBytes, Is.EqualTo(1000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Combine_TwoSpeeds_MergesAndAverages()
|
||||
{
|
||||
speeds.AddUploadSample(new ByteSize(1000), TimeSpan.FromSeconds(1)); // 1000 b/s
|
||||
var other = new TransferSpeeds();
|
||||
other.AddUploadSample(new ByteSize(3000), TimeSpan.FromSeconds(1)); // 3000 b/s
|
||||
|
||||
var combined = speeds.Combine(other);
|
||||
// avg(1000, 3000) = 2000
|
||||
Assert.That(combined.GetUploadSpeed()!.SizeInBytes, Is.EqualTo(2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user