Polish gh-764
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
09a853f555
commit
be5ec12e1b
5 changed files with 117 additions and 49 deletions
|
|
@ -1466,6 +1466,7 @@ public final class McpSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tool build() {
|
public Tool build() {
|
||||||
|
Assert.hasText(name, "name must not be empty");
|
||||||
return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta);
|
return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,30 +4,33 @@
|
||||||
|
|
||||||
package io.modelcontextprotocol.server;
|
package io.modelcontextprotocol.server;
|
||||||
|
|
||||||
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.read.ListAppender;
|
||||||
import io.modelcontextprotocol.spec.McpSchema;
|
import io.modelcontextprotocol.spec.McpSchema;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
||||||
import io.modelcontextprotocol.spec.McpServerTransportProvider;
|
import io.modelcontextprotocol.spec.McpServerTransportProvider;
|
||||||
import io.modelcontextprotocol.util.ToolNameValidator;
|
import io.modelcontextprotocol.util.ToolNameValidator;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link McpServerFeatures.AsyncToolSpecification.Builder}.
|
* Tests for {@link McpServerFeatures.AsyncToolSpecification.Builder}.
|
||||||
*
|
*
|
||||||
|
|
@ -276,15 +279,23 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
|
|
||||||
private McpServerTransportProvider transportProvider;
|
private McpServerTransportProvider transportProvider;
|
||||||
|
|
||||||
|
private final Logger logger = (Logger) LoggerFactory.getLogger(ToolNameValidator.class);
|
||||||
|
|
||||||
|
private final ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
transportProvider = mock(McpServerTransportProvider.class);
|
transportProvider = mock(McpServerTransportProvider.class);
|
||||||
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
||||||
|
logAppender.start();
|
||||||
|
logger.addAppender(logAppender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void tearDown() {
|
void tearDown() {
|
||||||
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
||||||
|
logger.detachAppender(logAppender);
|
||||||
|
logAppender.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -297,25 +308,27 @@ class AsyncToolSpecificationBuilderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void systemPropertyFalseShouldWarnOnly() {
|
void lenientDefaultShouldLogOnInvalidName() {
|
||||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.async(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
assertThatCode(() -> McpServer.async(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void perServerFalseShouldWarnOnly() {
|
void lenientConfigurationShouldLogOnInvalidName() {
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.async(transportProvider)
|
assertThatCode(() -> McpServer.async(transportProvider)
|
||||||
.strictToolNameValidation(false)
|
.strictToolNameValidation(false)
|
||||||
.tool(invalidTool, (exchange, args) -> null)).doesNotThrowAnyException();
|
.tool(invalidTool, (exchange, args) -> null)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void perServerTrueShouldOverrideSystemProperty() {
|
void serverConfigurationShouldOverrideDefault() {
|
||||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,29 @@
|
||||||
|
|
||||||
package io.modelcontextprotocol.server;
|
package io.modelcontextprotocol.server;
|
||||||
|
|
||||||
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.read.ListAppender;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
||||||
|
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
||||||
import io.modelcontextprotocol.spec.McpServerTransportProvider;
|
import io.modelcontextprotocol.spec.McpServerTransportProvider;
|
||||||
import io.modelcontextprotocol.util.ToolNameValidator;
|
import io.modelcontextprotocol.util.ToolNameValidator;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
|
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link McpServerFeatures.SyncToolSpecification.Builder}.
|
* Tests for {@link McpServerFeatures.SyncToolSpecification.Builder}.
|
||||||
|
|
@ -114,15 +117,23 @@ class SyncToolSpecificationBuilderTest {
|
||||||
|
|
||||||
private McpServerTransportProvider transportProvider;
|
private McpServerTransportProvider transportProvider;
|
||||||
|
|
||||||
|
private final Logger logger = (Logger) LoggerFactory.getLogger(ToolNameValidator.class);
|
||||||
|
|
||||||
|
private final ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
transportProvider = mock(McpServerTransportProvider.class);
|
transportProvider = mock(McpServerTransportProvider.class);
|
||||||
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
||||||
|
logAppender.start();
|
||||||
|
logger.addAppender(logAppender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void tearDown() {
|
void tearDown() {
|
||||||
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
System.clearProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY);
|
||||||
|
logger.detachAppender(logAppender);
|
||||||
|
logAppender.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -135,25 +146,27 @@ class SyncToolSpecificationBuilderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void systemPropertyFalseShouldWarnOnly() {
|
void lenientDefaultShouldLogOnInvalidName() {
|
||||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.sync(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
assertThatCode(() -> McpServer.sync(transportProvider).tool(invalidTool, (exchange, args) -> null))
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void perServerFalseShouldWarnOnly() {
|
void lenientConfigurationShouldLogOnInvalidName() {
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
assertThatCode(() -> McpServer.sync(transportProvider)
|
assertThatCode(() -> McpServer.sync(transportProvider)
|
||||||
.strictToolNameValidation(false)
|
.strictToolNameValidation(false)
|
||||||
.tool(invalidTool, (exchange, args) -> null)).doesNotThrowAnyException();
|
.tool(invalidTool, (exchange, args) -> null)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).hasSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void perServerTrueShouldOverrideSystemProperty() {
|
void serverConfigurationShouldOverrideDefault() {
|
||||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1765,14 +1765,4 @@ public class McpSchemaTests {
|
||||||
{"progressToken":"progress-token-789","progress":0.25}"""));
|
{"progressToken":"progress-token-789","progress":0.25}"""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool Name Validation Tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testToolBuilderWithValidName() {
|
|
||||||
McpSchema.Tool tool = McpSchema.Tool.builder().name("valid_tool-name.v1").description("A test tool").build();
|
|
||||||
|
|
||||||
assertThat(tool.name()).isEqualTo("valid_tool-name.v1");
|
|
||||||
assertThat(tool.description()).isEqualTo("A test tool");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,21 @@
|
||||||
|
|
||||||
package io.modelcontextprotocol.util;
|
package io.modelcontextprotocol.util;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.Logger;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.read.ListAppender;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
|
@ -16,33 +27,49 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
*/
|
*/
|
||||||
class ToolNameValidatorTests {
|
class ToolNameValidatorTests {
|
||||||
|
|
||||||
|
private final Logger logger = (Logger) LoggerFactory.getLogger(ToolNameValidator.class);
|
||||||
|
|
||||||
|
private final ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
logAppender.start();
|
||||||
|
logger.addAppender(logAppender);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
logger.detachAppender(logAppender);
|
||||||
|
logAppender.stop();
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(strings = { "getUser", "DATA_EXPORT_v2", "admin.tools.list", "my-tool", "Tool123", "a", "A",
|
@ValueSource(strings = { "getUser", "DATA_EXPORT_v2", "admin.tools.list", "my-tool", "Tool123", "a", "A",
|
||||||
"_private", "tool_name", "tool-name", "tool.name", "UPPERCASE", "lowercase", "MixedCase123" })
|
"_private", "tool_name", "tool-name", "tool.name", "UPPERCASE", "lowercase", "MixedCase123" })
|
||||||
void validToolNames_strictMode(String name) {
|
void validToolNames(String name) {
|
||||||
assertThatCode(() -> ToolNameValidator.validate(name, true)).doesNotThrowAnyException();
|
assertThatCode(() -> ToolNameValidator.validate(name, true)).doesNotThrowAnyException();
|
||||||
|
ToolNameValidator.validate(name, false);
|
||||||
|
assertThat(logAppender.list).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validToolName_maxLength() {
|
void validToolNameMaxLength() {
|
||||||
String name = "a".repeat(128);
|
String name = "a".repeat(128);
|
||||||
assertThatCode(() -> ToolNameValidator.validate(name, true)).doesNotThrowAnyException();
|
assertThatCode(() -> ToolNameValidator.validate(name, true)).doesNotThrowAnyException();
|
||||||
|
ToolNameValidator.validate(name, false);
|
||||||
|
assertThat(logAppender.list).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void invalidToolName_null_strictMode() {
|
void nullOrEmpty() {
|
||||||
assertThatThrownBy(() -> ToolNameValidator.validate(null, true)).isInstanceOf(IllegalArgumentException.class)
|
assertThatThrownBy(() -> ToolNameValidator.validate(null, true)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("null or empty");
|
.hasMessageContaining("null or empty");
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void invalidToolName_empty_strictMode() {
|
|
||||||
assertThatThrownBy(() -> ToolNameValidator.validate("", true)).isInstanceOf(IllegalArgumentException.class)
|
assertThatThrownBy(() -> ToolNameValidator.validate("", true)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("null or empty");
|
.hasMessageContaining("null or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void invalidToolName_tooLong_strictMode() {
|
void strictLength() {
|
||||||
String name = "a".repeat(129);
|
String name = "a".repeat(129);
|
||||||
assertThatThrownBy(() -> ToolNameValidator.validate(name, true)).isInstanceOf(IllegalArgumentException.class)
|
assertThatThrownBy(() -> ToolNameValidator.validate(name, true)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("128 characters");
|
.hasMessageContaining("128 characters");
|
||||||
|
|
@ -79,18 +106,42 @@ class ToolNameValidatorTests {
|
||||||
"tööl", // non-ASCII
|
"tööl", // non-ASCII
|
||||||
"工具" // unicode
|
"工具" // unicode
|
||||||
})
|
})
|
||||||
void invalidToolNames_specialCharacters_strictMode(String name) {
|
void strictInvalidCharacters(String name) {
|
||||||
assertThatThrownBy(() -> ToolNameValidator.validate(name, true)).isInstanceOf(IllegalArgumentException.class)
|
assertThatThrownBy(() -> ToolNameValidator.validate(name, true)).isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("invalid characters");
|
.hasMessageContaining("invalid characters");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void invalidToolName_nonStrictMode_doesNotThrow() {
|
void lenientNull() {
|
||||||
// strict=false means warn only, should not throw
|
|
||||||
assertThatCode(() -> ToolNameValidator.validate("invalid name", false)).doesNotThrowAnyException();
|
|
||||||
assertThatCode(() -> ToolNameValidator.validate(null, false)).doesNotThrowAnyException();
|
assertThatCode(() -> ToolNameValidator.validate(null, false)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).satisfies(hasWarning("null or empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lenientEmpty() {
|
||||||
assertThatCode(() -> ToolNameValidator.validate("", false)).doesNotThrowAnyException();
|
assertThatCode(() -> ToolNameValidator.validate("", false)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).satisfies(hasWarning("null or empty"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lenientLength() {
|
||||||
assertThatCode(() -> ToolNameValidator.validate("a".repeat(129), false)).doesNotThrowAnyException();
|
assertThatCode(() -> ToolNameValidator.validate("a".repeat(129), false)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).satisfies(hasWarning("128 characters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lenientInvalidCharacters() {
|
||||||
|
assertThatCode(() -> ToolNameValidator.validate("invalid name", false)).doesNotThrowAnyException();
|
||||||
|
assertThat(logAppender.list).satisfies(hasWarning("invalid characters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<List<? extends ILoggingEvent>> hasWarning(String errorMessage) {
|
||||||
|
return logs -> {
|
||||||
|
assertThat(logs).hasSize(1).first().satisfies(log -> {
|
||||||
|
assertThat(log.getLevel()).isEqualTo(Level.WARN);
|
||||||
|
assertThat(log.getFormattedMessage()).contains(errorMessage);
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue