ConfigDeserializerTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tika.serialization;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.apache.tika.config.ParseContextConfig;
import org.apache.tika.parser.ParseContext;
public class ConfigDeserializerTest {
/**
* Simple test config class to verify immutability
*/
public static class TestConfig {
private String name = "default";
private int value = 100;
private boolean enabled = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
@Test
public void testDefaultConfigImmutability() throws Exception {
// Create a default config
TestConfig defaultConfig = new TestConfig();
defaultConfig.setName("default");
defaultConfig.setValue(100);
defaultConfig.setEnabled(false);
// Store original values
String originalName = defaultConfig.getName();
int originalValue = defaultConfig.getValue();
boolean originalEnabled = defaultConfig.isEnabled();
// Create ParseContext with user config that overrides some values
ParseContext context = new ParseContext();
context.setJsonConfig("test-config", "{\"name\":\"override\",\"value\":200}");
// Get merged config
TestConfig mergedConfig = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class, defaultConfig);
// Verify merged config has user overrides
assertNotNull(mergedConfig);
assertEquals("override", mergedConfig.getName());
assertEquals(200, mergedConfig.getValue());
assertEquals(false, mergedConfig.isEnabled()); // Not overridden, should use default
// CRITICAL: Verify defaultConfig was NOT modified
assertEquals(originalName, defaultConfig.getName(), "defaultConfig.name should not be modified");
assertEquals(originalValue, defaultConfig.getValue(), "defaultConfig.value should not be modified");
assertEquals(originalEnabled, defaultConfig.isEnabled(), "defaultConfig.enabled should not be modified");
// Verify we got a different object
assertNotSame(defaultConfig, mergedConfig, "Should return a new config object, not the default");
}
@Test
public void testDefaultConfigImmutabilityMultipleCalls() throws Exception {
// Create a shared default config (simulating what parsers do)
TestConfig sharedDefault = new TestConfig();
sharedDefault.setName("shared");
sharedDefault.setValue(50);
sharedDefault.setEnabled(true);
// First request with one override
ParseContext context1 = new ParseContext();
context1.setJsonConfig("test-config", "{\"value\":100}");
TestConfig config1 = ConfigDeserializer.getConfig(context1, "test-config", TestConfig.class, sharedDefault);
// Second request with different override
ParseContext context2 = new ParseContext();
context2.setJsonConfig("test-config", "{\"name\":\"request2\",\"enabled\":false}");
TestConfig config2 = ConfigDeserializer.getConfig(context2, "test-config", TestConfig.class, sharedDefault);
// Verify each request got its own merged config
assertEquals("shared", config1.getName());
assertEquals(100, config1.getValue());
assertEquals(true, config1.isEnabled());
assertEquals("request2", config2.getName());
assertEquals(50, config2.getValue()); // Used default
assertEquals(false, config2.isEnabled());
// CRITICAL: Verify shared default was never modified
assertEquals("shared", sharedDefault.getName());
assertEquals(50, sharedDefault.getValue());
assertEquals(true, sharedDefault.isEnabled());
// Verify all three are different objects
assertNotSame(sharedDefault, config1);
assertNotSame(sharedDefault, config2);
assertNotSame(config1, config2);
}
@Test
public void testNoDefaultConfig() throws Exception {
ParseContext context = new ParseContext();
context.setJsonConfig("test-config", "{\"name\":\"test\",\"value\":123}");
TestConfig config = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class, null);
assertNotNull(config);
assertEquals("test", config.getName());
assertEquals(123, config.getValue());
assertEquals(false, config.isEnabled()); // Default value from class
}
@Test
public void testNoUserConfig() throws Exception {
TestConfig defaultConfig = new TestConfig();
defaultConfig.setName("default");
defaultConfig.setValue(999);
// No JSON config in ParseContext
ParseContext context = new ParseContext();
TestConfig config = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class, defaultConfig);
// Should return the default config as-is
assertEquals(defaultConfig, config);
assertEquals("default", config.getName());
assertEquals(999, config.getValue());
}
@Test
public void testNoJsonConfig() throws Exception {
TestConfig defaultConfig = new TestConfig();
defaultConfig.setName("default");
ParseContext context = new ParseContext();
// No JSON config set
TestConfig config = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class, defaultConfig);
assertEquals(defaultConfig, config);
}
@Test
public void testHasConfig() throws Exception {
ParseContext context = new ParseContext();
context.setJsonConfig("parser-a", "{\"key\":\"value\"}");
context.setJsonConfig("parser-b", "{\"key\":\"value\"}");
assertTrue(ConfigDeserializer.hasConfig(context, "parser-a"));
assertTrue(ConfigDeserializer.hasConfig(context, "parser-b"));
assertFalse(ConfigDeserializer.hasConfig(context, "parser-c"));
}
@Test
public void testHasConfigNoJsonConfigs() throws Exception {
ParseContext context = new ParseContext();
assertFalse(ConfigDeserializer.hasConfig(context, "parser-a"));
}
@Test
public void testHasConfigNullContext() throws Exception {
assertFalse(ConfigDeserializer.hasConfig(null, "parser-a"));
}
@Test
public void testGetConfigNullContext() throws Exception {
TestConfig defaultConfig = new TestConfig();
TestConfig config = ConfigDeserializer.getConfig(null, "test-config", TestConfig.class, defaultConfig);
assertEquals(defaultConfig, config);
}
@Test
public void testGetConfigWithoutDefault() throws Exception {
ParseContext context = new ParseContext();
context.setJsonConfig("test-config", "{\"name\":\"test\"}");
TestConfig config = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class);
assertNotNull(config);
assertEquals("test", config.getName());
}
@Test
public void testGetConfigWithoutDefaultNoUserConfig() throws Exception {
ParseContext context = new ParseContext();
TestConfig config = ConfigDeserializer.getConfig(context, "test-config", TestConfig.class);
assertNull(config);
}
@Test
public void testParseContextConfigWrapperDelegation() throws Exception {
// Test that ParseContextConfig correctly delegates to ConfigDeserializer
// when tika-serialization is on the classpath
TestConfig defaultConfig = new TestConfig();
defaultConfig.setName("default");
defaultConfig.setValue(100);
ParseContext context = new ParseContext();
context.setJsonConfig("test-parser", "{\"name\":\"override\",\"value\":200}");
// Use the wrapper
TestConfig config = ParseContextConfig.getConfig(context, "test-parser", TestConfig.class, defaultConfig);
// Should get merged config
assertNotNull(config);
assertEquals("override", config.getName());
assertEquals(200, config.getValue());
// Verify immutability
assertEquals("default", defaultConfig.getName());
assertEquals(100, defaultConfig.getValue());
}
@Test
public void testParseContextConfigWrapperNoConfig() throws Exception {
// Test wrapper when no config is present
TestConfig defaultConfig = new TestConfig();
defaultConfig.setName("default");
ParseContext context = new ParseContext();
TestConfig config = ParseContextConfig.getConfig(context, "test-parser", TestConfig.class, defaultConfig);
assertEquals(defaultConfig, config);
}
@Test
public void testParseContextConfigWrapperIsAvailable() {
// Verify ConfigDeserializer is detected as available in this test environment
assertTrue(ParseContextConfig.isConfigDeserializerAvailable(),
"ConfigDeserializer should be available when tika-serialization is on classpath");
}
@Test
public void testParseContextConfigWrapperHasConfig() {
ParseContext context = new ParseContext();
context.setJsonConfig("parser-a", "{\"key\":\"value\"}");
assertTrue(ParseContextConfig.hasConfig(context, "parser-a"));
assertFalse(ParseContextConfig.hasConfig(context, "parser-b"));
}
}