using System.Net; using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Tiku.Domain.Catalog; using Tiku.Domain.Common; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.IntegrationTests.Api; public sealed class ScorelineEndpointTests { [Fact] public async Task Public_scoreline_records_support_dynamic_filters() { await using var factory = new ApiTestFactory(); var tenantId = Guid.NewGuid(); var regionId = Guid.NewGuid(); var schoolId = Guid.NewGuid(); var majorId = Guid.NewGuid(); var tenantCode = tenantId.ToString("N"); await factory.SeedAsync( new Tenant { Id = tenantId, Slug = tenantCode, Name = "Test Tenant", Status = TenantStatus.Active, Metadata = JsonDefaults.Object() }, new Region { Id = regionId, TenantId = tenantId, Name = "四川" }, new School { Id = schoolId, TenantId = tenantId, RegionId = regionId, Name = "美术学院" }, new Major { Id = majorId, TenantId = tenantId, RegionId = regionId, SchoolId = schoolId, Name = "视觉传达" }, new ScorelineField { TenantId = tenantId, RegionId = regionId, FieldKey = "cultureScore", FieldName = "文化分", FieldType = "number", IsFilter = true, IsTrend = true }, new ScorelineRecord { TenantId = tenantId, RegionId = regionId, SchoolId = schoolId, MajorId = majorId, Year = 2026, SchoolName = "美术学院", MajorName = "视觉传达", FieldValues = JsonSerializer.SerializeToElement(new { cultureScore = 420, scoreType = "统考" }) }, new ScorelineRecord { TenantId = tenantId, RegionId = regionId, SchoolId = schoolId, MajorId = majorId, Year = 2025, SchoolName = "美术学院", MajorName = "视觉传达", FieldValues = JsonSerializer.SerializeToElement(new { cultureScore = 360, scoreType = "统考" }) }); using var client = factory.CreateClient(); using var response = await client.GetAsync( $"/api/scoreline/records?tenantCode={tenantCode}®ionId={regionId}&min.cultureScore=400"); var json = await ReadJsonAsync(response); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(1, json.RootElement.GetProperty("total").GetInt32()); var item = json.RootElement.GetProperty("items").EnumerateArray().Single(); Assert.Equal(2026, item.GetProperty("year").GetInt32()); Assert.Equal(420, item.GetProperty("fieldValues").GetProperty("cultureScore").GetInt32()); using var firstCursorResponse = await client.GetAsync( $"/api/scoreline/records/cursor?tenantCode={tenantCode}®ionId={regionId}&pageSize=1"); var firstCursorJson = await ReadJsonAsync(firstCursorResponse); Assert.Equal(HttpStatusCode.OK, firstCursorResponse.StatusCode); Assert.True(firstCursorJson.RootElement.GetProperty("hasMore").GetBoolean()); var firstCursorItem = firstCursorJson.RootElement.GetProperty("items").EnumerateArray().Single(); var firstCursorId = firstCursorItem.GetProperty("id").GetGuid(); var nextCursor = firstCursorJson.RootElement.GetProperty("nextCursor").GetString(); Assert.False(string.IsNullOrWhiteSpace(nextCursor)); using var secondCursorResponse = await client.GetAsync( $"/api/scoreline/records/cursor?tenantCode={tenantCode}®ionId={regionId}&pageSize=1&cursor={Uri.EscapeDataString(nextCursor!)}"); var secondCursorJson = await ReadJsonAsync(secondCursorResponse); Assert.Equal(HttpStatusCode.OK, secondCursorResponse.StatusCode); Assert.False(secondCursorJson.RootElement.GetProperty("hasMore").GetBoolean()); var secondCursorItem = secondCursorJson.RootElement.GetProperty("items").EnumerateArray().Single(); Assert.NotEqual(firstCursorId, secondCursorItem.GetProperty("id").GetGuid()); Assert.Equal(2025, secondCursorItem.GetProperty("year").GetInt32()); using var invalidCursorResponse = await client.GetAsync( $"/api/scoreline/records/cursor?tenantCode={tenantCode}®ionId={regionId}&cursor=not-a-cursor"); var invalidCursorJson = await ReadJsonAsync(invalidCursorResponse); Assert.Equal(HttpStatusCode.BadRequest, invalidCursorResponse.StatusCode); Assert.Equal("scoreline_cursor_invalid", invalidCursorJson.RootElement.GetProperty("code").GetString()); } [Fact] public async Task Public_scoreline_without_tenant_returns_not_found() { await using var factory = new ApiTestFactory(); using var client = factory.CreateClient(); using var response = await client.GetAsync("/api/scoreline/fields"); var json = await ReadJsonAsync(response); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); Assert.Equal("tenant_not_found", json.RootElement.GetProperty("code").GetString()); } private static async Task ReadJsonAsync(HttpResponseMessage response) { var stream = await response.Content.ReadAsStreamAsync(); return await JsonDocument.ParseAsync(stream); } }