diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 340e2f1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: false - -language: java - -jdk: - - openjdk11 - -cache: - directories: - - $HOME/.m2/repository - -before_script: - - java -version - -script: - - mvn test - diff --git a/README.md b/README.md index e648bbb..d5e3d48 100644 --- a/README.md +++ b/README.md @@ -5,30 +5,41 @@ A simple example plugin demonstrating how to create custom script engines for [Fess](https://fess.codelibs.org/), the open-source enterprise search server. +## What Is a Script Engine? + +A Fess script engine turns a **template** plus a **parameter map** into a value. Fess uses script +engines wherever an administrator can enter a small script or expression — for example to compute a +document boost, derive a field value during crawling, or run logic in a scheduled job. The built-in +`groovy` engine evaluates full Groovy scripts; this example shows the minimal shape of a custom one. + ## Overview -This plugin provides a minimal implementation of a custom script engine for Fess. The `ExampleEngine` serves as a template and starting point for developers who want to create their own script engines with custom template processing logic. +This plugin provides a minimal implementation of a custom script engine for Fess. The `ExampleEngine` +serves as a template and starting point for developers who want to create their own script engines +with custom template processing logic. ### Key Features -- **Simple Implementation**: Demonstrates the basic structure of a Fess script engine -- **Template Pass-through**: Returns template strings unchanged (useful for testing and learning) -- **Full Integration**: Properly integrated with Fess's dependency injection container -- **Comprehensive Tests**: Includes extensive test cases covering edge cases and various scenarios +- **Real, Minimal Evaluation**: Performs simple `${key}` placeholder substitution from the parameter map +- **Idiomatic Structure**: Demonstrates the standard structure of a Fess script engine +- **Self-Registration**: Registers itself with the script engine factory via the DI container +- **Focused Tests**: Meaningful tests covering substitution, missing keys, null/blank input, and factory lookup ## Architecture The plugin extends Fess's `AbstractScriptEngine` class and implements: -- **Template Evaluation**: Process template strings with parameter maps -- **Engine Identification**: Provides a unique name ("example") for the script engine -- **DI Integration**: Configured via LastaDi container for seamless Fess integration +- **Template Evaluation** (`evaluate`): Substitutes `${key}` placeholders with values from the parameter map. + A blank template returns `null`; a missing or `null` value leaves the placeholder untouched. +- **Engine Identification** (`getName`): Provides the unique name (`"example"`) used to register and look up the engine +- **DI Integration**: `fess_se++.xml` registers the engine into Fess's `scriptEngineFactory` at startup + via a `register` postConstruct (the `++` suffix means the fragment is additively merged into the core `fess_se.xml`) ## Installation ### Prerequisites -- Fess 15.0.0 or later +- Fess 15.7.0 or later - Java 21 or later ### Download @@ -42,20 +53,28 @@ You can download the plugin JAR from [Maven Central](https://repo1.maven.org/mav 3. Restart Fess server 4. The "example" script engine will be available for use -For detailed installation instructions, see the [Fess Plugin Guide](https://fess.codelibs.org/15.0/admin/plugin-guide.html). +For detailed installation instructions, see the [Fess Plugin Guide](https://fess.codelibs.org/15.7/admin/plugin-guide.html). ## Usage -Once installed, you can use the "example" script engine in your Fess configuration: +There is **no extra configuration to "use" the engine** — the plugin self-registers via +`fess_se++.xml` when Fess starts. Once installed, the engine is available by its registered name, +`example`, anywhere Fess lets you pick a script type, including: + +- **Data store crawling** — field-mapping scripts that compute index field values +- **Document boost** — boost expressions evaluated per document during crawling +- **Scheduled jobs** — jobs whose *Script Type* is set to an engine name +- **Path mappings / replacements** — value transformations that accept a script type + +In those places, select or enter `example` as the script type and provide a template such as +`Hello ${name}`. Internally Fess resolves the engine through the factory: -```xml - +```java +ComponentUtil.getScriptEngineFactory().getScriptEngine("example").evaluate(template, paramMap); ``` -The engine will process templates by returning them unchanged, making it useful for: -- Testing script engine integration -- Learning how to implement custom script engines -- As a starting point for more complex implementations +This example engine substitutes `${key}` placeholders with values from the parameter map, which +makes it a useful starting point for learning the API and for building richer engines. ## Development @@ -73,12 +92,11 @@ mvn clean package mvn test ``` -The test suite includes 19 comprehensive test cases covering: -- Basic functionality -- Edge cases (null/empty inputs) -- Various data types and special characters -- Multi-line templates and large content -- Instance independence and data integrity +The test suite includes focused test cases covering: +- Placeholder substitution (single, multiple, and non-string values) +- Missing-key and null-value behavior (placeholder left untouched) +- Blank/null template handling (returns `null`) +- Engine name and factory registration/lookup by name ### Code Quality @@ -99,12 +117,16 @@ mvn javadoc:javadoc src/ ├── main/java/ │ └── org/codelibs/fess/script/example/ -│ └── ExampleEngine.java # Main script engine implementation +│ └── ExampleEngine.java # Script engine implementation (${key} substitution) ├── main/resources/ -│ └── fess_se++.xml # DI container configuration -└── test/java/ - └── org/codelibs/fess/script/example/ - └── ExampleEngineTest.java # Comprehensive test suite +│ └── fess_se++.xml # DI fragment that registers the engine (additive merge) +└── test/ + ├── java/ + │ └── org/codelibs/fess/script/example/ + │ ├── ExampleEngineTest.java # Engine tests + factory lookup + │ └── UnitScriptTestCase.java # Minimal UTFlute test base for the plugin + └── resources/ + └── test_app.xml # Test DI container (includes scriptEngineFactory) ``` ## Creating Your Own Script Engine diff --git a/src/main/java/org/codelibs/fess/script/example/ExampleEngine.java b/src/main/java/org/codelibs/fess/script/example/ExampleEngine.java index 0efa39e..327d0d5 100644 --- a/src/main/java/org/codelibs/fess/script/example/ExampleEngine.java +++ b/src/main/java/org/codelibs/fess/script/example/ExampleEngine.java @@ -15,16 +15,36 @@ */ package org.codelibs.fess.script.example; +import java.util.Collections; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.script.AbstractScriptEngine; /** - * Example script engine implementation that demonstrates how to create custom script engines for Fess. - * This implementation simply returns the template string unchanged without any processing. + * Example script engine that demonstrates how to implement a custom script engine for Fess. + * + *

A script engine takes a {@code template} string plus a {@code paramMap} of named values + * and produces a result. Fess invokes engines through {@link AbstractScriptEngine}: this example + * registers itself under the name {@code "example"} (see {@link #getName()}) and is wired into the + * DI container by {@code fess_se++.xml}.

+ * + *

To keep the example easy to follow, this engine performs simple {@code ${key}} placeholder + * substitution: every {@code ${key}} occurrence in the template is replaced with the matching + * value from {@code paramMap}. For example, the template {@code "Hello ${name}"} with + * {@code {name=Fess}} evaluates to {@code "Hello Fess"}.

+ * + *

When you build your own engine, the two things you customize are the evaluation logic in + * {@link #evaluate(String, Map)} and the engine identifier in {@link #getName()}. The real + * {@code GroovyEngine} in Fess follows the same shape but evaluates full Groovy scripts.

*/ public class ExampleEngine extends AbstractScriptEngine { + /** Matches {@code ${key}} placeholders where {@code key} is one or more non-"}" characters. */ + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}"); + /** * Creates a new instance of ExampleEngine. */ @@ -33,21 +53,50 @@ public ExampleEngine() { } /** - * Evaluates the given template with the provided parameter map. - * In this example implementation, the template is returned unchanged without any processing. + * Evaluates the given template by substituting {@code ${key}} placeholders with values + * from the parameter map. + * + *

Null-safety mirrors {@code GroovyEngine}: a blank template returns {@code null}, and a + * {@code null} parameter map is treated as an empty map. A {@code ${key}} whose key is missing + * from the map (or maps to {@code null}) is left untouched in the output, so unresolved + * placeholders remain visible rather than being silently dropped.

* - * @param template the template string to evaluate - * @param paramMap the parameter map containing variables for template evaluation - * @return the template string unchanged + *

CUSTOMIZE HERE: replace this substitution logic with whatever evaluation your engine + * needs (a real scripting language, an expression evaluator, an external template engine, + * etc.).

+ * + * @param template the template string to evaluate (null-safe, returns null if blank) + * @param paramMap the parameters available to the template (null-safe, treated as empty if null) + * @return the evaluated string, or null if the template is blank */ @Override public Object evaluate(final String template, final Map paramMap) { - return template; + if (StringUtil.isBlank(template)) { + return null; + } + + final Map safeParamMap = paramMap != null ? paramMap : Collections.emptyMap(); + + final Matcher matcher = PLACEHOLDER_PATTERN.matcher(template); + final StringBuilder buffer = new StringBuilder(); + while (matcher.find()) { + final String key = matcher.group(1); + final Object value = safeParamMap.get(key); + // Leave the original "${key}" in place when the key is missing or null. + final String replacement = value != null ? value.toString() : matcher.group(); + matcher.appendReplacement(buffer, Matcher.quoteReplacement(replacement)); + } + matcher.appendTail(buffer); + return buffer.toString(); } /** * Returns the name of this script engine. * + *

CUSTOMIZE HERE: this is the identifier the engine is registered and looked up under + * (e.g. via {@code ScriptEngineFactory.getScriptEngine("example")}). Change it to your own + * engine's unique name.

+ * * @return the name "example" that identifies this script engine */ @Override diff --git a/src/main/resources/fess_se++.xml b/src/main/resources/fess_se++.xml index e117415..b42464b 100644 --- a/src/main/resources/fess_se++.xml +++ b/src/main/resources/fess_se++.xml @@ -1,6 +1,14 @@ + diff --git a/src/test/java/org/codelibs/fess/script/example/ExampleEngineTest.java b/src/test/java/org/codelibs/fess/script/example/ExampleEngineTest.java index dfb2e1d..ddb822a 100644 --- a/src/test/java/org/codelibs/fess/script/example/ExampleEngineTest.java +++ b/src/test/java/org/codelibs/fess/script/example/ExampleEngineTest.java @@ -15,392 +15,92 @@ */ package org.codelibs.fess.script.example; -import org.junit.jupiter.api.TestInfo; - import java.util.HashMap; import java.util.Map; +import org.codelibs.fess.script.ScriptEngine; import org.codelibs.fess.util.ComponentUtil; -import org.codelibs.fess.script.example.UnitScriptTestCase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; public class ExampleEngineTest extends UnitScriptTestCase { - public ExampleEngine exampleEngine; - @Override - protected String prepareConfigFile() { - return "test_app.xml"; - } + private ExampleEngine exampleEngine; @Override - protected boolean isSuppressTestCaseTransaction() { - return true; - } - - @Override - public void setUp(TestInfo testInfo) throws Exception { + public void setUp(final TestInfo testInfo) throws Exception { super.setUp(testInfo); exampleEngine = new ExampleEngine(); } - @Override - public void tearDown(TestInfo testInfo) throws Exception { - ComponentUtil.setFessConfig(null); - super.tearDown(testInfo); - } - - public void test_evaluate() { - final Map params = new HashMap<>(); - assertEquals("test", exampleEngine.evaluate("test", params)); - } - - public void test_getName() { - assertEquals("example", exampleEngine.getName()); - } - - // Comprehensive test cases for evaluating various input scenarios - - public void test_evaluate_withNullTemplate() { - final Map params = new HashMap<>(); - assertNull(exampleEngine.evaluate(null, params)); - } - - public void test_evaluate_withEmptyTemplate() { - final Map params = new HashMap<>(); - assertEquals("", exampleEngine.evaluate("", params)); - } - - public void test_evaluate_withNullParameterMap() { - assertEquals("test", exampleEngine.evaluate("test", null)); - } - - public void test_evaluate_withEmptyParameterMap() { + @Test + public void test_evaluate_substitutesPlaceholders() { final Map params = new HashMap<>(); - assertEquals("test", exampleEngine.evaluate("test", params)); + params.put("name", "Fess"); + assertEquals("Hello Fess", exampleEngine.evaluate("Hello ${name}", params)); } - public void test_evaluate_withPopulatedParameterMap() { + @Test + public void test_evaluate_substitutesMultipleAndNonStringValues() { final Map params = new HashMap<>(); - params.put("key1", "value1"); - params.put("key2", 123); - params.put("key3", true); - assertEquals("template content", exampleEngine.evaluate("template content", params)); + params.put("user", "alice"); + params.put("count", 3); + assertEquals("alice has 3 items", exampleEngine.evaluate("${user} has ${count} items", params)); } - public void test_evaluate_withSpecialCharacters() { + @Test + public void test_evaluate_missingKeyIsLeftUntouched() { final Map params = new HashMap<>(); - final String template = "Special chars: !@#$%^&*(){}[]|\\:;\"'<>,.?/~`"; - assertEquals(template, exampleEngine.evaluate(template, params)); + params.put("known", "value"); + // The unknown placeholder remains visible instead of being dropped. + assertEquals("value and ${unknown}", exampleEngine.evaluate("${known} and ${unknown}", params)); } - public void test_evaluate_withMultilineTemplate() { + @Test + public void test_evaluate_nullValueIsLeftUntouched() { final Map params = new HashMap<>(); - final String template = "Line 1\nLine 2\nLine 3"; - assertEquals(template, exampleEngine.evaluate(template, params)); + params.put("key", null); + assertEquals("${key}", exampleEngine.evaluate("${key}", params)); } - public void test_evaluate_withJapaneseCharacters() { - final Map params = new HashMap<>(); - final String template = "こんにちは世界"; - assertEquals(template, exampleEngine.evaluate(template, params)); + @Test + public void test_evaluate_noPlaceholdersReturnsTemplate() { + assertEquals("plain text", exampleEngine.evaluate("plain text", new HashMap<>())); } - public void test_evaluate_withLongTemplate() { - final Map params = new HashMap<>(); - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - sb.append("This is a long template content. "); - } - final String template = sb.toString(); - assertEquals(template, exampleEngine.evaluate(template, params)); + @Test + public void test_evaluate_nullParamMapTreatedAsEmpty() { + assertEquals("${name}", exampleEngine.evaluate("${name}", null)); } - public void test_evaluate_withComplexParameterMap() { + @Test + public void test_evaluate_blankTemplateReturnsNull() { final Map params = new HashMap<>(); - params.put("string", "value"); - params.put("number", 42); - params.put("boolean", true); - params.put("null", null); - params.put("map", new HashMap()); - assertEquals("complex template", exampleEngine.evaluate("complex template", params)); + assertNull(exampleEngine.evaluate(null, params)); + assertNull(exampleEngine.evaluate("", params)); + assertNull(exampleEngine.evaluate(" ", params)); } - public void test_getName_consistency() { - assertEquals("example", exampleEngine.getName()); - assertEquals("example", exampleEngine.getName()); + @Test + public void test_getName() { assertEquals("example", exampleEngine.getName()); } - public void test_getName_returnsConstant() { - final String name1 = exampleEngine.getName(); - final String name2 = exampleEngine.getName(); - assertSame(name1, name2); - } + /** + * Demonstrates how an installed engine is consumed: it self-registers under its name and is + * then retrieved from the factory. {@code register()} adds the engine to the + * {@code scriptEngineFactory} (provided by {@code test_app.xml}) under {@link + * ExampleEngine#getName()}. + */ + @Test + public void test_registerAndLookupByName() { + exampleEngine.register(); - public void test_constructor_initialization() { - final ExampleEngine engine = new ExampleEngine(); + final ScriptEngine engine = ComponentUtil.getScriptEngineFactory().getScriptEngine("example"); assertNotNull(engine); - assertEquals("example", engine.getName()); - } - - public void test_multipleInstances_independence() { - final ExampleEngine engine1 = new ExampleEngine(); - final ExampleEngine engine2 = new ExampleEngine(); - - assertNotSame(engine1, engine2); - assertEquals(engine1.getName(), engine2.getName()); - - final Map params = new HashMap<>(); - assertEquals("test", engine1.evaluate("test", params)); - assertEquals("test", engine2.evaluate("test", params)); - } - - public void test_evaluate_preservesTemplateIntegrity() { - final Map params = new HashMap<>(); - params.put("modify", "attempt"); - - final String originalTemplate = "original template"; - final Object result = exampleEngine.evaluate(originalTemplate, params); - - assertEquals(originalTemplate, result); - assertTrue(result instanceof String); - assertEquals(String.class, result.getClass()); - } - - public void test_evaluate_withWhitespaceOnly() { - final Map params = new HashMap<>(); - assertEquals(" ", exampleEngine.evaluate(" ", params)); - assertEquals(" ", exampleEngine.evaluate(" ", params)); - assertEquals("\t", exampleEngine.evaluate("\t", params)); - assertEquals("\n", exampleEngine.evaluate("\n", params)); - } - - public void test_evaluate_returnTypeConsistency() { - final Map params = new HashMap<>(); - final Object result = exampleEngine.evaluate("test", params); - assertTrue(result instanceof String); - assertEquals("test", result); - } - - // Additional comprehensive test cases for better coverage - - public void test_evaluate_withUnicodeCharacters() { - final Map params = new HashMap<>(); - // Test various Unicode characters including emojis - final String template = "Hello 🌍 World 🚀 Test 😀"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withVariousEmojiSequences() { - final Map params = new HashMap<>(); - final String template = "👨‍👩‍👧‍👦 👍🏻 🏴󠁧󠁢󠁥󠁮󠁧󠁿"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withMixedLanguages() { - final Map params = new HashMap<>(); - final String template = "English 日本語 中文 한글 العربية Русский"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withSQLInjectionLikeStrings() { - final Map params = new HashMap<>(); - final String template = "'; DROP TABLE users; --"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withXSSLikeStrings() { - final Map params = new HashMap<>(); - final String template = ""; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withHTMLEntities() { - final Map params = new HashMap<>(); - final String template = "<div>&"'</div>"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withPathTraversalLikeStrings() { - final Map params = new HashMap<>(); - final String template = "../../etc/passwd"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withControlCharacters() { - final Map params = new HashMap<>(); - final String template = "Line1\r\nLine2\u0000Null\u0007Bell"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withVeryLongString() { - final Map params = new HashMap<>(); - final StringBuilder sb = new StringBuilder(); - // Create a string of 10MB - for (int i = 0; i < 10000; i++) { - sb.append("This is a very long string for testing memory handling. "); - } - final String template = sb.toString(); - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_consecutiveCalls() { - final Map params = new HashMap<>(); - // Test that consecutive calls don't affect each other - assertEquals("first", exampleEngine.evaluate("first", params)); - assertEquals("second", exampleEngine.evaluate("second", params)); - assertEquals("third", exampleEngine.evaluate("third", params)); - } - - public void test_evaluate_parameterMapImmutability() { - final Map params = new HashMap<>(); - params.put("key1", "value1"); - params.put("key2", "value2"); - - final String template = "test"; - final int originalSize = params.size(); - exampleEngine.evaluate(template, params); - - // Verify that the parameter map was not modified - assertEquals(originalSize, params.size()); - assertEquals("value1", params.get("key1")); - assertEquals("value2", params.get("key2")); - } - - public void test_evaluate_withNullValuesInParameterMap() { - final Map params = new HashMap<>(); - params.put("key1", null); - params.put("key2", "value"); - params.put("key3", null); - - final String template = "test with nulls"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withNestedMapsInParameters() { - final Map params = new HashMap<>(); - final Map nestedMap = new HashMap<>(); - nestedMap.put("nested1", "value1"); - nestedMap.put("nested2", 123); - params.put("outer", nestedMap); - - final String template = "nested test"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withListsInParameters() { - final Map params = new HashMap<>(); - final java.util.List list = new java.util.ArrayList<>(); - list.add("item1"); - list.add("item2"); - list.add("item3"); - params.put("list", list); - - final String template = "list test"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_templateImmutability() { - final Map params = new HashMap<>(); - final String originalTemplate = "original"; - final Object result = exampleEngine.evaluate(originalTemplate, params); - - // Verify that the returned value equals the original - assertEquals(originalTemplate, result); - // Verify that it's still a String - assertTrue(result instanceof String); - } - - public void test_evaluate_withRegexSpecialCharacters() { - final Map params = new HashMap<>(); - final String template = ".*+?^${}()|[]\\"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withJSONLikeString() { - final Map params = new HashMap<>(); - final String template = "{\"key\": \"value\", \"number\": 123, \"nested\": {\"inner\": true}}"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withXMLLikeString() { - final Map params = new HashMap<>(); - final String template = "text"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_getName_notNull() { - assertNotNull(exampleEngine.getName()); - } - - public void test_getName_notEmpty() { - assertFalse(exampleEngine.getName().isEmpty()); - } - - public void test_getName_exactValue() { - final String name = exampleEngine.getName(); - assertEquals("example", name); - assertEquals(7, name.length()); - } - - public void test_multipleInstancesWithDifferentTemplates() { - final ExampleEngine engine1 = new ExampleEngine(); - final ExampleEngine engine2 = new ExampleEngine(); - - final Map params1 = new HashMap<>(); - final Map params2 = new HashMap<>(); - - assertEquals("template1", engine1.evaluate("template1", params1)); - assertEquals("template2", engine2.evaluate("template2", params2)); - } - - public void test_evaluate_withBoundaryLengthStrings() { - final Map params = new HashMap<>(); - - // Test single character - assertEquals("a", exampleEngine.evaluate("a", params)); - - // Test two characters - assertEquals("ab", exampleEngine.evaluate("ab", params)); - - // Test exactly 255 characters - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 255; i++) { - sb.append("x"); - } - final String str255 = sb.toString(); - assertEquals(str255, exampleEngine.evaluate(str255, params)); - } - - public void test_evaluate_withBackslashCharacters() { - final Map params = new HashMap<>(); - final String template = "C:\\Users\\Test\\file.txt"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_withQuotationMarks() { - final Map params = new HashMap<>(); - final String template = "He said \"Hello\" and she replied 'Hi'"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - - public void test_evaluate_stateIndependence() { - final Map params = new HashMap<>(); - // First call - exampleEngine.evaluate("first call", params); - // Second call should not be affected by the first - final Object result = exampleEngine.evaluate("second call", params); - assertEquals("second call", result); - } - - public void test_evaluate_withNumbersOnlyString() { - final Map params = new HashMap<>(); - final String template = "1234567890"; - assertEquals(template, exampleEngine.evaluate(template, params)); - } - public void test_evaluate_withMixedContent() { final Map params = new HashMap<>(); - final String template = "Text123!@#$%^&*()_+-=[]{}|;':\",./<>?`~\n\t\r"; - assertEquals(template, exampleEngine.evaluate(template, params)); + params.put("name", "world"); + assertEquals("hi world", engine.evaluate("hi ${name}", params)); } } diff --git a/src/test/java/org/codelibs/fess/script/example/UnitScriptTestCase.java b/src/test/java/org/codelibs/fess/script/example/UnitScriptTestCase.java index f8b2518..124cbc8 100644 --- a/src/test/java/org/codelibs/fess/script/example/UnitScriptTestCase.java +++ b/src/test/java/org/codelibs/fess/script/example/UnitScriptTestCase.java @@ -20,8 +20,18 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.TestInfo; +/** + * Minimal test base for the example plugin. + * + *

Fess core script tests (e.g. {@code GroovyEngineTest}) extend + * {@code org.codelibs.fess.unit.UnitFessTestCase}. That class lives in the Fess + * test sources, so it is published only in the Fess test-jar, which this plugin does + * not depend on (the {@code org.codelibs.fess:fess} dependency is the main jar, {@code provided} + * scope). Because {@code UnitFessTestCase} is therefore not on the plugin classpath, this plugin + * keeps its own thin base that extends UTFlute's {@code LastaFluteTestCase} directly and provides + * the same JUnit 4-style assertion bridges used across Fess tests.

+ */ public abstract class UnitScriptTestCase extends LastaFluteTestCase { - private static final ThreadLocal currentTestInfo = new ThreadLocal<>(); @Override protected String prepareConfigFile() { @@ -29,81 +39,46 @@ protected String prepareConfigFile() { } @Override - protected void setUp(TestInfo testInfo) throws Exception { - currentTestInfo.set(testInfo); - super.setUp(testInfo); - } - - @Override - protected void tearDown(TestInfo testInfo) throws Exception { + protected void tearDown(final TestInfo testInfo) throws Exception { ComponentUtil.setFessConfig(null); super.tearDown(testInfo); } - protected String getName() { - TestInfo info = currentTestInfo.get(); - return info != null ? info.getDisplayName() : "unknown"; - } - // ===== Assert methods for JUnit 4/5 compatibility ===== - protected void fail(String message) { + protected void fail(final String message) { Assertions.fail(message); } - protected void assertTrue(String message, boolean condition) { - Assertions.assertTrue(condition, message); - } - - protected void assertFalse(String message, boolean condition) { - Assertions.assertFalse(condition, message); - } - - protected void assertEquals(String message, Object expected, Object actual) { - Assertions.assertEquals(expected, actual, message); - } - - protected void assertEquals(String message, long expected, long actual) { - Assertions.assertEquals(expected, actual, message); + protected void assertTrue(final boolean condition) { + Assertions.assertTrue(condition); } - protected void assertEquals(String message, double expected, double actual, double delta) { - Assertions.assertEquals(expected, actual, delta, message); - } - - protected void assertEquals(double expected, double actual, double delta) { - Assertions.assertEquals(expected, actual, delta); - } - - protected void assertEquals(String message, float expected, float actual, float delta) { - Assertions.assertEquals(expected, actual, delta, message); - } - - protected void assertEquals(float expected, float actual, float delta) { - Assertions.assertEquals(expected, actual, delta); + protected void assertTrue(final String message, final boolean condition) { + Assertions.assertTrue(condition, message); } - protected void assertNotNull(String message, Object object) { - Assertions.assertNotNull(object, message); + protected void assertFalse(final boolean condition) { + Assertions.assertFalse(condition); } - protected void assertNull(String message, Object object) { - Assertions.assertNull(object, message); + protected void assertFalse(final String message, final boolean condition) { + Assertions.assertFalse(condition, message); } - protected void assertSame(Object expected, Object actual) { - Assertions.assertSame(expected, actual); + protected void assertEquals(final Object expected, final Object actual) { + Assertions.assertEquals(expected, actual); } - protected void assertSame(String message, Object expected, Object actual) { - Assertions.assertSame(expected, actual, message); + protected void assertEquals(final String message, final Object expected, final Object actual) { + Assertions.assertEquals(expected, actual, message); } - protected void assertNotSame(Object expected, Object actual) { - Assertions.assertNotSame(expected, actual); + protected void assertNull(final Object object) { + Assertions.assertNull(object); } - protected void assertNotSame(String message, Object expected, Object actual) { - Assertions.assertNotSame(expected, actual, message); + protected void assertNotNull(final Object object) { + Assertions.assertNotNull(object); } } diff --git a/src/test/resources/test_app.xml b/src/test/resources/test_app.xml index 3e2a057..66d1a76 100644 --- a/src/test/resources/test_app.xml +++ b/src/test/resources/test_app.xml @@ -4,4 +4,8 @@ + +