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
@@ -1 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"67e2d3a0-f0f5-7000-310e-486d64a665e3","SPClientServiceRequestDuration":"10","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:\u00222c99a486-d6c9-4a4b-8d6f-a9faa364c92c\u0022,\u0022Url\u0022:\u0022https://bertonline.sharepoint.com/sites/prov-2\u0022}"}
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"cbad17a2-e0d2-1001-48eb-758d556dae29","SPClientServiceRequestDuration":"15","X-SharePointHealthScore":"0","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
@@ -1 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"67e2d3a0-c0fd-7000-310e-43e6750b6760","SPClientServiceRequestDuration":"9","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022GroupId\u0022:\u0022d40d729b-df60-4b57-ac8f-102595090e0a\u0022,\u0022Id\u0022:\u0022f92f9e40-1110-43ef-aa0e-0822e13fb7ba\u0022}"}
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"cbad17a2-80de-1001-1a81-3e61f4a8eb4e","SPClientServiceRequestDuration":"15","X-SharePointHealthScore":"1","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
@@ -1 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"68e2d3a0-9003-7000-310e-4ce93b2d99e9","SPClientServiceRequestDuration":"21","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022value\u0022:[{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:2,\u0022Name\u0022:\u0022SiteAssets\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/prov-2/SiteAssets\u0022,\u0022TimeCreated\u0022:\u00222021-10-04T07:41:22Z\u0022,\u0022TimeLastModified\u0022:\u00222023-08-17T17:12:32Z\u0022,\u0022UniqueId\u0022:\u002241f6e832-6aa3-41e1-9754-c56290074888\u0022,\u0022WelcomePage\u0022:\u0022\u0022}]}"}
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"cbad17a2-b0e6-1001-48eb-783d506192de","SPClientServiceRequestDuration":"25","X-SharePointHealthScore":"3","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022value\u0022:[{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022ExistsWithException\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:3,\u0022Name\u0022:\u0022SiteAssets\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SiteAssets\u0022,\u0022TimeCreated\u0022:\u00222026-05-26T09:48:41Z\u0022,\u0022TimeLastModified\u0022:\u00222026-05-27T08:59:29Z\u0022,\u0022UniqueId\u0022:\u0022fedbe107-ce83-487a-9f35-d90fdc8fdb98\u0022,\u0022WelcomePage\u0022:\u0022\u0022}]}"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"68e2d3a0-7009-7000-310e-44c743fec8dd","SPClientServiceRequestDuration":"81","X-SharePointHealthScore":"2","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":"{\u0022Exists\u0022:true,\u0022ExistsAllowThrowForPolicyFailures\u0022:true,\u0022IsWOPIEnabled\u0022:false,\u0022ItemCount\u0022:0,\u0022Name\u0022:\u0022TEST\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/prov-2/SiteAssets/TEST\u0022,\u0022TimeCreated\u0022:\u00222023-08-25T09:14:57Z\u0022,\u0022TimeLastModified\u0022:\u00222023-08-25T09:14:57Z\u0022,\u0022UniqueId\u0022:\u0022afc87d7d-f7f6-4d4e-b1d7-4304c0bacdb4\u0022,\u0022WelcomePage\u0022:\u0022\u0022}"}
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"cbad17a2-20ef-1001-4178-0fd358b45370","SPClientServiceRequestDuration":"84","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:\u0022TEST\u0022,\u0022ProgID\u0022:null,\u0022ServerRelativeUrl\u0022:\u0022/sites/pnpcoresdktestgroup/SiteAssets/TEST\u0022,\u0022TimeCreated\u0022:\u00222026-05-27T09:06:59Z\u0022,\u0022TimeLastModified\u0022:\u00222026-05-27T09:06:59Z\u0022,\u0022UniqueId\u0022:\u002294c7d362-a589-4b74-9498-dc73958e1de0\u0022,\u0022WelcomePage\u0022:\u0022\u0022}"}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"68e2d3a0-3014-7000-310e-47baafbf1dea","SPClientServiceRequestDuration":"368","X-SharePointHealthScore":"0","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":""}
{"IsSuccessStatusCode":true,"StatusCode":200,"Headers":{"SPRequestGuid":"cbad17a2-20fb-1001-48eb-7cbc861fe372","SPClientServiceRequestDuration":"167","X-SharePointHealthScore":"1","X-SP-SERVERSTATE":"ReadOnly=0"},"Response":""}
Loading