Files
Sharepoint-Toolbox/SharepointToolbox.Tests/Auth/MsalClientFactoryTests.cs
Dev 02955199f6 feat(01-04): MsalClientFactory with per-clientId PCA and MsalCacheHelper
- Creates one IPublicClientApplication per ClientId (never shared)
- Persists token cache to configurable directory (default: %AppData%\SharepointToolbox\auth\msal_{clientId}.cache)
- SemaphoreSlim(1,1) prevents duplicate creation under concurrent calls
- CacheDirectory property exposed for test injection
- 4 unit tests: same-instance, different-instance, concurrent, AppData path
2026-04-02 12:22:54 +02:00

76 lines
2.3 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using SharepointToolbox.Infrastructure.Auth;
using Xunit;
namespace SharepointToolbox.Tests.Auth;
[Trait("Category", "Unit")]
public class MsalClientFactoryTests : IDisposable
{
private readonly string _tempCacheDir;
public MsalClientFactoryTests()
{
_tempCacheDir = Path.Combine(Path.GetTempPath(), "MsalClientFactoryTests_" + Guid.NewGuid());
Directory.CreateDirectory(_tempCacheDir);
}
public void Dispose()
{
try { Directory.Delete(_tempCacheDir, recursive: true); } catch { /* best effort */ }
}
[Fact]
public async Task GetOrCreateAsync_SameClientId_ReturnsSameInstance()
{
var factory = new MsalClientFactory(_tempCacheDir);
var pca1 = await factory.GetOrCreateAsync("clientA");
var pca2 = await factory.GetOrCreateAsync("clientA");
Assert.Same(pca1, pca2);
}
[Fact]
public async Task GetOrCreateAsync_DifferentClientIds_ReturnDifferentInstances()
{
var factory = new MsalClientFactory(_tempCacheDir);
var pcaA = await factory.GetOrCreateAsync("clientA");
var pcaB = await factory.GetOrCreateAsync("clientB");
Assert.NotSame(pcaA, pcaB);
}
[Fact]
public async Task GetOrCreateAsync_ConcurrentCalls_DoNotCreateDuplicateInstances()
{
var factory = new MsalClientFactory(_tempCacheDir);
// Run 10 concurrent calls with the same clientId
var tasks = Enumerable.Range(0, 10)
.Select(_ => factory.GetOrCreateAsync("clientConcurrent"))
.ToArray();
var results = await Task.WhenAll(tasks);
// All 10 results must be the exact same instance
var first = results[0];
Assert.All(results, r => Assert.Same(first, r));
}
[Fact]
public void CacheDirectory_ResolvesToAppData_Not_Hardcoded()
{
// The default (no-arg) constructor must use %AppData%\SharepointToolbox\auth
var factory = new MsalClientFactory();
var expectedBase = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var expectedDir = Path.Combine(expectedBase, "SharepointToolbox", "auth");
Assert.Equal(expectedDir, factory.CacheDirectory);
}
}