forked from admin/VolcanicAgenticCore
Content step 2 (InboundMedia.AddFilesAsync, InboxFiles): files land in <cwd>/inbox/<date>-<safe name>[-N], image documents within Telegram:InlineImageMaxBytes are attached inline, oversize files and failed downloads get a notice and no run. Documents, videos, audio and voice are accepted; the unsupported reply is now 'I can read text, photos, files and voice messages.' Spec: Updates helpers are named DocumentMessage/VideoMessage/VoiceMessage/AudioMessage; the getFile 'file is too big' notice reads 'over 20 MB'; the too-large inline note is a parenthesised suffix after the size; size formats spelled out.
90 lines
4.0 KiB
C#
90 lines
4.0 KiB
C#
using Telegram.Bot.Requests;
|
|
using Volcanic.AgenticCore.Telegram.Inbound;
|
|
using Volcanic.AgenticCore.Telegram.Tests.Support;
|
|
|
|
namespace Volcanic.AgenticCore.Telegram.Tests;
|
|
|
|
public sealed class InboundComposerTests
|
|
{
|
|
private static InboundComposer Composer(ChatHost host) => host.Composer;
|
|
|
|
[Fact]
|
|
public async Task Text_becomes_the_body_with_reply_and_forward_headers()
|
|
{
|
|
await using var host = new ChatHost();
|
|
var chat = await host.ChatAsync();
|
|
var update = Updates.Forward("Geothermal plant opens", Updates.Channel("Daily News", "Anna K."));
|
|
update.Message!.ReplyToMessage = Updates.BotMessage("Which plant do you mean?");
|
|
|
|
var part = await Composer(host).ComposeAsync(chat, update.Message, CancellationToken.None);
|
|
|
|
Assert.NotNull(part);
|
|
Assert.Equal(update.Message.Id, part!.MessageId);
|
|
Assert.Equal(new DateTimeOffset(DateTime.SpecifyKind(update.Message.Date, DateTimeKind.Utc)), part.SentAt);
|
|
Assert.Equal("[Reply to your message: \"Which plant do you mean?\"]", part.ReplyHeader);
|
|
Assert.Equal("Daily News (Anna K.)", part.ForwardLabel);
|
|
Assert.Equal(["Geothermal plant opens"], part.Body);
|
|
Assert.Empty(part.Images);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Caption_and_the_largest_photo_become_body_and_image()
|
|
{
|
|
await using var host = new ChatHost();
|
|
var chat = await host.ChatAsync();
|
|
var update = Updates.AlbumPhoto("g1", caption: "three views");
|
|
update.Message!.Photo = [
|
|
new() { FileId = "small", FileUniqueId = "s", Width = 10, Height = 10 },
|
|
new() { FileId = "large", FileUniqueId = "l", Width = 100, Height = 100 },
|
|
];
|
|
|
|
var part = await Composer(host).ComposeAsync(chat, update.Message, CancellationToken.None);
|
|
|
|
Assert.Equal(["three views"], part!.Body);
|
|
Assert.Equal("image/jpeg", Assert.Single(part.Images).MimeType);
|
|
Assert.Equal("g1", part.MediaGroupId);
|
|
Assert.Equal("large", Assert.Single(host.Bot.Requests.OfType<GetFileRequest>()).FileId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_failed_photo_download_is_reported_and_a_bare_photo_composes_to_null()
|
|
{
|
|
await using var host = new ChatHost();
|
|
var chat = await host.ChatAsync();
|
|
host.Bot.OnSend = r => { if (r is GetFileRequest) throw FakeBotClient.NetworkDown(); };
|
|
|
|
var part = await Composer(host).ComposeAsync(chat, Updates.Photo().Message!, CancellationToken.None);
|
|
await chat.Renderer.DrainAsync().WaitAsync(TimeSpan.FromSeconds(5));
|
|
|
|
Assert.Null(part);
|
|
Assert.Contains(host.Texts, t => t.Contains("I could not download that photo.", StringComparison.Ordinal));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_captioned_photo_that_fails_to_download_keeps_its_caption()
|
|
{
|
|
await using var host = new ChatHost();
|
|
var chat = await host.ChatAsync();
|
|
host.Bot.OnSend = r => { if (r is GetFileRequest) throw FakeBotClient.NetworkDown(); };
|
|
|
|
var part = await Composer(host).ComposeAsync(chat, Updates.Photo("what is this?").Message!, CancellationToken.None);
|
|
|
|
Assert.Equal(["what is this?"], part!.Body);
|
|
Assert.Empty(part.Images);
|
|
}
|
|
|
|
[Fact]
|
|
public void Text_photos_files_and_voice_are_supported()
|
|
{
|
|
Assert.True(InboundComposer.IsSupported(Updates.Text("hi").Message!));
|
|
Assert.True(InboundComposer.IsSupported(Updates.Photo().Message!));
|
|
Assert.True(InboundComposer.IsSupported(Updates.DocumentMessage("a.pdf", "application/pdf", 3).Message!));
|
|
Assert.True(InboundComposer.IsSupported(Updates.VideoMessage(null, 3).Message!));
|
|
Assert.True(InboundComposer.IsSupported(Updates.AudioMessage("a.mp3", "audio/mpeg", 3).Message!));
|
|
Assert.True(InboundComposer.IsSupported(Updates.VoiceMessage().Message!));
|
|
Assert.False(InboundComposer.IsSupported(Updates.StickerMessage().Message!));
|
|
Assert.True(InboundComposer.HasAttachment(Updates.Photo("caption").Message!));
|
|
Assert.False(InboundComposer.HasAttachment(Updates.Text("/status").Message!));
|
|
}
|
|
}
|