SocialDisconnectHandlerTests.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. using Domain.Entities.Members;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.Extensions.Logging.Abstractions;
  4. using Microsoft.Extensions.Options;
  5. using SharedKernel;
  6. using DisconnectCommand = Application.Features.Api.Auth.SocialDisconnect.Command;
  7. using DisconnectHandler = Application.Features.Api.Auth.SocialDisconnect.Handler;
  8. namespace Application.Tests;
  9. [TestClass]
  10. public sealed class SocialDisconnectHandlerTests
  11. {
  12. private const string KakaoAdminKey = "test-admin-key-123";
  13. private static DisconnectHandler CreateHandler(Infrastructure.Persistence.AppDbContext db)
  14. {
  15. var options = Options.Create(new AppSettings
  16. {
  17. OAuth = new AppSettings.OAuthSection
  18. {
  19. Kakao = new AppSettings.OAuthSection.OAuthProviderSection
  20. {
  21. ClientId = "kakao-rest-key",
  22. AdminKey = KakaoAdminKey
  23. },
  24. Naver = new AppSettings.OAuthSection.OAuthProviderSection
  25. {
  26. ClientId = "naver-client",
  27. ClientSecret = "naver-secret"
  28. }
  29. }
  30. });
  31. return new DisconnectHandler(db, options, NullLogger<DisconnectHandler>.Instance);
  32. }
  33. private static async Task<(int MemberID, string ProviderUserKey)> SeedKakaoLinkAsync(Infrastructure.Persistence.AppDbContext db)
  34. {
  35. var memberID = await TestDb.CreateMemberAsync(db);
  36. var providerUserKey = Guid.NewGuid().ToString("N");
  37. var token = MemberOAuthToken.Create(memberID, "Kakao", "enc:access", "enc:refresh", DateTime.UtcNow.AddHours(1), "account_email", providerUserKey);
  38. await db.MemberOAuthToken.AddAsync(token);
  39. await db.SaveChangesAsync(default);
  40. return (memberID, providerUserKey);
  41. }
  42. [TestMethod]
  43. public async Task Kakao_ValidAdminKey_MatchedKey_DeletesLink()
  44. {
  45. // 기대 행위: Admin 키가 일치하고 user_id 가 매칭되면 해당 연동행을 삭제하고 성공(Removed=1)한다.
  46. using var db = TestDb.Create();
  47. var (memberID, providerUserKey) = await SeedKakaoLinkAsync(db);
  48. var handler = CreateHandler(db);
  49. var result = await handler.Handle(new DisconnectCommand("kakao", providerUserKey, $"KakaoAK {KakaoAdminKey}"), default);
  50. Assert.IsTrue(result.IsSuccess, $"검증 통과 시 성공해야 한다: {result.Error?.Code}");
  51. Assert.AreEqual(1, result.Value.Removed);
  52. using var verify = TestDb.Create();
  53. Assert.IsFalse(await verify.MemberOAuthToken.AnyAsync(c => c.MemberID == memberID && c.Provider == "Kakao"), "연동행이 삭제되어야 한다");
  54. }
  55. [TestMethod]
  56. public async Task Kakao_ValidAdminKey_NoMatch_ReturnsSuccessIdempotent()
  57. {
  58. // 기대 행위: Admin 키는 맞지만 매칭되는 연동행이 없으면 멱등 성공(Removed=0)으로 처리한다 (이미 해제됐을 수 있음).
  59. using var db = TestDb.Create();
  60. var handler = CreateHandler(db);
  61. var result = await handler.Handle(new DisconnectCommand("kakao", "nonexistent-user-id", $"KakaoAK {KakaoAdminKey}"), default);
  62. Assert.IsTrue(result.IsSuccess, "미매칭이어도 멱등 성공이어야 한다");
  63. Assert.AreEqual(0, result.Value.Removed);
  64. }
  65. [TestMethod]
  66. public async Task Kakao_WrongAdminKey_Rejected_Unauthorized()
  67. {
  68. // 기대 행위: Admin 키가 불일치하면 Unauthorized 로 거부하고 연동행은 삭제하지 않는다.
  69. using var db = TestDb.Create();
  70. var (memberID, providerUserKey) = await SeedKakaoLinkAsync(db);
  71. var handler = CreateHandler(db);
  72. var result = await handler.Handle(new DisconnectCommand("kakao", providerUserKey, "KakaoAK wrong-key"), default);
  73. Assert.IsFalse(result.IsSuccess, "잘못된 Admin 키는 거부되어야 한다");
  74. Assert.AreEqual("Auth.DisconnectUnauthorized", result.Error.Code);
  75. using var verify = TestDb.Create();
  76. Assert.IsTrue(await verify.MemberOAuthToken.AnyAsync(c => c.MemberID == memberID && c.Provider == "Kakao"), "거부 시 연동행은 유지되어야 한다");
  77. }
  78. [TestMethod]
  79. public async Task Naver_NoAdminKeyRequired_MatchedKey_DeletesLink()
  80. {
  81. // 기대 행위: 네이버는 Admin 키 검증 없이 관대하게 수신 — id 매칭 시 삭제한다.
  82. using var db = TestDb.Create();
  83. var memberID = await TestDb.CreateMemberAsync(db);
  84. var providerUserKey = Guid.NewGuid().ToString("N");
  85. var token = MemberOAuthToken.Create(memberID, "Naver", "enc:access", null, DateTime.UtcNow.AddHours(1), "", providerUserKey);
  86. await db.MemberOAuthToken.AddAsync(token);
  87. await db.SaveChangesAsync(default);
  88. var handler = CreateHandler(db);
  89. var result = await handler.Handle(new DisconnectCommand("naver", providerUserKey, null), default);
  90. Assert.IsTrue(result.IsSuccess, $"네이버는 Admin 키 없이도 성공해야 한다: {result.Error?.Code}");
  91. Assert.AreEqual(1, result.Value.Removed);
  92. using var verify = TestDb.Create();
  93. Assert.IsFalse(await verify.MemberOAuthToken.AnyAsync(c => c.MemberID == memberID && c.Provider == "Naver"));
  94. }
  95. }