feat: add /meta command for channel metadata retrieval

- Implemented the `/meta` command to fetch and display channel metadata including room ID, topic, message count, unique user count, estimated size, and protection level.
- Added `ChannelMetaDto` to encapsulate channel metadata.
- Updated `ChannelsController` to handle the new `/meta` endpoint.
- Introduced `UploadLimits` configuration for admin-defined upload size limits for files, images, audio, and avatars.
- Enhanced error handling and user feedback for metadata retrieval.
- Updated documentation to reflect changes in encryption and room metadata.
- Added tests for the new functionality and upload limits.
This commit is contained in:
HueByte
2026-07-16 07:04:58 +02:00
parent dbf6565d18
commit e797ec2542
22 changed files with 526 additions and 93 deletions
+18
View File
@@ -472,4 +472,22 @@ public class CommandHandlerTests
Assert.True(result.Handled);
Assert.True(quitCalled);
}
// ── /meta ─────────────────────────────────────────────────────────
[Theory]
[InlineData("/meta")]
[InlineData("/info")]
public async Task HandleAsync_Meta_RaisesRoomInfo(string input)
{
var handler = CreateHandler();
var raised = false;
handler.OnRoomInfo += () => { raised = true; return Task.CompletedTask; };
var result = await handler.HandleAsync(input);
Assert.True(result.Handled);
Assert.False(result.IsError);
Assert.True(raised);
}
}
+5
View File
@@ -270,6 +270,11 @@ internal sealed class FakeChannelService : IChannelService
public Task<ChannelDto?> GetChannelByNameAsync(string channelName) =>
Task.FromResult(ChannelByNameToReturn);
public ChannelMetaDto? ChannelMetaToReturn { get; set; }
public Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName) =>
Task.FromResult(ChannelMetaToReturn);
public Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) =>
Task.FromResult(MembershipResult);
}
+66
View File
@@ -0,0 +1,66 @@
using EchoHub.Core.Constants;
using EchoHub.Core.Models;
using EchoHub.Server.Config;
using Microsoft.Extensions.Configuration;
using Xunit;
namespace EchoHub.Tests;
public class UploadLimitsTests
{
[Fact]
public void Defaults_MirrorHubConstants()
{
var limits = new UploadLimits();
Assert.Equal(HubConstants.MaxFileSizeBytes, limits.MaxFileSizeBytes);
Assert.Equal(HubConstants.MaxImageSizeBytes, limits.MaxImageSizeBytes);
Assert.Equal(HubConstants.MaxAudioFileSizeBytes, limits.MaxAudioSizeBytes);
Assert.Equal(HubConstants.MaxAvatarSizeBytes, limits.MaxAvatarSizeBytes);
Assert.Equal(HubConstants.MaxAttachmentsPerMessage, limits.MaxAttachmentsPerMessage);
}
[Fact]
public void MaxForKind_MapsEachAttachmentKind()
{
var limits = new UploadLimits
{
MaxImageSizeMB = 5,
MaxAudioSizeMB = 7,
MaxFileSizeMB = 11,
};
Assert.Equal(5L * 1024 * 1024, limits.MaxForKind(AttachmentKind.Image));
Assert.Equal(7L * 1024 * 1024, limits.MaxForKind(AttachmentKind.Audio));
Assert.Equal(11L * 1024 * 1024, limits.MaxForKind(AttachmentKind.File));
}
[Fact]
public void MaxRequestBodyBytes_IsFileSizeTimesAttachmentCap()
{
var limits = new UploadLimits { MaxFileSizeMB = 20, MaxAttachmentsPerMessage = 4 };
Assert.Equal(20L * 1024 * 1024 * 4, limits.MaxRequestBodyBytes);
}
[Fact]
public void BoundFromConfiguration_OverridesDefaults()
{
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Uploads:MaxFileSizeMB"] = "250",
["Uploads:MaxImageSizeMB"] = "25",
["Uploads:MaxAttachmentsPerMessage"] = "3",
})
.Build();
var limits = config.GetSection("Uploads").Get<UploadLimits>()!;
Assert.Equal(250L * 1024 * 1024, limits.MaxFileSizeBytes);
Assert.Equal(25L * 1024 * 1024, limits.MaxImageSizeBytes);
Assert.Equal(3, limits.MaxAttachmentsPerMessage);
// Unspecified values keep their HubConstants-derived defaults.
Assert.Equal(HubConstants.MaxAudioFileSizeBytes, limits.MaxAudioSizeBytes);
}
}