diff --git a/IncubatingIntegrationTest/ListTest.cs b/IncubatingIntegrationTest/ListTest.cs index 37adc7e8..0a455786 100644 --- a/IncubatingIntegrationTest/ListTest.cs +++ b/IncubatingIntegrationTest/ListTest.cs @@ -74,6 +74,12 @@ public async Task ListPushFrontFetch_ValueIsByteArray_RefreshTtl() Assert.Equal(2, response.ByteArrayList!.Count); } + [Fact] + public async Task ListPushFrontAsync_ValueIsByteArrayTruncateTailToSizeIsZero_ThrowsException() + { + await Assert.ThrowsAsync(async () => await client.ListPushFrontAsync("myCache", "listName", new byte[] { 0x00 }, false, truncateTailToSize: 0)); + } + [Theory] [InlineData(null, "my-list", "my-value")] [InlineData("cache", null, "my-value")] @@ -139,6 +145,12 @@ public async Task ListPushFrontFetch_ValueIsString_RefreshTtl() Assert.Equal(2, response.StringList()!.Count); } + [Fact] + public async Task ListPushFrontAsync_ValueIsStringTruncateTailToSizeIsZero_ThrowsException() + { + await Assert.ThrowsAsync(async () => await client.ListPushFrontAsync("myCache", "listName", "value", false, truncateTailToSize: 0)); + } + [Theory] [InlineData(null, "my-list", new byte[] { 0x00 })] [InlineData("cache", null, new byte[] { 0x00 })] @@ -204,6 +216,12 @@ public async Task ListPushBackFetch_ValueIsByteArray_RefreshTtl() Assert.Equal(2, response.ByteArrayList!.Count); } + [Fact] + public async Task ListPushBackAsync_ValueIsByteArrayTruncateHeadToSizeIsZero_ThrowsException() + { + await Assert.ThrowsAsync(async () => await client.ListPushBackAsync("myCache", "listName", new byte[] { 0x00 }, false, truncateHeadToSize: 0)); + } + [Theory] [InlineData(null, "my-list", "my-value")] [InlineData("cache", null, "my-value")] @@ -269,6 +287,12 @@ public async Task ListPushBackFetch_ValueIsString_RefreshTtl() Assert.Equal(2, response.StringList()!.Count); } + [Fact] + public async Task ListPushBackAsync_ValueIsStringTruncateHeadToSizeIsZero_ThrowsException() + { + await Assert.ThrowsAsync(async () => await client.ListPushBackAsync("myCache", "listName", "value", false, truncateHeadToSize: 0)); + } + [Theory] [InlineData(null, "my-list")] [InlineData("cache", null)] diff --git a/Momento/Incubating/SimpleCacheClient.cs b/Momento/Incubating/SimpleCacheClient.cs index eba5dc30..ac8d695a 100644 --- a/Momento/Incubating/SimpleCacheClient.cs +++ b/Momento/Incubating/SimpleCacheClient.cs @@ -674,13 +674,16 @@ public async Task SetDeleteAsync(string cacheName, strin /// The value to push to the front of the list. /// Update `listName`'s TTL if it already exists. /// TTL for the list in cache. This TTL takes precedence over the TTL used when initializing a cache client. Defaults to client TTL. + /// Ensure the list does not exceed this length. Remove excess from tail of list. Must be a positive number. /// Task representing the result of the push operation. /// Any of `cacheName` or `listName` or `value` is `null`. - public async Task ListPushFrontAsync(string cacheName, string listName, byte[] value, bool refreshTtl, uint? ttlSeconds = null) + /// `truncateTailToSize` is zero. + public async Task ListPushFrontAsync(string cacheName, string listName, byte[] value, bool refreshTtl, uint? ttlSeconds = null, uint? truncateTailToSize = null) { Utils.ArgumentNotNull(cacheName, nameof(cacheName)); Utils.ArgumentNotNull(listName, nameof(listName)); Utils.ArgumentNotNull(value, nameof(value)); + Utils.ArgumentStrictlyPositive(truncateTailToSize, nameof(truncateTailToSize)); return await this.dataClient.ListPushFrontAsync(cacheName, listName, value, refreshTtl, ttlSeconds); } @@ -697,13 +700,16 @@ public async Task ListPushFrontAsync(string cacheNam /// The value to push to the front of the list. /// Update `listName`'s TTL if it already exists. /// TTL for the list in cache. This TTL takes precedence over the TTL used when initializing a cache client. Defaults to client TTL. + /// Ensure the list does not exceed this length. Remove excess from tail of list. Must be a positive number. /// Task representing the result of the push operation. /// Any of `cacheName` or `listName` or `value` is `null`. - public async Task ListPushFrontAsync(string cacheName, string listName, string value, bool refreshTtl, uint? ttlSeconds = null) + /// `truncateTailToSize` is zero. + public async Task ListPushFrontAsync(string cacheName, string listName, string value, bool refreshTtl, uint? ttlSeconds = null, uint? truncateTailToSize = null) { Utils.ArgumentNotNull(cacheName, nameof(cacheName)); Utils.ArgumentNotNull(listName, nameof(listName)); Utils.ArgumentNotNull(value, nameof(value)); + Utils.ArgumentStrictlyPositive(truncateTailToSize, nameof(truncateTailToSize)); return await this.dataClient.ListPushFrontAsync(cacheName, listName, value, refreshTtl, ttlSeconds); } @@ -720,13 +726,16 @@ public async Task ListPushFrontAsync(string cacheNam /// The value to push to the back of the list. /// Update `listName`'s TTL if it already exists. /// TTL for the list in cache. This TTL takes precedence over the TTL used when initializing a cache client. Defaults to client TTL. + /// Ensure the list does not exceed this length. Remove excess from head of list. Must be a positive number. /// Task representing the result of the push operation. /// Any of `cacheName` or `listName` or `value` is `null`. - public async Task ListPushBackAsync(string cacheName, string listName, byte[] value, bool refreshTtl, uint? ttlSeconds = null) + /// `truncateHeadToSize` is zero. + public async Task ListPushBackAsync(string cacheName, string listName, byte[] value, bool refreshTtl, uint? ttlSeconds = null, uint? truncateHeadToSize = null) { Utils.ArgumentNotNull(cacheName, nameof(cacheName)); Utils.ArgumentNotNull(listName, nameof(listName)); Utils.ArgumentNotNull(value, nameof(value)); + Utils.ArgumentStrictlyPositive(truncateHeadToSize, nameof(truncateHeadToSize)); return await this.dataClient.ListPushBackAsync(cacheName, listName, value, refreshTtl, ttlSeconds); } @@ -743,13 +752,16 @@ public async Task ListPushBackAsync(string cacheName, /// The value to push to the back of the list. /// Update `listName`'s TTL if it already exists. /// TTL for the list in cache. This TTL takes precedence over the TTL used when initializing a cache client. Defaults to client TTL. + /// Ensure the list does not exceed this length. Remove excess from head of list. Must be a positive number. /// Task representing the result of the push operation. /// Any of `cacheName` or `listName` or `value` is `null`. - public async Task ListPushBackAsync(string cacheName, string listName, string value, bool refreshTtl, uint? ttlSeconds = null) + /// `truncateHeadToSize` is zero. + public async Task ListPushBackAsync(string cacheName, string listName, string value, bool refreshTtl, uint? ttlSeconds = null, uint? truncateHeadToSize = null) { Utils.ArgumentNotNull(cacheName, nameof(cacheName)); Utils.ArgumentNotNull(listName, nameof(listName)); Utils.ArgumentNotNull(value, nameof(value)); + Utils.ArgumentStrictlyPositive(truncateHeadToSize, nameof(truncateHeadToSize)); return await this.dataClient.ListPushBackAsync(cacheName, listName, value, refreshTtl, ttlSeconds); } diff --git a/Momento/Internal/Utils.cs b/Momento/Internal/Utils.cs index 899d71c5..8a4ecbba 100644 --- a/Momento/Internal/Utils.cs +++ b/Momento/Internal/Utils.cs @@ -76,6 +76,20 @@ public static void ElementsNotNull(IEnumerable argument, string paramName) } } + /// + /// Throw an exception if the argument is zero. + /// + /// The integer to zero test. + /// Name of the integer to propagate to the exception. + /// `argument` is zero. + public static void ArgumentStrictlyPositive(uint? argument, string paramName) + { + if (argument == 0) + { + throw new ArgumentOutOfRangeException(paramName, "Number must be strictly positive."); + } + } + /// /// Defines methods to support comparing containers of reference items by their /// contents (structure) instead of by reference.