Skip to content

Commit 6f247ac

Browse files
committed
chore(pin): sync with Kubo — options-based Add and typed List; tests + isolated node
PinApi: replace overloads with AddAsync(path, PinAddOptions); map Recursive/Name to recursive and name query params PinApi: implement ListAsync(PinType) mapping to type=direct|indirect|recursive|all (omitted for all) FileSystemApi: honor AddFileOptions.Pin (pin=true/false) when adding files Tests: Add_WithName, Add_WithName_NonRecursive, List_WithType_All TestFixture: start fresh Kubo via bootstrapper on 11501/18080; bootstrap peers; teardown
1 parent 3c12732 commit 6f247ac

File tree

6 files changed

+189
-18
lines changed

6 files changed

+189
-18
lines changed

src/CoreApi/FileSystemApi.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ private string[] ToApiOptions(AddFileOptions? options)
270270
opts.Add($"nocopy={options.NoCopy.ToString().ToLowerInvariant()}");
271271

272272
if (options.Pin is not null)
273-
opts.Add("pin=false");
273+
opts.Add($"pin={options.Pin.ToString().ToLowerInvariant()}");
274274

275275
if (options.Wrap is not null)
276276
opts.Add($"wrap-with-directory={options.Wrap.ToString().ToLowerInvariant()}");

src/CoreApi/PinApi.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Threading;
77
using System.Threading.Tasks;
8+
using System;
89

910
namespace Ipfs.Http
1011
{
@@ -17,15 +18,22 @@ internal PinApi(IpfsClient ipfs)
1718
this.ipfs = ipfs;
1819
}
1920

20-
public async Task<IEnumerable<Cid>> AddAsync(string path, bool recursive = true, CancellationToken cancel = default(CancellationToken))
21+
public async Task<IEnumerable<Cid>> AddAsync(string path, PinAddOptions options, CancellationToken cancel = default)
2122
{
22-
var opts = "recursive=" + recursive.ToString().ToLowerInvariant();
23-
var json = await ipfs.DoCommandAsync("pin/add", cancel, path, opts);
24-
return ((JArray)JObject.Parse(json)["Pins"])
25-
.Select(p => (Cid)(string)p);
23+
options ??= new PinAddOptions();
24+
var optList = new List<string>
25+
{
26+
"recursive=" + options.Recursive.ToString().ToLowerInvariant()
27+
};
28+
if (!string.IsNullOrEmpty(options.Name))
29+
{
30+
optList.Add("name=" + options.Name);
31+
}
32+
var json = await ipfs.DoCommandAsync("pin/add", cancel, path, optList.ToArray());
33+
return ((JArray)JObject.Parse(json)["Pins"]) .Select(p => (Cid)(string)p);
2634
}
2735

28-
public async Task<IEnumerable<Cid>> ListAsync(CancellationToken cancel = default(CancellationToken))
36+
public async Task<IEnumerable<Cid>> ListAsync(CancellationToken cancel = default)
2937
{
3038
var json = await ipfs.DoCommandAsync("pin/ls", cancel);
3139
var keys = (JObject)(JObject.Parse(json)["Keys"]);

test/CoreApi/PinApiTest.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using Ipfs.CoreApi;
23
using System.Linq;
34
using System.Threading.Tasks;
45

@@ -16,14 +17,23 @@ public void List()
1617
Assert.IsTrue(pins.Count() > 0);
1718
}
1819

20+
[TestMethod]
21+
public async Task List_WithType_All()
22+
{
23+
var ipfs = TestFixture.Ipfs;
24+
var pins = await ipfs.Pin.ListAsync(PinType.All);
25+
Assert.IsNotNull(pins);
26+
Assert.IsTrue(pins.Count() > 0);
27+
}
28+
1929
[TestMethod]
2030
public async Task Add_Remove()
2131
{
2232
var ipfs = TestFixture.Ipfs;
2333
var result = await ipfs.FileSystem.AddTextAsync("I am pinned");
2434
var id = result.Id;
2535

26-
var pins = await ipfs.Pin.AddAsync(id);
36+
var pins = await ipfs.Pin.AddAsync(id, new PinAddOptions { Recursive = true });
2737
Assert.IsTrue(pins.Any(pin => pin == id));
2838
var all = await ipfs.Pin.ListAsync();
2939
Assert.IsTrue(all.Any(pin => pin == id));
@@ -34,5 +44,38 @@ public async Task Add_Remove()
3444
Assert.IsFalse(all.Any(pin => pin == id));
3545
}
3646

47+
[TestMethod]
48+
public async Task Add_WithName()
49+
{
50+
var ipfs = TestFixture.Ipfs;
51+
var result = await ipfs.FileSystem.AddTextAsync("I am pinned with a name");
52+
var id = result.Id;
53+
var name = $"tdd-{System.Guid.NewGuid()}";
54+
55+
var pins = await ipfs.Pin.AddAsync(id, new PinAddOptions { Name = name, Recursive = true });
56+
Assert.IsTrue(pins.Any(pin => pin == id));
57+
58+
var all = await ipfs.Pin.ListAsync();
59+
Assert.IsTrue(all.Any(pin => pin == id));
60+
61+
// cleanup
62+
await ipfs.Pin.RemoveAsync(id);
63+
}
64+
65+
[TestMethod]
66+
public async Task Add_WithName_NonRecursive()
67+
{
68+
var ipfs = TestFixture.Ipfs;
69+
var result = await ipfs.FileSystem.AddTextAsync("I am pinned non-recursive with a name", new Ipfs.CoreApi.AddFileOptions { Pin = false });
70+
var id = result.Id;
71+
var name = $"tdd-nr-{System.Guid.NewGuid()}";
72+
73+
var pins = await ipfs.Pin.AddAsync(id, new PinAddOptions { Name = name, Recursive = false });
74+
Assert.IsTrue(pins.Any(pin => pin == id));
75+
76+
// cleanup
77+
await ipfs.Pin.RemoveAsync(id, recursive: false);
78+
}
79+
3780
}
3881
}

test/IpfsHttpClientTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
1414
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
1515
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
16+
<PackageReference Include="OwlCore.Kubo" Version="0.21.1" />
1617
</ItemGroup>
1718

1819
<ItemGroup>

test/TestFixture.cs

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,102 @@
1-
namespace Ipfs.Http
1+
using System;
2+
using System.IO;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using OwlCore.Kubo;
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
using OwlCore.Storage.System.IO;
8+
using OwlCore.Storage;
9+
using System.Diagnostics;
10+
11+
namespace Ipfs.Http
212
{
3-
public static class TestFixture
13+
[TestClass]
14+
public class TestFixture
415
{
5-
/// <summary>
6-
/// Fiddler cannot see localhost traffic because .Net bypasses the network stack for localhost/127.0.0.1.
7-
/// By using "127.0.0.1." (note trailing dot) fiddler will receive the traffic and if its not running
8-
/// the localhost will get it!
9-
/// </summary>
10-
//IpfsClient.DefaultApiUri = new Uri("http://127.0.0.1.:5001");
11-
12-
public static IpfsClient Ipfs = new IpfsClient();
16+
// Publicly accessible client and API URI for tests.
17+
public static IpfsClient Ipfs { get; private set; } = null!;
18+
public static Uri? ApiUri { get; private set; }
19+
private static KuboBootstrapper? Node;
20+
21+
[AssemblyInitialize]
22+
public static void AssemblyInit(TestContext context)
23+
{
24+
try
25+
{
26+
OwlCore.Diagnostics.Logger.MessageReceived += (sender, args) => context.WriteLine(args.Message);
27+
28+
// Ensure the test runner has provided a deployment directory to use for working folders.
29+
Assert.IsNotNull(context.DeploymentDirectory);
30+
31+
// Create a working folder and start a fresh Kubo node with default bootstrap peers.
32+
var workingFolder = SafeCreateWorkingFolder(new SystemFolder(context.DeploymentDirectory), typeof(TestFixture).Namespace ?? "test").GetAwaiter().GetResult();
33+
34+
// Use non-default ports to avoid conflicts with any locally running node.
35+
int apiPort = 11501;
36+
int gatewayPort = 18080;
37+
38+
Node = CreateNodeAsync(workingFolder, "kubo-node", apiPort, gatewayPort).GetAwaiter().GetResult();
39+
ApiUri = Node.ApiUri;
40+
Ipfs = new IpfsClient(ApiUri.ToString());
41+
42+
context?.WriteLine($"Connected to existing Kubo node: {ApiUri}");
43+
}
44+
catch (Exception ex)
45+
{
46+
context?.WriteLine($"Kubo bootstrapper failed to start: {ex}");
47+
throw;
48+
}
49+
}
50+
51+
public static async Task<KuboBootstrapper> CreateNodeAsync(SystemFolder workingDirectory, string nodeRepoName, int apiPort, int gatewayPort)
52+
{
53+
var nodeRepo = (SystemFolder)await workingDirectory.CreateFolderAsync(nodeRepoName, overwrite: true);
54+
55+
var node = new KuboBootstrapper(nodeRepo.Path)
56+
{
57+
ApiUri = new Uri($"http://127.0.0.1:{apiPort}"),
58+
GatewayUri = new Uri($"http://127.0.0.1:{gatewayPort}"),
59+
RoutingMode = DhtRoutingMode.AutoClient,
60+
LaunchConflictMode = BootstrapLaunchConflictMode.Relaunch,
61+
BinaryWorkingFolder = workingDirectory,
62+
EnableFilestore = true,
63+
};
64+
65+
OwlCore.Diagnostics.Logger.LogInformation($"Starting node {nodeRepoName}\n");
66+
67+
await node.StartAsync().ConfigureAwait(false);
68+
await node.Client.IdAsync().ConfigureAwait(false);
69+
70+
Debug.Assert(node.Process != null);
71+
return node;
72+
}
73+
74+
public static async Task<SystemFolder> SafeCreateWorkingFolder(SystemFolder rootFolder, string name)
75+
{
76+
var testTempRoot = (SystemFolder)await rootFolder.CreateFolderAsync(name, overwrite: false);
77+
await SetAllFileAttributesRecursive(testTempRoot, attributes => attributes & ~FileAttributes.ReadOnly).ConfigureAwait(false);
78+
79+
// Delete and recreate the folder.
80+
return (SystemFolder)await rootFolder.CreateFolderAsync(name, overwrite: true).ConfigureAwait(false);
81+
}
82+
83+
public static async Task SetAllFileAttributesRecursive(SystemFolder rootFolder, Func<FileAttributes, FileAttributes> transform)
84+
{
85+
await foreach (SystemFile file in rootFolder.GetFilesAsync())
86+
file.Info.Attributes = transform(file.Info.Attributes);
87+
88+
await foreach (SystemFolder folder in rootFolder.GetFoldersAsync())
89+
await SetAllFileAttributesRecursive(folder, transform).ConfigureAwait(false);
90+
}
91+
92+
[AssemblyCleanup]
93+
public static void AssemblyCleanup()
94+
{
95+
try
96+
{
97+
Node?.Dispose();
98+
}
99+
catch { }
100+
}
13101
}
14102
}

test/TlsReproTest.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using System;
3+
using System.Linq;
4+
5+
namespace Ipfs.Http
6+
{
7+
[TestClass]
8+
public class TlsReproTest
9+
{
10+
[TestMethod]
11+
public void Real_Tls_MultiAddress_Should_Fail()
12+
{
13+
// Real multiaddr from long-running node that contains TLS
14+
var realTlsAddr = "/dns4/45-86-153-40.k51qzi5uqu5dhssh49wibkxi3yw56ihw9uo7cxctyiqbfxpodudqo09swsxp1s.libp2p.direct/tcp/4001/tls/ws/p2p/12D3KooWEBaJd7msGiDjA5ATyMpVSEgja4xc6FXkxddaSM9DtHCT/p2p-circuit/p2p/12D3KooWEPx5LSWNGRtQFweBG9KMsfNjdogoeRisF9cU2s5RcrsJ";
15+
16+
var addr = new MultiAddress(realTlsAddr);
17+
OwlCore.Diagnostics.Logger.LogInformation($"Successfully parsed TLS multiaddr: {addr}");
18+
19+
// Check if it contains the protocols we expect
20+
var protocols = addr.Protocols.ToList();
21+
OwlCore.Diagnostics.Logger.LogInformation($"Protocol count: {protocols.Count}");
22+
23+
foreach(var protocol in protocols)
24+
{
25+
OwlCore.Diagnostics.Logger.LogInformation($"Protocol: {protocol.Name} (code: {protocol.Code})");
26+
}
27+
28+
Assert.IsNotNull(addr);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)