From 915645d985d93f370eef034bd8f34a427f06aec9 Mon Sep 17 00:00:00 2001 From: hywznn Date: Wed, 19 Aug 2026 20:09:09 +0900 Subject: [PATCH 1/2] test: verify Renewal HWPX generation contract --- .../RenewalRuntimeContractValidator.java | 2 +- .../renewal/GeneratedDocumentService.java | 6 ++++ .../RenewalRuntimeContractValidatorTest.java | 24 ++++++++++++- .../task/RenewalExecutionIntegrationTest.java | 36 ++++++++++++------- .../renewal/GeneratedDocumentServiceTest.java | 25 +++++++++++-- .../renewal/RenewalExecutionServiceTest.java | 2 +- 6 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java b/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java index 9410d3f7..3a81005d 100644 --- a/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java +++ b/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java @@ -52,7 +52,7 @@ public final class RenewalRuntimeContractValidator { "immigration_integrated_application_v34", "identity_guaranty_v129" ); - private static final Set DOCUMENT_FORMATS = Set.of("hwp", "hwpx"); + private static final Set DOCUMENT_FORMATS = Set.of("hwpx"); private final AiRuntimeBoundaryPolicy boundaryPolicy; diff --git a/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java b/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java index 8622a22f..6b61fd10 100644 --- a/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java +++ b/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java @@ -111,6 +111,12 @@ List prepare( } private void validateRequiredValues(RenewalGeneratedDocument document) { + if (!"hwpx".equals(document.format())) { + throw new AiRuntimeCallException( + AiRuntimeFailureCode.INVALID_RESPONSE_CONTRACT, + "Renewal draft format must be HWPX." + ); + } Set required = REQUIRED_VALUES_BY_TEMPLATE.get(document.templateId()); boolean missingRequiredValue = required == null || required.stream().anyMatch(key -> !hasValue(document.values().get(key))); diff --git a/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java b/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java index 9564cd55..fab393eb 100644 --- a/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java +++ b/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java @@ -85,6 +85,20 @@ void rejectsAnIntentUsedAsAWorkflowId() { .isInstanceOf(AiRuntimeContractException.class); } + @Test + void rejectsLegacyHwpGeneratedDocuments() { + RenewalRunRequest request = request(); + + assertThatThrownBy(() -> validator.validateResponse( + request, + generateResponse( + request, + Map.of("employee_name", "NGUYEN VAN AN"), + "hwp" + ) + )).isInstanceOf(AiRuntimeContractException.class); + } + @Test void rejectsAResponseFromAnotherAttempt() { RenewalRunRequest request = request(); @@ -376,6 +390,14 @@ workflowId, new BigDecimal("0.91"), "NEEDS_INFO", "NEEDS_INFO", "ask_hr", private RenewalRunResponse generateResponse( RenewalRunRequest request, Map values + ) { + return generateResponse(request, values, "hwpx"); + } + + private RenewalRunResponse generateResponse( + RenewalRunRequest request, + Map values, + String format ) { return new RenewalRunResponse( request.requestId(), request.attemptId(), request.taskId(), "EXPIRY_RENEWAL", @@ -384,7 +406,7 @@ private RenewalRunResponse generateResponse( "generate", "PHASE_4", "STEP_13", Map.of(), List.of(), List.of(), null, null, false, null, null, null, List.of(new RenewalGeneratedDocument( - "standard_labor_contract_v6", "표준근로계약서", "hwp", "stub", null, null, + "standard_labor_contract_v6", "표준근로계약서", format, "stub", null, null, List.copyOf(values.keySet()), List.of(), values )), List.of(), null, List.of("GENERATE_DRAFTS", "READY_FOR_REVIEW"), diff --git a/src/test/java/com/fowoco/server/task/RenewalExecutionIntegrationTest.java b/src/test/java/com/fowoco/server/task/RenewalExecutionIntegrationTest.java index d4b3d624..7afd688f 100644 --- a/src/test/java/com/fowoco/server/task/RenewalExecutionIntegrationTest.java +++ b/src/test/java/com/fowoco/server/task/RenewalExecutionIntegrationTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -96,7 +97,7 @@ void resetAndSeed() { reset(runtimeClient); reset(documentGenerationClient, fileStorage); when(documentGenerationClient.generate(any())).thenReturn(new GeneratedDocumentFile( - "표준근로계약서.hwp", "hwp", validHwpFile() + "표준근로계약서.hwpx", "hwpx", validHwpxFile() )); capturedRequest.set(null); jdbcTemplate.update("DELETE FROM document_request_draft"); @@ -468,6 +469,8 @@ void generatesAndLinksADraftWithoutPersistingTheRawValues() throws Exception { .isEqualTo("standard_labor_contract_v6"); assertThat(JsonPath.read(response.body(), "$.generated_documents[0].status")) .isEqualTo("GENERATED"); + assertThat(JsonPath.read(response.body(), "$.generated_documents[0].format")) + .isEqualTo("hwpx"); assertThat(JsonPath.read(response.body(), "$.generated_documents[0].stored_file_id")) .isNotBlank(); assertThat(JsonPath.read(response.body(), "$.generated_documents[0].worker_document_id")) @@ -475,9 +478,18 @@ void generatesAndLinksADraftWithoutPersistingTheRawValues() throws Exception { assertThat(response.body()).doesNotContain("NGUYEN VAN AN", "passport_number"); assertThat(jdbcTemplate.queryForObject( - "SELECT COUNT(*) FROM stored_file WHERE task_id = ? AND worker_id = ?", + """ + SELECT COUNT(*) FROM stored_file + WHERE task_id = ? AND worker_id = ? + AND mime_type = 'application/vnd.hancom.hwpx' + AND LOWER(name) LIKE '%.hwpx' + """, Integer.class, TASK_A, WORKER_A )).isEqualTo(1); + verify(documentGenerationClient).generate(argThat(request -> + "hwpx".equals(request.format()) + && "standard_labor_contract_v6".equals(request.templateId()) + )); assertThat(jdbcTemplate.queryForObject( """ SELECT COUNT(*) FROM worker_document @@ -641,7 +653,7 @@ private RenewalGeneratedDocument generatedDocument(String templateId) { return new RenewalGeneratedDocument( templateId, "Renewal test document", - "hwp", + "hwpx", "stub", null, null, @@ -705,15 +717,15 @@ private RenewalRunResponse workerGuideReviewResponse(RenewalRunRequest request) ); } - private byte[] validHwpFile() { - try (org.apache.poi.poifs.filesystem.POIFSFileSystem fileSystem = - new org.apache.poi.poifs.filesystem.POIFSFileSystem()) { - byte[] header = new byte[256]; - byte[] signature = "HWP Document File".getBytes(StandardCharsets.US_ASCII); - System.arraycopy(signature, 0, header, 0, signature.length); - fileSystem.createDocument(new java.io.ByteArrayInputStream(header), "FileHeader"); - java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream(); - fileSystem.writeFilesystem(output); + private byte[] validHwpxFile() { + java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream(); + try (java.util.zip.ZipOutputStream zip = new java.util.zip.ZipOutputStream(output)) { + zip.putNextEntry(new java.util.zip.ZipEntry("mimetype")); + zip.write("application/hwp+zip".getBytes(StandardCharsets.UTF_8)); + zip.closeEntry(); + zip.putNextEntry(new java.util.zip.ZipEntry("Contents/section0.xml")); + zip.write("Renewal draft".getBytes(StandardCharsets.UTF_8)); + zip.closeEntry(); return output.toByteArray(); } catch (java.io.IOException exception) { throw new IllegalStateException(exception); diff --git a/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java b/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java index 14249d06..5759d248 100644 --- a/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java +++ b/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java @@ -36,7 +36,7 @@ void setUp() { var request = invocation. getArgument(0); return new GeneratedDocumentFile( - request.templateId() + ".hwp", + request.templateId() + "." + request.format(), request.format(), request.templateId().getBytes(StandardCharsets.UTF_8) ); @@ -83,7 +83,7 @@ void rejectsADraftWithMissingRequiredMappedValuesBeforeGeneration() { RenewalGeneratedDocument incomplete = new RenewalGeneratedDocument( "standard_labor_contract_v6", "standard_labor_contract_v6", - "hwp", + "hwpx", "READY", null, null, @@ -100,6 +100,25 @@ void rejectsADraftWithMissingRequiredMappedValuesBeforeGeneration() { verify(generationClient, times(0)).generate(any()); } + @Test + void rejectsLegacyHwpDraftBeforeCallingTheGenerator() { + RenewalGeneratedDocument hwp = new RenewalGeneratedDocument( + "standard_labor_contract_v6", + "standard_labor_contract_v6", + "hwp", + "READY", + null, + null, + List.of(), + List.of(), + values("standard_labor_contract_v6") + ); + + assertThatThrownBy(() -> service.prepare("RECONTRACT", List.of(hwp))) + .isInstanceOf(AiRuntimeCallException.class); + verify(generationClient, times(0)).generate(any()); + } + @Test void mapsGeneratedTemplatesToTheirActualDocumentTypes() { assertThat(service.documentType("standard_labor_contract_v6")) @@ -123,7 +142,7 @@ private List caseWideDocuments() { private RenewalGeneratedDocument document(String templateId) { return new RenewalGeneratedDocument( - templateId, templateId, "hwp", "READY", + templateId, templateId, "hwpx", "READY", null, null, List.of(), List.of(), values(templateId) ); } diff --git a/src/test/java/com/fowoco/server/task/application/renewal/RenewalExecutionServiceTest.java b/src/test/java/com/fowoco/server/task/application/renewal/RenewalExecutionServiceTest.java index 16274e56..6b5f1747 100644 --- a/src/test/java/com/fowoco/server/task/application/renewal/RenewalExecutionServiceTest.java +++ b/src/test/java/com/fowoco/server/task/application/renewal/RenewalExecutionServiceTest.java @@ -58,7 +58,7 @@ void setUp() { null, null, taskSnapshot() ); RenewalGeneratedDocument document = new RenewalGeneratedDocument( - "standard_labor_contract_v6", "표준근로계약서", "hwp", "READY", + "standard_labor_contract_v6", "표준근로계약서", "hwpx", "READY", null, null, List.of(), List.of(), Map.of("worker_name", "응웬반안") ); response = new RenewalRunResponse( From 68362f5e8e7b34b568a69d2555d8d99cbf53ca33 Mon Sep 17 00:00:00 2001 From: hywznn Date: Wed, 19 Aug 2026 20:23:48 +0900 Subject: [PATCH 2/2] test: preserve Renewal HWP compatibility --- .../RenewalRuntimeContractValidator.java | 2 +- .../renewal/GeneratedDocumentService.java | 6 ------ .../RenewalRuntimeContractValidatorTest.java | 6 +++--- .../renewal/GeneratedDocumentServiceTest.java | 15 ++++++++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java b/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java index 3a81005d..9410d3f7 100644 --- a/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java +++ b/src/main/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidator.java @@ -52,7 +52,7 @@ public final class RenewalRuntimeContractValidator { "immigration_integrated_application_v34", "identity_guaranty_v129" ); - private static final Set DOCUMENT_FORMATS = Set.of("hwpx"); + private static final Set DOCUMENT_FORMATS = Set.of("hwp", "hwpx"); private final AiRuntimeBoundaryPolicy boundaryPolicy; diff --git a/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java b/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java index 6b61fd10..8622a22f 100644 --- a/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java +++ b/src/main/java/com/fowoco/server/task/application/renewal/GeneratedDocumentService.java @@ -111,12 +111,6 @@ List prepare( } private void validateRequiredValues(RenewalGeneratedDocument document) { - if (!"hwpx".equals(document.format())) { - throw new AiRuntimeCallException( - AiRuntimeFailureCode.INVALID_RESPONSE_CONTRACT, - "Renewal draft format must be HWPX." - ); - } Set required = REQUIRED_VALUES_BY_TEMPLATE.get(document.templateId()); boolean missingRequiredValue = required == null || required.stream().anyMatch(key -> !hasValue(document.values().get(key))); diff --git a/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java b/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java index fab393eb..51c28555 100644 --- a/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java +++ b/src/test/java/com/fowoco/server/aiintegration/application/validation/RenewalRuntimeContractValidatorTest.java @@ -86,17 +86,17 @@ void rejectsAnIntentUsedAsAWorkflowId() { } @Test - void rejectsLegacyHwpGeneratedDocuments() { + void acceptsLegacyHwpGeneratedDocumentsForBackwardCompatibility() { RenewalRunRequest request = request(); - assertThatThrownBy(() -> validator.validateResponse( + assertThatCode(() -> validator.validateResponse( request, generateResponse( request, Map.of("employee_name", "NGUYEN VAN AN"), "hwp" ) - )).isInstanceOf(AiRuntimeContractException.class); + )).doesNotThrowAnyException(); } @Test diff --git a/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java b/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java index 5759d248..0217525e 100644 --- a/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java +++ b/src/test/java/com/fowoco/server/task/application/renewal/GeneratedDocumentServiceTest.java @@ -101,8 +101,8 @@ void rejectsADraftWithMissingRequiredMappedValuesBeforeGeneration() { } @Test - void rejectsLegacyHwpDraftBeforeCallingTheGenerator() { - RenewalGeneratedDocument hwp = new RenewalGeneratedDocument( + void preservesLegacyHwpFormatForBackwardCompatibility() { + RenewalGeneratedDocument legacyHwp = new RenewalGeneratedDocument( "standard_labor_contract_v6", "standard_labor_contract_v6", "hwp", @@ -114,9 +114,14 @@ void rejectsLegacyHwpDraftBeforeCallingTheGenerator() { values("standard_labor_contract_v6") ); - assertThatThrownBy(() -> service.prepare("RECONTRACT", List.of(hwp))) - .isInstanceOf(AiRuntimeCallException.class); - verify(generationClient, times(0)).generate(any()); + PreparedRenewalDocument prepared = service.prepare( + "RECONTRACT", + List.of(legacyHwp) + ).get(0); + + assertThat(prepared.descriptor().format()).isEqualTo("hwp"); + assertThat(prepared.file().format()).isEqualTo("hwp"); + assertThat(prepared.file().fileName()).endsWith(".hwp"); } @Test