using Morph.AiKit.Agents;
using Morph.AiKit.Providers;
using Morph.AiKit.Tests.Fakes;
using Xunit;
namespace Morph.AiKit.Tests;
///
/// A companheira obrigatoria de AgentApi.Images/AgentApi.Videos existirem:
/// um alias de midia nao vira agente.
///
///
///
/// Um registry so e um vocabulario so de alias e a decisao (decisions.md D3) — e
/// justamente por isso a recusa precisa existir. Sem ela, um alias de imagem passado por
/// engano viraria um agente de chat apontando para um modelo que nao fala chat: a falha
/// apareceria no primeiro turno, com uma mensagem do provider que nao diz o que aconteceu.
///
///
/// Enum novo sem recusa e uma armadilha nova. Este arquivo e o que impede que ela volte.
///
///
public sealed class MediaAliasIsNotAnAgentTests
{
private const string MediaAliasDocument = """
{
"version": "2.0",
"defaultAlias": "texto",
"aliases": {
"texto": {
"provider": "openai",
"model": "gpt-5.4",
"api": "responses",
"pricing": { "inputPerMTok": 1.25, "outputPerMTok": 10.0 }
},
"imagem": {
"provider": "openai",
"model": "gpt-image-2",
"api": "images",
"pricingWaiver": "tarifa de imagem cobra por imagem/qualidade, nao por token: ver MediaPricing."
},
"video": {
"provider": "google",
"model": "veo-3.1-fast-generate-preview",
"api": "videos",
"pricingWaiver": "tarifa de video cobra por segundo de saida: ver MediaPricing."
}
}
}
""";
private static AgentSpec Spec(string alias, AgentApi? api = null)
=> new() { Alias = alias, Instructions = "voce e um agente de teste", Api = api };
/// O registry ACEITA images e videos — um vocabulario so de alias.
[Theory]
[InlineData("imagem", AgentApi.Images)]
[InlineData("video", AgentApi.Videos)]
public void TheRegistryUnderstandsTheMediaProtocols(string alias, AgentApi expected)
{
using var registry = ModelRegistry.FromJson(MediaAliasDocument, new FakeProviderClientFactory());
Assert.Equal(expected, registry.Resolve(alias).Api);
}
/// E a fabrica RECUSA, na construcao, com erro nomeado.
[Theory]
[InlineData("imagem")]
[InlineData("video")]
public void TheFactoryRefusesAMediaAliasByName(string alias)
{
using var registry = ModelRegistry.FromJson(MediaAliasDocument, new FakeProviderClientFactory());
var factory = new AgentFactory(registry);
var ex = Assert.Throws(() => factory.Create(Spec(alias)));
Assert.Equal(AgentFactoryError.MediaAliasIsNotAnAgent, ex.Reason);
Assert.Contains("EnqueueAsync", ex.Message, StringComparison.Ordinal);
}
///
/// A recusa tambem cobre o caminho pela SPEC: um alias de conversa com
/// Api = Images na spec e a mesma confusao, pela outra porta.
///
[Fact]
public void TheFactoryRefusesAMediaProtocolDeclaredOnTheSpec()
{
using var registry = ModelRegistry.FromJson(MediaAliasDocument, new FakeProviderClientFactory());
var factory = new AgentFactory(registry);
var ex = Assert.Throws(() => factory.Create(Spec("texto", AgentApi.Images)));
Assert.Equal(AgentFactoryError.MediaAliasIsNotAnAgent, ex.Reason);
}
/// Alias de conversa continua passando — a recusa nova nao pode ser um bloqueio geral.
[Fact]
public void AChatAliasStillBuildsAnAgent()
{
using var registry = ModelRegistry.FromJson(MediaAliasDocument, new FakeProviderClientFactory());
var factory = new AgentFactory(registry);
Assert.NotNull(factory.Create(Spec("texto")));
}
/// O predicado mora num lugar so — duas copias e como as duas discordam.
[Theory]
[InlineData(AgentApi.Responses, false)]
[InlineData(AgentApi.Chat, false)]
[InlineData(AgentApi.Images, true)]
[InlineData(AgentApi.Videos, true)]
public void IsMediaAnswersForEveryProtocol(AgentApi api, bool expected)
=> Assert.Equal(expected, api.IsMedia());
/// Um api inventado continua sendo recusado na carga do documento.
[Fact]
public void AnUnknownApiIsStillRefusedWithTheFullListOfValidValues()
{
var document = MediaAliasDocument.Replace("\"api\": \"images\"", "\"api\": \"holograma\"", StringComparison.Ordinal);
var ex = Assert.Throws(
() => ModelRegistry.FromJson(document, new FakeProviderClientFactory()));
Assert.Contains("images", ex.Message, StringComparison.Ordinal);
Assert.Contains("videos", ex.Message, StringComparison.Ordinal);
}
}