Files
VolcanicAgenticCore/tests/Volcanic.AgenticCore.Extensions.Tests/Anthropic/AnthropicExtensionTests.cs

103 lines
5.6 KiB
C#

using System.Text.Json;
using Microsoft.Extensions.Logging;
using Volcanic.AgenticCore.Abstractions.Extensions;
using Volcanic.AgenticCore.Abstractions.Extensions.Events;
using Volcanic.AgenticCore.Abstractions.Messages;
using Volcanic.AgenticCore.Abstractions.Providers;
using Volcanic.AgenticCore.Abstractions.Tools;
using Volcanic.AgenticCore.Extensions.Anthropic;
using Volcanic.AgenticCore.Features.Models;
namespace Volcanic.AgenticCore.Extensions.Tests.Anthropic;
public class AnthropicExtensionTests
{
[Fact]
public async Task Activate_registers_the_anthropic_provider_with_the_default_catalogue()
{
var api = new FakeExtensionApi();
var extension = new AnthropicProviderExtension();
Assert.Equal("anthropic-provider", extension.Name);
await extension.ActivateAsync(api, CancellationToken.None);
var registration = Assert.Single(api.Providers);
Assert.Equal("anthropic", registration.Id);
Assert.Equal("Anthropic", registration.Name);
Assert.Equal("anthropic-messages", registration.Api);
Assert.Equal("https://api.anthropic.com", registration.BaseUrl);
Assert.Equal("$ANTHROPIC_API_KEY", registration.ApiKey);
Assert.True(registration.RequiresApiKey);
Assert.NotNull(registration.Stream);
Assert.Equal(["claude-opus-5", "claude-sonnet-5", "claude-fable-5-1", "claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"], registration.Models.Select(m => m.Id).ToArray());
Assert.All(registration.Models, m =>
{
Assert.Equal("anthropic", m.Provider);
Assert.Equal("anthropic-messages", m.Api);
Assert.True(m.Reasoning);
Assert.True(m.SupportsImages);
Assert.True(m.ContextWindow >= 200_000);
Assert.NotNull(m.ThinkingLevelMap![ThinkingLevel.Medium]);
Assert.NotNull(m.ThinkingLevelMap[ThinkingLevel.High]);
});
}
[Fact]
public async Task Activate_honours_custom_models_and_api_key_spec()
{
var api = new FakeExtensionApi();
var custom = AnthropicModels.Haiku45 with { Id = "claude-custom", Name = "Custom" };
var extension = new AnthropicProviderExtension(models: [custom], apiKeySpec: "!op read secret");
await extension.ActivateAsync(api, CancellationToken.None);
var registration = Assert.Single(api.Providers);
Assert.Equal("!op read secret", registration.ApiKey);
Assert.Equal("claude-custom", Assert.Single(registration.Models).Id);
}
[Fact]
public async Task Registration_resolves_through_the_model_registry()
{
var registry = new ModelRegistry(new ApiKeyResolver(null, _ => null));
var api = new FakeExtensionApi();
await new AnthropicProviderExtension().ActivateAsync(api, CancellationToken.None);
registry.Register(api.Providers[0]);
var model = registry.Find("anthropic/claude-opus-5");
Assert.NotNull(model);
Assert.Equal("Claude Opus 5", model!.Name);
Assert.Equal("anthropic-messages", model.Api);
Assert.Equal("https://api.anthropic.com", model.BaseUrl);
Assert.Equal(6, registry.ListModels().Count);
}
/// <summary>Only <see cref="RegisterProvider"/> is supported; everything else throws.</summary>
private sealed class FakeExtensionApi : IExtensionApi
{
public List<ProviderRegistration> Providers { get; } = [];
public string ExtensionName => "test";
public ILogger Logger => throw new NotSupportedException();
public IEventBus Events => throw new NotSupportedException();
public IDisposable On<TEvent>(ExtensionHandler<TEvent> handler) where TEvent : ExtensionEvent => throw new NotSupportedException();
public void RegisterTool(ITool tool) => throw new NotSupportedException();
public void RegisterCommand(string name, CommandDefinition command) => throw new NotSupportedException();
public void RegisterProvider(ProviderRegistration provider) => Providers.Add(provider);
public void UnregisterProvider(string providerId) => throw new NotSupportedException();
public void SendMessage(CustomMessage message, MessageDelivery delivery = MessageDelivery.Steer, bool triggerTurn = true) => throw new NotSupportedException();
public void SendUserMessage(string text, IReadOnlyList<ImageContent>? images = null, MessageDelivery delivery = MessageDelivery.Steer) => throw new NotSupportedException();
public void AppendEntry(string customType, JsonElement? data = null) => throw new NotSupportedException();
public Task<bool> SetModelAsync(Model model, CancellationToken cancellationToken = default) => throw new NotSupportedException();
public ThinkingLevel GetThinkingLevel() => throw new NotSupportedException();
public void SetThinkingLevel(ThinkingLevel level) => throw new NotSupportedException();
public IReadOnlyList<string> GetActiveTools() => throw new NotSupportedException();
public void SetActiveTools(IEnumerable<string> names) => throw new NotSupportedException();
public IReadOnlyList<ToolInfo> GetAllTools() => throw new NotSupportedException();
public IReadOnlyList<CommandInfo> GetCommands() => throw new NotSupportedException();
public string? GetSessionName() => throw new NotSupportedException();
public void SetSessionName(string? name) => throw new NotSupportedException();
public Task<ExecResult> ExecAsync(string command, IReadOnlyList<string> args, ExecOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException();
}
}