236 lines
11 KiB
C#
236 lines
11 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
|
|
namespace WordVersioning.Api.IntegrationTests;
|
|
|
|
/// <summary>FR-9 — template CRUD + auto-detect SDT tags ตอน upload/replace ไฟล์ (AC-11 ส่วน backend)</summary>
|
|
[Collection(IntegrationTestCollection.Name)]
|
|
public sealed class TemplateTests(IntegrationTestFixture fixture)
|
|
{
|
|
/// <summary>สร้าง template จาก BuildTemplateDocx() — คืน (id, fields ที่ detect ได้) — ใช้ร่วมกับ test อื่น</summary>
|
|
public static async Task<(Guid TemplateId, List<TemplateFieldItem> Fields)> CreateDefaultTemplateAsync(
|
|
HttpClient client, string name)
|
|
{
|
|
using var response = await client.PostAsync(
|
|
"api/templates", ApiExtensions.TemplateForm(TestDocx.BuildTemplateDocx(), name));
|
|
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
|
var result = await response.Content.ReadFromJsonAsync<CreateTemplateResult>(IntegrationTestFixture.Json);
|
|
return (result!.Id, result.Fields);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_DetectsAllSdtTags_WithDefaults()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, fields) = await CreateDefaultTemplateAsync(client, $"สัญญา {Guid.NewGuid():N}");
|
|
|
|
// 5 tags ในไฟล์ (employee_name ซ้ำ 2 จุด → dedup เหลือ 4) ตามลำดับที่ปรากฏ
|
|
Assert.Equal(
|
|
["employee_name", "position", "salary", "start_date"],
|
|
fields.Select(f => f.Key).ToList());
|
|
|
|
// defaults: label = tag, type = text, required = false (FR-9)
|
|
Assert.All(fields, f =>
|
|
{
|
|
Assert.Equal(f.Key, f.Label);
|
|
Assert.Equal("text", f.Type);
|
|
Assert.False(f.IsRequired);
|
|
});
|
|
|
|
// GET detail คืนนิยามชุดเดิม
|
|
using var detail = await client.GetAsync($"api/templates/{templateId}");
|
|
var detailBody = await detail.Content.ReadFromJsonAsync<TemplateDetailResult>(IntegrationTestFixture.Json);
|
|
Assert.Equal(4, detailBody!.Fields.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_RejectsNonDocxFile()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var form = new MultipartFormDataContent();
|
|
form.Add(new StringContent("ไม่ใช่ template"), "name");
|
|
var file = new ByteArrayContent("plain"u8.ToArray());
|
|
file.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
|
|
form.Add(file, "file", "template.txt");
|
|
|
|
using var response = await client.PostAsync("api/templates", form);
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_RejectsMissingName()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var form = new MultipartFormDataContent();
|
|
var file = new ByteArrayContent(TestDocx.BuildPlainDocx());
|
|
file.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(TestDocx.DocxContentType);
|
|
form.Add(file, "file", "template.docx"); // ไม่ส่ง name
|
|
|
|
using var response = await client.PostAsync("api/templates", form);
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_SavesFieldDefinitions_AndMetadata()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, fields) = await CreateDefaultTemplateAsync(client, $"แก้นิยาม {Guid.NewGuid():N}");
|
|
|
|
var updated = new
|
|
{
|
|
description = "สัญญาจ้างฉบับมาตรฐาน",
|
|
fields = fields.Select((f, i) => new
|
|
{
|
|
key = f.Key,
|
|
label = $"ป้ายกำกับ {f.Key}",
|
|
type = f.Key == "salary" ? "number" : f.Key == "start_date" ? "date" : "text",
|
|
isRequired = f.Key == "employee_name",
|
|
sortOrder = fields.Count - i, // กลับลำดับ
|
|
}),
|
|
};
|
|
|
|
using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", updated, IntegrationTestFixture.Json);
|
|
Assert.Equal(HttpStatusCode.OK, put.StatusCode);
|
|
var body = await put.Content.ReadFromJsonAsync<TemplateDetailResult>(IntegrationTestFixture.Json);
|
|
|
|
Assert.Equal("สัญญาจ้างฉบับมาตรฐาน", body!.Description);
|
|
Assert.Equal("ป้ายกำกับ employee_name", body.Fields[3].Label); // กลับลำดับแล้ว employee_name ท้ายสุด
|
|
Assert.Equal("number", body.Fields.First(f => f.Key == "salary").Type);
|
|
Assert.Equal("date", body.Fields.First(f => f.Key == "start_date").Type);
|
|
Assert.True(body.Fields.First(f => f.Key == "employee_name").IsRequired);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_RejectsUnknownFieldKey()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, _) = await CreateDefaultTemplateAsync(client, $"key นอกไฟล์ {Guid.NewGuid():N}");
|
|
|
|
using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", new
|
|
{
|
|
fields = new[] { new { key = "not_in_file", label = "ไม่มีในไฟล์", type = "text", isRequired = false, sortOrder = 0 } },
|
|
}, IntegrationTestFixture.Json);
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, put.StatusCode); // เพิ่ม/ลบ key ต้องผ่าน replace ไฟล์เท่านั้น
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_RejectsInvalidFieldType()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, fields) = await CreateDefaultTemplateAsync(client, $"type ผิด {Guid.NewGuid():N}");
|
|
|
|
using var put = await client.PutAsJsonAsync($"api/templates/{templateId}", new
|
|
{
|
|
fields = fields.Select(f => new { key = f.Key, label = f.Key, type = "datetime", isRequired = false, sortOrder = f.SortOrder }),
|
|
}, IntegrationTestFixture.Json);
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, put.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReplaceFile_DetectsAddedAndRemovedTags()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, _) = await CreateDefaultTemplateAsync(client, $"replace {Guid.NewGuid():N}");
|
|
|
|
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<ReplaceFileResult>(IntegrationTestFixture.Json);
|
|
|
|
// เดิม: employee_name, position, salary, start_date → ใหม่: employee_name, note
|
|
Assert.Equal(["note"], body!.AddedTags);
|
|
Assert.Equal(
|
|
new[] { "position", "salary", "start_date" }.Order(),
|
|
body.RemovedTags.Order());
|
|
Assert.Equal(["employee_name", "note"], body.Fields.Select(f => f.Key).ToList());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Deactivate_HidesFromDefaultList_ButVisibleWithIncludeInactive()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, _) = await CreateDefaultTemplateAsync(client, $"deactivate {Guid.NewGuid():N}");
|
|
|
|
using var deactivate = await client.DeleteAsync($"api/templates/{templateId}");
|
|
Assert.Equal(HttpStatusCode.NoContent, deactivate.StatusCode);
|
|
|
|
using var activeList = await client.GetAsync("api/templates");
|
|
var active = await activeList.Content.ReadFromJsonAsync<List<TemplateListItemResult>>(IntegrationTestFixture.Json);
|
|
Assert.DoesNotContain(active!, t => t.Id == templateId);
|
|
|
|
using var allList = await client.GetAsync("api/templates?includeInactive=true");
|
|
var all = await allList.Content.ReadFromJsonAsync<List<TemplateListItemResult>>(IntegrationTestFixture.Json);
|
|
Assert.Contains(all!, t => t.Id == templateId && !t.IsActive);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Deactivate_ThenGenerate_Returns400_ButDocumentsOfTemplateStillAccessible()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
var (templateId, _) = await CreateDefaultTemplateAsync(client, $"deactivate+generate {Guid.NewGuid():N}");
|
|
|
|
// สร้างเอกสาร 1 ฉบับก่อน deactivate
|
|
var values = new Dictionary<string, string>
|
|
{
|
|
["employee_name"] = "สมชาย ใจดี",
|
|
["position"] = "โปรแกรมเมอร์",
|
|
["salary"] = "50000",
|
|
["start_date"] = "2026-08-20",
|
|
};
|
|
using var generate = await client.PostAsJsonAsync("api/documents/generate",
|
|
new { templateId, values }, IntegrationTestFixture.Json);
|
|
Assert.Equal(HttpStatusCode.Created, generate.StatusCode);
|
|
var doc = await generate.Content.ReadFromJsonAsync<GenerateResult>(IntegrationTestFixture.Json);
|
|
|
|
using var deactivate = await client.DeleteAsync($"api/templates/{templateId}");
|
|
Assert.Equal(HttpStatusCode.NoContent, deactivate.StatusCode);
|
|
|
|
// สร้างเอกสารใหม่ไม่ได้ (FR-9 policy)
|
|
using var generateAgain = await client.PostAsJsonAsync("api/documents/generate",
|
|
new { templateId, values }, IntegrationTestFixture.Json);
|
|
Assert.Equal(HttpStatusCode.BadRequest, generateAgain.StatusCode);
|
|
|
|
// แต่เอกสารเก่ายังเข้าถึงได้ครบ: list + detail + history + download (FR-9 + FR-12)
|
|
using var list = await client.GetAsync($"api/templates/{templateId}/documents");
|
|
Assert.Equal(HttpStatusCode.OK, list.StatusCode);
|
|
var documents = await list.Content.ReadFromJsonAsync<List<TemplateDocItem>>(IntegrationTestFixture.Json);
|
|
var listItem = documents!.Single();
|
|
Assert.Equal(doc!.DocumentId, listItem.DocumentId);
|
|
Assert.Equal(1, listItem.VersionCount);
|
|
|
|
using var detail = await client.GetAsync($"api/documents/{doc!.DocumentId}");
|
|
Assert.Equal(HttpStatusCode.OK, detail.StatusCode);
|
|
Assert.True((await detail.Content.ReadFromJsonAsync<DocDetail>(IntegrationTestFixture.Json))!.TemplateIsActive is false);
|
|
|
|
using var history = await client.GetAsync($"api/documents/{doc.DocumentId}/versions");
|
|
Assert.Equal(HttpStatusCode.OK, history.StatusCode);
|
|
|
|
using var download = await client.GetAsync($"api/documents/{doc.DocumentId}/versions/{doc.VersionId}/download");
|
|
Assert.Equal(HttpStatusCode.OK, download.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Get_UnknownTemplate_Returns404()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.GetAsync($"api/templates/{Guid.NewGuid()}");
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ListDocuments_UnknownTemplate_Returns404()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.GetAsync($"api/templates/{Guid.NewGuid()}/documents");
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
}
|