164 lines
6.2 KiB
C#
164 lines
6.2 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using Paca.App;
|
|
using Paca.Browser.Profiles;
|
|
using Paca.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.E2E;
|
|
|
|
/// <summary>
|
|
/// 20종 추상 아바타 프리셋 및 사용자 커스텀 이미지 업로드 TDD 검증 게이트
|
|
/// </summary>
|
|
public sealed class ProfileAvatarPresetAndUploadGateTests
|
|
{
|
|
private static string AppAssetsAvatarDir =>
|
|
Path.Combine(SourceTree.Root, "src", "Paca.App", "Assets", "Avatars");
|
|
|
|
[Fact]
|
|
public void Gate1_PresetAvatars_20Images_ExistAndValidBitmap_Audit()
|
|
{
|
|
// 1. Assets/Avatars 디렉터리가 존재해야 함
|
|
Assert.True(Directory.Exists(AppAssetsAvatarDir), $"Avatars directory missing: {AppAssetsAvatarDir}");
|
|
|
|
// 2. avatar_01.png ~ avatar_20.png 20종 존재 검증
|
|
for (int i = 1; i <= 20; i++)
|
|
{
|
|
string fileName = $"avatar_{i:D2}.png";
|
|
string filePath = Path.Combine(AppAssetsAvatarDir, fileName);
|
|
Assert.True(File.Exists(filePath), $"Preset avatar missing: {fileName}");
|
|
|
|
var fileInfo = new FileInfo(filePath);
|
|
Assert.True(fileInfo.Length >= 1024, $"Avatar file {fileName} is too small ({fileInfo.Length} bytes)");
|
|
|
|
// 유효한 이미지인지 디코딩 무결성 검증
|
|
using var stream = File.OpenRead(filePath);
|
|
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
|
Assert.True(decoder.Frames.Count > 0, $"Avatar file {fileName} has no valid frames");
|
|
Assert.True(decoder.Frames[0].PixelWidth > 0 && decoder.Frames[0].PixelHeight > 0);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate2_ProfileAvatarHelper_GetPresetAvatars_Returns20Items()
|
|
{
|
|
var presets = ProfileAvatarHelper.GetPresetAvatarPaths();
|
|
Assert.NotNull(presets);
|
|
Assert.Equal(20, presets.Count);
|
|
Assert.All(presets, p => Assert.True(File.Exists(p) || p.StartsWith("pack://")));
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate3_ProfileManager_PreservesAvatarImagePath_OnSaveAndReload_Audit()
|
|
{
|
|
var tempDir = Path.Combine(Path.GetTempPath(), "paca_test_avatars_" + Guid.NewGuid().ToString("N"));
|
|
try
|
|
{
|
|
Directory.CreateDirectory(tempDir);
|
|
var manager = new ProfileManager(tempDir);
|
|
var dummyImagePath = Path.Combine(tempDir, "custom_avatar.png");
|
|
File.WriteAllBytes(dummyImagePath, new byte[] { 0x89, 0x50, 0x4E, 0x47 }); // minimal png header
|
|
|
|
var prof = manager.AddProfile("아바타 테스트", "#4F46E5", "\ue19f", dummyImagePath);
|
|
Assert.Equal(dummyImagePath, prof.AvatarImagePath);
|
|
|
|
// 다시 로드하여 영속화 일치 검증
|
|
var reloadedManager = new ProfileManager(tempDir);
|
|
var reloadedProf = reloadedManager.GetProfile(prof.Id);
|
|
Assert.NotNull(reloadedProf);
|
|
Assert.Equal(dummyImagePath, reloadedProf.AvatarImagePath);
|
|
|
|
// 업데이트 시 이미지 변경 및 제거 검증
|
|
reloadedManager.UpdateProfile(prof.Id, "이름 변경", "#059669", "\ue0d7", null);
|
|
var clearedProf = reloadedManager.GetProfile(prof.Id);
|
|
Assert.NotNull(clearedProf);
|
|
Assert.Null(clearedProf.AvatarImagePath);
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(tempDir))
|
|
{
|
|
try { Directory.Delete(tempDir, true); } catch { }
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Gate4_ProfileAvatarBadge_RendersImageBrush_WhenImagePathSet_Audit()
|
|
{
|
|
using var sta = StaDispatcher.Start();
|
|
WpfTestApp.Ensure(sta.Dispatcher);
|
|
var success = await sta.Dispatcher.InvokeAsync(() =>
|
|
{
|
|
// 더미 16x16 PNG 비트맵 생성
|
|
var rtb = new RenderTargetBitmap(16, 16, 96, 96, PixelFormats.Pbgra32);
|
|
var dv = new DrawingVisual();
|
|
using (var dc = dv.RenderOpen())
|
|
{
|
|
dc.DrawRectangle(Brushes.Indigo, null, new Rect(0, 0, 16, 16));
|
|
}
|
|
rtb.Render(dv);
|
|
|
|
var encoder = new PngBitmapEncoder();
|
|
encoder.Frames.Add(BitmapFrame.Create(rtb));
|
|
var tempPng = Path.Combine(Path.GetTempPath(), $"paca_avatar_render_{Guid.NewGuid():N}.png");
|
|
using (var fs = File.Create(tempPng))
|
|
{
|
|
encoder.Save(fs);
|
|
}
|
|
|
|
try
|
|
{
|
|
var brush = ProfileAvatarHelper.CreateAvatarBrush(tempPng);
|
|
Assert.NotNull(brush);
|
|
Assert.Equal(Stretch.UniformToFill, brush.Stretch);
|
|
|
|
var badge = new System.Windows.Controls.Border
|
|
{
|
|
Width = 24,
|
|
Height = 24,
|
|
CornerRadius = new CornerRadius(12),
|
|
Background = brush
|
|
};
|
|
badge.Measure(new Size(24, 24));
|
|
badge.Arrange(new Rect(0, 0, 24, 24));
|
|
badge.UpdateLayout();
|
|
|
|
var testRtb = new RenderTargetBitmap(24, 24, 96, 96, PixelFormats.Pbgra32);
|
|
testRtb.Render(badge);
|
|
var px = new byte[24 * 24 * 4];
|
|
testRtb.CopyPixels(px, 24 * 4, 0);
|
|
|
|
// 중앙 픽셀(12, 12)이 Indigo 계열(투명이 아님)인지 확인
|
|
int centerIdx = (12 * 24 + 12) * 4;
|
|
return px[centerIdx + 3] > 0; // Alpha > 0
|
|
}
|
|
finally
|
|
{
|
|
try { File.Delete(tempPng); } catch { }
|
|
}
|
|
});
|
|
|
|
Assert.True(success);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(".png", true)]
|
|
[InlineData(".jpg", true)]
|
|
[InlineData(".jpeg", true)]
|
|
[InlineData(".webp", true)]
|
|
[InlineData(".bmp", true)]
|
|
[InlineData(".exe", false)]
|
|
[InlineData(".bat", false)]
|
|
[InlineData(".txt", false)]
|
|
public void Gate5_ProfileAvatarUpload_ValidatesExtension_Audit(string ext, bool expectedValid)
|
|
{
|
|
var isValid = ProfileAvatarHelper.IsSupportedImageExtension(ext);
|
|
Assert.Equal(expectedValid, isValid);
|
|
}
|
|
}
|