mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-13 19:51:24 +08:00
fix: make plugin hooks run on Node 21+ and green the suite under modern Node (#2184)
ROOT CAUSE: hooks load plugin-hook-bootstrap.js via `node -e "...; process.argv.splice(1,0,s); require(s)"`. On Node 21+, require.main is `undefined` under --eval, so the `if (require.main === module)` guard was false and main() never ran — every plugin hook silently no-op'd (e.g. the MCP-health PreToolUse hook stopped blocking). CI (Node 18/20) hid this; it only surfaces on Node 21+. Fix: also run main() when require.main is undefined (the eval-bootstrap case), while staying dormant on real imports. Also clears pre-existing main debt the full local suite enforces: - catalog:sync — README/docs agent+skill counts drifted after recent merges - tests/ci/supply-chain-watch-workflow: update checkout SHA to the merged v6.0.3 (#2183) - markdownlint + check-unicode-safety --write across docs/skills Suite: 2683/2683 green under Node v25; lint + unicode clean. Co-authored-by: ECC Test <ecc@example.test>
This commit is contained in:
@@ -70,18 +70,18 @@ public class OrderProcessingService {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class ProcessingService {
|
||||
|
||||
|
||||
public void processDocument(Document doc) {
|
||||
LogContext logContext = CustomLog.getCurrentContext();
|
||||
try (SafeAutoCloseable ignored = CustomLog.startScope(logContext)) {
|
||||
logContext.put("documentId", doc.getId().toString());
|
||||
logContext.put("documentType", doc.getType());
|
||||
logContext.put("userId", SecurityContext.getUserId());
|
||||
|
||||
|
||||
log.info("Iniciando procesamiento de documento");
|
||||
|
||||
|
||||
processInternal(doc);
|
||||
|
||||
|
||||
log.info("Procesamiento de documento completado");
|
||||
} catch (Exception e) {
|
||||
log.error("Error en el procesamiento de documento", e);
|
||||
@@ -101,7 +101,7 @@ public class ProcessingService {
|
||||
<includeMdc>true</includeMdc>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
<logger name="com.example" level="INFO"/>
|
||||
<root level="WARN">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
@@ -118,7 +118,7 @@ public class ProcessingService {
|
||||
public class EventService {
|
||||
private final EventRepository eventRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
public void createSuccessEvent(Object payload, String eventType) {
|
||||
Objects.requireNonNull(payload, "El payload no puede ser null");
|
||||
Event event = new Event();
|
||||
@@ -126,11 +126,11 @@ public class EventService {
|
||||
event.setStatus(EventStatus.SUCCESS);
|
||||
event.setPayload(serializePayload(payload));
|
||||
event.setTimestamp(Instant.now());
|
||||
|
||||
|
||||
eventRepository.persist(event);
|
||||
log.info("Evento de éxito creado: {}", eventType);
|
||||
}
|
||||
|
||||
|
||||
public void createErrorEvent(Object payload, String eventType, String errorMessage) {
|
||||
Objects.requireNonNull(payload, "El payload no puede ser null");
|
||||
if (errorMessage == null || errorMessage.isBlank()) {
|
||||
@@ -142,11 +142,11 @@ public class EventService {
|
||||
event.setErrorMessage(errorMessage);
|
||||
event.setPayload(serializePayload(payload));
|
||||
event.setTimestamp(Instant.now());
|
||||
|
||||
|
||||
eventRepository.persist(event);
|
||||
log.error("Evento de error creado: {} - {}", eventType, errorMessage);
|
||||
}
|
||||
|
||||
|
||||
private String serializePayload(Object payload) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(payload);
|
||||
@@ -165,10 +165,10 @@ public class EventService {
|
||||
@RequiredArgsConstructor
|
||||
public class BusinessRulesPublisher {
|
||||
private final ProducerTemplate producerTemplate;
|
||||
|
||||
|
||||
public void publishSync(BusinessRulesPayload payload) {
|
||||
producerTemplate.sendBody(
|
||||
"direct:business-rules-publisher",
|
||||
"direct:business-rules-publisher",
|
||||
payload
|
||||
);
|
||||
}
|
||||
@@ -180,23 +180,23 @@ public class BusinessRulesPublisher {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class BusinessRulesRoute extends RouteBuilder {
|
||||
|
||||
|
||||
@ConfigProperty(name = "camel.rabbitmq.queue.business-rules")
|
||||
String businessRulesQueue;
|
||||
|
||||
|
||||
@ConfigProperty(name = "rabbitmq.host")
|
||||
String rabbitHost;
|
||||
|
||||
|
||||
@ConfigProperty(name = "rabbitmq.port")
|
||||
Integer rabbitPort;
|
||||
|
||||
|
||||
@Override
|
||||
public void configure() {
|
||||
from("direct:business-rules-publisher")
|
||||
.routeId("business-rules-publisher")
|
||||
.log("Publicando mensaje en RabbitMQ: ${body}")
|
||||
.marshal().json(JsonLibrary.Jackson)
|
||||
.toF("spring-rabbitmq:%s?hostname=%s&portNumber=%d",
|
||||
.toF("spring-rabbitmq:%s?hostname=%s&portNumber=%d",
|
||||
businessRulesQueue, rabbitHost, rabbitPort);
|
||||
}
|
||||
}
|
||||
@@ -207,14 +207,14 @@ public class BusinessRulesRoute extends RouteBuilder {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class DocumentProcessingRoute extends RouteBuilder {
|
||||
|
||||
|
||||
@Override
|
||||
public void configure() {
|
||||
onException(ValidationException.class)
|
||||
.handled(true)
|
||||
.to("direct:validation-error-handler")
|
||||
.log("Error de validación: ${exception.message}");
|
||||
|
||||
|
||||
from("direct:process-document")
|
||||
.routeId("document-processing")
|
||||
.log("Procesando documento: ${header.documentId}")
|
||||
@@ -237,19 +237,19 @@ public class DocumentProcessingRoute extends RouteBuilder {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class FileMonitoringRoute extends RouteBuilder {
|
||||
|
||||
|
||||
@ConfigProperty(name = "file.input.directory")
|
||||
String inputDirectory;
|
||||
|
||||
|
||||
@ConfigProperty(name = "file.processed.directory")
|
||||
String processedDirectory;
|
||||
|
||||
|
||||
@ConfigProperty(name = "file.error.directory")
|
||||
String errorDirectory;
|
||||
|
||||
|
||||
@Override
|
||||
public void configure() {
|
||||
from("file:" + inputDirectory + "?move=" + processedDirectory +
|
||||
from("file:" + inputDirectory + "?move=" + processedDirectory +
|
||||
"&moveFailed=" + errorDirectory + "&delay=5000")
|
||||
.routeId("file-monitor")
|
||||
.log("Procesando archivo: ${header.CamelFileName}")
|
||||
@@ -302,7 +302,7 @@ public class DocumentResource {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class DocumentRepository implements PanacheRepository<Document> {
|
||||
|
||||
|
||||
public List<Document> findByStatus(DocumentStatus status, int page, int size) {
|
||||
return find("status = ?1 order by createdAt desc", status)
|
||||
.page(page, size)
|
||||
@@ -331,11 +331,11 @@ public class DocumentService {
|
||||
document.setDescription(request.description());
|
||||
document.setStatus(DocumentStatus.PENDING);
|
||||
document.setCreatedAt(Instant.now());
|
||||
|
||||
|
||||
repo.persist(document);
|
||||
|
||||
|
||||
eventService.createSuccessEvent(document, "DOCUMENT_CREATED");
|
||||
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
@@ -352,7 +352,7 @@ public record CreateDocumentRequest(
|
||||
|
||||
public record DocumentResponse(Long id, String referenceNumber, DocumentStatus status) {
|
||||
public static DocumentResponse from(Document document) {
|
||||
return new DocumentResponse(document.getId(), document.getReferenceNumber(),
|
||||
return new DocumentResponse(document.getId(), document.getReferenceNumber(),
|
||||
document.getStatus());
|
||||
}
|
||||
}
|
||||
@@ -368,7 +368,7 @@ public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViol
|
||||
String message = exception.getConstraintViolations().stream()
|
||||
.map(cv -> cv.getPropertyPath() + ": " + cv.getMessage())
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "validation_error", "message", message))
|
||||
.build();
|
||||
@@ -385,25 +385,25 @@ public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViol
|
||||
public class FileStorageService {
|
||||
private final S3Client s3Client;
|
||||
private final ExecutorService executorService;
|
||||
|
||||
|
||||
public CompletableFuture<StoredDocumentInfo> uploadOriginalFile(
|
||||
InputStream inputStream,
|
||||
long size,
|
||||
InputStream inputStream,
|
||||
long size,
|
||||
LogContext logContext,
|
||||
InvoiceFormat format) {
|
||||
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (SafeAutoCloseable ignored = CustomLog.startScope(logContext)) {
|
||||
String path = generateStoragePath(format);
|
||||
|
||||
|
||||
PutObjectRequest request = PutObjectRequest.builder()
|
||||
.bucket(bucketName)
|
||||
.key(path)
|
||||
.contentLength(size)
|
||||
.build();
|
||||
|
||||
|
||||
s3Client.putObject(request, RequestBody.fromInputStream(inputStream, size));
|
||||
|
||||
|
||||
return new StoredDocumentInfo(path, size, Instant.now());
|
||||
} catch (Exception e) {
|
||||
log.error("Error al subir archivo a S3", e);
|
||||
|
||||
@@ -28,7 +28,7 @@ Buenas prácticas para asegurar aplicaciones Quarkus con autenticación, autoriz
|
||||
@Path("/api/protected")
|
||||
@Authenticated
|
||||
public class ProtectedResource {
|
||||
|
||||
|
||||
@Inject
|
||||
JsonWebToken jwt;
|
||||
|
||||
@@ -65,19 +65,19 @@ quarkus.oidc.credentials.secret=${OIDC_SECRET}
|
||||
@Provider
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public class CustomAuthFilter implements ContainerRequestFilter {
|
||||
|
||||
|
||||
@Inject
|
||||
SecurityIdentity identity;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) {
|
||||
String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
|
||||
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String token = authHeader.substring(7);
|
||||
if (!validateToken(token)) {
|
||||
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
|
||||
@@ -98,7 +98,7 @@ public class CustomAuthFilter implements ContainerRequestFilter {
|
||||
@Path("/api/admin")
|
||||
@RolesAllowed("ADMIN")
|
||||
public class AdminResource {
|
||||
|
||||
|
||||
@GET
|
||||
@Path("/users")
|
||||
public List<UserDto> listUsers() {
|
||||
@@ -116,7 +116,7 @@ public class AdminResource {
|
||||
|
||||
@Path("/api/users")
|
||||
public class UserResource {
|
||||
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@@ -124,7 +124,7 @@ public class UserResource {
|
||||
@Path("/{id}")
|
||||
@RolesAllowed("USER")
|
||||
public Response getUser(@PathParam("id") Long id) {
|
||||
if (!securityIdentity.hasRole("ADMIN") &&
|
||||
if (!securityIdentity.hasRole("ADMIN") &&
|
||||
!isOwner(id, securityIdentity.getPrincipal().getName())) {
|
||||
return Response.status(Response.Status.FORBIDDEN).build();
|
||||
}
|
||||
@@ -138,7 +138,7 @@ public class UserResource {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class SecurityService {
|
||||
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@@ -146,7 +146,7 @@ public class SecurityService {
|
||||
if (securityIdentity.isAnonymous()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (securityIdentity.hasRole("ADMIN")) {
|
||||
return true;
|
||||
}
|
||||
@@ -216,7 +216,7 @@ List<User> users = User.list("email = ?1 and active = ?2", email, true);
|
||||
Optional<User> user = User.find("username", username).firstResultOptional();
|
||||
|
||||
// BIEN: Parámetros nombrados
|
||||
List<User> users = User.list("email = :email and age > :minAge",
|
||||
List<User> users = User.list("email = :email and age > :minAge",
|
||||
Parameters.with("email", email).and("minAge", 18));
|
||||
```
|
||||
|
||||
@@ -243,7 +243,7 @@ public class User extends PanacheEntity {
|
||||
```java
|
||||
@ApplicationScoped
|
||||
public class PasswordService {
|
||||
|
||||
|
||||
public String hash(String plainPassword) {
|
||||
return BcryptUtil.bcryptHash(plainPassword);
|
||||
}
|
||||
@@ -297,7 +297,7 @@ public class RateLimitFilter implements ContainerRequestFilter {
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) {
|
||||
String clientId = getClientIdentifier();
|
||||
RateLimiter limiter = limiters.computeIfAbsent(clientId,
|
||||
RateLimiter limiter = limiters.computeIfAbsent(clientId,
|
||||
k -> RateLimiter.create(100.0)); // 100 solicitudes por segundo
|
||||
|
||||
if (!limiter.tryAcquire()) {
|
||||
@@ -324,17 +324,17 @@ public class RateLimitFilter implements ContainerRequestFilter {
|
||||
```java
|
||||
@Provider
|
||||
public class SecurityHeadersFilter implements ContainerResponseFilter {
|
||||
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext request, ContainerResponseContext response) {
|
||||
MultivaluedMap<String, Object> headers = response.getHeaders();
|
||||
|
||||
|
||||
headers.putSingle("X-Frame-Options", "DENY");
|
||||
headers.putSingle("X-Content-Type-Options", "nosniff");
|
||||
headers.putSingle("X-XSS-Protection", "1; mode=block");
|
||||
headers.putSingle("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
|
||||
// CSP: evitar 'unsafe-inline' para script-src; usar nonces o hashes
|
||||
headers.putSingle("Content-Security-Policy",
|
||||
headers.putSingle("Content-Security-Policy",
|
||||
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
|
||||
}
|
||||
}
|
||||
@@ -351,11 +351,11 @@ public class AuditService {
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
public void logAccess(String resource, String action) {
|
||||
String user = securityIdentity.isAnonymous()
|
||||
? "anonymous"
|
||||
String user = securityIdentity.isAnonymous()
|
||||
? "anonymous"
|
||||
: securityIdentity.getPrincipal().getName();
|
||||
|
||||
LOG.infof("AUDIT: user=%s action=%s resource=%s timestamp=%s",
|
||||
|
||||
LOG.infof("AUDIT: user=%s action=%s resource=%s timestamp=%s",
|
||||
user, action, resource, Instant.now());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +32,19 @@ Orientación TDD para servicios Quarkus 3.x con 80%+ de cobertura (unit + integr
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("Pruebas Unitarias de OrderService")
|
||||
class OrderServiceTest {
|
||||
|
||||
|
||||
@Mock
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
|
||||
@Mock
|
||||
private EventService eventService;
|
||||
|
||||
|
||||
@Mock
|
||||
private FulfillmentPublisher fulfillmentPublisher;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private OrderService orderService;
|
||||
|
||||
|
||||
private CreateOrderCommand validCommand;
|
||||
|
||||
@BeforeEach
|
||||
@@ -58,16 +58,16 @@ class OrderServiceTest {
|
||||
@Nested
|
||||
@DisplayName("Pruebas para createOrder")
|
||||
class CreateOrder {
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("Debe persistir orden y publicar evento de fulfillment")
|
||||
void givenValidCommand_whenCreateOrder_thenPersistsAndPublishes() {
|
||||
// ARRANGE
|
||||
doNothing().when(orderRepository).persist(any(Order.class));
|
||||
|
||||
|
||||
// ACT
|
||||
OrderReceipt receipt = orderService.createOrder(validCommand);
|
||||
|
||||
|
||||
// ASSERT
|
||||
assertThat(receipt).isNotNull();
|
||||
assertThat(receipt.customerId()).isEqualTo("customer-123");
|
||||
@@ -81,7 +81,7 @@ class OrderServiceTest {
|
||||
void givenMissingCustomerId_whenCreateOrder_thenThrowsBadRequest() {
|
||||
// ARRANGE
|
||||
CreateOrderCommand invalid = new CreateOrderCommand("", validCommand.lines());
|
||||
|
||||
|
||||
// ACT & ASSERT
|
||||
WebApplicationException exception = assertThrows(
|
||||
WebApplicationException.class,
|
||||
@@ -99,13 +99,13 @@ class OrderServiceTest {
|
||||
// ARRANGE
|
||||
doThrow(new PersistenceException("base de datos no disponible"))
|
||||
.when(orderRepository).persist(any(Order.class));
|
||||
|
||||
|
||||
// ACT & ASSERT
|
||||
PersistenceException exception = assertThrows(
|
||||
PersistenceException.class,
|
||||
() -> orderService.createOrder(validCommand)
|
||||
);
|
||||
|
||||
|
||||
assertThat(exception.getMessage()).contains("base de datos no disponible");
|
||||
verify(eventService).createErrorEvent(
|
||||
eq(validCommand),
|
||||
@@ -168,20 +168,20 @@ class BusinessRulesRouteTest {
|
||||
// ARRANGE
|
||||
MockEndpoint mockRabbitMQ = camelContext.getEndpoint("mock:rabbitmq", MockEndpoint.class);
|
||||
mockRabbitMQ.expectedMessageCount(1);
|
||||
|
||||
|
||||
camelContext.getRouteController().stopRoute("business-rules-publisher");
|
||||
AdviceWith.adviceWith(camelContext, "business-rules-publisher", advice -> {
|
||||
advice.replaceFromWith("direct:business-rules-publisher");
|
||||
advice.weaveByToString(".*spring-rabbitmq.*").replace().to("mock:rabbitmq");
|
||||
});
|
||||
camelContext.getRouteController().startRoute("business-rules-publisher");
|
||||
|
||||
|
||||
// ACT
|
||||
producerTemplate.sendBody("direct:business-rules-publisher", testPayload);
|
||||
|
||||
|
||||
// ASSERT
|
||||
mockRabbitMQ.assertIsSatisfied(5000);
|
||||
|
||||
|
||||
assertThat(mockRabbitMQ.getExchanges()).hasSize(1);
|
||||
String body = mockRabbitMQ.getExchanges().get(0).getIn().getBody(String.class);
|
||||
assertThat(body).contains("\"documentId\":1");
|
||||
@@ -199,17 +199,17 @@ class EventServiceTest {
|
||||
|
||||
@Mock
|
||||
private EventRepository eventRepository;
|
||||
|
||||
|
||||
@Mock
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private EventService eventService;
|
||||
|
||||
@Nested
|
||||
@DisplayName("Pruebas para createSuccessEvent")
|
||||
class CreateSuccessEvent {
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("Debe crear evento de éxito con atributos correctos")
|
||||
void givenValidPayload_whenCreateSuccessEvent_thenEventPersisted() throws Exception {
|
||||
@@ -217,13 +217,13 @@ class EventServiceTest {
|
||||
BusinessRulesPayload testPayload = new BusinessRulesPayload();
|
||||
testPayload.setDocumentId(1L);
|
||||
when(objectMapper.writeValueAsString(testPayload)).thenReturn("{\"documentId\":1}");
|
||||
|
||||
|
||||
// ACT
|
||||
assertDoesNotThrow(() ->
|
||||
assertDoesNotThrow(() ->
|
||||
eventService.createSuccessEvent(testPayload, "DOCUMENT_PROCESSED"));
|
||||
|
||||
|
||||
// ASSERT
|
||||
verify(eventRepository).persist(argThat(event ->
|
||||
verify(eventRepository).persist(argThat(event ->
|
||||
event.getType().equals("DOCUMENT_PROCESSED") &&
|
||||
event.getStatus() == EventStatus.SUCCESS &&
|
||||
event.getTimestamp() != null
|
||||
@@ -235,13 +235,13 @@ class EventServiceTest {
|
||||
void givenNullPayload_whenCreateSuccessEvent_thenThrowsException() {
|
||||
// ARRANGE
|
||||
Object nullPayload = null;
|
||||
|
||||
|
||||
// ACT & ASSERT
|
||||
NullPointerException exception = assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> eventService.createSuccessEvent(nullPayload, "EVENT_TYPE")
|
||||
);
|
||||
|
||||
|
||||
assertThat(exception.getMessage()).isEqualTo("Payload cannot be null");
|
||||
verify(eventRepository, never()).persist(any());
|
||||
}
|
||||
@@ -250,7 +250,7 @@ class EventServiceTest {
|
||||
@Nested
|
||||
@DisplayName("Pruebas para createErrorEvent")
|
||||
class CreateErrorEvent {
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@DisplayName("Debe rechazar mensajes de error inválidos")
|
||||
@ValueSource(strings = {"", " "})
|
||||
@@ -263,7 +263,7 @@ class EventServiceTest {
|
||||
IllegalArgumentException.class,
|
||||
() -> eventService.createErrorEvent(testPayload, "ERROR", blankMessage)
|
||||
);
|
||||
|
||||
|
||||
assertThat(exception.getMessage()).contains("Error message cannot be blank");
|
||||
}
|
||||
}
|
||||
@@ -278,10 +278,10 @@ class FileStorageServiceTest {
|
||||
|
||||
@Mock
|
||||
private S3Client s3Client;
|
||||
|
||||
|
||||
@Mock
|
||||
private ExecutorService executorService;
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
@@ -293,15 +293,15 @@ class FileStorageServiceTest {
|
||||
((Runnable) invocation.getArgument(0)).run();
|
||||
return null;
|
||||
}).when(executorService).execute(any(Runnable.class));
|
||||
|
||||
|
||||
when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
|
||||
.thenThrow(new StorageException("S3 no disponible"));
|
||||
|
||||
|
||||
// ACT
|
||||
CompletableFuture<StoredDocumentInfo> future =
|
||||
fileStorageService.uploadOriginalFile(testInputStream, 1024L,
|
||||
CompletableFuture<StoredDocumentInfo> future =
|
||||
fileStorageService.uploadOriginalFile(testInputStream, 1024L,
|
||||
testLogContext, InvoiceFormat.UBL);
|
||||
|
||||
|
||||
// ASSERT
|
||||
assertThatThrownBy(() -> future.join())
|
||||
.isInstanceOf(CompletionException.class)
|
||||
|
||||
Reference in New Issue
Block a user