using System.Net; using System.Net.Http.Json; using Microsoft.EntityFrameworkCore; namespace WordVersioning.Api.IntegrationTests; /// FR-14 — editor ต่อ field: เลือกได้ตาม type, ไม่ส่ง = default ของ type, /// ค่าเท่ากับ default normalize เป็น null ใน DB, ค่าไม่รู้จัก/ไม่เข้ากับ type → 400 (AC-17) [Collection(IntegrationTestCollection.Name)] public sealed class FieldEditorTests(IntegrationTestFixture fixture) { private async Task<(Guid TemplateId, List Fields)> CreateTemplateAsync(HttpClient client) => await TemplateTests.CreateDefaultTemplateAsync(client, $"editor {Guid.NewGuid():N}"); [Fact] public async Task Update_TextFieldEditorTextarea_PersistsAndEchoed() { using var client = fixture.CreateClient(); var (templateId, fields) = await CreateTemplateAsync(client); var updated = new { fields = fields.Select(f => new { key = f.Key, label = f.Key, type = "text", editor = f.Key == "employee_name" ? "textarea" : null as string, isRequired = false, sortOrder = f.SortOrder, }), }; using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json); Assert.Equal(HttpStatusCode.OK, put.StatusCode); var body = await put.Content.ReadFromJsonAsync(IntegrationTestFixture.Json); Assert.Equal("textarea", body!.Fields.Single(f => f.Key == "employee_name").Editor); await using var db = fixture.CreateDb(); var row = await db.TemplateFields.AsNoTracking().SingleAsync(f => f.TemplateId == templateId && f.Key == "employee_name"); Assert.Equal("textarea", row.Editor); } /// ไม่ส่ง editor → response ได้ default ของ type แต่ DB เก็บ null ทั้งหมด (null = default) [Fact] public async Task Update_OmittedEditor_ResolvesDefaultPerType_StoresNull() { using var client = fixture.CreateClient(); var (templateId, fields) = await CreateTemplateAsync(client); var updated = new { fields = fields.Select(f => new { key = f.Key, label = f.Key, type = f.Key == "salary" ? "number" : f.Key == "start_date" ? "date" : "text", isRequired = false, sortOrder = f.SortOrder, }), }; using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json); Assert.Equal(HttpStatusCode.OK, put.StatusCode); var body = await put.Content.ReadFromJsonAsync(IntegrationTestFixture.Json); Assert.Equal("textbox", body!.Fields.Single(f => f.Key == "employee_name").Editor); Assert.Equal("number", body.Fields.Single(f => f.Key == "salary").Editor); Assert.Equal("date", body.Fields.Single(f => f.Key == "start_date").Editor); await using var db = fixture.CreateDb(); var rows = await db.TemplateFields.AsNoTracking().Where(f => f.TemplateId == templateId).ToListAsync(); Assert.All(rows, r => Assert.Null(r.Editor)); // DB ไม่เก็บค่า default ซ้ำซ้อน } /// ส่ง editor ตรงกับ default ("textbox" ของ text) → response echo ค่าเดิมแต่ DB normalize เป็น null [Fact] public async Task Update_EditorEqualToDefault_NormalizedToNull() { using var client = fixture.CreateClient(); var (templateId, fields) = await CreateTemplateAsync(client); var updated = new { fields = fields.Select(f => new { key = f.Key, label = f.Key, type = "text", editor = "textbox", isRequired = false, sortOrder = f.SortOrder, }), }; using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json); Assert.Equal(HttpStatusCode.OK, put.StatusCode); var body = await put.Content.ReadFromJsonAsync(IntegrationTestFixture.Json); Assert.All(body!.Fields, f => Assert.Equal("textbox", f.Editor)); await using var db = fixture.CreateDb(); var rows = await db.TemplateFields.AsNoTracking().Where(f => f.TemplateId == templateId).ToListAsync(); Assert.All(rows, r => Assert.Null(r.Editor)); } [Fact] public async Task Update_UnknownEditor_Returns400() { using var client = fixture.CreateClient(); var (templateId, fields) = await CreateTemplateAsync(client); var updated = new { fields = fields.Select(f => new { key = f.Key, label = f.Key, type = "text", editor = f.Key == "employee_name" ? "dropdown" : null as string, isRequired = false, sortOrder = f.SortOrder, }), }; using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json); Assert.Equal(HttpStatusCode.BadRequest, put.StatusCode); } /// editor ถูก allow-list ทั่วไปแต่ไม่เข้ากับ type ของ field นั้น → 400 [Theory] [InlineData("number", "textarea")] [InlineData("text", "date")] [InlineData("date", "textbox")] public async Task Update_EditorIncompatibleWithType_Returns400(string type, string editor) { using var client = fixture.CreateClient(); var (templateId, fields) = await CreateTemplateAsync(client); var updated = new { fields = fields.Select(f => new { key = f.Key, label = f.Key, type = f.Key == "employee_name" ? type : "text", editor = f.Key == "employee_name" ? editor : null as string, isRequired = false, sortOrder = f.SortOrder, }), }; using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json); Assert.Equal(HttpStatusCode.BadRequest, put.StatusCode); } /// tag ที่เพิ่มจากการ replace ไฟล์ได้ editor default (textbox) โดย DB เก็บ null [Fact] public async Task ReplaceFile_AddedTagGetsDefaultEditor() { using var client = fixture.CreateClient(); var (templateId, _) = await CreateTemplateAsync(client); using var put = await client.PutAsync( $"api/templates/{templateId}/file", new MultipartFormDataContent { { new ByteArrayContent(TestDocx.BuildReplacementTemplateDocx()) { Headers = { ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(TestDocx.DocxContentType) } }, "file", "replaced.docx" }, }); Assert.Equal(HttpStatusCode.OK, put.StatusCode); var body = await put.Content.ReadFromJsonAsync(IntegrationTestFixture.Json); var note = body!.Fields.Single(f => f.Key == "note"); Assert.Equal("note", note.Key); // added tag Assert.Equal("textbox", note.Editor); await using var db = fixture.CreateDb(); var row = await db.TemplateFields.AsNoTracking().SingleAsync(f => f.TemplateId == templateId && f.Key == "note"); Assert.Null(row.Editor); } }