refactor: подтянуть Legal NER, переформатировать код и добавить тесты

Слияние с 685ec97 (третья ступень NER для юридических реквизитов),
код приведён к google-java-format, добавлены юнит-тесты
AdaptiveConcurrencyLimiter/SystemsConfig/PayloadCipher.
This commit is contained in:
Максименко Никита Владимирович
2026-09-23 22:02:45 +03:00
parent 84b5adcb3f
commit 573d94cca8
79 changed files with 7385 additions and 6430 deletions
+80 -66
View File
@@ -1,88 +1,102 @@
package ru.pdguard;
import io.restassured.path.json.JsonPath;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import java.util.Map;
import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import io.restassured.path.json.JsonPath;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
/** Демонстрационное плечо: потребитель → маскирование → LLM → демаскирование. */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ProxyResourceTest {
@LocalServerPort
int port;
@LocalServerPort int port;
private static final String PROMPT =
"Составь письмо клиенту Иванову Ивану Ивановичу, паспорт 4509 123456, почта ivan@mail.ru";
private static final String PROMPT =
"Составь письмо клиенту Иванову Ивану Ивановичу, паспорт 4509 123456, почта ivan@mail.ru";
private JsonPath proxy(String prompt) {
return given()
.port(port)
.contentType("application/json")
.body(Map.of("prompt", prompt))
.when().post("/proxy")
.then().statusCode(200)
.extract().jsonPath();
}
private JsonPath proxy(String prompt) {
return given()
.port(port)
.contentType("application/json")
.body(Map.of("prompt", prompt))
.when()
.post("/proxy")
.then()
.statusCode(200)
.extract()
.jsonPath();
}
@Test
void personalDataDoesNotReachTheModel() {
JsonPath json = proxy(PROMPT);
String toModel = json.getString("prompt_masked");
@Test
void personalDataDoesNotReachTheModel() {
JsonPath json = proxy(PROMPT);
String toModel = json.getString("prompt_masked");
assertFalse(toModel.contains("Иванову Ивану Ивановичу"), toModel);
assertFalse(toModel.contains("4509 123456"), toModel);
assertFalse(toModel.contains("ivan@mail.ru"), toModel);
}
assertFalse(toModel.contains("Иванову Ивану Ивановичу"), toModel);
assertFalse(toModel.contains("4509 123456"), toModel);
assertFalse(toModel.contains("ivan@mail.ru"), toModel);
}
@Test
void consumerGetsTheAnswerWithOriginalValues() {
JsonPath json = proxy(PROMPT);
String answer = json.getString("response");
@Test
void consumerGetsTheAnswerWithOriginalValues() {
JsonPath json = proxy(PROMPT);
String answer = json.getString("response");
assertTrue(answer.contains("Иванову Ивану Ивановичу"), answer);
assertTrue(answer.contains("4509 123456"), answer);
assertTrue(answer.contains("ivan@mail.ru"), answer);
}
assertTrue(answer.contains("Иванову Ивану Ивановичу"), answer);
assertTrue(answer.contains("4509 123456"), answer);
assertTrue(answer.contains("ivan@mail.ru"), answer);
}
@Test
void responseShowsTheWholeChain() {
JsonPath json = proxy(PROMPT);
@Test
void responseShowsTheWholeChain() {
JsonPath json = proxy(PROMPT);
assertTrue(json.getString("prompt_masked").contains("[FIO_1]"),
"в модель уходит обратимая подстановка: " + json.getString("prompt_masked"));
assertTrue(json.getString("llm_response_masked").contains("[FIO_1]"),
"ответ модели ещё содержит подстановки");
assertFalse(json.getString("response").contains("[FIO_1]"),
"потребителю подстановки не видны");
assertEquals("заглушка", json.getString("llm"));
assertFalse(json.getMap("replaced").isEmpty(), "таблица замен не должна быть пустой");
}
assertTrue(
json.getString("prompt_masked").contains("[FIO_1]"),
"в модель уходит обратимая подстановка: " + json.getString("prompt_masked"));
assertTrue(
json.getString("llm_response_masked").contains("[FIO_1]"),
"ответ модели ещё содержит подстановки");
assertFalse(json.getString("response").contains("[FIO_1]"), "потребителю подстановки не видны");
assertEquals("заглушка", json.getString("llm"));
assertFalse(json.getMap("replaced").isEmpty(), "таблица замен не должна быть пустой");
}
@Test
void textWithoutPersonalDataPassesThrough() {
JsonPath json = proxy("Объясни разницу между вкладом и накопительным счётом");
assertEquals("Объясни разницу между вкладом и накопительным счётом", json.getString("prompt_masked"));
}
@Test
void textWithoutPersonalDataPassesThrough() {
JsonPath json = proxy("Объясни разницу между вкладом и накопительным счётом");
assertEquals(
"Объясни разницу между вкладом и накопительным счётом", json.getString("prompt_masked"));
}
@Test
void rejectsEmptyPrompt() {
given().port(port).contentType("application/json").body(Map.of("prompt", " "))
.when().post("/proxy").then().statusCode(400);
}
@Test
void rejectsEmptyPrompt() {
given()
.port(port)
.contentType("application/json")
.body(Map.of("prompt", " "))
.when()
.post("/proxy")
.then()
.statusCode(400);
}
@Test
void disabledSystemIsRefused() {
given().port(port).contentType("application/json")
.header("X-System-Id", "disabled")
.body(Map.of("prompt", PROMPT))
.when().post("/proxy").then().statusCode(403);
}
}
@Test
void disabledSystemIsRefused() {
given()
.port(port)
.contentType("application/json")
.header("X-System-Id", "disabled")
.body(Map.of("prompt", PROMPT))
.when()
.post("/proxy")
.then()
.statusCode(403);
}
}