diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName.CompanyName.ProjectName.Application.Tests.csproj b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName.CompanyName.ProjectName.Application.Tests.csproj index a661f6026..938edc57b 100644 --- a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName.CompanyName.ProjectName.Application.Tests.csproj +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName.CompanyName.ProjectName.Application.Tests.csproj @@ -7,12 +7,17 @@ + + + + + diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/DataSeeder/ProjectNameDataSeederTests.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/DataSeeder/ProjectNameDataSeederTests.cs new file mode 100644 index 000000000..dd8ff6727 --- /dev/null +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/DataSeeder/ProjectNameDataSeederTests.cs @@ -0,0 +1,137 @@ +using Microsoft.Extensions.DependencyInjection; +using PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore.DataSeeder; +using PackageName.CompanyName.ProjectName.Users; +using Shouldly; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; +using Volo.Abp.Uow; +using Xunit; + +namespace PackageName.CompanyName.ProjectName.DataSeeder +{ + /// + /// 数据种子初始化测试 + /// + [Collection("Database")] + public class ProjectNameDataSeederTests : ProjectNameApplicationTestBase + { + private readonly IProjectNameDataSeeder _inspectionDataSeeder; + private readonly IRepository _userRepository; + private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly IIdentityUserRepository _identityUserRepository; + + public ProjectNameDataSeederTests() + { + _inspectionDataSeeder = GetRequiredService(); + _userRepository = GetRequiredService>(); + _identityRoleRepository = GetRequiredService(); + _identityUserRepository = GetRequiredService(); + } + + [Fact] + public async Task Should_Seed_Data_Successfully() + { + // Arrange + var context = new DataSeedContext(); + + // Act + await _inspectionDataSeeder.SeedAsync(context); + + // Assert - 使用单元工作方法包装所有数据库操作 + await WithUnitOfWorkAsync(async () => + { + // 测试角色 + var roles = await _identityRoleRepository.GetListAsync(); + roles.Count.ShouldBeGreaterThanOrEqualTo(7); // 至少应该有 7 个角色 + + var superAdminRole = await _identityRoleRepository.FindByNormalizedNameAsync("超级管理员".ToUpperInvariant()); + superAdminRole.ShouldNotBeNull(); + // 测试用户 + var users = await _userRepository.GetListAsync(); + users.Count.ShouldBeGreaterThanOrEqualTo(10); // 至少应该有 10 个用户 + + foreach (var user in users) + { + user.IdentityUserId.ShouldNotBe(Guid.Empty); + + var identityUser = await _identityUserRepository.GetAsync(user.IdentityUserId); + identityUser.ShouldNotBeNull(); + } + + return true; + }); + } + + [Theory] + [InlineData("超级管理员")] + [InlineData("普通用户")] + public async Task Should_Create_Roles(string roleName) + { + // Arrange + var context = new DataSeedContext(); + await _inspectionDataSeeder.SeedAsync(context); + + // Act & Assert - 使用单元工作方法包装 + await WithUnitOfWorkAsync(async () => + { + var role = await _identityRoleRepository.FindByNormalizedNameAsync(roleName.ToUpperInvariant()); + role.ShouldNotBeNull(); + role.Name.ShouldBe(roleName); + + return true; + }); + } + + [Theory] + [InlineData("testuser1")] + [InlineData("testuser2")] + public async Task Should_Create_Users(string nickName) + { + // Arrange + var context = new DataSeedContext(); + await _inspectionDataSeeder.SeedAsync(context); + + // Act & Assert - 使用单元工作方法包装 + await WithUnitOfWorkAsync(async () => + { + var users = await _userRepository.GetListAsync(); + var user = users.FirstOrDefault(u => u.NickName == nickName); + user.ShouldNotBeNull(); + user.NickName.ShouldBe(nickName); + user.IdentityUserId.ShouldNotBe(Guid.Empty); + + var identityUser = await _identityUserRepository.GetAsync(user.IdentityUserId); + identityUser.ShouldNotBeNull(); + identityUser.Name.ShouldBe(nickName); + + return true; + }); + } + + // 添加单元工作方法 + protected override Task WithUnitOfWorkAsync(Func> func) + { + return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); + } + + // 可选:添加重载方法以支持更多场景 + protected async override Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func> func) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + var result = await func(); + await uow.CompleteAsync(); + return result; + } + } + } + } +} diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/ProjectNameApplicationTestModule.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/ProjectNameApplicationTestModule.cs index 7e9fa658a..e07d40f8e 100644 --- a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/ProjectNameApplicationTestModule.cs +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/ProjectNameApplicationTestModule.cs @@ -1,11 +1,63 @@ +using Hangfire; +using Hangfire.MemoryStorage; +using LINGYUN.Abp.Identity; +using LINGYUN.Abp.Identity.Session; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore.DataSeeder; +using PackageName.CompanyName.ProjectName.EntityFrameworkCore; using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.Security.Claims; namespace PackageName.CompanyName.ProjectName; [DependsOn( typeof(ProjectNameDomainTestModule), - typeof(ProjectNameApplicationModule) + typeof(ProjectNameApplicationModule), + typeof(AbpIdentityApplicationModule), + typeof(AbpPermissionManagementDomainIdentityModule), + typeof(ProjectNameEntityFrameworkCoreTestModule) )] public class ProjectNameApplicationTestModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + // //设置ILogger为NullLogger + context.Services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)); + context.Services.AddTransient(); + context.Services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance)); + context.Services.AddTransient(); + context.Services.AddTransient(); + + // 增加配置文件定义,在新建租户时需要 + Configure(options => + { + // 允许中文用户名 + options.User.AllowedUserNameCharacters = null; + // 支持弱密码 + options.Password.RequireDigit = false; + options.Password.RequiredLength = 1; + options.Password.RequireLowercase = false; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + }); + Configure(options => + { + options.IsDynamicClaimsEnabled = true; + }); + Configure(options => + { + options.IsCleanupEnabled = true; + }); + // 配置Hangfire + context.Services.AddHangfire(config => + { + config.UseMemoryStorage(); + }); + } } diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestFileProvider.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestFileProvider.cs new file mode 100644 index 000000000..c7a075cb2 --- /dev/null +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestFileProvider.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.IO; + +namespace PackageName.CompanyName.ProjectName; + +public class TestFileProvider : IFileProvider +{ + private readonly Dictionary _files; + + public TestFileProvider() + { + _files = new Dictionary(); + } + + public IDirectoryContents GetDirectoryContents(string subpath) + { + return new NotFoundDirectoryContents(); + } + + public IFileInfo GetFileInfo(string subpath) + { + if (_files.TryGetValue(subpath, out var fileInfo)) + { + return fileInfo; + } + return new NotFoundFileInfo(subpath); + } + + public IChangeToken Watch(string filter) + { + return NullChangeToken.Singleton; + } + + public void AddFile(string path, string contents) + { + _files[path] = new TestFileInfo(path, contents); + } +} + +public class TestFileInfo : IFileInfo +{ + private readonly string _contents; + + public TestFileInfo(string name, string contents) + { + Name = name; + _contents = contents; + } + + public bool Exists => true; + public long Length => _contents.Length; + public string PhysicalPath => null; + public string Name { get; } + public DateTimeOffset LastModified => DateTimeOffset.UtcNow; + public bool IsDirectory => false; + + public Stream CreateReadStream() + { + return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(_contents)); + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestHostEnvironment.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestHostEnvironment.cs new file mode 100644 index 000000000..ae140f85d --- /dev/null +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/TestHostEnvironment.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using System; + +namespace PackageName.CompanyName.ProjectName; + +public class TestHostEnvironment : IHostEnvironment +{ + public TestHostEnvironment() + { + EnvironmentName = "Test"; + ApplicationName = "TestApplication"; + ContentRootPath = AppDomain.CurrentDomain.BaseDirectory; + ContentRootFileProvider = new PhysicalFileProvider(ContentRootPath); + } + + public string EnvironmentName { get; set; } + public string ApplicationName { get; set; } + public string ContentRootPath { get; set; } + public IFileProvider ContentRootFileProvider { get; set; } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/Users/UserAppServiceTests.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/Users/UserAppServiceTests.cs new file mode 100644 index 000000000..212d79fe2 --- /dev/null +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.Application.Tests/PackageName/CompanyName/ProjectName/Users/UserAppServiceTests.cs @@ -0,0 +1,265 @@ +using PackageName.CompanyName.ProjectName.Users.Dtos; +using Shouldly; +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Validation; +using Xunit; + +namespace PackageName.CompanyName.ProjectName.Users +{ + /// + /// UserAppService 的单元测试 + /// + [Collection("Database")] + public class UserAppServiceTests : ProjectNameApplicationTestBase + { + private readonly IUserAppService _userAppService; + private readonly IUserManager _userManager; + + public UserAppServiceTests() + { + _userAppService = GetRequiredService(); + _userManager = GetRequiredService(); + } + + [Theory] + [InlineData("testuser1", "Test123456!", true)] + [InlineData("testuser2", "Test123456!", false)] + public async Task Should_Create_User( + string nickName, + string password, + bool isActive) + { + // Arrange + var input = new CreateUpdateUserDto + { + NickName = nickName, + Password = password, + IsActive = isActive + }; + + // Act + var result = await _userAppService.CreateAsync(input); + + // Assert + result.ShouldNotBeNull(); + result.NickName.ShouldBe(nickName); + result.IsActive.ShouldBe(isActive); + } + + [Theory] + [InlineData("", "Test123456!", "用户名称不能为空")] + [InlineData("test", "123", "密码长度必须在6-20个字符之间")] + public async Task Should_Not_Create_User_With_Invalid_Input(string nickName, string password, + string expectedErrorMessage) + { + // Arrange + var input = new CreateUpdateUserDto + { + NickName = nickName, + Password = password + }; + + // Act & Assert + var exception = await Assert.ThrowsAsync(async () => + { + await _userAppService.CreateAsync(input); + }); + + exception.ValidationErrors.ShouldContain(x => x.ErrorMessage.Contains(expectedErrorMessage)); + } + + [Fact] + public async Task Should_Get_User_List() + { + // Arrange + await CreateTestUserAsync("testuser1", "Test123456!"); + await CreateTestUserAsync("testuser2", "Test123456!"); + + // Act + var result = await _userAppService.GetListAsync( + new UserPagedAndSortedResultRequestDto + { + MaxResultCount = 10, + SkipCount = 0, + Sorting = "NickName" + }); + + // Assert + result.ShouldNotBeNull(); + result.TotalCount.ShouldBeGreaterThanOrEqualTo(2); + result.Items.ShouldContain(x => x.NickName == "testuser1"); + result.Items.ShouldContain(x => x.NickName == "testuser2"); + } + + [Fact] + public async Task Should_Filter_Users_By_NickName() + { + // Arrange + await CreateTestUserAsync("testuser1", "Test123456!"); + await CreateTestUserAsync("testuser2", "Test123456!"); + await CreateTestUserAsync("otheruser", "Test123456!"); + + // Act + var result = await _userAppService.GetListAsync( + new UserPagedAndSortedResultRequestDto + { + MaxResultCount = 10, + SkipCount = 0, + Sorting = "NickName", + NickName = "testuser" + }); + + // Assert + result.ShouldNotBeNull(); + result.TotalCount.ShouldBe(2); + result.Items.ShouldContain(x => x.NickName == "testuser1"); + result.Items.ShouldContain(x => x.NickName == "testuser2"); + result.Items.ShouldNotContain(x => x.NickName == "otheruser"); + } + + [Fact] + public async Task Should_Update_User() + { + // Arrange + var user = await CreateTestUserAsync("updatetest", "Test123456!"); + var updateInput = new CreateUpdateUserDto + { + NickName = "updateduser", + Password = "NewPassword123!", + ContactInfo = "13800138000", + Position = "开发工程师", + IsActive = true + }; + + // Act + var result = await _userAppService.UpdateAsync(user.Id, updateInput); + + // Assert + result.ShouldNotBeNull(); + result.NickName.ShouldBe("updateduser"); + result.ContactInfo.ShouldBe("13800138000"); + result.Position.ShouldBe("开发工程师"); + + // 验证更新后的用户信息 + var updatedUser = await _userAppService.GetAsync(user.Id); + updatedUser.NickName.ShouldBe("updateduser"); + } + + [Fact] + public async Task Should_Not_Update_Non_Existing_User() + { + // Arrange + var input = new CreateUpdateUserDto + { + NickName = "testuser", + Password = "Test123456!" + }; + + // Act & Assert + await Assert.ThrowsAsync(async () => + { + await _userAppService.UpdateAsync(Guid.NewGuid(), input); + }); + } + + [Fact] + public async Task Should_Delete_User() + { + // Arrange + var user = await CreateTestUserAsync("deletetest", "Test123456!"); + + // Act + await _userAppService.DeleteAsync(user.Id); + + // Assert - 尝试获取已删除的用户应该抛出异常 + await Assert.ThrowsAsync(async () => + { + await _userAppService.GetAsync(user.Id); + }); + } + + [Fact] + public async Task Should_Change_User_Password() + { + // Arrange + var user = await CreateTestUserAsync("passwordtest", "OldPassword123!"); + + // Act & Assert + await _userAppService.ChangePasswordAsync(user.Id, "OldPassword123!", "NewPassword123!"); + + // 尝试用新密码登录(这个需要集成测试才能完整测试) + // 这里我们只是验证方法执行不会抛出异常 + } + + [Fact] + public async Task Should_Reset_User_Password() + { + // Arrange + var user = await CreateTestUserAsync("resetpasswordtest", "OldPassword123!"); + + // Act & Assert + await _userAppService.ResetPasswordAsync(user.Id, "NewPassword123!"); + + // 同样,完整测试需要验证用户能用新密码登录,这需要集成测试 + } + + [Fact] + public async Task Should_Set_User_Active_Status() + { + // Arrange + var user = await CreateTestUserAsync("activestatustest", "Password123!"); + + // Act + await _userAppService.SetUserActiveStatusAsync(user.Id, false); + var disabledUser = await _userAppService.GetAsync(user.Id); + + await _userAppService.SetUserActiveStatusAsync(user.Id, true); + var enabledUser = await _userAppService.GetAsync(user.Id); + + // Assert + disabledUser.IsActive.ShouldBeFalse(); + enabledUser.IsActive.ShouldBeTrue(); + } + + [Theory] + [InlineData("13900000000", "工程师")] + [InlineData("13800000000", "设计师")] + [InlineData(null, null)] + public async Task Should_Create_User_With_Optional_Fields(string contactInfo, string position) + { + // Arrange + var input = new CreateUpdateUserDto + { + NickName = $"user_{Guid.NewGuid():N}", + Password = "Test123456!", + ContactInfo = contactInfo, + Position = position + }; + + // Act + var result = await _userAppService.CreateAsync(input); + + // Assert + result.ShouldNotBeNull(); + result.ContactInfo.ShouldBe(contactInfo); + result.Position.ShouldBe(position); + } + + private async Task CreateTestUserAsync(string nickName, string password) + { + return await WithUnitOfWorkAsync(async () => + { + var input = new CreateUpdateUserDto + { + NickName = nickName, + Password = password, + IsActive = true + }; + + return await _userAppService.CreateAsync(input); + }); + } + } +} \ No newline at end of file diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests.csproj b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests.csproj index 0a3e9f02c..944bbe75a 100644 --- a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests.csproj +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests.csproj @@ -8,10 +8,14 @@ - + + + + + diff --git a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreTestModule.cs b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreTestModule.cs index da8ce7c6c..b5fff765e 100644 --- a/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreTestModule.cs +++ b/aspnet-core/templates/aio/content/tests/PackageName.CompanyName.ProjectName.EntityFrameworkCore.Tests/PackageName/CompanyName/ProjectName/EntityFrameworkCore/ProjectNameEntityFrameworkCoreTestModule.cs @@ -1,38 +1,74 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using PackageName.CompanyName.ProjectName.AIO.EntityFrameworkCore; using System; +using Volo.Abp; +using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; +using Volo.Abp.Threading; +using Volo.Abp.Timing; using Volo.Abp.Uow; namespace PackageName.CompanyName.ProjectName.EntityFrameworkCore; [DependsOn( typeof(ProjectNameTestBaseModule), - typeof(ProjectNameEntityFrameworkCoreModule) - )] + typeof(ProjectNameEntityFrameworkCoreModule), + typeof(SingleMigrationsEntityFrameworkCoreModule) +)] public class ProjectNameEntityFrameworkCoreTestModule : AbpModule { + // 数据库配置 + private const string DefaultPostgresConnectionString = + "Host=127.0.0.1;Port=5432;Database=test_db;User Id=postgres;Password=postgres;"; + public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddEntityFrameworkInMemoryDatabase(); + var connectionString = Environment.GetEnvironmentVariable("TEST_CONNECTION_STRING") ?? + DefaultPostgresConnectionString; - var databaseName = Guid.NewGuid().ToString(); + // 配置数据库连接字符串 + Configure(options => + { + options.ConnectionStrings.Default = connectionString; + }); - Configure(options => + Configure(options => { options.Kind = DateTimeKind.Utc; }); + context.Services.AddAbpDbContext(options => { - options.Configure(abpDbContextConfigurationContext => - { - abpDbContextConfigurationContext.DbContextOptions.EnableDetailedErrors(); - abpDbContextConfigurationContext.DbContextOptions.EnableSensitiveDataLogging(); + options.AddDefaultRepositories(true); + }); - abpDbContextConfigurationContext.DbContextOptions.UseInMemoryDatabase(databaseName); - }); + // 配置所有DbContext + Configure(options => + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + options.UseNpgsql(); }); Configure(options => { - options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; }); } -} + + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) + { + var dbContext = context.ServiceProvider.GetRequiredService(); + // 重置数据库 + dbContext.Database.EnsureDeleted(); + // // 创建数据库 + dbContext.Database.EnsureCreated(); + dbContext.Database.GenerateCreateScript(); + // dbContext.Database.Migrate(); + + // 初始化种子数据 + var dataSeeder = context.ServiceProvider.GetRequiredService(); + AsyncHelper.RunSync(() => dataSeeder.SeedAsync()); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + } +} \ No newline at end of file