feat(05-01): make helper methods internal and add unit tests

- Changed IsThrottleException to internal static in ExecuteQueryRetryHelper
- Changed BuildPagedViewXml to internal static in SharePointPaginationHelper
- Created ExecuteQueryRetryHelperTests: 5 tests (throttle true x3, non-throttle false, nested false)
- Created SharePointPaginationHelperTests: 5 tests (null, empty, whitespace, replace, append)
This commit is contained in:
Dev
2026-04-03 16:34:54 +02:00
parent 0122a47c9e
commit 4d7e9ea02a
4 changed files with 84 additions and 2 deletions

View File

@@ -0,0 +1,33 @@
using SharepointToolbox.Core.Helpers;
namespace SharepointToolbox.Tests.Helpers;
[Trait("Category", "Unit")]
public class ExecuteQueryRetryHelperTests
{
[Theory]
[InlineData("The request has been throttled -- 429")]
[InlineData("Service unavailable 503")]
[InlineData("SharePoint has throttled your request")]
public void IsThrottleException_ThrottleMessages_ReturnsTrue(string message)
{
var ex = new Exception(message);
Assert.True(ExecuteQueryRetryHelper.IsThrottleException(ex));
}
[Fact]
public void IsThrottleException_NonThrottleMessage_ReturnsFalse()
{
var ex = new Exception("File not found");
Assert.False(ExecuteQueryRetryHelper.IsThrottleException(ex));
}
[Fact]
public void IsThrottleException_NestedThrottleInInnerException_ReturnsFalse()
{
// Documents current behavior: only top-level Message is checked.
// Inner exceptions with "429" are NOT currently detected.
var ex = new Exception("outer", new Exception("429"));
Assert.False(ExecuteQueryRetryHelper.IsThrottleException(ex));
}
}

View File

@@ -0,0 +1,49 @@
using SharepointToolbox.Core.Helpers;
namespace SharepointToolbox.Tests.Helpers;
[Trait("Category", "Unit")]
public class SharePointPaginationHelperTests
{
[Fact]
public void BuildPagedViewXml_NullInput_ReturnsViewWithRowLimit()
{
var result = SharePointPaginationHelper.BuildPagedViewXml(null, 2000);
Assert.Equal("<View><RowLimit>2000</RowLimit></View>", result);
}
[Fact]
public void BuildPagedViewXml_EmptyString_ReturnsViewWithRowLimit()
{
var result = SharePointPaginationHelper.BuildPagedViewXml("", 2000);
Assert.Equal("<View><RowLimit>2000</RowLimit></View>", result);
}
[Fact]
public void BuildPagedViewXml_WhitespaceOnly_ReturnsViewWithRowLimit()
{
var result = SharePointPaginationHelper.BuildPagedViewXml(" ", 2000);
Assert.Equal("<View><RowLimit>2000</RowLimit></View>", result);
}
[Fact]
public void BuildPagedViewXml_ExistingRowLimit_Replaces()
{
var input = "<View><RowLimit>100</RowLimit></View>";
var result = SharePointPaginationHelper.BuildPagedViewXml(input, 2000);
Assert.Equal("<View><RowLimit>2000</RowLimit></View>", result);
}
[Fact]
public void BuildPagedViewXml_NoRowLimit_AppendsBeforeClosingView()
{
var input = "<View><Query><OrderBy><FieldRef Name='Title'/></OrderBy></Query></View>";
var result = SharePointPaginationHelper.BuildPagedViewXml(input, 2000);
Assert.Contains("<RowLimit>2000</RowLimit>", result);
Assert.EndsWith("</View>", result);
// Ensure RowLimit is inserted before the closing </View>
var rowLimitIndex = result.IndexOf("<RowLimit>2000</RowLimit>", StringComparison.Ordinal);
var closingViewIndex = result.LastIndexOf("</View>", StringComparison.Ordinal);
Assert.True(rowLimitIndex < closingViewIndex, "RowLimit should appear before </View>");
}
}

View File

@@ -36,7 +36,7 @@ public static class ExecuteQueryRetryHelper
} }
} }
private static bool IsThrottleException(Exception ex) internal static bool IsThrottleException(Exception ex)
{ {
var msg = ex.Message; var msg = ex.Message;
return msg.Contains("429") || msg.Contains("503") || return msg.Contains("429") || msg.Contains("503") ||

View File

@@ -36,7 +36,7 @@ public static class SharePointPaginationHelper
while (query.ListItemCollectionPosition != null); while (query.ListItemCollectionPosition != null);
} }
private static string BuildPagedViewXml(string? existingXml, int rowLimit) internal static string BuildPagedViewXml(string? existingXml, int rowLimit)
{ {
// Inject or replace RowLimit in existing CAML, or create minimal view // Inject or replace RowLimit in existing CAML, or create minimal view
if (string.IsNullOrWhiteSpace(existingXml)) if (string.IsNullOrWhiteSpace(existingXml))