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 @@
+
+