Exploration des activités
This commit is contained in:
parent
da994435cb
commit
3c51e66e50
20 changed files with 743 additions and 61 deletions
161
src/Yavsc.Api.Test/ActivityApiControllerTests.cs
Normal file
161
src/Yavsc.Api.Test/ActivityApiControllerTests.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yavsc.Abstract.Workflow;
|
||||
using Yavsc.Api.Test.Fixtures;
|
||||
using Yavsc.Tests.Shared;
|
||||
|
||||
namespace Yavsc.Api.Test;
|
||||
|
||||
[Collection("Yavsc Api")]
|
||||
public sealed class ActivityApiControllerTests : IClassFixture<ApiWebServerFixture>
|
||||
{
|
||||
private readonly ApiWebServerFixture _fixture;
|
||||
|
||||
public ActivityApiControllerTests(ApiWebServerFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
private HttpClient NewClient(string subject = "alice", string scope = "api")
|
||||
{
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
|
||||
};
|
||||
var http = new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = new Uri(_fixture.BaseAddress)
|
||||
};
|
||||
http.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", TestTokenIssuer.Issue(subject, scope));
|
||||
return http;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetUsers_returns_declared_user_for_exact_activity_code()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using var http = NewClient();
|
||||
|
||||
var response = await http.GetAsync("/api/v1/activity/dev/users", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(payload);
|
||||
Assert.Single(payload!);
|
||||
Assert.Equal("alice", payload[0].PerformerId);
|
||||
Assert.Equal("alice", payload[0].UserName);
|
||||
Assert.True(payload[0].HasPerformerProfile);
|
||||
Assert.True(payload[0].Active);
|
||||
Assert.Equal("dev", payload[0].ActivityCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetUsers_returns_user_even_when_performer_inactive()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using (var scope = _fixture.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<Yavsc.Models.ApplicationDbContext>();
|
||||
var performer = db.Performers.Single(p => p.PerformerId == "alice");
|
||||
performer.Active = false;
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
using var http = NewClient();
|
||||
var response = await http.GetAsync("/api/v1/activity/dev/users", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(payload);
|
||||
Assert.Single(payload!);
|
||||
Assert.Equal("alice", payload[0].PerformerId);
|
||||
Assert.False(payload[0].Active);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Catalog_and_Performers_are_consistent_for_dev_activity()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using var http = NewClient();
|
||||
|
||||
var catalogResponse = await http.GetAsync("/api/v1/activity/catalog", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, catalogResponse.StatusCode);
|
||||
|
||||
var catalog = await catalogResponse.Content.ReadFromJsonAsync<List<ActivityBrowseItemDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(catalog);
|
||||
|
||||
var dev = catalog!.Single(a => a.Code == "dev");
|
||||
Assert.True(dev.PerformerCount > 0);
|
||||
|
||||
var performersResponse = await http.GetAsync("/api/v1/activity/dev/users", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, performersResponse.StatusCode);
|
||||
|
||||
var performers = await performersResponse.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(performers);
|
||||
Assert.Equal(dev.PerformerCount, performers!.Count);
|
||||
Assert.Contains(performers, p => p.PerformerId == "alice");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Catalog_does_not_list_activity_without_declaration()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using var http = NewClient();
|
||||
|
||||
var catalogResponse = await http.GetAsync("/api/v1/activity/catalog", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, catalogResponse.StatusCode);
|
||||
|
||||
var catalog = await catalogResponse.Content.ReadFromJsonAsync<List<ActivityBrowseItemDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(catalog);
|
||||
Assert.DoesNotContain(catalog!, a => a.Code == "ghost");
|
||||
Assert.Contains(catalog!, a => a.Code == "dev");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Catalog_lists_declared_activity_even_when_performer_inactive()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using var http = NewClient();
|
||||
|
||||
var catalogResponse = await http.GetAsync("/api/v1/activity/catalog", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, catalogResponse.StatusCode);
|
||||
|
||||
var catalog = await catalogResponse.Content.ReadFromJsonAsync<List<ActivityBrowseItemDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(catalog);
|
||||
Assert.Contains(catalog!, a => a.Code == "declared-only");
|
||||
|
||||
var response = await http.GetAsync("/api/v1/activity/declared-only/users", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var payload = await response.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(payload);
|
||||
Assert.Single(payload!);
|
||||
Assert.Equal("bob", payload[0].PerformerId);
|
||||
Assert.Equal("bob", payload[0].UserName);
|
||||
Assert.True(payload[0].HasPerformerProfile);
|
||||
Assert.False(payload[0].Active);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Performers_alias_returns_same_payload_as_users_endpoint()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
using var http = NewClient();
|
||||
|
||||
var usersResponse = await http.GetAsync("/api/v1/activity/dev/users", TestContext.Current.CancellationToken);
|
||||
var performersResponse = await http.GetAsync("/api/v1/activity/dev/performers", TestContext.Current.CancellationToken);
|
||||
Assert.Equal(HttpStatusCode.OK, usersResponse.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, performersResponse.StatusCode);
|
||||
|
||||
var usersPayload = await usersResponse.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
var performersPayload = await performersResponse.Content.ReadFromJsonAsync<List<ActivityPerformerDto>>(TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(usersPayload);
|
||||
Assert.NotNull(performersPayload);
|
||||
Assert.Equal(usersPayload!.Count, performersPayload!.Count);
|
||||
Assert.Equal(usersPayload[0].PerformerId, performersPayload[0].PerformerId);
|
||||
Assert.Equal(usersPayload[0].UserName, performersPayload[0].UserName);
|
||||
}
|
||||
}
|
||||
6
src/Yavsc.Api.Test/ApiCollection.cs
Normal file
6
src/Yavsc.Api.Test/ApiCollection.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Yavsc.Api.Test;
|
||||
|
||||
[CollectionDefinition("Yavsc Api")]
|
||||
public sealed class ApiCollection
|
||||
{
|
||||
}
|
||||
7
src/Yavsc.Api.Test/Directory.Packages.props
Normal file
7
src/Yavsc.Api.Test/Directory.Packages.props
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<!--
|
||||
Yavsc.Api.Test has no project-specific package versions. All
|
||||
package versions are declared at the repository root.
|
||||
-->
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Packages.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</Project>
|
||||
210
src/Yavsc.Api.Test/Fixtures/ApiWebServerFixture.cs
Normal file
210
src/Yavsc.Api.Test/Fixtures/ApiWebServerFixture.cs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Yavsc.Controllers;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Relationship;
|
||||
using Yavsc.Models.Workflow;
|
||||
using Yavsc.Tests.Shared;
|
||||
|
||||
namespace Yavsc.Api.Test.Fixtures;
|
||||
|
||||
public sealed class ApiWebServerFixture : WebHostFixture
|
||||
{
|
||||
protected override int HttpsPort => 5104;
|
||||
|
||||
private static SqliteConnection? _sharedSqliteConnection;
|
||||
private static readonly object _sqliteLock = new();
|
||||
|
||||
protected override WebApplication BuildApp(WebApplicationBuilder builder)
|
||||
{
|
||||
SqliteConnection sharedConnection;
|
||||
lock (_sqliteLock)
|
||||
{
|
||||
if (_sharedSqliteConnection is null)
|
||||
{
|
||||
_sharedSqliteConnection = new SqliteConnection(
|
||||
"Data Source=YavscApiTests;Mode=Memory;Cache=Shared");
|
||||
_sharedSqliteConnection.Open();
|
||||
}
|
||||
sharedConnection = _sharedSqliteConnection;
|
||||
}
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(opt =>
|
||||
opt.UseSqlite(sharedConnection));
|
||||
|
||||
builder.Services.AddControllers()
|
||||
.AddApplicationPart(typeof(ActivityApiController).Assembly);
|
||||
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
builder.Services.AddAuthentication("Bearer")
|
||||
.AddJwtBearer("Bearer", options =>
|
||||
{
|
||||
options.IncludeErrorDetails = true;
|
||||
options.MapInboundClaims = false;
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = TestTokenIssuer.Issuer,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = TestTokenIssuer.SigningKey,
|
||||
NameClaimType = "sub",
|
||||
RoleClaimType = Yavsc.Constants.RoleClaimType,
|
||||
};
|
||||
});
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
protected override async Task<WebApplication> ConfigurePipelineAsync(WebApplication app)
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
db.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
return app;
|
||||
}
|
||||
|
||||
public string BaseAddress => Addresses.First(a => a.StartsWith("https://", StringComparison.Ordinal));
|
||||
|
||||
public void ResetAndSeedActivityGraph()
|
||||
{
|
||||
using var scope = Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
Id = "alice",
|
||||
UserName = "alice",
|
||||
Email = "alice@example.test",
|
||||
EmailConfirmed = true,
|
||||
FullName = "Alice",
|
||||
};
|
||||
db.Users.Add(user);
|
||||
|
||||
var location = new Location
|
||||
{
|
||||
Address = "1 rue du Test",
|
||||
Latitude = 48.8566,
|
||||
Longitude = 2.3522,
|
||||
};
|
||||
db.Add(location);
|
||||
db.SaveChanges();
|
||||
|
||||
var activity = new Activity
|
||||
{
|
||||
Code = "dev",
|
||||
Name = "Dev",
|
||||
Hidden = false,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow,
|
||||
};
|
||||
db.Activities.Add(activity);
|
||||
|
||||
db.Activities.Add(new Activity
|
||||
{
|
||||
Code = "ghost",
|
||||
Name = "Ghost",
|
||||
Hidden = false,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow,
|
||||
});
|
||||
|
||||
var performer = new PerformerProfile
|
||||
{
|
||||
PerformerId = "alice",
|
||||
SIREN = "123456789",
|
||||
OrganizationAddressId = location.Id,
|
||||
AcceptNotifications = true,
|
||||
AcceptPublicContact = true,
|
||||
Active = true,
|
||||
Rate = 5,
|
||||
WebSite = "https://alice.dev",
|
||||
};
|
||||
db.Performers.Add(performer);
|
||||
|
||||
db.UserActivities.Add(new UserActivity
|
||||
{
|
||||
UserId = "alice",
|
||||
DoesCode = "dev",
|
||||
Weight = 100,
|
||||
});
|
||||
|
||||
db.Users.Add(new ApplicationUser
|
||||
{
|
||||
Id = "bob",
|
||||
UserName = "bob",
|
||||
Email = "bob@example.test",
|
||||
EmailConfirmed = true,
|
||||
FullName = "Bob",
|
||||
});
|
||||
|
||||
db.Activities.Add(new Activity
|
||||
{
|
||||
Code = "declared-only",
|
||||
Name = "Declared Only",
|
||||
Hidden = false,
|
||||
DateCreated = DateTime.UtcNow,
|
||||
DateModified = DateTime.UtcNow,
|
||||
});
|
||||
|
||||
db.Performers.Add(new PerformerProfile
|
||||
{
|
||||
PerformerId = "bob",
|
||||
SIREN = "987654321",
|
||||
OrganizationAddressId = location.Id,
|
||||
AcceptNotifications = false,
|
||||
AcceptPublicContact = false,
|
||||
Active = false,
|
||||
Rate = 0,
|
||||
WebSite = "",
|
||||
});
|
||||
|
||||
db.UserActivities.Add(new UserActivity
|
||||
{
|
||||
UserId = "bob",
|
||||
DoesCode = "declared-only",
|
||||
Weight = 10,
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
base.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_sqliteLock)
|
||||
{
|
||||
if (_sharedSqliteConnection is not null)
|
||||
{
|
||||
_sharedSqliteConnection.Close();
|
||||
_sharedSqliteConnection.Dispose();
|
||||
_sharedSqliteConnection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/Yavsc.Api.Test/HomeControllerTests.cs
Normal file
69
src/Yavsc.Api.Test/HomeControllerTests.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yavsc.Api.Test.Fixtures;
|
||||
using Yavsc.Controllers;
|
||||
using Yavsc.Models.Workflow;
|
||||
|
||||
namespace Yavsc.Api.Test;
|
||||
|
||||
[Collection("Yavsc Api")]
|
||||
public sealed class HomeControllerTests : IClassFixture<ApiWebServerFixture>
|
||||
{
|
||||
private readonly ApiWebServerFixture _fixture;
|
||||
|
||||
public HomeControllerTests(ApiWebServerFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Index_does_not_list_activity_without_declaration()
|
||||
{
|
||||
_fixture.ResetAndSeedActivityGraph();
|
||||
|
||||
using var scope = _fixture.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<Yavsc.Models.ApplicationDbContext>();
|
||||
|
||||
var controller = new HomeController(
|
||||
NullLogger<HomeController>.Instance,
|
||||
localizer: null!,
|
||||
context: db,
|
||||
settingsOptions: Options.Create(new SiteSettings()),
|
||||
env: new TestEnvironment());
|
||||
|
||||
controller.ControllerContext = new ControllerContext
|
||||
{
|
||||
HttpContext = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("sub", "alice"),
|
||||
new Claim(ClaimTypes.NameIdentifier, "alice")
|
||||
}, "Bearer"))
|
||||
}
|
||||
};
|
||||
|
||||
var result = await controller.Index(id: null);
|
||||
var view = Assert.IsType<ViewResult>(result);
|
||||
var model = Assert.IsAssignableFrom<IEnumerable<Activity>>(view.Model);
|
||||
|
||||
Assert.Contains(model, a => a.Code == "dev");
|
||||
Assert.DoesNotContain(model, a => a.Code == "ghost");
|
||||
}
|
||||
|
||||
private sealed class TestEnvironment : IWebHostEnvironment
|
||||
{
|
||||
public string ApplicationName { get; set; } = "Yavsc.Api.Test";
|
||||
public IFileProvider WebRootFileProvider { get; set; } = null!;
|
||||
public string WebRootPath { get; set; } = string.Empty;
|
||||
public string EnvironmentName { get; set; } = "Development";
|
||||
public string ContentRootPath { get; set; } = string.Empty;
|
||||
public IFileProvider ContentRootFileProvider { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
33
src/Yavsc.Api.Test/Yavsc.Api.Test.csproj
Normal file
33
src/Yavsc.Api.Test/Yavsc.Api.Test.csproj
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Yavsc.Api.Test</RootNamespace>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<PackageReference Include="xunit.v3" />
|
||||
<PackageReference Include="xunit.v3.common" />
|
||||
<PackageReference Include="xunit.v3.extensibility.core" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
||||
<ProjectReference Include="..\Yavsc.Server\Yavsc.Server.csproj" />
|
||||
<ProjectReference Include="..\Yavsc.Api\Yavsc.Api.csproj" />
|
||||
<ProjectReference Include="..\Yavsc.Org\Yavsc.Org.csproj" />
|
||||
<ProjectReference Include="..\Yavsc.Tests.Shared\Yavsc.Tests.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Loading…
Reference in a new issue