mirror of
https://github.com/bitwarden/server.git
synced 2025-02-08 00:31:27 +01:00
Scaffold Events Integration Tests (#5355)
* Scaffold Events Integration Tests * Format
This commit is contained in:
parent
bd394eabe9
commit
408ddd9388
@ -125,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notifications.Test", "test\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.Dapper.Test", "test\Infrastructure.Dapper.Test\Infrastructure.Dapper.Test.csproj", "{4A725DB3-BE4F-4C23-9087-82D0610D67AF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Events.IntegrationTest", "test\Events.IntegrationTest\Events.IntegrationTest.csproj", "{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -313,6 +315,10 @@ Global
|
||||
{4A725DB3-BE4F-4C23-9087-82D0610D67AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A725DB3-BE4F-4C23-9087-82D0610D67AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A725DB3-BE4F-4C23-9087-82D0610D67AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -363,6 +369,7 @@ Global
|
||||
{81673EFB-7134-4B4B-A32F-1EA05F0EF3CE} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{90D85D8F-5577-4570-A96E-5A2E185F0F6F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{4A725DB3-BE4F-4C23-9087-82D0610D67AF} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||
|
@ -0,0 +1,29 @@
|
||||
using System.Net.Http.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Events.Models;
|
||||
|
||||
namespace Bit.Events.IntegrationTest.Controllers;
|
||||
|
||||
public class CollectControllerTests
|
||||
{
|
||||
// This is a very simple test, and should be updated to assert more things, but for now
|
||||
// it ensures that the events startup doesn't throw any errors with fairly basic configuration.
|
||||
[Fact]
|
||||
public async Task Post_Works()
|
||||
{
|
||||
var eventsApplicationFactory = new EventsApplicationFactory();
|
||||
var (accessToken, _) = await eventsApplicationFactory.LoginWithNewAccount();
|
||||
var client = eventsApplicationFactory.CreateAuthedClient(accessToken);
|
||||
|
||||
var response = await client.PostAsJsonAsync<IEnumerable<EventModel>>("collect",
|
||||
[
|
||||
new EventModel
|
||||
{
|
||||
Type = EventType.User_ClientExportedVault,
|
||||
Date = DateTime.UtcNow,
|
||||
},
|
||||
]);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
29
test/Events.IntegrationTest/Events.IntegrationTest.csproj
Normal file
29
test/Events.IntegrationTest/Events.IntegrationTest.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNetTestSdkVersion)" />
|
||||
<PackageReference Include="xunit" Version="$(XUnitVersion)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioVersion)">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorVersion)">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Events\Events.csproj" />
|
||||
<ProjectReference Include="..\IntegrationTestCommon\IntegrationTestCommon.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
57
test/Events.IntegrationTest/EventsApplicationFactory.cs
Normal file
57
test/Events.IntegrationTest/EventsApplicationFactory.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using Bit.Identity.Models.Request.Accounts;
|
||||
using Bit.IntegrationTestCommon.Factories;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Events.IntegrationTest;
|
||||
|
||||
public class EventsApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
{
|
||||
private readonly IdentityApplicationFactory _identityApplicationFactory;
|
||||
private const string _connectionString = "DataSource=:memory:";
|
||||
|
||||
public EventsApplicationFactory()
|
||||
{
|
||||
SqliteConnection = new SqliteConnection(_connectionString);
|
||||
SqliteConnection.Open();
|
||||
|
||||
_identityApplicationFactory = new IdentityApplicationFactory();
|
||||
_identityApplicationFactory.SqliteConnection = SqliteConnection;
|
||||
}
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
base.ConfigureWebHost(builder);
|
||||
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
|
||||
{
|
||||
options.BackchannelHttpHandler = _identityApplicationFactory.Server.CreateHandler();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for registering and logging in to a new account
|
||||
/// </summary>
|
||||
public async Task<(string Token, string RefreshToken)> LoginWithNewAccount(string email = "integration-test@bitwarden.com", string masterPasswordHash = "master_password_hash")
|
||||
{
|
||||
await _identityApplicationFactory.RegisterAsync(new RegisterRequestModel
|
||||
{
|
||||
Email = email,
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
});
|
||||
|
||||
return await _identityApplicationFactory.TokenFromPasswordAsync(email, masterPasswordHash);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
SqliteConnection!.Dispose();
|
||||
}
|
||||
}
|
1
test/Events.IntegrationTest/GlobalUsings.cs
Normal file
1
test/Events.IntegrationTest/GlobalUsings.cs
Normal file
@ -0,0 +1 @@
|
||||
global using Xunit;
|
@ -14,6 +14,7 @@ using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NSubstitute;
|
||||
@ -188,44 +189,27 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
|
||||
// QUESTION: The normal licensing service should run fine on developer machines but not in CI
|
||||
// should we have a fork here to leave the normal service for developers?
|
||||
// TODO: Eventually add the license file to CI
|
||||
var licensingService = services.First(sd => sd.ServiceType == typeof(ILicensingService));
|
||||
services.Remove(licensingService);
|
||||
services.AddSingleton<ILicensingService, NoopLicensingService>();
|
||||
Replace<ILicensingService, NoopLicensingService>(services);
|
||||
|
||||
// FUTURE CONSIDERATION: Add way to run this self hosted/cloud, for now it is cloud only
|
||||
var pushRegistrationService = services.First(sd => sd.ServiceType == typeof(IPushRegistrationService));
|
||||
services.Remove(pushRegistrationService);
|
||||
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
||||
Replace<IPushRegistrationService, NoopPushRegistrationService>(services);
|
||||
|
||||
// Even though we are cloud we currently set this up as cloud, we can use the EF/selfhosted service
|
||||
// instead of using Noop for this service
|
||||
// TODO: Install and use azurite in CI pipeline
|
||||
var eventWriteService = services.First(sd => sd.ServiceType == typeof(IEventWriteService));
|
||||
services.Remove(eventWriteService);
|
||||
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
|
||||
Replace<IEventWriteService, RepositoryEventWriteService>(services);
|
||||
|
||||
var eventRepositoryService = services.First(sd => sd.ServiceType == typeof(IEventRepository));
|
||||
services.Remove(eventRepositoryService);
|
||||
services.AddSingleton<IEventRepository, EventRepository>();
|
||||
Replace<IEventRepository, EventRepository>(services);
|
||||
|
||||
var mailDeliveryService = services.First(sd => sd.ServiceType == typeof(IMailDeliveryService));
|
||||
services.Remove(mailDeliveryService);
|
||||
services.AddSingleton<IMailDeliveryService, NoopMailDeliveryService>();
|
||||
Replace<IMailDeliveryService, NoopMailDeliveryService>(services);
|
||||
|
||||
var captchaValidationService = services.First(sd => sd.ServiceType == typeof(ICaptchaValidationService));
|
||||
services.Remove(captchaValidationService);
|
||||
services.AddSingleton<ICaptchaValidationService, NoopCaptchaValidationService>();
|
||||
Replace<ICaptchaValidationService, NoopCaptchaValidationService>(services);
|
||||
|
||||
// TODO: Install and use azurite in CI pipeline
|
||||
var installationDeviceRepository =
|
||||
services.First(sd => sd.ServiceType == typeof(IInstallationDeviceRepository));
|
||||
services.Remove(installationDeviceRepository);
|
||||
services.AddSingleton<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>();
|
||||
Replace<IInstallationDeviceRepository, NoopRepos.InstallationDeviceRepository>(services);
|
||||
|
||||
// TODO: Install and use azurite in CI pipeline
|
||||
var referenceEventService = services.First(sd => sd.ServiceType == typeof(IReferenceEventService));
|
||||
services.Remove(referenceEventService);
|
||||
services.AddSingleton<IReferenceEventService, NoopReferenceEventService>();
|
||||
Replace<IReferenceEventService, NoopReferenceEventService>(services);
|
||||
|
||||
// Our Rate limiter works so well that it begins to fail tests unless we carve out
|
||||
// one whitelisted ip. We should still test the rate limiter though and they should change the Ip
|
||||
@ -245,14 +229,9 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
|
||||
services.AddSingleton<ILoggerFactory, NullLoggerFactory>();
|
||||
|
||||
// Noop StripePaymentService - this could be changed to integrate with our Stripe test account
|
||||
var stripePaymentService = services.First(sd => sd.ServiceType == typeof(IPaymentService));
|
||||
services.Remove(stripePaymentService);
|
||||
services.AddSingleton(Substitute.For<IPaymentService>());
|
||||
Replace(services, Substitute.For<IPaymentService>());
|
||||
|
||||
var organizationBillingService =
|
||||
services.First(sd => sd.ServiceType == typeof(IOrganizationBillingService));
|
||||
services.Remove(organizationBillingService);
|
||||
services.AddSingleton(Substitute.For<IOrganizationBillingService>());
|
||||
Replace(services, Substitute.For<IOrganizationBillingService>());
|
||||
});
|
||||
|
||||
foreach (var configureTestService in _configureTestServices)
|
||||
@ -261,6 +240,35 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
|
||||
}
|
||||
}
|
||||
|
||||
private static void Replace<TService, TNewImplementation>(IServiceCollection services)
|
||||
where TService : class
|
||||
where TNewImplementation : class, TService
|
||||
{
|
||||
services.RemoveAll<TService>();
|
||||
services.AddSingleton<TService, TNewImplementation>();
|
||||
}
|
||||
|
||||
private static void Replace<TService>(IServiceCollection services, TService implementation)
|
||||
where TService : class
|
||||
{
|
||||
services.RemoveAll<TService>();
|
||||
services.AddSingleton<TService>(implementation);
|
||||
}
|
||||
|
||||
public HttpClient CreateAuthedClient(string accessToken)
|
||||
{
|
||||
var handler = Server.CreateHandler((context) =>
|
||||
{
|
||||
context.Request.Headers.Authorization = $"Bearer {accessToken}";
|
||||
});
|
||||
|
||||
return new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = Server.BaseAddress,
|
||||
Timeout = TimeSpan.FromSeconds(200),
|
||||
};
|
||||
}
|
||||
|
||||
public DatabaseContext GetDatabaseContext()
|
||||
{
|
||||
var scope = Services.CreateScope();
|
||||
|
Loading…
Reference in New Issue
Block a user