feat(03-01): create Phase 3 export stubs and test scaffolds

- Add StorageCsvExportService, StorageHtmlExportService stub (Plan 03-03)
- Add SearchCsvExportService, SearchHtmlExportService stub (Plan 03-05)
- Add DuplicatesHtmlExportService stub (Plan 03-05)
- Add StorageServiceTests, SearchServiceTests, DuplicatesServiceTests scaffolds
- Add export test scaffolds for all 4 Phase 3 export services
- 7 pure-logic tests pass (VersionSizeBytes + MakeKey); 4 CSOM stubs skip
This commit is contained in:
Dev
2026-04-02 15:25:20 +02:00
parent b52f60f8eb
commit 08e4d2ee7d
12 changed files with 439 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services.Export;
using Xunit;
namespace SharepointToolbox.Tests.Services.Export;
public class DuplicatesHtmlExportServiceTests
{
private static DuplicateGroup MakeGroup(string name, int count) => new()
{
GroupKey = $"{name}|1024",
Name = name,
Items = Enumerable.Range(1, count).Select(i => new DuplicateItem
{
Name = name,
Path = $"https://contoso.sharepoint.com/sites/Site{i}/{name}",
Library = "Shared Documents",
SizeBytes = 1024
}).ToList()
};
[Fact]
public void BuildHtml_WithGroups_ContainsGroupCards()
{
var svc = new DuplicatesHtmlExportService();
var groups = new List<DuplicateGroup> { MakeGroup("report.docx", 3) };
var html = svc.BuildHtml(groups);
Assert.Contains("<!DOCTYPE html>", html);
Assert.Contains("report.docx", html);
}
[Fact]
public void BuildHtml_WithMultipleGroups_AllGroupNamesPresent()
{
var svc = new DuplicatesHtmlExportService();
var groups = new List<DuplicateGroup>
{
MakeGroup("budget.xlsx", 2),
MakeGroup("photo.jpg", 4)
};
var html = svc.BuildHtml(groups);
Assert.Contains("budget.xlsx", html);
Assert.Contains("photo.jpg", html);
}
[Fact]
public void BuildHtml_WithEmptyList_ReturnsValidHtml()
{
var svc = new DuplicatesHtmlExportService();
var html = svc.BuildHtml(new List<DuplicateGroup>());
Assert.Contains("<!DOCTYPE html>", html);
}
}

View File

@@ -0,0 +1,82 @@
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services.Export;
using Xunit;
namespace SharepointToolbox.Tests.Services.Export;
public class SearchExportServiceTests
{
private static SearchResult MakeSample() => new()
{
Title = "Q1 Budget.xlsx",
Path = "https://contoso.sharepoint.com/sites/Finance/Shared Documents/Q1 Budget.xlsx",
FileExtension = "xlsx",
Created = new DateTime(2024, 1, 10),
LastModified = new DateTime(2024, 3, 20),
Author = "Alice Smith",
ModifiedBy = "Bob Jones",
SizeBytes = 48_000
};
// -- CSV tests -----------------------------------------------------------
[Fact]
public void BuildCsv_WithKnownResults_ContainsExpectedHeader()
{
var svc = new SearchCsvExportService();
var csv = svc.BuildCsv(new List<SearchResult> { MakeSample() });
Assert.Contains("File Name", csv);
Assert.Contains("Extension", csv);
Assert.Contains("Created", csv);
Assert.Contains("Created By", csv);
Assert.Contains("Modified By", csv);
Assert.Contains("Size", csv);
}
[Fact]
public void BuildCsv_WithEmptyList_ReturnsHeaderOnly()
{
var svc = new SearchCsvExportService();
var csv = svc.BuildCsv(new List<SearchResult>());
Assert.NotEmpty(csv);
var lines = csv.Split('\n', StringSplitOptions.RemoveEmptyEntries);
Assert.Single(lines);
}
[Fact]
public void BuildCsv_ResultValues_AppearInOutput()
{
var svc = new SearchCsvExportService();
var csv = svc.BuildCsv(new List<SearchResult> { MakeSample() });
Assert.Contains("Alice Smith", csv);
Assert.Contains("xlsx", csv);
}
// -- HTML tests ----------------------------------------------------------
[Fact]
public void BuildHtml_WithResults_ContainsSortableColumnScript()
{
var svc = new SearchHtmlExportService();
var html = svc.BuildHtml(new List<SearchResult> { MakeSample() });
Assert.Contains("<!DOCTYPE html>", html);
Assert.Contains("sort", html); // sortable columns JS
Assert.Contains("Q1 Budget.xlsx", html);
}
[Fact]
public void BuildHtml_WithResults_ContainsFilterInput()
{
var svc = new SearchHtmlExportService();
var html = svc.BuildHtml(new List<SearchResult> { MakeSample() });
Assert.Contains("filter", html); // filter input element
}
[Fact]
public void BuildHtml_WithEmptyList_ReturnsValidHtml()
{
var svc = new SearchHtmlExportService();
var html = svc.BuildHtml(new List<SearchResult>());
Assert.Contains("<!DOCTYPE html>", html);
}
}

View File

@@ -0,0 +1,52 @@
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services.Export;
using Xunit;
namespace SharepointToolbox.Tests.Services.Export;
public class StorageCsvExportServiceTests
{
[Fact]
public void BuildCsv_WithKnownNodes_ProducesHeaderRow()
{
var svc = new StorageCsvExportService();
var nodes = new List<StorageNode>
{
new() { Name = "Shared Documents", Library = "Shared Documents", SiteTitle = "MySite",
TotalSizeBytes = 1024, FileStreamSizeBytes = 800, TotalFileCount = 5,
LastModified = new DateTime(2024, 1, 15) }
};
var csv = svc.BuildCsv(nodes);
Assert.Contains("Library", csv);
Assert.Contains("Site", csv);
Assert.Contains("Files", csv);
Assert.Contains("Total Size", csv);
Assert.Contains("Version Size", csv);
Assert.Contains("Last Modified", csv);
}
[Fact]
public void BuildCsv_WithEmptyList_ReturnsHeaderOnly()
{
var svc = new StorageCsvExportService();
var csv = svc.BuildCsv(new List<StorageNode>());
Assert.NotEmpty(csv); // must have at least the header row
var lines = csv.Split('\n', StringSplitOptions.RemoveEmptyEntries);
Assert.Single(lines); // only header, no data rows
}
[Fact]
public void BuildCsv_NodeValues_AppearInOutput()
{
var svc = new StorageCsvExportService();
var nodes = new List<StorageNode>
{
new() { Name = "Reports", Library = "Reports", SiteTitle = "ProjectSite",
TotalSizeBytes = 2048, FileStreamSizeBytes = 1024, TotalFileCount = 10 }
};
var csv = svc.BuildCsv(nodes);
Assert.Contains("Reports", csv);
Assert.Contains("ProjectSite", csv);
Assert.Contains("10", csv);
}
}

View File

@@ -0,0 +1,51 @@
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services.Export;
using Xunit;
namespace SharepointToolbox.Tests.Services.Export;
public class StorageHtmlExportServiceTests
{
[Fact]
public void BuildHtml_WithNodes_ContainsToggleJs()
{
var svc = new StorageHtmlExportService();
var nodes = new List<StorageNode>
{
new() { Name = "Shared Documents", Library = "Shared Documents", SiteTitle = "Site1",
TotalSizeBytes = 5000, FileStreamSizeBytes = 4000, TotalFileCount = 20,
Children = new List<StorageNode>
{
new() { Name = "Archive", Library = "Shared Documents", SiteTitle = "Site1",
TotalSizeBytes = 1000, FileStreamSizeBytes = 800, TotalFileCount = 5 }
} }
};
var html = svc.BuildHtml(nodes);
Assert.Contains("toggle(", html);
Assert.Contains("<!DOCTYPE html>", html);
Assert.Contains("Shared Documents", html);
}
[Fact]
public void BuildHtml_WithEmptyList_ReturnsValidHtml()
{
var svc = new StorageHtmlExportService();
var html = svc.BuildHtml(new List<StorageNode>());
Assert.Contains("<!DOCTYPE html>", html);
Assert.Contains("<html", html);
}
[Fact]
public void BuildHtml_WithMultipleLibraries_EachLibraryAppearsInOutput()
{
var svc = new StorageHtmlExportService();
var nodes = new List<StorageNode>
{
new() { Name = "Documents", Library = "Documents", SiteTitle = "Site1", TotalSizeBytes = 1000 },
new() { Name = "Images", Library = "Images", SiteTitle = "Site1", TotalSizeBytes = 2000 }
};
var html = svc.BuildHtml(nodes);
Assert.Contains("Documents", html);
Assert.Contains("Images", html);
}
}