using WxAgent.Core; using Xunit; namespace WxAgent.Core.Tests; public sealed class WechatContactQueryTests { [Fact] public void ParametersAreBoundAndFetchOneExtraRow() { const string filter = "O'Brien_%'; DROP TABLE contact;--"; var parameters = WechatContactQuery.Parameters(2, 4, filter, true).ToDictionary(pair => pair.Key, pair => pair.Value); Assert.Equal(3, parameters["$limit"]); Assert.Equal(4, parameters["$offset"]); Assert.Equal(1, parameters["$groups"]); Assert.Equal(filter, parameters["$contains"]); Assert.DoesNotContain(filter, WechatContactQuery.Sql); Assert.Contains("ORDER BY username COLLATE BINARY", WechatContactQuery.Sql); Assert.Equal(0, WechatContactQuery.Parameters(2, 0, null, false).Last().Value); Assert.Null(WechatContactQuery.Parameters(2, 0, null, null).Last().Value); } [Theory] [InlineData(0, 0)] [InlineData(10001, 0)] [InlineData(1, -1)] [InlineData(2, int.MaxValue)] public void InvalidBoundsAreRejected(int limit, int offset) => Assert.Equal(WxAgentErrorCode.InvalidArgument, Assert.Throws(() => WechatContactQuery.Parameters(limit, offset, null, null)).Code); [Fact] public void PageSignalsTruncationWithoutCollapsingDuplicateDisplayNames() { WechatDatabaseContact[] contacts = [new("a", "same", null, null), new("b", "same", null, null), new("c", null, "remark", null)]; var page = WechatContactQuery.Page(contacts, 2, 4); Assert.True(page.HasMore); Assert.Equal(6, page.NextOffset); Assert.Equal(new[] { "a", "b" }, page.Contacts.Select(contact => contact.Username)); var last = WechatContactQuery.Page(contacts[2..], 2, 6); Assert.False(last.HasMore); Assert.Equal("remark", last.Contacts[0].DisplayName); Assert.False(WechatContactQuery.Page([], 2, 0).HasMore); Assert.Equal("wxid", new WechatDatabaseContact("wxid", "", " ", null).DisplayName); } }