171 lines
8.4 KiB
C#
171 lines
8.4 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Security.Cryptography;
|
|
using System.Text.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace WordVersioning.Api.IntegrationTests;
|
|
|
|
/// <summary>FR-5 (upload), FR-6 (history), FR-7 (download) — ผ่าน HTTP จริง + ตรวจสถานะใน Postgres ตรงๆ</summary>
|
|
[Collection(IntegrationTestCollection.Name)]
|
|
public sealed class UploadVersionTests(IntegrationTestFixture fixture)
|
|
{
|
|
[Fact]
|
|
public async Task FirstUpload_CreatesDocumentAndVersionRow_FromJwtClaims()
|
|
{
|
|
var documentId = Guid.NewGuid();
|
|
var clientVersionId = Guid.NewGuid();
|
|
var bytes = TestDocx.BuildPlainDocx("version 1 content");
|
|
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.PostAsync(
|
|
$"api/documents/{documentId}/versions",
|
|
ApiExtensions.VersionForm(bytes, "เอกสารทดสอบ.docx", clientVersionId.ToString()));
|
|
|
|
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
|
|
var result = await response.Content.ReadFromJsonAsync<UploadVersionResult>(IntegrationTestFixture.Json);
|
|
Assert.Equal(1, result!.VersionNumber);
|
|
Assert.NotEqual(Guid.Empty, result.VersionId);
|
|
|
|
// ตรวจ DB จริง: Document auto-create + ค่า tracking จาก JWT claims (AC-5)
|
|
await using var db = fixture.CreateDb();
|
|
var doc = await db.Documents.AsNoTracking().SingleAsync(d => d.Id == documentId);
|
|
Assert.Equal("เอกสารทดสอบ.docx", doc.FileName);
|
|
Assert.Null(doc.TemplateId); // track เองจาก add-in — ไม่ผูก template
|
|
|
|
var version = await db.DocumentVersions.AsNoTracking().SingleAsync(v => v.DocumentId == documentId);
|
|
Assert.Equal(1, version.VersionNumber);
|
|
Assert.Equal(fixture.TestUserId, version.CreatedByUserId); // sub จาก token เท่านั้น
|
|
Assert.Equal(Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant(), version.ContentHash);
|
|
Assert.Equal($"{documentId}/v0001_{clientVersionId}.docx", version.ObjectKey);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SecondUpload_IncrementsVersionNumber_AndHistorySortsNewestFirst()
|
|
{
|
|
var documentId = Guid.NewGuid();
|
|
using var client = fixture.CreateClient();
|
|
|
|
var v1 = TestDocx.BuildPlainDocx("v1");
|
|
using var first = await client.PostAsync(
|
|
$"api/documents/{documentId}/versions",
|
|
ApiExtensions.VersionForm(v1, "doc.docx", Guid.NewGuid().ToString()));
|
|
var firstResult = await first.Content.ReadFromJsonAsync<UploadVersionResult>(IntegrationTestFixture.Json);
|
|
|
|
var v2 = TestDocx.BuildPlainDocx("v2 — แก้ไขแล้ว");
|
|
using var second = await client.PostAsync(
|
|
$"api/documents/{documentId}/versions",
|
|
ApiExtensions.VersionForm(v2, "doc.docx", Guid.NewGuid().ToString()));
|
|
Assert.Equal(HttpStatusCode.Created, second.StatusCode);
|
|
var secondResult = await second.Content.ReadFromJsonAsync<UploadVersionResult>(IntegrationTestFixture.Json);
|
|
Assert.Equal(2, secondResult!.VersionNumber);
|
|
|
|
// FR-6: เรียงใหม่ → เก่า
|
|
using var history = await client.GetAsync($"api/documents/{documentId}/versions");
|
|
var items = await history.Content.ReadFromJsonAsync<List<VersionItem>>(IntegrationTestFixture.Json);
|
|
Assert.Equal(2, items!.Count);
|
|
Assert.Equal(secondResult.VersionId, items[0].VersionId);
|
|
Assert.Equal(2, items[0].VersionNumber);
|
|
Assert.Equal(firstResult!.VersionId, items[1].VersionId);
|
|
Assert.Equal(1, items[1].VersionNumber);
|
|
Assert.All(items, i => Assert.Equal(fixture.TestUserId, i.CreatedByUserId));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Download_ReturnsExactUploadedBytes_WithDocxContentType()
|
|
{
|
|
var documentId = Guid.NewGuid();
|
|
var bytes = TestDocx.BuildPlainDocx("ไบต์ต้องตรงกันตอนดาวน์โหลด (AC-8 ส่วน backend)");
|
|
using var client = fixture.CreateClient();
|
|
|
|
using var upload = await client.PostAsync(
|
|
$"api/documents/{documentId}/versions",
|
|
ApiExtensions.VersionForm(bytes, "roundtrip.docx", Guid.NewGuid().ToString()));
|
|
var uploaded = await upload.Content.ReadFromJsonAsync<UploadVersionResult>(IntegrationTestFixture.Json);
|
|
|
|
using var download = await client.GetAsync(
|
|
$"api/documents/{documentId}/versions/{uploaded!.VersionId}/download");
|
|
|
|
Assert.Equal(HttpStatusCode.OK, download.StatusCode);
|
|
Assert.Equal(TestDocx.DocxContentType, download.Content.Headers.ContentType!.MediaType);
|
|
var downloaded = await download.Content.ReadAsByteArrayAsync();
|
|
Assert.Equal(bytes, downloaded); // byte-for-byte ผ่าน MinIO ไป-กลับ
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Download_UnknownVersion_Returns404()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.GetAsync(
|
|
$"api/documents/{Guid.NewGuid()}/versions/{Guid.NewGuid()}/download");
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetVersions_UnknownDocument_Returns404()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.GetAsync($"api/documents/{Guid.NewGuid()}/versions");
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Upload_NonDocxFile_Returns400()
|
|
{
|
|
var documentId = Guid.NewGuid();
|
|
using var client = fixture.CreateClient();
|
|
using var form = new MultipartFormDataContent();
|
|
var file = new ByteArrayContent("นี่ไม่ใช่ word"u8.ToArray());
|
|
file.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
|
|
form.Add(file, "file", "notes.txt"); // ชื่อไม่ลงท้าย .docx
|
|
form.Add(new StringContent(Guid.NewGuid().ToString()), "clientVersionId");
|
|
|
|
using var response = await client.PostAsync($"api/documents/{documentId}/versions", form);
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
|
|
await using var db = fixture.CreateDb();
|
|
Assert.False(await db.Documents.AsNoTracking().AnyAsync(d => d.Id == documentId)); // ไม่มี side effect
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Upload_InvalidClientVersionId_Returns400()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.PostAsync(
|
|
$"api/documents/{Guid.NewGuid()}/versions",
|
|
ApiExtensions.VersionForm(TestDocx.BuildPlainDocx(), "doc.docx", "ไม่ใช่ GUID"));
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Upload_InvalidFieldsJson_Returns400()
|
|
{
|
|
using var client = fixture.CreateClient();
|
|
using var response = await client.PostAsync(
|
|
$"api/documents/{Guid.NewGuid()}/versions",
|
|
ApiExtensions.VersionForm(TestDocx.BuildPlainDocx(), "doc.docx", Guid.NewGuid().ToString(), "not json"));
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
/// <summary>เอกสารไม่ผูก template → snapshot เก็บ map ดิบตามที่ส่งมา (behavior ของ Phase 2)</summary>
|
|
[Fact]
|
|
public async Task Upload_WithoutTemplate_StoresRawFieldsMapInSnapshot()
|
|
{
|
|
var documentId = Guid.NewGuid();
|
|
using var client = fixture.CreateClient();
|
|
|
|
using var upload = await client.PostAsync(
|
|
$"api/documents/{documentId}/versions",
|
|
ApiExtensions.VersionForm(
|
|
TestDocx.BuildPlainDocx(), "doc.docx", Guid.NewGuid().ToString(),
|
|
"""{"custom_tag":"ค่าที่ส่งมา"}"""));
|
|
|
|
Assert.Equal(HttpStatusCode.Created, upload.StatusCode);
|
|
using var history = await client.GetAsync($"api/documents/{documentId}/versions");
|
|
var items = await history.Content.ReadFromJsonAsync<List<VersionItem>>(IntegrationTestFixture.Json);
|
|
|
|
var fields = items!.Single().Fields!.Value;
|
|
Assert.Equal(JsonValueKind.Object, fields.ValueKind);
|
|
Assert.Equal("ค่าที่ส่งมา", fields.GetProperty("custom_tag").GetString());
|
|
}
|
|
}
|