Skip to content
84 changes: 70 additions & 14 deletions src/sdk/PnP.Core.Test/SharePoint/FoldersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public async Task GetListSubFoldersTest()
IFolder parentFolder = (await context.Web.Lists.GetByTitleAsync("Documents", p => p.RootFolder)).RootFolder;
IFolder mockFolder = await parentFolder.Folders.AddAsync("TEST");

List<IFolder> folders = await PnP.Core.QueryModel.QueryableExtensions.ToListAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders).ConfigureAwait(false);
List<IFolder> folders = await PnP.Core.QueryModel.QueryableExtensions.ToListAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders).ConfigureAwait(false);

Assert.AreNotEqual(0, folders.Count);

Expand Down Expand Up @@ -606,14 +606,70 @@ public async Task EnsureListFolderInExistingHiarchyTest()
Assert.IsTrue(folderToDelete.Name == "sub1");
Assert.IsFalse(folderToDelete.IsPropertyAvailable(p => p.StorageMetrics));

var folderToLoadWithExpression = await parentFolder.EnsureFolderAsync("sub1/sub2", p => p.Name, p => p.StorageMetrics);
var folderToLoadWithExpression = await parentFolder.EnsureFolderAsync("sub1/sub2", p => p.Name, p => p.StorageMetrics);
Assert.IsTrue(folderToLoadWithExpression != null);
Assert.IsTrue(folderToLoadWithExpression.Name == "sub2");
Assert.IsTrue(folderToLoadWithExpression.IsPropertyAvailable(p => p.StorageMetrics));

await folderToDelete.DeleteAsync();
}
}

[TestMethod]
public async Task EnsureListFolderIdempotentTest()
{
//TestCommon.Instance.Mocking = false;
using (var context = await TestCommon.Instance.GetContextAsync(TestCommon.TestSite))
{
IFolder parentFolder = (await context.Web.Lists.GetByTitleAsync("Site Pages", p => p.RootFolder)).RootFolder;

var addedFolder = await parentFolder.EnsureFolderAsync("sub1/sub2");
Assert.IsTrue(addedFolder != null);
Assert.IsTrue(addedFolder.Name == "sub2");

// Calling EnsureFolderAsync again for the same path must succeed and return the same folder
var ensuredFolder = await parentFolder.EnsureFolderAsync("sub1/sub2");
Assert.IsTrue(ensuredFolder != null);
Assert.IsTrue(ensuredFolder.Name == "sub2");
Assert.AreEqual(addedFolder.UniqueId, ensuredFolder.UniqueId);

var folderToDelete = await parentFolder.EnsureFolderAsync("sub1");
await folderToDelete.DeleteAsync();
}
}

[TestMethod]
public async Task EnsureListFolderConcurrentTest()
{
// This test exercises the race condition handling in EnsureFolderAsync:
// multiple concurrent calls targeting the same folder path must all succeed
// even when one of them loses the create race and gets an "already exists" error.

//TestCommon.Instance.Mocking = false;
using (var context = await TestCommon.Instance.GetContextAsync(TestCommon.TestSite))
{
IFolder parentFolder = (await context.Web.Lists.GetByTitleAsync("Site Pages", p => p.RootFolder)).RootFolder;

try
{
var tasks = Enumerable.Range(0, 5)
.Select(_ => parentFolder.EnsureFolderAsync("sub1/sub2/sub3"))
.ToArray();

var results = await Task.WhenAll(tasks);

Assert.IsTrue(results.All(r => r != null));
Assert.IsTrue(results.All(r => r.Name == "sub3"));
Assert.AreEqual(1, results.Select(r => r.UniqueId).Distinct().Count());
}
finally
{
var folderToDelete = await parentFolder.EnsureFolderAsync("sub1");
await folderToDelete.DeleteAsync();
}
}
}

#endregion

[TestMethod]
Expand Down Expand Up @@ -684,7 +740,7 @@ public async Task DeleteFolderTest()

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders,
ct => ct.Name == "TO DELETE FOLDER").ConfigureAwait(false);
ct => ct.Name == "TO DELETE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);
}
Expand Down Expand Up @@ -1118,7 +1174,7 @@ public async Task RecycleFolderTest()
Assert.AreNotEqual(Guid.Empty, recycleBinId);

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);

Expand All @@ -1145,7 +1201,7 @@ public async Task RecycleFolderCurrentBatchAsyncTest()
Assert.AreNotEqual(Guid.Empty, batchRecycle.Result.Value);

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);

Expand All @@ -1170,7 +1226,7 @@ public async Task RecycleFolderCurrentBatchTest()
await context.ExecuteAsync();

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);

Expand All @@ -1194,7 +1250,7 @@ public async Task RecycleFolderBatchAsyncTest()
await context.ExecuteAsync(batch);

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);

Expand All @@ -1218,7 +1274,7 @@ public async Task RecycleFolderBatchTest()
await context.ExecuteAsync(batch);

// Test if the folder is still found
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);
IFolder folderToFind = await QueryableExtensions.FirstOrDefaultAsync(context.Web.Lists.GetByTitle("Documents", p => p.RootFolder).RootFolder.Folders, ct => ct.Name == "TO RECYCLE FOLDER").ConfigureAwait(false);

Assert.IsNull(folderToFind);

Expand All @@ -1238,7 +1294,7 @@ public async Task RecycleNestedFolderTest()
var addedFolder = await parentFolder.EnsureFolderAsync("sub1/sub2");
Assert.IsNotNull(addedFolder);
Assert.AreEqual("sub2", addedFolder.Name);

IFolder folder1ToDelete = await context.Web.GetFolderByServerRelativeUrlAsync(addedFolder.ServerRelativeUrl);
Guid recycleBin1Id = folder1ToDelete.Recycle();
Assert.AreNotEqual(Guid.Empty, recycleBin1Id);
Expand All @@ -1252,13 +1308,13 @@ public async Task RecycleNestedFolderTest()
var error = e.Error as SharePointRestError;
Assert.AreEqual(404, error.HttpResponseCode);
}

await CleanupMockFolderFromRecycleBin(context, recycleBin1Id);

var folderToDelete = await parentFolder.EnsureFolderAsync("sub1");
Assert.IsNotNull(folderToDelete);
Assert.AreEqual("sub1", folderToDelete.Name);

IFolder folder2ToDelete = await context.Web.GetFolderByServerRelativeUrlAsync(folderToDelete.ServerRelativeUrl);
Guid recycleBin2Id = folder2ToDelete.Recycle();
Assert.AreNotEqual(Guid.Empty, recycleBin2Id);
Expand All @@ -1272,11 +1328,11 @@ public async Task RecycleNestedFolderTest()
var error = e.Error as SharePointRestError;
Assert.AreEqual(404, error.HttpResponseCode);
}

await CleanupMockFolderFromRecycleBin(context, recycleBin2Id);
}
}

#endregion

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-c086-1001-8fd7-e004c3439b6a","SPClientServiceRequestDuration":"12","X-SharePointHealthScore":"3","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022RegionalSettings\u0022:{\u0022TimeZone\u0022:{\u0022Description\u0022:\u0022(UTC-08:00) Pacific Time (US and Canada)\u0022,\u0022Id\u0022:13,\u0022Information\u0022:{\u0022Bias\u0022:480,\u0022DaylightBias\u0022:-60,\u0022StandardBias\u0022:0}},\u0022AdjustHijriDays\u0022:0,\u0022AlternateCalendarType\u0022:0,\u0022AM\u0022:\u0022AM\u0022,\u0022CalendarType\u0022:1,\u0022Collation\u0022:25,\u0022CollationLCID\u0022:2070,\u0022DateFormat\u0022:0,\u0022DateSeparator\u0022:\u0022/\u0022,\u0022DecimalSeparator\u0022:\u0022.\u0022,\u0022DigitGrouping\u0022:\u00223;0\u0022,\u0022FirstDayOfWeek\u0022:0,\u0022FirstWeekOfYear\u0022:0,\u0022IsEastAsia\u0022:false,\u0022IsRightToLeft\u0022:false,\u0022IsUIRightToLeft\u0022:false,\u0022ListSeparator\u0022:\u0022,\u0022,\u0022LocaleId\u0022:1033,\u0022NegativeSign\u0022:\u0022-\u0022,\u0022NegNumberMode\u0022:1,\u0022PM\u0022:\u0022PM\u0022,\u0022PositiveSign\u0022:\u0022\u0022,\u0022ShowWeeks\u0022:false,\u0022ThousandSeparator\u0022:\u0022,\u0022,\u0022Time24\u0022:false,\u0022TimeMarkerPosition\u0022:0,\u0022TimeSeparator\u0022:\u0022:\u0022,\u0022WorkDayEndHour\u0022:1020,\u0022WorkDays\u0022:62,\u0022WorkDayStartHour\u0022:480},\u0022Id\u0022:\u00226f2d6824-5143-4222-89cb-1ee645a69f51\u0022,\u0022Url\u0022:\u0022https://3fgb00.sharepoint.com/sites/pnpcoresdktestgroup\u0022}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-8093-1001-8fd7-e209b4f2a4f4","SPClientServiceRequestDuration":"21","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022GroupId\u0022:\u0022c1a99f87-f498-4ea8-9b44-2c1da34fcaa8\u0022,\u0022Id\u0022:\u0022477b4438-a3a4-4f63-8d6a-c797c26a357d\u0022}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-109d-1001-8fd7-e37aa509bd34","SPClientServiceRequestDuration":"23","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022RootFolder\u0022:{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022ExistsWithException\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:1,\u0022Name\u0022:\u0022SitePages\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SitePages\u0022,\u0022TimeCreated\u0022:\u00222026-05-22T21:21:41Z\u0022,\u0022TimeLastModified\u0022:\u00222026-06-04T07:14:30Z\u0022,\u0022UniqueId\u0022:\u0022fc5a6803-a30f-4fa1-b99e-71d7fc40cc02\u0022,\u0022WelcomePage\u0022:\u0022\u0022},\u0022Id\u0022:\u0022f7eed5a4-0bbe-4b10-a0d8-5be1398b2fd3\u0022}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":404,"Headers":{"SPRequestGuid":"9b3a1aa2-e0a3-1001-8fd7-e26ad021f41e","SPClientServiceRequestDuration":"36","X-SharePointHealthScore":"2","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2147024894, System.IO.FileNotFoundException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022File Not Found.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":404,"Headers":{"SPRequestGuid":"9b3a1aa2-e0a5-1001-4178-067af1d35777","SPClientServiceRequestDuration":"47","X-SharePointHealthScore":"2","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2147024894, System.IO.FileNotFoundException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022File Not Found.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":404,"Headers":{"SPRequestGuid":"9b3a1aa2-00a8-1001-8fd7-e19953be78bd","SPClientServiceRequestDuration":"37","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2147024894, System.IO.FileNotFoundException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022File Not Found.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":404,"Headers":{"SPRequestGuid":"9b3a1aa2-60a7-1001-8fd7-e38f19f766c7","SPClientServiceRequestDuration":"37","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2147024894, System.IO.FileNotFoundException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022File Not Found.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":404,"Headers":{"SPRequestGuid":"9b3a1aa2-60a6-1001-8fd7-e3e63924a400","SPClientServiceRequestDuration":"92","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2147024894, System.IO.FileNotFoundException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022File Not Found.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-b0ab-1001-8fd7-eb35e5828039","SPClientServiceRequestDuration":"78","X-SharePointHealthScore":"2","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022ExistsWithException\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:0,\u0022Name\u0022:\u0022sub1\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SitePages/sub1\u0022,\u0022TimeCreated\u0022:\u00222026-06-04T07:15:41Z\u0022,\u0022TimeLastModified\u0022:\u00222026-06-04T07:15:41Z\u0022,\u0022UniqueId\u0022:\u0022d0d8eac9-d912-41fa-991f-cd9f2b3cecae\u0022,\u0022WelcomePage\u0022:\u0022\u0022}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":400,"Headers":{"SPRequestGuid":"9b3a1aa2-40ae-1001-8fd7-e81d402d29e6","SPClientServiceRequestDuration":"56","X-SharePointHealthScore":"2","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2130575257, Microsoft.SharePoint.SPException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022A file or folder with the name sites/pnpcoresdktestgroup/SitePages/sub1 already exists.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":400,"Headers":{"SPRequestGuid":"9b3a1aa2-20af-1001-733e-2a20ac2b803c","SPClientServiceRequestDuration":"62","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2130575257, Microsoft.SharePoint.SPException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022A file or folder with the name sites/pnpcoresdktestgroup/SitePages/sub1 already exists.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":400,"Headers":{"SPRequestGuid":"9b3a1aa2-80b0-1001-8fd7-ef8a9d43d3a6","SPClientServiceRequestDuration":"91","X-SharePointHealthScore":"3","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2130575257, Microsoft.SharePoint.SPException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022A file or folder with the name sites/pnpcoresdktestgroup/SitePages/sub1 already exists.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":false,"StatusCode":400,"Headers":{"SPRequestGuid":"9b3a1aa2-a0b3-1001-8fd7-e4dc982c4d0c","SPClientServiceRequestDuration":"81","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022odata.error\u0022:{\u0022code\u0022:\u0022-2130575257, Microsoft.SharePoint.SPException\u0022,\u0022message\u0022:{\u0022lang\u0022:\u0022en-US\u0022,\u0022value\u0022:\u0022A file or folder with the name sites/pnpcoresdktestgroup/SitePages/sub1 already exists.\u0022}}}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-00b5-1001-8fd7-eda36fd2b8de","SPClientServiceRequestDuration":"98","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022ExistsWithException\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:0,\u0022Name\u0022:\u0022sub2\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SitePages/sub1/sub2\u0022,\u0022TimeCreated\u0022:\u00222026-06-04T07:15:42Z\u0022,\u0022TimeLastModified\u0022:\u00222026-06-04T07:15:42Z\u0022,\u0022UniqueId\u0022:\u002290685d4a-d898-4b9e-9300-1ab0bc69f852\u0022,\u0022WelcomePage\u0022:\u0022\u0022}"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"9b3a1aa2-80b8-1001-8fd7-ee22e19aa9e9","SPClientServiceRequestDuration":"18","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022ExistsWithException\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:1,\u0022Name\u0022:\u0022sub1\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SitePages/sub1\u0022,\u0022TimeCreated\u0022:\u00222026-06-04T07:15:41Z\u0022,\u0022TimeLastModified\u0022:\u00222026-06-04T07:15:42Z\u0022,\u0022UniqueId\u0022:\u0022d0d8eac9-d912-41fa-991f-cd9f2b3cecae\u0022,\u0022WelcomePage\u0022:\u0022\u0022}"}
Loading
Loading