From 3b3a29cb91a49fa0596b8b73c21d52b6d495a96a Mon Sep 17 00:00:00 2001
From: MSuMshk <2039814060@qq.com>
Date: Fri, 30 Jan 2026 00:53:58 +0000
Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20tests=20?=
=?UTF-8?q?=E7=9B=AE=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../GetAnnouncementByIdQueryHandlerTests.cs | 54 --
...etTenantsAnnouncementsQueryHandlerTests.cs | 122 ----
...CreateAnnouncementCommandValidatorTests.cs | 110 ----
...ublishAnnouncementCommandValidatorTests.cs | 62 --
...RevokeAnnouncementCommandValidatorTests.cs | 62 --
...UpdateAnnouncementCommandValidatorTests.cs | 88 ---
.../TakeoutSaaS.Application.Tests.csproj | 35 -
.../TestUtilities/AnnouncementTestData.cs | 72 ---
.../App/Dictionary/DictionaryApiTests.cs | 608 ------------------
.../Tenants/AnnouncementRegressionTests.cs | 98 ---
.../App/Tenants/AnnouncementWorkflowTests.cs | 218 -------
.../TenantAnnouncementRepositoryScopeTests.cs | 58 --
.../PermissionAuthorizationHandlerTests.cs | 80 ---
.../Fixtures/DictionarySqliteTestDatabase.cs | 58 --
.../Fixtures/SqliteTestDatabase.cs | 58 --
.../Fixtures/TestCurrentUserAccessor.cs | 10 -
.../Fixtures/TestDictionaryHybridCache.cs | 46 --
.../Fixtures/TestIdGenerator.cs | 20 -
.../Fixtures/TestTenantProvider.cs | 10 -
.../AnnouncementQueryPerformanceTests.cs | 70 --
.../TakeoutSaaS.Integration.Tests.csproj | 39 --
21 files changed, 1978 deletions(-)
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetAnnouncementByIdQueryHandlerTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/CreateAnnouncementCommandValidatorTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/PublishAnnouncementCommandValidatorTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/RevokeAnnouncementCommandValidatorTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/UpdateAnnouncementCommandValidatorTests.cs
delete mode 100644 tests/TakeoutSaaS.Application.Tests/TakeoutSaaS.Application.Tests.csproj
delete mode 100644 tests/TakeoutSaaS.Application.Tests/TestUtilities/AnnouncementTestData.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/App/Dictionary/DictionaryApiTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementWorkflowTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Authorization/PermissionAuthorizationHandlerTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/DictionarySqliteTestDatabase.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/SqliteTestDatabase.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/TestCurrentUserAccessor.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/TestDictionaryHybridCache.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/TestIdGenerator.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Fixtures/TestTenantProvider.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/Performance/AnnouncementQueryPerformanceTests.cs
delete mode 100644 tests/TakeoutSaaS.Integration.Tests/TakeoutSaaS.Integration.Tests.csproj
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetAnnouncementByIdQueryHandlerTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetAnnouncementByIdQueryHandlerTests.cs
deleted file mode 100644
index a6cdfe8..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetAnnouncementByIdQueryHandlerTests.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using FluentAssertions;
-using Moq;
-using TakeoutSaaS.Application.App.Tenants.Handlers;
-using TakeoutSaaS.Application.App.Tenants.Queries;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Repositories;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Handlers;
-
-///
-/// 单元测试。
-///
-public sealed class GetAnnouncementByIdQueryHandlerTests
-{
- [Fact]
- public async Task GivenAnnouncementMissing_WhenHandle_ThenReturnsNull()
- {
- // 1. 准备
- var announcementRepository = new Mock();
- announcementRepository
- .Setup(x => x.FindByIdAsync(99, 500, It.IsAny()))
- .ReturnsAsync((TenantAnnouncement?)null);
-
- // 2. (空行后) 执行
- var handler = new GetAnnouncementByIdQueryHandler(announcementRepository.Object);
- var result = await handler.Handle(new GetAnnouncementByIdQuery { TenantId = 99, AnnouncementId = 500 }, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Should().BeNull();
- }
-
- [Fact]
- public async Task GivenAnnouncementExists_WhenHandle_ThenReturnsDtoWithoutReadState()
- {
- // 1. 准备
- var announcement = AnnouncementTestData.CreateAnnouncement(10, 200, 1, DateTime.UtcNow);
- var announcementRepository = new Mock();
- announcementRepository
- .Setup(x => x.FindByIdAsync(200, 10, It.IsAny()))
- .ReturnsAsync(announcement);
-
- // 2. (空行后) 执行
- var handler = new GetAnnouncementByIdQueryHandler(announcementRepository.Object);
- var result = await handler.Handle(new GetAnnouncementByIdQuery { TenantId = 200, AnnouncementId = 10 }, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Should().NotBeNull();
- result!.Id.Should().Be(announcement.Id);
- result.TenantId.Should().Be(announcement.TenantId);
- result.IsRead.Should().BeFalse();
- result.ReadAt.Should().BeNull();
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs
deleted file mode 100644
index 7fbf0b1..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using FluentAssertions;
-using Moq;
-using TakeoutSaaS.Application.App.Tenants.Handlers;
-using TakeoutSaaS.Application.App.Tenants.Queries;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-using TakeoutSaaS.Domain.Tenants.Repositories;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Handlers;
-
-///
-/// 单元测试。
-///
-public sealed class GetTenantsAnnouncementsQueryHandlerTests
-{
- [Fact]
- public async Task GivenQuery_WhenHandle_ThenOrdersAndPaginates()
- {
- // 1. 准备
- var announcements = new List
- {
- AnnouncementTestData.CreateAnnouncement(1, 42, priority: 1, effectiveFrom: DateTime.UtcNow.AddDays(-1)),
- AnnouncementTestData.CreateAnnouncement(2, 42, priority: 2, effectiveFrom: DateTime.UtcNow.AddDays(-3)),
- AnnouncementTestData.CreateAnnouncement(3, 42, priority: 2, effectiveFrom: DateTime.UtcNow.AddDays(-1)),
- AnnouncementTestData.CreateAnnouncement(4, 42, priority: 0, effectiveFrom: DateTime.UtcNow)
- };
-
- // 2. (空行后) 模拟数据库端排序:按 priority DESC, effectiveFrom DESC
- var sortedAnnouncements = announcements
- .OrderByDescending(x => x.Priority)
- .ThenByDescending(x => x.EffectiveFrom)
- .ToList();
- var announcementRepository = new Mock();
- announcementRepository
- .Setup(x => x.SearchAsync(
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny()))
- .ReturnsAsync(sortedAnnouncements);
-
- // 3. (空行后) 执行
- var handler = new GetTenantsAnnouncementsQueryHandler(announcementRepository.Object);
- var query = new GetTenantsAnnouncementsQuery
- {
- TenantId = 42,
- Page = 2,
- PageSize = 2
- };
- var result = await handler.Handle(query, CancellationToken.None);
-
- // 4. (空行后) 断言
- announcementRepository.Verify(x => x.SearchAsync(
- query.TenantId,
- query.Keyword,
- query.Status,
- query.AnnouncementType,
- query.IsActive,
- query.EffectiveFrom,
- query.EffectiveTo,
- null,
- true,
- 12, // estimatedLimit = page * size * 3 = 2 * 2 * 3 = 12
- It.IsAny()), Times.Once);
-
- result.TotalCount.Should().Be(4);
- result.Items.Select(x => x.Id).Should().Equal(1, 4);
- result.Page.Should().Be(2);
- result.PageSize.Should().Be(2);
- }
-
- [Fact]
- public async Task GivenOnlyEffective_WhenHandle_ThenFiltersScheduledPublish()
- {
- // 1. 准备
- var announcement1 = AnnouncementTestData.CreateAnnouncement(1, 42, priority: 1, effectiveFrom: DateTime.UtcNow.AddDays(-1));
- announcement1.ScheduledPublishAt = DateTime.UtcNow.AddMinutes(-10);
- var announcement2 = AnnouncementTestData.CreateAnnouncement(2, 42, priority: 1, effectiveFrom: DateTime.UtcNow.AddDays(-1));
- announcement2.ScheduledPublishAt = DateTime.UtcNow.AddMinutes(30);
- var announcements = new List
- {
- announcement1,
- announcement2
- };
- var announcementRepository = new Mock();
- announcementRepository
- .Setup(x => x.SearchAsync(
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny(),
- It.IsAny()))
- .ReturnsAsync(announcements);
-
- // 2. (空行后) 执行
- var handler = new GetTenantsAnnouncementsQueryHandler(announcementRepository.Object);
- var query = new GetTenantsAnnouncementsQuery
- {
- TenantId = 42,
- OnlyEffective = true,
- Page = 1,
- PageSize = 10
- };
- var result = await handler.Handle(query, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Items.Should().ContainSingle();
- result.Items[0].Id.Should().Be(1);
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/CreateAnnouncementCommandValidatorTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/CreateAnnouncementCommandValidatorTests.cs
deleted file mode 100644
index 9abd854..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/CreateAnnouncementCommandValidatorTests.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using FluentValidation.TestHelper;
-using TakeoutSaaS.Application.App.Tenants.Validators;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Validators;
-
-public sealed class CreateAnnouncementCommandValidatorTests
-{
- private readonly CreateAnnouncementCommandValidator _validator = new();
-
- [Fact]
- public void GivenEmptyTitle_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with { Title = "" };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Title);
- }
-
- [Fact]
- public void GivenTitleTooLong_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with { Title = new string('A', 129) };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Title);
- }
-
- [Fact]
- public void GivenEmptyContent_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with { Content = "" };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Content);
- }
-
- [Fact]
- public void GivenEmptyTargetType_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with { TargetType = "" };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.TargetType);
- }
-
- [Fact]
- public void GivenTenantIdZeroAndNotPlatform_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with
- {
- TenantId = 0,
- PublisherScope = PublisherScope.Tenant
- };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x);
- }
-
- [Fact]
- public void GivenEffectiveToBeforeFrom_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand() with
- {
- EffectiveFrom = DateTime.UtcNow,
- EffectiveTo = DateTime.UtcNow.AddMinutes(-5)
- };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.EffectiveTo);
- }
-
- [Fact]
- public void GivenValidCommand_WhenValidate_ThenShouldNotHaveErrors()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidCreateCommand();
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldNotHaveAnyValidationErrors();
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/PublishAnnouncementCommandValidatorTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/PublishAnnouncementCommandValidatorTests.cs
deleted file mode 100644
index e325755..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/PublishAnnouncementCommandValidatorTests.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using FluentValidation.TestHelper;
-using TakeoutSaaS.Application.App.Tenants.Validators;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Validators;
-
-public sealed class PublishAnnouncementCommandValidatorTests
-{
- private readonly PublishAnnouncementCommandValidator _validator = new();
-
- [Fact]
- public void GivenAnnouncementIdZero_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidPublishCommand() with { AnnouncementId = 0 };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.AnnouncementId);
- }
-
- [Fact]
- public void GivenNullRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidPublishCommand() with { RowVersion = null! };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenEmptyRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidPublishCommand() with { RowVersion = Array.Empty() };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenValidCommand_WhenValidate_ThenShouldNotHaveErrors()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidPublishCommand();
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldNotHaveAnyValidationErrors();
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/RevokeAnnouncementCommandValidatorTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/RevokeAnnouncementCommandValidatorTests.cs
deleted file mode 100644
index 694c8a5..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/RevokeAnnouncementCommandValidatorTests.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using FluentValidation.TestHelper;
-using TakeoutSaaS.Application.App.Tenants.Validators;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Validators;
-
-public sealed class RevokeAnnouncementCommandValidatorTests
-{
- private readonly RevokeAnnouncementCommandValidator _validator = new();
-
- [Fact]
- public void GivenAnnouncementIdZero_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidRevokeCommand() with { AnnouncementId = 0 };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.AnnouncementId);
- }
-
- [Fact]
- public void GivenNullRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidRevokeCommand() with { RowVersion = null! };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenEmptyRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidRevokeCommand() with { RowVersion = Array.Empty() };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenValidCommand_WhenValidate_ThenShouldNotHaveErrors()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidRevokeCommand();
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldNotHaveAnyValidationErrors();
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/UpdateAnnouncementCommandValidatorTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/UpdateAnnouncementCommandValidatorTests.cs
deleted file mode 100644
index bed8efe..0000000
--- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Validators/UpdateAnnouncementCommandValidatorTests.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using FluentValidation.TestHelper;
-using TakeoutSaaS.Application.App.Tenants.Validators;
-using TakeoutSaaS.Application.Tests.TestUtilities;
-
-namespace TakeoutSaaS.Application.Tests.App.Tenants.Validators;
-
-public sealed class UpdateAnnouncementCommandValidatorTests
-{
- private readonly UpdateAnnouncementCommandValidator _validator = new();
-
- [Fact]
- public void GivenEmptyTitle_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand() with { Title = "" };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Title);
- }
-
- [Fact]
- public void GivenTitleTooLong_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand() with { Title = new string('A', 129) };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Title);
- }
-
- [Fact]
- public void GivenEmptyContent_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand() with { Content = "" };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.Content);
- }
-
- [Fact]
- public void GivenNullRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand() with { RowVersion = null! };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenEmptyRowVersion_WhenValidate_ThenShouldHaveError()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand() with { RowVersion = Array.Empty() };
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldHaveValidationErrorFor(x => x.RowVersion);
- }
-
- [Fact]
- public void GivenValidCommand_WhenValidate_ThenShouldNotHaveErrors()
- {
- // Arrange
- var command = AnnouncementTestData.CreateValidUpdateCommand();
-
- // Act
- var result = _validator.TestValidate(command);
-
- // Assert
- result.ShouldNotHaveAnyValidationErrors();
- }
-}
diff --git a/tests/TakeoutSaaS.Application.Tests/TakeoutSaaS.Application.Tests.csproj b/tests/TakeoutSaaS.Application.Tests/TakeoutSaaS.Application.Tests.csproj
deleted file mode 100644
index 0e333a1..0000000
--- a/tests/TakeoutSaaS.Application.Tests/TakeoutSaaS.Application.Tests.csproj
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
- net10.0
- enable
- enable
- false
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/TakeoutSaaS.Application.Tests/TestUtilities/AnnouncementTestData.cs b/tests/TakeoutSaaS.Application.Tests/TestUtilities/AnnouncementTestData.cs
deleted file mode 100644
index 6ac7f18..0000000
--- a/tests/TakeoutSaaS.Application.Tests/TestUtilities/AnnouncementTestData.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using TakeoutSaaS.Application.App.Tenants.Commands;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-
-namespace TakeoutSaaS.Application.Tests.TestUtilities;
-
-public static class AnnouncementTestData
-{
- public static CreateTenantAnnouncementCommand CreateValidCreateCommand()
- => new()
- {
- TenantId = 100,
- Title = "公告标题",
- Content = "公告内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = 1,
- EffectiveFrom = DateTime.UtcNow.AddHours(-1),
- EffectiveTo = DateTime.UtcNow.AddHours(2),
- PublisherScope = PublisherScope.Tenant,
- TargetType = "ALL_TENANTS",
- TargetParameters = null
- };
-
- public static UpdateTenantAnnouncementCommand CreateValidUpdateCommand()
- => new()
- {
- TenantId = 100,
- AnnouncementId = 9001,
- Title = "更新公告",
- Content = "更新内容",
- TargetType = "ALL_TENANTS",
- TargetParameters = null,
- RowVersion = new byte[] { 1, 2, 3 }
- };
-
- public static PublishAnnouncementCommand CreateValidPublishCommand()
- => new()
- {
- AnnouncementId = 9001,
- RowVersion = new byte[] { 1, 2, 3 }
- };
-
- public static RevokeAnnouncementCommand CreateValidRevokeCommand()
- => new()
- {
- AnnouncementId = 9001,
- RowVersion = new byte[] { 1, 2, 3 }
- };
-
- public static TenantAnnouncement CreateAnnouncement(
- long id,
- long tenantId,
- int priority,
- DateTime effectiveFrom,
- AnnouncementStatus status = AnnouncementStatus.Draft)
- => new()
- {
- Id = id,
- TenantId = tenantId,
- Title = $"公告-{id}",
- Content = "内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = priority,
- EffectiveFrom = effectiveFrom,
- EffectiveTo = null,
- PublisherScope = PublisherScope.Tenant,
- Status = status,
- TargetType = string.Empty,
- TargetParameters = null,
- RowVersion = new byte[] { 1, 1, 1 }
- };
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Dictionary/DictionaryApiTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Dictionary/DictionaryApiTests.cs
deleted file mode 100644
index dde9318..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/App/Dictionary/DictionaryApiTests.cs
+++ /dev/null
@@ -1,608 +0,0 @@
-using System.Text;
-using System.Text.Json;
-using FluentAssertions;
-using Microsoft.AspNetCore.Http;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
-using TakeoutSaaS.Application.Dictionary.Contracts;
-using TakeoutSaaS.Application.Dictionary.Services;
-using TakeoutSaaS.Domain.Dictionary.Entities;
-using TakeoutSaaS.Domain.Dictionary.Enums;
-using TakeoutSaaS.Domain.Dictionary.ValueObjects;
-using TakeoutSaaS.Infrastructure.Dictionary.ImportExport;
-using TakeoutSaaS.Infrastructure.Dictionary.Repositories;
-using TakeoutSaaS.Integration.Tests.Fixtures;
-using TakeoutSaaS.Shared.Abstractions.Constants;
-using TakeoutSaaS.Shared.Abstractions.Exceptions;
-
-namespace TakeoutSaaS.Integration.Tests.App.Dictionary;
-
-public sealed class DictionaryApiTests
-{
- [Fact]
- public async Task Test_CreateDictionaryGroup_ReturnsCreated()
- {
- using var database = new DictionarySqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 0, userId: 11);
- var tenantProvider = new TestTenantProvider(0);
- var cache = new TestDictionaryHybridCache();
-
- var service = BuildCommandService(context, tenantProvider, cache);
- var result = await service.CreateGroupAsync(new CreateDictionaryGroupRequest
- {
- Code = "ORDER_STATUS",
- Name = "Order Status",
- Scope = DictionaryScope.System,
- AllowOverride = true,
- Description = "Order lifecycle"
- });
-
- result.Id.Should().NotBe(0);
- result.Code.Should().Be("order_status");
- result.Scope.Should().Be(DictionaryScope.System);
-
- using var verifyContext = database.CreateContext(tenantId: 0);
- var stored = await verifyContext.DictionaryGroups
- .IgnoreQueryFilters()
- .FirstOrDefaultAsync(group => group.Id == result.Id);
-
- stored.Should().NotBeNull();
- stored!.TenantId.Should().Be(0);
- stored.Code.Value.Should().Be("order_status");
- }
-
- [Fact]
- public async Task Test_GetDictionaryGroups_ReturnsPaged()
- {
- using var database = new DictionarySqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 0, userId: 11);
-
- context.DictionaryGroups.AddRange(
- CreateSystemGroup(101, "order_status"),
- CreateSystemGroup(102, "payment_method"));
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var tenantProvider = new TestTenantProvider(0);
- var cache = new TestDictionaryHybridCache();
- var service = BuildQueryService(context, tenantProvider, cache);
-
- var page = await service.GetGroupsAsync(new DictionaryGroupQuery
- {
- Scope = DictionaryScope.System,
- Page = 1,
- PageSize = 1
- });
-
- page.TotalCount.Should().Be(2);
- page.Items.Should().HaveCount(1);
- }
-
- [Fact]
- public async Task Test_UpdateDictionaryGroup_WithValidRowVersion_ReturnsOk()
- {
- using var database = new DictionarySqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 0, userId: 11);
- var tenantProvider = new TestTenantProvider(0);
- var cache = new TestDictionaryHybridCache();
- var service = BuildCommandService(context, tenantProvider, cache);
-
- var created = await service.CreateGroupAsync(new CreateDictionaryGroupRequest
- {
- Code = "PAYMENT_METHOD",
- Name = "Payment Method",
- Scope = DictionaryScope.System,
- AllowOverride = true
- });
-
- var updated = await service.UpdateGroupAsync(created.Id, new UpdateDictionaryGroupRequest
- {
- Name = "Payment Method Updated",
- Description = "Updated",
- AllowOverride = false,
- IsEnabled = false,
- RowVersion = created.RowVersion
- });
-
- updated.Name.Should().Be("Payment Method Updated");
- updated.AllowOverride.Should().BeFalse();
- updated.IsEnabled.Should().BeFalse();
- }
-
- [Fact]
- public async Task Test_UpdateDictionaryGroup_WithStaleRowVersion_ReturnsConflict()
- {
- using var database = new DictionarySqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 0, userId: 11);
- var tenantProvider = new TestTenantProvider(0);
- var cache = new TestDictionaryHybridCache();
- var service = BuildCommandService(context, tenantProvider, cache);
-
- var created = await service.CreateGroupAsync(new CreateDictionaryGroupRequest
- {
- Code = "SHIPPING_METHOD",
- Name = "Shipping Method",
- Scope = DictionaryScope.System,
- AllowOverride = true
- });
-
- var request = new UpdateDictionaryGroupRequest
- {
- Name = "Shipping Method Updated",
- AllowOverride = true,
- IsEnabled = true,
- RowVersion = new byte[] { 9 }
- };
-
- Func act = async () => await service.UpdateGroupAsync(created.Id, request);
-
- var exception = await act.Should().ThrowAsync();
- exception.Which.ErrorCode.Should().Be(ErrorCodes.Conflict);
- }
-
- [Fact]
- public async Task Test_DeleteDictionaryGroup_SoftDeletesGroup()
- {
- using var database = new DictionarySqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 0, userId: 11);
-
- var group = CreateSystemGroup(201, "user_role");
- var item = CreateSystemItem(210, group.Id, "ADMIN", 10);
- context.DictionaryGroups.Add(group);
- context.DictionaryItems.Add(item);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var tenantProvider = new TestTenantProvider(0);
- var cache = new TestDictionaryHybridCache();
- var service = BuildCommandService(context, tenantProvider, cache);
-
- var result = await service.DeleteGroupAsync(group.Id);
- result.Should().BeTrue();
-
- using var verifyContext = database.CreateContext(tenantId: 0);
- var deletedGroup = await verifyContext.DictionaryGroups
- .IgnoreQueryFilters()
- .FirstAsync(x => x.Id == group.Id);
- deletedGroup.DeletedAt.Should().NotBeNull();
-
- var deletedItem = await verifyContext.DictionaryItems
- .IgnoreQueryFilters()
- .FirstAsync(x => x.Id == item.Id);
- deletedItem.DeletedAt.Should().NotBeNull();
- }
-
- [Fact]
- public async Task Test_EnableOverride_CreatesOverrideConfig()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- systemContext.DictionaryGroups.Add(CreateSystemGroup(301, "order_status", allowOverride: true));
- await systemContext.SaveChangesAsync();
- }
-
- using var tenantContext = database.CreateContext(tenantId: 100, userId: 21);
- var cache = new TestDictionaryHybridCache();
- var service = BuildOverrideService(tenantContext, cache);
-
- var result = await service.EnableOverrideAsync(100, "ORDER_STATUS");
-
- result.OverrideEnabled.Should().BeTrue();
- result.SystemDictionaryGroupCode.Should().Be("order_status");
-
- var stored = await tenantContext.TenantDictionaryOverrides
- .IgnoreQueryFilters()
- .FirstOrDefaultAsync(x => x.TenantId == 100 && x.SystemDictionaryGroupId == 301);
- stored.Should().NotBeNull();
- stored!.OverrideEnabled.Should().BeTrue();
- }
-
- [Fact]
- public async Task Test_DisableOverride_ClearsCustomization()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- systemContext.DictionaryGroups.Add(CreateSystemGroup(401, "payment_method", allowOverride: true));
- await systemContext.SaveChangesAsync();
- }
-
- using var tenantContext = database.CreateContext(tenantId: 100, userId: 21);
- var cache = new TestDictionaryHybridCache();
- var service = BuildOverrideService(tenantContext, cache);
-
- await service.EnableOverrideAsync(100, "payment_method");
- var disabled = await service.DisableOverrideAsync(100, "payment_method");
-
- disabled.Should().BeTrue();
-
- var stored = await tenantContext.TenantDictionaryOverrides
- .IgnoreQueryFilters()
- .FirstAsync(x => x.TenantId == 100 && x.SystemDictionaryGroupId == 401);
- stored.OverrideEnabled.Should().BeFalse();
- }
-
- [Fact]
- public async Task Test_UpdateHiddenItems_FiltersSystemItems()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- var group = CreateSystemGroup(501, "shipping_method", allowOverride: true);
- systemContext.DictionaryGroups.Add(group);
- systemContext.DictionaryItems.AddRange(
- CreateSystemItem(510, group.Id, "PLATFORM", 10),
- CreateSystemItem(511, group.Id, "MERCHANT", 20));
- await systemContext.SaveChangesAsync();
- }
-
- using var tenantContext = database.CreateContext(tenantId: 200, userId: 22);
- var cache = new TestDictionaryHybridCache();
- var overrideService = BuildOverrideService(tenantContext, cache);
- var mergeService = BuildMergeService(tenantContext);
-
- await overrideService.UpdateHiddenItemsAsync(200, "shipping_method", new[] { 511L });
- var merged = await mergeService.MergeItemsAsync(200, 501);
-
- merged.Should().Contain(item => item.Id == 510);
- merged.Should().NotContain(item => item.Id == 511);
- }
-
- [Fact]
- public async Task Test_GetMergedDictionary_ReturnsMergedResult()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- var group = CreateSystemGroup(601, "order_status", allowOverride: true);
- systemContext.DictionaryGroups.Add(group);
- systemContext.DictionaryItems.AddRange(
- CreateSystemItem(610, group.Id, "PENDING", 10),
- CreateSystemItem(611, group.Id, "ACCEPTED", 20));
- await systemContext.SaveChangesAsync();
- }
-
- using (var tenantContext = database.CreateContext(tenantId: 300, userId: 33))
- {
- var tenantGroup = CreateTenantGroup(701, 300, "order_status");
- tenantContext.DictionaryGroups.Add(tenantGroup);
- tenantContext.DictionaryItems.Add(CreateTenantItem(720, tenantGroup.Id, "CUSTOM", 15));
- await tenantContext.SaveChangesAsync();
- }
-
- using var queryContext = database.CreateContext(tenantId: 300, userId: 33);
- var cache = new TestDictionaryHybridCache();
- var overrideService = BuildOverrideService(queryContext, cache);
- await overrideService.EnableOverrideAsync(300, "order_status");
- await overrideService.UpdateHiddenItemsAsync(300, "order_status", new[] { 611L });
- await overrideService.UpdateCustomSortOrderAsync(300, "order_status", new Dictionary
- {
- [720L] = 1,
- [610L] = 2
- });
-
- var queryService = BuildQueryService(queryContext, new TestTenantProvider(300), cache);
- var merged = await queryService.GetMergedDictionaryAsync("order_status");
-
- merged.Should().Contain(item => item.Id == 610);
- merged.Should().Contain(item => item.Id == 720 && item.Source == "tenant");
- merged.Should().NotContain(item => item.Id == 611);
- merged.First().Id.Should().Be(720);
- }
-
- [Fact]
- public async Task Test_ExportCsv_GeneratesValidFile()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- var group = CreateSystemGroup(801, "payment_method", allowOverride: true);
- systemContext.DictionaryGroups.Add(group);
- systemContext.DictionaryItems.Add(CreateSystemItem(810, group.Id, "ALIPAY", 10));
- await systemContext.SaveChangesAsync();
- }
-
- using var exportContext = database.CreateContext(tenantId: 0, userId: 11);
- var cache = new TestDictionaryHybridCache();
- var service = BuildImportExportService(exportContext, new TestTenantProvider(0), new TestCurrentUserAccessor(11), cache);
-
- await using var stream = new MemoryStream();
- await service.ExportToCsvAsync(801, stream);
- var csv = Encoding.UTF8.GetString(stream.ToArray());
-
- csv.Should().Contain("code,key,value,sortOrder,isEnabled,description,source");
- csv.Should().Contain("payment_method");
- csv.Should().Contain("ALIPAY");
- }
-
- [Fact]
- public async Task Test_ImportCsv_WithSkipMode_SkipsDuplicates()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- var group = CreateSystemGroup(901, "order_status", allowOverride: true);
- systemContext.DictionaryGroups.Add(group);
- systemContext.DictionaryItems.Add(CreateSystemItem(910, group.Id, "PENDING", 10));
- await systemContext.SaveChangesAsync();
- }
-
- using var importContext = database.CreateContext(tenantId: 0, userId: 11);
- var cache = new TestDictionaryHybridCache();
- var service = BuildImportExportService(importContext, new TestTenantProvider(0), new TestCurrentUserAccessor(11), cache);
-
- var csv = BuildCsv(
- new[] { "code", "key", "value", "sortOrder", "isEnabled", "description", "source" },
- new[]
- {
- new[] { "order_status", "PENDING", BuildValueJson("待接单", "Pending"), "10", "true", "重复项", "system" },
- new[] { "order_status", "COMPLETED", BuildValueJson("已完成", "Completed"), "20", "true", "新增项", "system" }
- });
-
- var result = await service.ImportFromCsvAsync(new DictionaryImportRequest
- {
- GroupId = 901,
- FileName = "import.csv",
- FileSize = Encoding.UTF8.GetByteCount(csv),
- ConflictMode = ConflictResolutionMode.Skip,
- FileStream = new MemoryStream(Encoding.UTF8.GetBytes(csv))
- });
-
- result.SkipCount.Should().Be(1);
- result.SuccessCount.Should().Be(1);
- }
-
- [Fact]
- public async Task Test_ImportCsv_WithOverwriteMode_UpdatesExisting()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- var group = CreateSystemGroup(1001, "payment_method", allowOverride: true);
- systemContext.DictionaryGroups.Add(group);
- systemContext.DictionaryItems.Add(CreateSystemItem(1010, group.Id, "ALIPAY", 10));
- await systemContext.SaveChangesAsync();
- }
-
- using var importContext = database.CreateContext(tenantId: 0, userId: 11);
- var cache = new TestDictionaryHybridCache();
- var service = BuildImportExportService(importContext, new TestTenantProvider(0), new TestCurrentUserAccessor(11), cache);
-
- var csv = BuildCsv(
- new[] { "code", "key", "value", "sortOrder", "isEnabled", "description", "source" },
- new[]
- {
- new[] { "payment_method", "ALIPAY", BuildValueJson("支付宝新版", "Alipay New"), "15", "true", "覆盖", "system" }
- });
-
- await service.ImportFromCsvAsync(new DictionaryImportRequest
- {
- GroupId = 1001,
- FileName = "import.csv",
- FileSize = Encoding.UTF8.GetByteCount(csv),
- ConflictMode = ConflictResolutionMode.Overwrite,
- FileStream = new MemoryStream(Encoding.UTF8.GetBytes(csv))
- });
-
- var updated = await importContext.DictionaryItems
- .IgnoreQueryFilters()
- .FirstAsync(item => item.GroupId == 1001 && item.Key == "ALIPAY");
-
- updated.Value.Should().Contain("支付宝新版");
- }
-
- [Fact]
- public async Task Test_ImportCsv_WithInvalidData_ReturnsErrors()
- {
- using var database = new DictionarySqliteTestDatabase();
-
- using (var systemContext = database.CreateContext(tenantId: 0, userId: 11))
- {
- systemContext.DictionaryGroups.Add(CreateSystemGroup(1101, "user_role", allowOverride: false));
- await systemContext.SaveChangesAsync();
- }
-
- using var importContext = database.CreateContext(tenantId: 0, userId: 11);
- var cache = new TestDictionaryHybridCache();
- var service = BuildImportExportService(importContext, new TestTenantProvider(0), new TestCurrentUserAccessor(11), cache);
-
- var csv = BuildCsv(
- new[] { "code", "key", "value", "sortOrder", "isEnabled", "description", "source" },
- new[]
- {
- new[] { "user_role", "ADMIN", "invalid-json", "10", "true", "bad", "system" }
- });
-
- var result = await service.ImportFromCsvAsync(new DictionaryImportRequest
- {
- GroupId = 1101,
- FileName = "import.csv",
- FileSize = Encoding.UTF8.GetByteCount(csv),
- ConflictMode = ConflictResolutionMode.Skip,
- FileStream = new MemoryStream(Encoding.UTF8.GetBytes(csv))
- });
-
- result.ErrorCount.Should().Be(1);
- result.SuccessCount.Should().Be(0);
- }
-
- private static DictionaryCommandService BuildCommandService(
- TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext context,
- TestTenantProvider tenantProvider,
- TestDictionaryHybridCache cache)
- {
- return new DictionaryCommandService(
- new DictionaryGroupRepository(context),
- new DictionaryItemRepository(context),
- cache,
- tenantProvider,
- new HttpContextAccessor { HttpContext = new DefaultHttpContext() },
- NullLogger.Instance);
- }
-
- private static DictionaryQueryService BuildQueryService(
- TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext context,
- TestTenantProvider tenantProvider,
- TestDictionaryHybridCache cache)
- {
- var groupRepository = new DictionaryGroupRepository(context);
- var itemRepository = new DictionaryItemRepository(context);
- var overrideRepository = new TenantDictionaryOverrideRepository(context);
- var mergeService = new DictionaryMergeService(groupRepository, itemRepository, overrideRepository);
-
- return new DictionaryQueryService(groupRepository, itemRepository, mergeService, cache, tenantProvider);
- }
-
- private static DictionaryOverrideService BuildOverrideService(
- TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext context,
- TestDictionaryHybridCache cache)
- {
- return new DictionaryOverrideService(
- new DictionaryGroupRepository(context),
- new DictionaryItemRepository(context),
- new TenantDictionaryOverrideRepository(context),
- cache);
- }
-
- private static DictionaryMergeService BuildMergeService(
- TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext context)
- {
- var groupRepository = new DictionaryGroupRepository(context);
- var itemRepository = new DictionaryItemRepository(context);
- var overrideRepository = new TenantDictionaryOverrideRepository(context);
- return new DictionaryMergeService(groupRepository, itemRepository, overrideRepository);
- }
-
- private static DictionaryImportExportService BuildImportExportService(
- TakeoutSaaS.Infrastructure.Dictionary.Persistence.DictionaryDbContext context,
- TestTenantProvider tenantProvider,
- TestCurrentUserAccessor currentUser,
- TestDictionaryHybridCache cache)
- {
- return new DictionaryImportExportService(
- new CsvDictionaryParser(),
- new JsonDictionaryParser(),
- new DictionaryGroupRepository(context),
- new DictionaryItemRepository(context),
- new DictionaryImportLogRepository(context),
- cache,
- tenantProvider,
- currentUser,
- new HttpContextAccessor { HttpContext = new DefaultHttpContext() },
- NullLogger.Instance);
- }
-
- private static DictionaryGroup CreateSystemGroup(long id, string code, bool allowOverride = true)
- {
- return new DictionaryGroup
- {
- Id = id,
- TenantId = 0,
- Code = new DictionaryCode(code),
- Name = code,
- Scope = DictionaryScope.System,
- AllowOverride = allowOverride,
- Description = "Test group",
- IsEnabled = true,
- RowVersion = new byte[] { 1 }
- };
- }
-
- private static DictionaryGroup CreateTenantGroup(long id, long tenantId, string code)
- {
- return new DictionaryGroup
- {
- Id = id,
- TenantId = tenantId,
- Code = new DictionaryCode(code),
- Name = code,
- Scope = DictionaryScope.Business,
- AllowOverride = false,
- Description = "Tenant group",
- IsEnabled = true,
- RowVersion = new byte[] { 1 }
- };
- }
-
- private static DictionaryItem CreateSystemItem(long id, long groupId, string key, int sortOrder)
- {
- return new DictionaryItem
- {
- Id = id,
- TenantId = 0,
- GroupId = groupId,
- Key = key,
- Value = BuildValueJson("测试", "Test"),
- IsDefault = false,
- IsEnabled = true,
- SortOrder = sortOrder,
- Description = "System item",
- RowVersion = new byte[] { 1 }
- };
- }
-
- private static DictionaryItem CreateTenantItem(long id, long groupId, string key, int sortOrder)
- {
- return new DictionaryItem
- {
- Id = id,
- TenantId = 300,
- GroupId = groupId,
- Key = key,
- Value = BuildValueJson("租户值", "Tenant Value"),
- IsDefault = false,
- IsEnabled = true,
- SortOrder = sortOrder,
- Description = "Tenant item",
- RowVersion = new byte[] { 1 }
- };
- }
-
- private static string BuildValueJson(string zh, string en)
- {
- var value = new I18nValue(new Dictionary
- {
- ["zh-CN"] = zh,
- ["en"] = en
- });
- return value.ToJson();
- }
-
- private static string BuildCsv(string[] headers, IEnumerable rows)
- {
- var builder = new StringBuilder();
- builder.AppendLine(string.Join(",", headers));
- foreach (var row in rows)
- {
- builder.AppendLine(string.Join(",", row.Select(EscapeCsvField)));
- }
-
- return builder.ToString();
- }
-
- private static string EscapeCsvField(string value)
- {
- if (value.Contains('"', StringComparison.Ordinal))
- {
- value = value.Replace("\"", "\"\"");
- }
-
- if (value.Contains(',', StringComparison.Ordinal) ||
- value.Contains('\n', StringComparison.Ordinal) ||
- value.Contains('\r', StringComparison.Ordinal) ||
- value.Contains('"', StringComparison.Ordinal))
- {
- return $"\"{value}\"";
- }
-
- return value;
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs
deleted file mode 100644
index f370a4b..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using FluentAssertions;
-using TakeoutSaaS.Application.App.Tenants.Commands;
-using TakeoutSaaS.Application.App.Tenants.Handlers;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Infrastructure.App.Repositories;
-using TakeoutSaaS.Integration.Tests.Fixtures;
-
-namespace TakeoutSaaS.Integration.Tests.App.Tenants;
-
-public sealed class AnnouncementRegressionTests
-{
- [Fact]
- public async Task GivenPublishedAnnouncement_WhenSearchByIsActive_ThenReturns()
- {
- // Arrange
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 600, userId: 12);
-
- var legacy = CreateAnnouncement(tenantId: 600, id: 9100);
- legacy.Status = AnnouncementStatus.Published;
-
- context.TenantAnnouncements.Add(legacy);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
-
- // Act
- var results = await repository.SearchAsync(
- tenantId: 600,
- keyword: null,
- status: null,
- type: null,
- isActive: true,
- effectiveFrom: null,
- effectiveTo: null,
- effectiveAt: null,
- cancellationToken: CancellationToken.None);
-
- // Assert
- results.Should().ContainSingle(x => x.Id == legacy.Id);
- }
-
- [Fact]
- public async Task GivenDraftAnnouncement_WhenUpdate_ThenUpdatesFieldsAndKeepsInactive()
- {
- // Arrange
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 700, userId: 12);
-
- var announcement = CreateAnnouncement(tenantId: 700, id: 9101);
- announcement.Status = AnnouncementStatus.Draft;
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var handler = new UpdateTenantAnnouncementCommandHandler(repository);
-
- var command = new UpdateTenantAnnouncementCommand
- {
- TenantId = 700,
- AnnouncementId = announcement.Id,
- Title = "更新后的标题",
- Content = "更新后的内容",
- TargetType = "ALL_TENANTS",
- RowVersion = announcement.RowVersion
- };
-
- // Act
- var result = await handler.Handle(command, CancellationToken.None);
-
- // Assert
- result.Should().NotBeNull();
- result!.Title.Should().Be("更新后的标题");
- result.Content.Should().Be("更新后的内容");
- result.IsActive.Should().BeFalse();
- }
-
- private static TenantAnnouncement CreateAnnouncement(long tenantId, long id)
- => new()
- {
- Id = id,
- TenantId = tenantId,
- Title = "旧公告",
- Content = "内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = 1,
- EffectiveFrom = DateTime.UtcNow.AddMinutes(-5),
- EffectiveTo = null,
- PublisherScope = PublisherScope.Tenant,
- Status = AnnouncementStatus.Draft,
- TargetType = "ALL_TENANTS",
- TargetParameters = null,
- RowVersion = new byte[] { 1 }
- };
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementWorkflowTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementWorkflowTests.cs
deleted file mode 100644
index 706c9c1..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementWorkflowTests.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-using FluentAssertions;
-using Microsoft.EntityFrameworkCore;
-using Moq;
-using TakeoutSaaS.Application.App.Tenants.Commands;
-using TakeoutSaaS.Application.App.Tenants.Handlers;
-using TakeoutSaaS.Application.Messaging.Abstractions;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Infrastructure.App.Repositories;
-using TakeoutSaaS.Integration.Tests.Fixtures;
-using TakeoutSaaS.Shared.Abstractions.Constants;
-using TakeoutSaaS.Shared.Abstractions.Exceptions;
-
-namespace TakeoutSaaS.Integration.Tests.App.Tenants;
-
-///
-/// 公告工作流集成测试。
-///
-public sealed class AnnouncementWorkflowTests
-{
- [Fact]
- public async Task GivenDraftAnnouncement_WhenPublish_ThenStatusIsPublishedAndActive()
- {
- // 1. 准备
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 100, userId: 11);
-
- var announcement = CreateDraftAnnouncement(tenantId: 100, id: 9001);
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var eventPublisher = new Mock();
- var handler = new PublishAnnouncementCommandHandler(repository, eventPublisher.Object);
-
- // 2. (空行后) 执行
- var result = await handler.Handle(new PublishAnnouncementCommand
- {
- TenantId = 100,
- AnnouncementId = announcement.Id,
- RowVersion = announcement.RowVersion
- }, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Should().NotBeNull();
- result!.Status.Should().Be(AnnouncementStatus.Published);
- result.IsActive.Should().BeTrue();
-
- using var verifyContext = database.CreateContext(tenantId: 100);
- var persisted = await verifyContext.TenantAnnouncements.FirstAsync(x => x.Id == announcement.Id);
- persisted.Status.Should().Be(AnnouncementStatus.Published);
- persisted.PublishedAt.Should().NotBeNull();
- }
-
- [Fact]
- public async Task GivenPublishedAnnouncement_WhenRevoke_ThenStatusIsRevokedAndInactive()
- {
- // 1. 准备
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 200, userId: 11);
-
- var announcement = CreateDraftAnnouncement(tenantId: 200, id: 9002);
- announcement.Status = AnnouncementStatus.Published;
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var eventPublisher = new Mock();
- var handler = new RevokeAnnouncementCommandHandler(repository, eventPublisher.Object);
-
- // 2. (空行后) 执行
- var result = await handler.Handle(new RevokeAnnouncementCommand
- {
- TenantId = 200,
- AnnouncementId = announcement.Id,
- RowVersion = announcement.RowVersion
- }, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Should().NotBeNull();
- result!.Status.Should().Be(AnnouncementStatus.Revoked);
- result.IsActive.Should().BeFalse();
-
- using var verifyContext = database.CreateContext(tenantId: 200);
- var persisted = await verifyContext.TenantAnnouncements.FirstAsync(x => x.Id == announcement.Id);
- persisted.Status.Should().Be(AnnouncementStatus.Revoked);
- persisted.RevokedAt.Should().NotBeNull();
- }
-
- [Fact]
- public async Task GivenRevokedAnnouncement_WhenPublish_ThenRepublishAndClearRevokedAt()
- {
- // 1. 准备
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 300, userId: 11);
-
- var announcement = CreateDraftAnnouncement(tenantId: 300, id: 9003);
- announcement.Status = AnnouncementStatus.Revoked;
- announcement.RevokedAt = DateTime.UtcNow.AddMinutes(-5);
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var eventPublisher = new Mock();
- var handler = new PublishAnnouncementCommandHandler(repository, eventPublisher.Object);
-
- // 2. (空行后) 执行
- var result = await handler.Handle(new PublishAnnouncementCommand
- {
- TenantId = 300,
- AnnouncementId = announcement.Id,
- RowVersion = announcement.RowVersion
- }, CancellationToken.None);
-
- // 3. (空行后) 断言
- result.Should().NotBeNull();
- result!.Status.Should().Be(AnnouncementStatus.Published);
- result.IsActive.Should().BeTrue();
-
- using var verifyContext = database.CreateContext(tenantId: 300);
- var persisted = await verifyContext.TenantAnnouncements.FirstAsync(x => x.Id == announcement.Id);
- persisted.Status.Should().Be(AnnouncementStatus.Published);
- persisted.RevokedAt.Should().BeNull();
- }
-
- [Fact]
- public async Task GivenPublishedAnnouncement_WhenUpdate_ThenThrowsBusinessException()
- {
- // Arrange
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 400, userId: 11);
-
- var announcement = CreateDraftAnnouncement(tenantId: 400, id: 9004);
- announcement.Status = AnnouncementStatus.Published;
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var handler = new UpdateTenantAnnouncementCommandHandler(repository);
-
- var command = new UpdateTenantAnnouncementCommand
- {
- TenantId = 400,
- AnnouncementId = announcement.Id,
- Title = "更新标题",
- Content = "更新内容",
- TargetType = "ALL_TENANTS",
- RowVersion = announcement.RowVersion
- };
-
- // Act
- Func act = async () => await handler.Handle(command, CancellationToken.None);
-
- // Assert
- var exception = await act.Should().ThrowAsync();
- exception.Which.ErrorCode.Should().Be(ErrorCodes.Conflict);
- }
-
- [Fact]
- public async Task GivenStaleRowVersion_WhenUpdate_ThenReturnsConflict()
- {
- // Arrange
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 500, userId: 11);
-
- var announcement = CreateDraftAnnouncement(tenantId: 500, id: 9005);
- announcement.RowVersion = new byte[] { 1 };
- context.TenantAnnouncements.Add(announcement);
- await context.SaveChangesAsync();
-
- await context.Database.ExecuteSqlRawAsync(
- "UPDATE tenant_announcements SET \"RowVersion\" = {0} WHERE \"Id\" = {1}",
- new byte[] { 9 }, announcement.Id);
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
- var handler = new UpdateTenantAnnouncementCommandHandler(repository);
-
- var command = new UpdateTenantAnnouncementCommand
- {
- TenantId = 500,
- AnnouncementId = announcement.Id,
- Title = "并发更新",
- Content = "内容",
- TargetType = "ALL_TENANTS",
- RowVersion = new byte[] { 1 }
- };
-
- // Act
- Func act = async () => await handler.Handle(command, CancellationToken.None);
-
- // Assert
- var exception = await act.Should().ThrowAsync();
- exception.Which.ErrorCode.Should().Be(ErrorCodes.Conflict);
- }
-
- private static TenantAnnouncement CreateDraftAnnouncement(long tenantId, long id)
- => new()
- {
- Id = id,
- TenantId = tenantId,
- Title = "公告",
- Content = "内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = 1,
- EffectiveFrom = DateTime.UtcNow.AddMinutes(-10),
- EffectiveTo = DateTime.UtcNow.AddMinutes(30),
- PublisherScope = PublisherScope.Tenant,
- Status = AnnouncementStatus.Draft,
- TargetType = "ALL_TENANTS",
- TargetParameters = null,
- RowVersion = new byte[] { 1 }
- };
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs
deleted file mode 100644
index cb7697f..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using FluentAssertions;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Infrastructure.App.Repositories;
-using TakeoutSaaS.Integration.Tests.Fixtures;
-
-namespace TakeoutSaaS.Integration.Tests.App.Tenants;
-
-public sealed class TenantAnnouncementRepositoryScopeTests
-{
- [Fact]
- public async Task GivenTenantAndPlatformAnnouncements_WhenSearchAsync_ThenReturnsBoth()
- {
- // Arrange
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 800);
-
- var tenantAnnouncement = CreateAnnouncement(tenantId: 800, id: 9200);
- var platformAnnouncement = CreateAnnouncement(tenantId: 0, id: 9201);
-
- context.TenantAnnouncements.AddRange(tenantAnnouncement, platformAnnouncement);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
-
- var repository = new EfTenantAnnouncementRepository(context);
-
- // Act
- var results = await repository.SearchAsync(
- tenantId: 800,
- keyword: null,
- status: null,
- type: null,
- isActive: null,
- effectiveFrom: null,
- effectiveTo: null,
- effectiveAt: null,
- cancellationToken: CancellationToken.None);
-
- // Assert
- results.Select(x => x.Id).Should().Contain(new[] { tenantAnnouncement.Id, platformAnnouncement.Id });
- }
-
- private static TenantAnnouncement CreateAnnouncement(long tenantId, long id)
- => new()
- {
- Id = id,
- TenantId = tenantId,
- Title = "公告",
- Content = "内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = 1,
- EffectiveFrom = DateTime.UtcNow.AddMinutes(-5),
- PublisherScope = PublisherScope.Tenant,
- Status = AnnouncementStatus.Draft,
- TargetType = "ALL_TENANTS",
- RowVersion = new byte[] { 1 }
- };
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Authorization/PermissionAuthorizationHandlerTests.cs b/tests/TakeoutSaaS.Integration.Tests/Authorization/PermissionAuthorizationHandlerTests.cs
deleted file mode 100644
index d530f34..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Authorization/PermissionAuthorizationHandlerTests.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System.Security.Claims;
-using FluentAssertions;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Authorization.Policy;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.DependencyInjection;
-using TakeoutSaaS.Module.Authorization.Policies;
-
-namespace TakeoutSaaS.Integration.Tests.Authorization;
-
-public sealed class PermissionAuthorizationHandlerTests
-{
- [Theory]
- [InlineData("platform-announcement:create")]
- [InlineData("platform-announcement:publish")]
- [InlineData("platform-announcement:revoke")]
- [InlineData("tenant-announcement:publish")]
- [InlineData("tenant-announcement:revoke")]
- public async Task GivenUserWithPermission_WhenAuthorize_ThenSucceeds(string permission)
- {
- // Arrange
- var requirement = new PermissionRequirement(new[] { permission });
- var identity = new ClaimsIdentity(new[]
- {
- new Claim(PermissionAuthorizationHandler.PermissionClaimType, permission)
- }, "Test");
- var user = new ClaimsPrincipal(identity);
- var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);
- var handler = new PermissionAuthorizationHandler();
-
- // Act
- await handler.HandleAsync(context);
-
- // Assert
- context.HasSucceeded.Should().BeTrue();
- }
-
- [Fact]
- public async Task GivenUserWithoutPermission_WhenAuthorize_ThenFails()
- {
- // Arrange
- var requirement = new PermissionRequirement(new[] { "platform-announcement:create" });
- var user = new ClaimsPrincipal(new ClaimsIdentity(authenticationType: "Test"));
- var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);
- var handler = new PermissionAuthorizationHandler();
-
- // Act
- await handler.HandleAsync(context);
-
- // Assert
- context.HasSucceeded.Should().BeFalse();
- }
-
- [Fact]
- public async Task GivenAuthenticatedUserWithoutPermission_WhenEvaluatingPolicy_ThenForbidden()
- {
- // Arrange
- var services = new ServiceCollection();
- services.AddAuthorization();
- services.AddSingleton();
- services.AddSingleton();
- var provider = services.BuildServiceProvider();
-
- var policyProvider = provider.GetRequiredService();
- var policy = await policyProvider.GetPolicyAsync(
- PermissionAuthorizationPolicyProvider.BuildPolicyName(new[] { "platform-announcement:create" }));
-
- var user = new ClaimsPrincipal(new ClaimsIdentity(authenticationType: "Test"));
- var authenticateResult = AuthenticateResult.Success(new AuthenticationTicket(user, "Test"));
- var httpContext = new DefaultHttpContext { RequestServices = provider, User = user };
- var evaluator = provider.GetRequiredService();
-
- // Act
- var result = await evaluator.AuthorizeAsync(policy!, authenticateResult, httpContext, resource: null);
-
- // Assert
- result.Forbidden.Should().BeTrue();
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/DictionarySqliteTestDatabase.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/DictionarySqliteTestDatabase.cs
deleted file mode 100644
index 176c414..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/DictionarySqliteTestDatabase.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using TakeoutSaaS.Infrastructure.Dictionary.Persistence;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-///
-/// 集成测试用 SQLite 内存数据库(字典库)。
-///
-public sealed class DictionarySqliteTestDatabase : IDisposable
-{
- private readonly SqliteConnection _connection;
- private readonly TestIdGenerator _idGenerator = new();
- private bool _initialized;
-
- public DictionarySqliteTestDatabase()
- {
- _connection = new SqliteConnection("Filename=:memory:");
- _connection.Open();
- Options = new DbContextOptionsBuilder()
- .UseSqlite(_connection)
- .EnableSensitiveDataLogging()
- .Options;
- }
-
- public DbContextOptions Options { get; }
-
- public DictionaryDbContext CreateContext(long tenantId, long userId = 0)
- {
- EnsureCreated();
- // 1. AdminApi 不使用租户上下文;tenantId 参数仅用于兼容测试调用方签名
- _ = tenantId;
-
- // 2. (空行后) 按需注入当前用户与 ID 生成器
- return new DictionaryDbContext(
- Options,
- userId == 0 ? null : new TestCurrentUserAccessor(userId),
- _idGenerator);
- }
-
- public void EnsureCreated()
- {
- if (_initialized)
- {
- return;
- }
-
- // 1. 创建并初始化数据库结构
- using var context = new DictionaryDbContext(Options, idGenerator: _idGenerator);
- context.Database.EnsureCreated();
- _initialized = true;
- }
-
- public void Dispose()
- {
- _connection.Dispose();
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/SqliteTestDatabase.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/SqliteTestDatabase.cs
deleted file mode 100644
index a66a5da..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/SqliteTestDatabase.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using TakeoutSaaS.Infrastructure.App.Persistence;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-///
-/// 集成测试用 SQLite 内存数据库(业务主库)。
-///
-public sealed class SqliteTestDatabase : IDisposable
-{
- private readonly SqliteConnection _connection;
- private readonly TestIdGenerator _idGenerator = new();
- private bool _initialized;
-
- public SqliteTestDatabase()
- {
- _connection = new SqliteConnection("Filename=:memory:");
- _connection.Open();
- Options = new DbContextOptionsBuilder()
- .UseSqlite(_connection)
- .EnableSensitiveDataLogging()
- .Options;
- }
-
- public DbContextOptions Options { get; }
-
- public TakeoutAdminDbContext CreateContext(long tenantId, long userId = 0)
- {
- EnsureCreated();
- // 1. AdminApi 不使用租户上下文;tenantId 参数仅用于兼容测试调用方签名
- _ = tenantId;
-
- // 2. (空行后) 按需注入当前用户与 ID 生成器
- return new TakeoutAdminDbContext(
- Options,
- userId == 0 ? null : new TestCurrentUserAccessor(userId),
- _idGenerator);
- }
-
- public void EnsureCreated()
- {
- if (_initialized)
- {
- return;
- }
-
- // 1. 创建并初始化数据库结构
- using var context = new TakeoutAdminDbContext(Options, idGenerator: _idGenerator);
- context.Database.EnsureCreated();
- _initialized = true;
- }
-
- public void Dispose()
- {
- _connection.Dispose();
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestCurrentUserAccessor.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestCurrentUserAccessor.cs
deleted file mode 100644
index b7ab4d3..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestCurrentUserAccessor.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using TakeoutSaaS.Shared.Abstractions.Security;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-public sealed class TestCurrentUserAccessor(long userId) : ICurrentUserAccessor
-{
- public long UserId { get; set; } = userId;
-
- public bool IsAuthenticated => UserId != 0;
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestDictionaryHybridCache.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestDictionaryHybridCache.cs
deleted file mode 100644
index 9bd13c4..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestDictionaryHybridCache.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.Collections.Concurrent;
-using TakeoutSaaS.Application.Dictionary.Abstractions;
-using TakeoutSaaS.Domain.Dictionary.Enums;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-public sealed class TestDictionaryHybridCache : IDictionaryHybridCache
-{
- private readonly ConcurrentDictionary _cache = new(StringComparer.OrdinalIgnoreCase);
-
- public async Task GetOrCreateAsync(
- string key,
- TimeSpan ttl,
- Func> factory,
- CancellationToken cancellationToken = default)
- {
- if (_cache.TryGetValue(key, out var cached) && cached is T typed)
- {
- return typed;
- }
-
- var value = await factory(cancellationToken);
- if (value is not null)
- {
- _cache[key] = value;
- }
-
- return value;
- }
-
- public Task InvalidateAsync(
- string prefix,
- CacheInvalidationOperation operation = CacheInvalidationOperation.Update,
- CancellationToken cancellationToken = default)
- {
- foreach (var key in _cache.Keys)
- {
- if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
- {
- _cache.TryRemove(key, out _);
- }
- }
-
- return Task.CompletedTask;
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestIdGenerator.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestIdGenerator.cs
deleted file mode 100644
index 5c61f3a..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestIdGenerator.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Threading;
-using TakeoutSaaS.Shared.Abstractions.Ids;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-///
-/// 集成测试用雪花 ID 生成器(递增模拟)。
-///
-public sealed class TestIdGenerator : IIdGenerator
-{
- private long _current;
-
- ///
- /// 生成下一个 ID。
- ///
- /// 递增的 long ID。
- public long NextId()
- => Interlocked.Increment(ref _current);
-}
-
diff --git a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestTenantProvider.cs b/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestTenantProvider.cs
deleted file mode 100644
index 89c0e4d..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Fixtures/TestTenantProvider.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using TakeoutSaaS.Shared.Abstractions.Tenancy;
-
-namespace TakeoutSaaS.Integration.Tests.Fixtures;
-
-public sealed class TestTenantProvider(long tenantId) : ITenantProvider
-{
- public long TenantId { get; set; } = tenantId;
-
- public long GetCurrentTenantId() => TenantId;
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/Performance/AnnouncementQueryPerformanceTests.cs b/tests/TakeoutSaaS.Integration.Tests/Performance/AnnouncementQueryPerformanceTests.cs
deleted file mode 100644
index 290991a..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/Performance/AnnouncementQueryPerformanceTests.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System.Diagnostics;
-using FluentAssertions;
-using TakeoutSaaS.Application.App.Tenants.Handlers;
-using TakeoutSaaS.Application.App.Tenants.Queries;
-using TakeoutSaaS.Domain.Tenants.Entities;
-using TakeoutSaaS.Domain.Tenants.Enums;
-using TakeoutSaaS.Infrastructure.App.Repositories;
-using TakeoutSaaS.Integration.Tests.Fixtures;
-
-namespace TakeoutSaaS.Integration.Tests.Performance;
-
-///
-/// 公告查询性能相关测试。
-///
-public sealed class AnnouncementQueryPerformanceTests
-{
- [Fact]
- public async Task GivenLargeDataset_WhenQueryingAnnouncements_ThenCompletesWithinThreshold()
- {
- // 1. 准备
- using var database = new SqliteTestDatabase();
- using var context = database.CreateContext(tenantId: 900);
- var announcements = new List();
- for (var i = 0; i < 1000; i++)
- {
- var tenantId = i % 2 == 0 ? 900 : 0;
- var targetType = i % 10 == 0 ? "ROLES" : "ALL_TENANTS";
- var targetParameters = i % 10 == 0 ? "{\"roles\":[\"ops\"]}" : null;
-
- announcements.Add(new TenantAnnouncement
- {
- Id = 10000 + i,
- TenantId = tenantId,
- Title = "公告",
- Content = "内容",
- AnnouncementType = TenantAnnouncementType.System,
- Priority = i % 5,
- EffectiveFrom = DateTime.UtcNow.AddDays(-1),
- PublisherScope = tenantId == 0 ? PublisherScope.Platform : PublisherScope.Tenant,
- Status = AnnouncementStatus.Published,
- TargetType = targetType,
- TargetParameters = targetParameters,
- RowVersion = new byte[] { 1 }
- });
- }
-
- context.TenantAnnouncements.AddRange(announcements);
- await context.SaveChangesAsync();
- context.ChangeTracker.Clear();
- var announcementRepository = new EfTenantAnnouncementRepository(context);
- var handler = new GetTenantsAnnouncementsQueryHandler(announcementRepository);
- var query = new GetTenantsAnnouncementsQuery
- {
- TenantId = 900,
- Page = 1,
- PageSize = 50
- };
-
- // 2. (空行后) 执行
- var stopwatch = Stopwatch.StartNew();
- var result = await handler.Handle(query, CancellationToken.None);
- stopwatch.Stop();
-
- // 3. (空行后) 断言:TotalCount 为估算口径(page * size * 3)过滤后的数量
- result.Items.Count.Should().Be(50); // 请求的页大小
- result.TotalCount.Should().BeLessThanOrEqualTo(150); // 最多是 estimatedLimit
- result.TotalCount.Should().BeGreaterThan(0); // 至少有一些结果
- stopwatch.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(5));
- }
-}
diff --git a/tests/TakeoutSaaS.Integration.Tests/TakeoutSaaS.Integration.Tests.csproj b/tests/TakeoutSaaS.Integration.Tests/TakeoutSaaS.Integration.Tests.csproj
deleted file mode 100644
index 24ff938..0000000
--- a/tests/TakeoutSaaS.Integration.Tests/TakeoutSaaS.Integration.Tests.csproj
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- net10.0
- enable
- enable
- false
-
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-