Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix media cleanup runtime error #3277

Merged
merged 2 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/System Application/App/Data Administration/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@
"logo": "",
"screenshots": [],
"platform": "27.0.0.0",
"dependencies": [
"internalsVisibleTo": [
{
"id": "fd3d0b8e-61fa-4f87-b2fc-c4a1f3e53a63",
"name": "Math",
"publisher": "Microsoft",
"version": "27.0.0.0"
"id": "6d4dab82-dc5c-4c77-8e1e-d9aaeb7d0420",
"name": "Data Administration Test",
"publisher": "Microsoft"
}
],
"idRanges": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

namespace System.DataAdministration;

using System.Utilities;
using System.Environment;

codeunit 1928 "Media Cleanup Impl."
Expand Down Expand Up @@ -245,20 +244,24 @@ codeunit 1928 "Media Cleanup Impl."
end;

// 322, 100 will result in [[1, 100], [101, 200], [201, 300], [301, 322]]
local procedure SplitListIntoSubLists(var InputList: List of [Guid]; SubListCount: Integer; var SplitList: List of [List of [Guid]])
procedure SplitListIntoSubLists(var InputList: List of [Guid]; SubListCount: Integer; var SplitList: List of [List of [Guid]])
var
Math: Codeunit Math;
ListNumber: Integer;
SubList: List of [Guid];
From: Integer;
ToInt: Integer;
FromIndex: Integer;
SubSize: Integer;
NumberOfBatches: Integer;
TempSubList: List of [Guid];
begin
for ListNumber := 0 to Round(InputList.Count() / SubListCount, 1) do begin
Clear(SubList);
From := ListNumber * SubListCount + 1;
ToInt := Math.Min(SubListCount, InputList.Count() - ListNumber * SubListCount);
SubList := InputList.GetRange(From, ToInt);
SplitList.Add(SubList);
NumberOfBatches := (InputList.Count() + SubListCount - 1) div SubListCount;

for ListNumber := 1 to NumberOfBatches do begin
Clear(TempSubList);
FromIndex := (ListNumber - 1) * SubListCount + 1;
SubSize := InputList.Count() - (FromIndex - 1);
if SubSize > SubListCount then
SubSize := SubListCount;
TempSubList := InputList.GetRange(FromIndex, SubSize);
SplitList.Add(TempSubList);
end;
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ codeunit 135018 "Media Cleanup Test"
var
LibraryAssert: Codeunit "Library Assert";
PermissionsMock: Codeunit "Permissions Mock";
MediaCleanupImpl: Codeunit "Media Cleanup Impl.";
Any: Codeunit Any;

[Test]
Expand Down Expand Up @@ -119,6 +120,94 @@ codeunit 135018 "Media Cleanup Test"
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreCleanedUpSingleFullSubListPortion()
var
TempTenantMedia: Record "Tenant Media" temporary;
MediaCleanup: Codeunit "Media Cleanup";
begin
PermissionsMock.ClearAssignments();
CreateDetachedMediaThroughMediaSet(10, 100 * 1024); // 10 media of 100 kb
CreateDetachedMedia(10, 100 * 1024); // 10 media of 100 kb
PermissionsMock.Set('Data Cleanup - Admin');
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.AreEqual(20, TempTenantMedia.Count(), 'Tenant media does not contain all detached media.');

MediaCleanup.DeleteDetachedTenantMediaSet();
MediaCleanup.DeleteDetachedTenantMedia();
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreCleanedUpNotDivisibleSubListBigger()
var
TempTenantMedia: Record "Tenant Media" temporary;
MediaCleanup: Codeunit "Media Cleanup";
begin
PermissionsMock.ClearAssignments();
CreateDetachedMediaThroughMediaSet(101, 100 * 1024); // 101 media of 100 kb
CreateDetachedMedia(101, 100 * 1024); // 101 media of 100 kb
PermissionsMock.Set('Data Cleanup - Admin');
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.AreEqual(202, TempTenantMedia.Count(), 'Tenant media does not contain all detached media.');

MediaCleanup.DeleteDetachedTenantMediaSet();
MediaCleanup.DeleteDetachedTenantMedia();
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreCleanedUpNotDivisibleSubListLess()
var
TempTenantMedia: Record "Tenant Media" temporary;
MediaCleanup: Codeunit "Media Cleanup";
begin
PermissionsMock.ClearAssignments();
CreateDetachedMediaThroughMediaSet(99, 100 * 1024); // 99 media of 100 kb
CreateDetachedMedia(99, 100 * 1024); // 99 media of 100 kb
PermissionsMock.Set('Data Cleanup - Admin');
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.AreEqual(198, TempTenantMedia.Count(), 'Tenant media does not contain all detached media.');

MediaCleanup.DeleteDetachedTenantMediaSet();
MediaCleanup.DeleteDetachedTenantMedia();
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreCleanedUpLessThanBatch()
var
TempTenantMedia: Record "Tenant Media" temporary;
MediaCleanup: Codeunit "Media Cleanup";
begin
PermissionsMock.ClearAssignments();
CreateDetachedMediaThroughMediaSet(7, 100 * 1024); // 7 media of 100 kb
CreateDetachedMedia(7, 100 * 1024); // 7 media of 100 kb
PermissionsMock.Set('Data Cleanup - Admin');
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.AreEqual(14, TempTenantMedia.Count(), 'Tenant media does not contain all detached media.');

MediaCleanup.DeleteDetachedTenantMediaSet();
MediaCleanup.DeleteDetachedTenantMedia();
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreSuccessWithEmptyData()
var
TempTenantMedia: Record "Tenant Media" temporary;
MediaCleanup: Codeunit "Media Cleanup";
begin
MediaCleanup.DeleteDetachedTenantMediaSet();
MediaCleanup.DeleteDetachedTenantMedia();
GetDetachedTenantMedia(TempTenantMedia);
LibraryAssert.IsTrue(TempTenantMedia.IsEmpty(), 'Detached Tenant media was not cleaned up properly.');
end;

[Test]
procedure EnsureDetachedMediaAndMediaSetAreCleanedUpThroughCodeunit()
var
Expand Down Expand Up @@ -199,6 +288,146 @@ codeunit 135018 "Media Cleanup Test"
LibraryAssert.IsFalse(TenantMediaSetup.IsEmpty(), 'Normal tenant media set is also affected.');
end;

[Test]
procedure SplitListIntoSubLists322Into100CountSublists()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
i: Integer;
begin
// Basic Test: 322 items with SubListCount = 100
// Expected outcome: 4 sublists (100, 100, 100, and 22 items respectively)
for i := 1 to 322 do
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 100, ResultSublists);

// Verify the total number of sublists is 4
LibraryAssert.AreEqual(4, ResultSublists.Count(), '322 items with batch size 100 should produce 4 sublists.');

// Verify the size of each sublist
LibraryAssert.AreEqual(100, ResultSublists.Get(1).Count(), 'First sublist should contain 100 items.');
LibraryAssert.AreEqual(100, ResultSublists.Get(2).Count(), 'Second sublist should contain 100 items.');
LibraryAssert.AreEqual(100, ResultSublists.Get(3).Count(), 'Third sublist should contain 100 items.');
LibraryAssert.AreEqual(22, ResultSublists.Get(4).Count(), 'Fourth sublist (remainder) should contain 22 items.');
end;

[Test]
procedure SplitListIntoSubLists350Into100CountSublists()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
i: Integer;
begin
// Basic Test: 350 items with SubListCount = 100
// Expected outcome: 4 sublists (100, 100, 100, and 50 items respectively)
for i := 1 to 350 do
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 100, ResultSublists);

// Verify the total number of sublists is 4
LibraryAssert.AreEqual(4, ResultSublists.Count(), '350 items with batch size 100 should produce 4 sublists.');

// Verify the size of each sublist
LibraryAssert.AreEqual(100, ResultSublists.Get(1).Count(), 'First sublist should contain 100 items.');
LibraryAssert.AreEqual(100, ResultSublists.Get(2).Count(), 'Second sublist should contain 100 items.');
LibraryAssert.AreEqual(100, ResultSublists.Get(3).Count(), 'Third sublist should contain 100 items.');
LibraryAssert.AreEqual(50, ResultSublists.Get(4).Count(), 'Fourth sublist (remainder) should contain 50 items.');
end;

[Test]
procedure SplitListIntoSubListsReturnsEmptyResult()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
begin
// Edge Case: Empty input list, SubListCount = 100
// Expected outcome: result is an empty list of sublists (no sublists)
MediaCleanupImpl.SplitListIntoSubLists(InputList, 100, ResultSublists);

// Verify that result is empty (no sublists)
LibraryAssert.AreEqual(0, ResultSublists.Count(), 'Empty input list should produce an empty result (0 sublists).');
end;

[Test]
procedure SplitListIntoSubListsFewerItemsThanBatchReturnsSingleSublist()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
i: Integer;
begin
// Edge Case: Input list has fewer items than SubListCount
// Example: 50 items, SubListCount = 100
// Expected outcome: a single sublist containing all 50 items (since no split needed)
for i := 1 to 50 do
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 100, ResultSublists);

// Verify that there is exactly 1 sublist
LibraryAssert.AreEqual(1, ResultSublists.Count(), '50 items with batch size 100 should produce 1 sublist (all items in one batch).');

// Verify that the single sublist contains all 50 items
LibraryAssert.AreEqual(50, ResultSublists.Get(1).Count(), 'The single sublist should contain all 50 items.');
end;

[Test]
procedure SplitListIntoSubListsCreatesIndividualItemSublists()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
i: Integer;
idx: Integer;
begin
// Edge Case: SubListCount = 1 (each item should be its own sublist)
// Example: 5 items, SubListCount = 1 -> expect 5 sublists, each with 1 item
for i := 1 to 5 do
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 1, ResultSublists);

// Verify that the number of sublists equals the number of items (5 sublists for 5 items)
LibraryAssert.AreEqual(5, ResultSublists.Count(), '5 items with batch size 1 should produce 5 sublists.');

// Verify each sublist contains exactly one item
for idx := 1 to ResultSublists.Count() do
LibraryAssert.AreEqual(1, ResultSublists.Get(idx).Count(), 'Sublist should contain exactly 1 item.');
end;

[Test]
procedure SplitListIntoSubListsReturnsSingleFullSublist()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
i: Integer;
begin
// Edge Case: SubListCount equals the input list count
// Example: 8 items, SubListCount = 8 -> expect a single sublist with all 8 items
for i := 1 to 8 do
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 8, ResultSublists);

// Verify that there is exactly 1 sublist
LibraryAssert.AreEqual(1, ResultSublists.Count(), '8 items with batch size 8 should produce 1 sublist.');

// Verify that the single sublist contains all 8 items
LibraryAssert.AreEqual(8, ResultSublists.Get(1).Count(), 'The single sublist should contain all 8 items.');
end;

[Test]
procedure SplitListIntoSubListsOneItemBatchOfOne()
var
InputList: List of [Guid];
ResultSublists: List of [List of [Guid]];
begin
// Boundary Case: Minimum input values (1 item, SubListCount = 1)
// Expected outcome: 1 sublist containing the single item
InputList.Add(Any.GuidValue());
MediaCleanupImpl.SplitListIntoSubLists(InputList, 1, ResultSublists);

// Verify one sublist is created
LibraryAssert.AreEqual(1, ResultSublists.Count(), '1 item with batch size 1 should result in 1 sublist.');

// Verify the sublist contains that one item
LibraryAssert.AreEqual(1, ResultSublists.Get(1).Count(), 'The single sublist should contain the one item.');
end;

local procedure GetDetachedTenantMedia(var TempTenantMedia: Record "Tenant Media" temporary)
var
Expand Down
Loading