From ac4f6d9db4f469d7d8cee0f0295df0f9be3fdf23 Mon Sep 17 00:00:00 2001 From: riina <54872398+riina@users.noreply.github.com> Date: Fri, 3 Mar 2023 00:14:12 -0800 Subject: [PATCH] fix list proxy not showing artifacts w no res --- .../ArtifactToolDumpProxyTests.cs | 92 +++++++++++++++++++ .../ArtifactToolListProxyTests.cs | 84 +++++++++++++++++ .../Proxies/ArtifactToolDumpProxy.cs | 1 + .../Proxies/ArtifactToolListProxy.cs | 33 +++++-- .../ProgrammableArtifactDumpTool.cs | 38 ++++++++ .../ProgrammableArtifactFindTool.cs | 48 +++++++++- .../ProgrammableArtifactListTool.cs | 48 +++++++++- 7 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 src/Art.Common.Tests/ArtifactToolDumpProxyTests.cs create mode 100644 src/Art.Common.Tests/ArtifactToolListProxyTests.cs diff --git a/src/Art.Common.Tests/ArtifactToolDumpProxyTests.cs b/src/Art.Common.Tests/ArtifactToolDumpProxyTests.cs new file mode 100644 index 0000000..a8c0023 --- /dev/null +++ b/src/Art.Common.Tests/ArtifactToolDumpProxyTests.cs @@ -0,0 +1,92 @@ +using System.Text.Json; +using Art.Common.Management; +using Art.Common.Proxies; +using Art.TestsBase; +using NUnit.Framework; + +namespace Art.Common.Tests; + +public class ArtifactToolDumpProxyTests +{ + [Test] + public async Task FindOnlyTool_WithoutArtifactList_Throws() + { + var profile = new ArtifactToolProfile("tool", null, null); + var tool = new ProgrammableArtifactFindTool((v, k) => + { + int i = int.Parse(k); + if (1 <= i && i <= 3) + { + return v.CreateData($"{i}"); + } + return null; + }); + await tool.InitializeAsync(profile: profile); + var proxy = new ArtifactToolDumpProxy(tool, new ArtifactToolDumpOptions(), null); + Assert.That(async () => await proxy.DumpAsync(), Throws.InstanceOf()); + } + + [Test] + public async Task FindOnlyTool_WithArtifactList_Success() + { + var options = new Dictionary { { "artifactList", JsonSerializer.SerializeToElement(new[] { "1", "2", "3" }) } }; + var profile = new ArtifactToolProfile("tool", null, options); + var arm = new InMemoryArtifactRegistrationManager(); + var config = new ArtifactToolConfig(arm, new NullArtifactDataManager()); + var tool = new ProgrammableArtifactFindTool((v, k) => + { + int i = int.Parse(k); + if (1 <= i && i <= 3) + { + return v.CreateData($"{i}"); + } + return null; + }); + await tool.InitializeAsync(config: config, profile: profile); + var proxy = new ArtifactToolDumpProxy(tool, new ArtifactToolDumpOptions(), null); + await proxy.DumpAsync(); + Assert.That((await arm.ListArtifactsAsync()).Select(v => int.Parse(v.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + [Test] + public async Task DumpOnlyTool_WithoutArtifactList_Success() + { + var options = new Dictionary { { "artifactList", JsonSerializer.SerializeToElement(new[] { "1", "2", "3" }) } }; + var profile = new ArtifactToolProfile("tool", null, options); + var arm = new InMemoryArtifactRegistrationManager(); + var config = new ArtifactToolConfig(arm, new NullArtifactDataManager()); + var tool = new AsyncProgrammableArtifactDumpTool(async v => + { + for (int i = 1; i <= 3; i++) + { + await v.DumpArtifactAsync(v.CreateData($"{i}")); + } + }); + await tool.InitializeAsync(config: config, profile: profile); + var proxy = new ArtifactToolDumpProxy(tool, new ArtifactToolDumpOptions(), null); + await proxy.DumpAsync(); + Assert.That((await arm.ListArtifactsAsync()).Select(v => int.Parse(v.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + [Test] + public async Task DumpOnlyTool_WithArtifactList_DoesNotFilter() + { + var options = new Dictionary { { "artifactList", JsonSerializer.SerializeToElement(new[] { "1", "2" }) } }; + var profile = new ArtifactToolProfile("tool", null, options); + var arm = new InMemoryArtifactRegistrationManager(); + var config = new ArtifactToolConfig(arm, new NullArtifactDataManager()); + var tool = new AsyncProgrammableArtifactDumpTool(async v => + { + for (int i = 1; i <= 3; i++) + { + await v.DumpArtifactAsync(v.CreateData($"{i}")); + } + }); + await tool.InitializeAsync(config: config, profile: profile); + var proxy = new ArtifactToolDumpProxy(tool, new ArtifactToolDumpOptions(), null); + await proxy.DumpAsync(); + Assert.That((await arm.ListArtifactsAsync()).Select(v => int.Parse(v.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + // TODO prioritisation tests +} diff --git a/src/Art.Common.Tests/ArtifactToolListProxyTests.cs b/src/Art.Common.Tests/ArtifactToolListProxyTests.cs new file mode 100644 index 0000000..637ef7e --- /dev/null +++ b/src/Art.Common.Tests/ArtifactToolListProxyTests.cs @@ -0,0 +1,84 @@ +using System.Text.Json; +using Art.Common.Proxies; +using Art.TestsBase; +using NUnit.Framework; + +namespace Art.Common.Tests; + +public class ArtifactToolListProxyTests +{ + [Test] + public async Task FindOnlyTool_WithoutArtifactList_Throws() + { + var profile = new ArtifactToolProfile("tool", null, null); + var tool = new ProgrammableArtifactFindTool((v, k) => + { + int i = int.Parse(k); + if (1 <= i && i <= 3) + { + return v.CreateData($"{i}"); + } + return null; + }); + await tool.InitializeAsync(profile: profile); + var proxy = new ArtifactToolListProxy(tool, new ArtifactToolListOptions(), null); + Assert.That(async () => await proxy.ListAsync().ToListAsync(), Throws.InstanceOf()); + } + + [Test] + public async Task FindOnlyTool_WithArtifactList_Success() + { + var options = new Dictionary { { "artifactList", JsonSerializer.SerializeToElement(new[] { "1", "2", "3" }) } }; + var profile = new ArtifactToolProfile("tool", null, options); + var tool = new ProgrammableArtifactFindTool((v, k) => + { + int i = int.Parse(k); + if (1 <= i && i <= 3) + { + return v.CreateData($"{i}"); + } + return null; + }); + await tool.InitializeAsync(profile: profile); + var proxy = new ArtifactToolListProxy(tool, new ArtifactToolListOptions(), null); + var results = await proxy.ListAsync().ToListAsync(); + Assert.That(results.Select(v => int.Parse(v.Info.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + [Test] + public async Task DumpOnlyTool_WithoutArtifactList_Success() + { + var profile = new ArtifactToolProfile("tool", null, null); + var tool = new AsyncProgrammableArtifactDumpTool(async v => + { + for (int i = 1; i <= 3; i++) + { + await v.DumpArtifactAsync(v.CreateData($"{i}")); + } + }); + await tool.InitializeAsync(profile: profile); + var proxy = new ArtifactToolListProxy(tool, new ArtifactToolListOptions(), null); + var results = await proxy.ListAsync().ToListAsync(); + Assert.That(results.Select(v => int.Parse(v.Info.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + [Test] + public async Task DumpOnlyTool_WithArtifactList_DoesNotFilter() + { + var options = new Dictionary { { "artifactList", JsonSerializer.SerializeToElement(new[] { "1", "2" }) } }; + var profile = new ArtifactToolProfile("tool", null, options); + var tool = new AsyncProgrammableArtifactDumpTool(async v => + { + for (int i = 1; i <= 3; i++) + { + await v.DumpArtifactAsync(v.CreateData($"{i}")); + } + }); + await tool.InitializeAsync(profile: profile); + var proxy = new ArtifactToolListProxy(tool, new ArtifactToolListOptions(), null); + var results = await proxy.ListAsync().ToListAsync(); + Assert.That(results.Select(v => int.Parse(v.Info.Key.Id)), Is.EquivalentTo(new[] { 1, 2, 3 })); + } + + // TODO prioritisation tests +} diff --git a/src/Art.Common/Proxies/ArtifactToolDumpProxy.cs b/src/Art.Common/Proxies/ArtifactToolDumpProxy.cs index d5a1590..233a1e2 100644 --- a/src/Art.Common/Proxies/ArtifactToolDumpProxy.cs +++ b/src/Art.Common/Proxies/ArtifactToolDumpProxy.cs @@ -51,6 +51,7 @@ private static void Validate(ArtifactToolDumpOptions options, bool constructor) /// Cancellation token. /// Task. /// Thrown when an invalid configuration is detected. + /// Thrown when a tool does not natively and cannot be made to support dumping. public async ValueTask DumpAsync(CancellationToken cancellationToken = default) { if (ArtifactTool == null) throw new InvalidOperationException("Artifact tool cannot be null"); diff --git a/src/Art.Common/Proxies/ArtifactToolListProxy.cs b/src/Art.Common/Proxies/ArtifactToolListProxy.cs index 07f3400..7387b06 100644 --- a/src/Art.Common/Proxies/ArtifactToolListProxy.cs +++ b/src/Art.Common/Proxies/ArtifactToolListProxy.cs @@ -44,6 +44,7 @@ public ArtifactToolListProxy(IArtifactTool artifactTool, ArtifactToolListOptions /// Cancellation token. /// Async-enumerable artifacts. /// Thrown when an invalid configuration is detected. + /// Thrown when a tool does not natively and cannot be made to support listing. public async IAsyncEnumerable ListAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { if (ArtifactTool == null) throw new InvalidOperationException("Artifact tool cannot be null"); @@ -87,23 +88,43 @@ await foreach (IArtifactData data in enumerable.ConfigureAwait(false)) } if (artifactTool is IArtifactDumpTool dumpTool) { - IArtifactDataManager previous = artifactTool.DataManager; + IArtifactDataManager previousAdm = artifactTool.DataManager; + IArtifactRegistrationManager previousArm = artifactTool.RegistrationManager; try { - InMemoryArtifactDataManager im = new(); - artifactTool.DataManager = im; + InMemoryArtifactDataManager adm = new(); + InMemoryArtifactRegistrationManager arm = new(); + artifactTool.DataManager = adm; + artifactTool.RegistrationManager = arm; await dumpTool.DumpAsync(cancellationToken).ConfigureAwait(false); - foreach ((ArtifactKey ak, List resources) in im.Artifacts) + HashSet known = new(); + foreach ((ArtifactKey ak, List resources) in adm.Artifacts) { - if (await artifactTool.RegistrationManager.TryGetArtifactAsync(ak, cancellationToken).ConfigureAwait(false) is not { } info) continue; + if (await artifactTool.RegistrationManager.TryGetArtifactAsync(ak, cancellationToken).ConfigureAwait(false) is not { } info) + { + continue; + } + if (!known.Add(ak)) + { + continue; + } ArtifactData data = new(info); data.AddRange(resources); yield return data; } + foreach (var info in await arm.ListArtifactsAsync(cancellationToken).ConfigureAwait(false)) + { + if (!known.Add(info.Key)) + { + continue; + } + yield return new ArtifactData(info); + } } finally { - artifactTool.DataManager = previous; + artifactTool.DataManager = previousAdm; + artifactTool.RegistrationManager = previousArm; } yield break; } diff --git a/src/Art.TestsBase/ProgrammableArtifactDumpTool.cs b/src/Art.TestsBase/ProgrammableArtifactDumpTool.cs index c435e59..b36f208 100644 --- a/src/Art.TestsBase/ProgrammableArtifactDumpTool.cs +++ b/src/Art.TestsBase/ProgrammableArtifactDumpTool.cs @@ -40,3 +40,41 @@ private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, SynchronousDum public override Type GetArtifactToolType() => typeof(ProgrammableArtifactDumpTool); } } + +public class AsyncProgrammableArtifactDumpTool : ArtifactTool, IArtifactDumpTool +{ + public delegate Task AsyncDumpDelegate(AsyncProgrammableArtifactDumpTool tool); + + public readonly AsyncDumpDelegate? AsyncDumpAction; + + public AsyncProgrammableArtifactDumpTool(AsyncDumpDelegate asyncDumpAction) + { + AsyncDumpAction = asyncDumpAction; + } + + public Task DumpAsync(CancellationToken cancellationToken = default) + { + if (AsyncDumpAction != null) + { + return AsyncDumpAction(this); + } + throw new NotImplementedException(); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(AsyncDumpDelegate asyncDumpDelegate) + { + return CreateRegistryEntry(ArtifactToolIDUtil.CreateToolId(), asyncDumpDelegate); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(ArtifactToolID artifactToolId, AsyncDumpDelegate asyncDumpDelegate) + { + return new CustomArtifactToolRegistryEntry(artifactToolId, asyncDumpDelegate); + } + + private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, AsyncDumpDelegate AsyncDumpDelegate) : ArtifactToolRegistryEntry(Id) + { + public override IArtifactTool CreateArtifactTool() => new AsyncProgrammableArtifactDumpTool(AsyncDumpDelegate); + + public override Type GetArtifactToolType() => typeof(AsyncProgrammableArtifactDumpTool); + } +} diff --git a/src/Art.TestsBase/ProgrammableArtifactFindTool.cs b/src/Art.TestsBase/ProgrammableArtifactFindTool.cs index 68e371b..f7d93b2 100644 --- a/src/Art.TestsBase/ProgrammableArtifactFindTool.cs +++ b/src/Art.TestsBase/ProgrammableArtifactFindTool.cs @@ -6,18 +6,18 @@ public class ProgrammableArtifactFindTool : ArtifactTool, IArtifactFindTool { public delegate IArtifactData? SynchronousFindDelegate(ProgrammableArtifactFindTool tool, string id); - public readonly SynchronousFindDelegate? FindFunc; + public readonly SynchronousFindDelegate? SynchronousFindFunc; - public ProgrammableArtifactFindTool(SynchronousFindDelegate? findFunc) + public ProgrammableArtifactFindTool(SynchronousFindDelegate? synchronousFindFunc) { - FindFunc = findFunc; + SynchronousFindFunc = synchronousFindFunc; } public Task FindAsync(string id, CancellationToken cancellationToken = default) { - if (FindFunc != null) + if (SynchronousFindFunc != null) { - return Task.FromResult(FindFunc(this, id)); + return Task.FromResult(SynchronousFindFunc(this, id)); } throw new NotImplementedException(); } @@ -39,3 +39,41 @@ private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, SynchronousFin public override Type GetArtifactToolType() => typeof(ProgrammableArtifactFindTool); } } + +public class AsyncProgrammableArtifactFindTool : ArtifactTool, IArtifactFindTool +{ + public delegate Task AsyncFindDelegate(AsyncProgrammableArtifactFindTool tool, string id); + + public readonly AsyncFindDelegate? AsyncFindFunc; + + public AsyncProgrammableArtifactFindTool(AsyncFindDelegate? asyncFindFunc) + { + AsyncFindFunc = asyncFindFunc; + } + + public Task FindAsync(string id, CancellationToken cancellationToken = default) + { + if (AsyncFindFunc != null) + { + return AsyncFindFunc(this, id); + } + throw new NotImplementedException(); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(AsyncFindDelegate asyncFindDelegate) + { + return CreateRegistryEntry(ArtifactToolIDUtil.CreateToolId(), asyncFindDelegate); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(ArtifactToolID artifactToolId, AsyncFindDelegate asyncFindDelegate) + { + return new CustomArtifactToolRegistryEntry(artifactToolId, asyncFindDelegate); + } + + private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, AsyncFindDelegate AsyncFindDelegate) : ArtifactToolRegistryEntry(Id) + { + public override IArtifactTool CreateArtifactTool() => new AsyncProgrammableArtifactFindTool(AsyncFindDelegate); + + public override Type GetArtifactToolType() => typeof(AsyncProgrammableArtifactFindTool); + } +} diff --git a/src/Art.TestsBase/ProgrammableArtifactListTool.cs b/src/Art.TestsBase/ProgrammableArtifactListTool.cs index b437c1b..9d017be 100644 --- a/src/Art.TestsBase/ProgrammableArtifactListTool.cs +++ b/src/Art.TestsBase/ProgrammableArtifactListTool.cs @@ -6,20 +6,20 @@ public class ProgrammableArtifactListTool : ArtifactTool, IArtifactListTool { public delegate List SynchronousListDelegate(ProgrammableArtifactListTool tool); - public readonly SynchronousListDelegate? ListFunc; + public readonly SynchronousListDelegate? SynchronousListFunc; - public ProgrammableArtifactListTool(SynchronousListDelegate? listFunc) + public ProgrammableArtifactListTool(SynchronousListDelegate? synchronousListFunc) { - ListFunc = listFunc; + SynchronousListFunc = synchronousListFunc; } public IAsyncEnumerable ListAsync(CancellationToken cancellationToken = default) { - if (ListFunc == null) + if (SynchronousListFunc == null) { throw new NotImplementedException(); } - return ListFunc(this).ToAsyncEnumerable(); + return SynchronousListFunc(this).ToAsyncEnumerable(); } public static ArtifactToolRegistryEntry CreateRegistryEntry(SynchronousListDelegate synchronousListDelegate) @@ -39,3 +39,41 @@ private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, SynchronousLis public override Type GetArtifactToolType() => typeof(ProgrammableArtifactListTool); } } + +public class AsyncProgrammableArtifactListTool : ArtifactTool, IArtifactListTool +{ + public delegate IAsyncEnumerable AsyncListDelegate(AsyncProgrammableArtifactListTool tool); + + public readonly AsyncListDelegate? AsyncListFunc; + + public AsyncProgrammableArtifactListTool(AsyncListDelegate? asyncListFunc) + { + AsyncListFunc = asyncListFunc; + } + + public IAsyncEnumerable ListAsync(CancellationToken cancellationToken = default) + { + if (AsyncListFunc == null) + { + throw new NotImplementedException(); + } + return AsyncListFunc(this); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(AsyncListDelegate asyncListDelegate) + { + return CreateRegistryEntry(ArtifactToolIDUtil.CreateToolId(), asyncListDelegate); + } + + public static ArtifactToolRegistryEntry CreateRegistryEntry(ArtifactToolID artifactToolId, AsyncListDelegate asyncListDelegate) + { + return new CustomArtifactToolRegistryEntry(artifactToolId, asyncListDelegate); + } + + private record CustomArtifactToolRegistryEntry(ArtifactToolID Id, AsyncListDelegate AsyncListDelegate) : ArtifactToolRegistryEntry(Id) + { + public override IArtifactTool CreateArtifactTool() => new AsyncProgrammableArtifactListTool(AsyncListDelegate); + + public override Type GetArtifactToolType() => typeof(AsyncProgrammableArtifactListTool); + } +}