forked from xiongyuxing/tiku-backend.net
226 lines
8.0 KiB
C#
226 lines
8.0 KiB
C#
using System.Net;
|
|
using System.Text.Json;
|
|
using Tiku.Domain.Common;
|
|
using Tiku.Domain.Catalog;
|
|
using Tiku.Domain.Content;
|
|
using Tiku.Domain.QuestionBanks;
|
|
using Tiku.Domain.Tenancy;
|
|
|
|
namespace Tiku.IntegrationTests.Api;
|
|
|
|
public sealed class QuestionBankEndpointTests
|
|
{
|
|
[Fact]
|
|
public async Task Question_banks_return_active_tenant_banks_only()
|
|
{
|
|
var tenantId = Guid.NewGuid();
|
|
var otherTenantId = Guid.NewGuid();
|
|
await using var factory = new ApiTestFactory();
|
|
await factory.SeedAsync(
|
|
PlatformTenant(),
|
|
Tenant(tenantId, "master"),
|
|
Tenant(otherTenantId, "other"),
|
|
new QuestionBank
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
Name = "A 题库",
|
|
Status = QuestionBankStatus.Active
|
|
},
|
|
new QuestionBank
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
Name = "归档题库",
|
|
Status = QuestionBankStatus.Archived
|
|
},
|
|
new QuestionBank
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = otherTenantId,
|
|
Name = "其他租户题库",
|
|
Status = QuestionBankStatus.Active
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.GetAsync("/api/catalog/question-banks?tenantCode=master");
|
|
var items = await ReadItemsAsync(response);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.Equal("A 题库", Assert.Single(items).GetProperty("name").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Questions_support_filters_and_current_version_projection()
|
|
{
|
|
var tenantId = Guid.NewGuid();
|
|
var bankId = Guid.NewGuid();
|
|
var subjectId = Guid.NewGuid();
|
|
var categoryId = Guid.NewGuid();
|
|
var collectionId = Guid.NewGuid();
|
|
var includedQuestionId = Guid.NewGuid();
|
|
var excludedQuestionId = Guid.NewGuid();
|
|
var versionId = Guid.NewGuid();
|
|
await using var factory = new ApiTestFactory();
|
|
await factory.SeedAsync(
|
|
PlatformTenant(),
|
|
Tenant(tenantId, "master"),
|
|
new Subject { Id = subjectId, TenantId = tenantId, Name = "测试科目" },
|
|
new Category { Id = categoryId, TenantId = tenantId, SubjectId = subjectId, Name = "测试分类" },
|
|
new QuestionBank { Id = bankId, TenantId = tenantId, Name = "题库" },
|
|
new QuestionCollection { Id = collectionId, TenantId = tenantId, Name = "题集", Status = ContentStatus.Active },
|
|
new Question
|
|
{
|
|
Id = excludedQuestionId,
|
|
TenantId = tenantId,
|
|
QuestionBankId = bankId,
|
|
SubjectId = subjectId,
|
|
Type = "choice",
|
|
Status = QuestionStatus.Archived
|
|
});
|
|
await factory.SeedQuestionWithVersionAsync(
|
|
new Question
|
|
{
|
|
Id = includedQuestionId,
|
|
TenantId = tenantId,
|
|
QuestionBankId = bankId,
|
|
SubjectId = subjectId,
|
|
CategoryId = categoryId,
|
|
Type = "choice",
|
|
TypeLabel = "单选题",
|
|
Difficulty = 2,
|
|
Status = QuestionStatus.Published
|
|
},
|
|
new QuestionVersion
|
|
{
|
|
Id = versionId,
|
|
TenantId = tenantId,
|
|
QuestionId = includedQuestionId,
|
|
VersionNo = 2,
|
|
Content = "题干关键词"
|
|
});
|
|
var questionReference = await factory.SeedQuestionReferenceAsync(
|
|
tenantId,
|
|
tenantId,
|
|
includedQuestionId);
|
|
await factory.SeedAsync(new QuestionCollectionItem
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
CollectionId = collectionId,
|
|
QuestionReferenceId = questionReference.Id,
|
|
QuestionOwnerTenantId = tenantId,
|
|
QuestionId = includedQuestionId
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.GetAsync(
|
|
$"/api/catalog/questions?tenantCode=master&questionBankId={bankId}&subjectId={subjectId}&categoryId={categoryId}&collectionId={collectionId}&type=choice&keyword=关键词");
|
|
var items = await ReadItemsAsync(response);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var item = Assert.Single(items);
|
|
Assert.Equal(includedQuestionId, item.GetProperty("id").GetGuid());
|
|
Assert.Equal(versionId, item.GetProperty("versionId").GetGuid());
|
|
Assert.Equal("题干关键词", item.GetProperty("content").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Question_detail_returns_not_found_for_archived_question()
|
|
{
|
|
var tenantId = Guid.NewGuid();
|
|
var questionId = Guid.NewGuid();
|
|
await using var factory = new ApiTestFactory();
|
|
await factory.SeedAsync(
|
|
PlatformTenant(),
|
|
Tenant(tenantId, "master"),
|
|
new Question
|
|
{
|
|
Id = questionId,
|
|
TenantId = tenantId,
|
|
Type = "choice",
|
|
Status = QuestionStatus.Archived
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.GetAsync($"/api/catalog/questions/{questionId}?tenantCode=master");
|
|
var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
|
|
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
Assert.Equal("question_not_found", body.RootElement.GetProperty("code").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Question_versions_are_returned_newest_first()
|
|
{
|
|
var tenantId = Guid.NewGuid();
|
|
var questionId = Guid.NewGuid();
|
|
await using var factory = new ApiTestFactory();
|
|
await factory.SeedAsync(
|
|
PlatformTenant(),
|
|
Tenant(tenantId, "master"),
|
|
new Question
|
|
{
|
|
Id = questionId,
|
|
TenantId = tenantId,
|
|
Type = "choice",
|
|
Status = QuestionStatus.Published
|
|
},
|
|
new QuestionVersion
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
QuestionId = questionId,
|
|
VersionNo = 1,
|
|
Content = "旧版本"
|
|
},
|
|
new QuestionVersion
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
TenantId = tenantId,
|
|
QuestionId = questionId,
|
|
VersionNo = 2,
|
|
Content = "新版本"
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.GetAsync($"/api/catalog/questions/{questionId}/versions?tenantCode=master");
|
|
var items = await ReadItemsAsync(response);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.Equal(["新版本", "旧版本"], items.Select(item => item.GetProperty("content").GetString()!).ToArray());
|
|
}
|
|
|
|
private static Tenant Tenant(Guid id, string slug)
|
|
{
|
|
return new Tenant
|
|
{
|
|
Id = id,
|
|
Slug = slug,
|
|
Name = slug,
|
|
Status = TenantStatus.Active,
|
|
Metadata = JsonDefaults.Object()
|
|
};
|
|
}
|
|
|
|
private static Tenant PlatformTenant() => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Slug = $"platform-{Guid.NewGuid():N}",
|
|
Name = "Platform Question Bank",
|
|
Status = TenantStatus.Active,
|
|
Mode = TenantMode.PlatformOwned,
|
|
Metadata = JsonDefaults.Object()
|
|
};
|
|
|
|
private static async Task<JsonElement[]> ReadItemsAsync(HttpResponseMessage response)
|
|
{
|
|
var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
|
|
return body.RootElement
|
|
.GetProperty("items")
|
|
.EnumerateArray()
|
|
.Select(item => item.Clone())
|
|
.ToArray();
|
|
}
|
|
}
|