Files
tiku-backend.net/Tiku.IntegrationTests/Api/VideoEndpointTests.cs

162 lines
6.1 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Api.Contracts;
using Tiku.Application.Assets;
using Tiku.Application.Catalog;
using Tiku.Domain.Common;
using Tiku.Domain.Content;
using Tiku.Domain.Identity;
using Tiku.Domain.QuestionBanks;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Auth;
using Tiku.Infrastructure.Persistence;
namespace Tiku.IntegrationTests.Api;
public sealed class VideoEndpointTests
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
{
Converters = { new JsonStringEnumConverter() }
};
[Fact]
public async Task Student_can_search_play_report_progress_and_query_question_videos()
{
await using var factory = new ApiTestFactory();
var seed = await SeedStudentAsync(factory);
var questionId = Guid.NewGuid();
var videoId = Guid.NewGuid();
await factory.SeedAsync(
new Question { Id = questionId, TenantId = seed.TenantId, Type = "choice", Status = QuestionStatus.Published },
new VideoExplanation
{
Id = videoId,
TenantId = seed.TenantId,
Title = "透视解析",
Description = "素描透视",
VideoUrl = "https://cdn.example.test/video.mp4",
DurationSeconds = 120,
IsActive = true
},
new QuestionVideo
{
TenantId = seed.TenantId,
QuestionId = questionId,
VideoId = videoId
});
using var client = factory.CreateClient();
await LoginAsync(client, seed);
var search = await client.GetFromJsonAsync<CatalogList<VideoExplanationCatalogItem>>("/api/videos/search?keyword=透视", JsonOptions);
var questionVideos = await client.GetFromJsonAsync<CatalogList<QuestionVideoCatalogItem>>($"/api/questions/videos?questionId={questionId}", JsonOptions);
var batch = await (await client.PostAsJsonAsync(
"/api/questions/videos/batch",
new QuestionVideoQueryDto { QuestionIds = [questionId] }))
.Content
.ReadFromJsonAsync<CatalogList<QuestionVideoCatalogItem>>(JsonOptions);
var playResponse = await client.PostAsJsonAsync(
"/api/videos/play",
new VideoPlayDto { VideoId = videoId, QuestionId = questionId });
var play = await playResponse.Content.ReadFromJsonAsync<VideoPlaybackItem>(JsonOptions);
var progressResponse = await client.PostAsJsonAsync(
"/api/videos/progress",
new VideoProgressDto
{
VideoId = videoId,
QuestionId = questionId,
PositionSeconds = 120,
DurationSeconds = 120,
WatchedSeconds = 120
});
var progress = await progressResponse.Content.ReadFromJsonAsync<VideoProgressItem>(JsonOptions);
Assert.Single(search!.Items);
Assert.Single(questionVideos!.Items);
Assert.Single(batch!.Items);
Assert.Equal(HttpStatusCode.OK, playResponse.StatusCode);
Assert.Equal("https://cdn.example.test/video.mp4", play!.PlayUrl);
Assert.Equal(HttpStatusCode.OK, progressResponse.StatusCode);
Assert.True(progress!.IsCompleted);
using var scope = factory.CreateSystemScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
Assert.Single(dbContext.VideoPlaybackProgress.Where(item =>
item.TenantId == seed.TenantId &&
item.UserId == seed.UserId &&
item.VideoId == videoId &&
item.QuestionId == questionId));
Assert.Single(dbContext.ContentAssetAccessEvents.Where(item =>
item.TenantId == seed.TenantId &&
item.UserId == seed.UserId &&
item.AssetType == "Video"));
}
[Fact]
public async Task Student_cannot_play_another_tenant_video()
{
await using var factory = new ApiTestFactory();
var tenantA = await SeedStudentAsync(factory, "13900001001");
var tenantB = await SeedStudentAsync(factory, "13900001002");
var videoId = Guid.NewGuid();
await factory.SeedAsync(new VideoExplanation
{
Id = videoId,
TenantId = tenantA.TenantId,
Title = "A tenant video",
VideoUrl = "https://cdn.example.test/a.mp4",
IsActive = true
});
using var client = factory.CreateClient();
await LoginAsync(client, tenantB);
var response = await client.PostAsJsonAsync(
"/api/videos/play",
new VideoPlayDto { VideoId = videoId });
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedStudentAsync(
ApiTestFactory factory,
string? phone = null)
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var resolvedPhone = phone ?? $"135{Random.Shared.Next(10000000, 99999999)}";
await factory.SeedAsync(
new Tenant
{
Id = tenantId,
Slug = tenantId.ToString("N"),
Name = "Video Tenant",
Status = TenantStatus.Active,
Metadata = JsonDefaults.Object()
},
new User
{
Id = userId,
Phone = resolvedPhone,
Name = "Video Student"
}.WithTestPassword(),
new TenantMembership
{
TenantId = tenantId,
UserId = userId,
Role = TenantRole.Student,
Status = MembershipStatus.Active
});
return (tenantId, userId, resolvedPhone);
}
private static async Task LoginAsync(
HttpClient client,
(Guid TenantId, Guid UserId, string Phone) seed)
{
client.UseAccessToken(await client.LoginAsTenantAsync(seed.TenantId, seed.Phone));
}
}