From 89791c2470c20cffde0ec612ac7f2115ad7402e2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 06:12:56 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A7=AA=20Add=20test=20file=20for=20Ho?= =?UTF-8?q?conReader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds HoconReaderTest to provide direct test coverage for package-private HoconReader. Covers constructor reflection, document flattening, list parsing, substitutions, system property fallbacks, credential hiding, and error handling for malformed input and missing required substitutions. Co-authored-by: matteobaccan <200925+matteobaccan@users.noreply.github.com> --- .../owner/extras/loaders/HoconReaderTest.java | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java diff --git a/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java b/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java new file mode 100644 index 000000000..e19171add --- /dev/null +++ b/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2012-2026, Luigi R. Viggiano, Matteo Baccan + * All rights reserved. + * + * This software is distributable under the BSD license. + * See the terms of the BSD license in the documentation provided with this software. + */ +package org.aeonbits.owner.extras.loaders; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Direct unit tests for {@link HoconReader}. + * + * @author Matteo Baccan + */ +public class HoconReaderTest { + + @Rule + public final TemporaryFolder folder = new TemporaryFolder(); + + private int documents; + + @Test + public void privateConstructorCanBeInstantiatedForCoverage() throws Exception { + Constructor constructor = HoconReader.class.getDeclaredConstructor(); + constructor.setAccessible(true); + HoconReader instance = constructor.newInstance(); + assertNotNull(instance); + } + + @Test + public void readsObjectAndFlattensKeys() throws Exception { + Properties result = read("server { host = localhost\n port = 8080 }"); + assertEquals("localhost", result.getProperty("server.host")); + assertEquals("8080", result.getProperty("server.port")); + } + + @Test + public void readsIndexedListsAndNestedObjectLists() throws Exception { + Properties result = read("ports = [80, 443]\nservers = [ { host = alpha }, { host = beta } ]"); + assertEquals("80", result.getProperty("ports[0]")); + assertEquals("443", result.getProperty("ports[1]")); + assertEquals("alpha", result.getProperty("servers[0].host")); + assertEquals("beta", result.getProperty("servers[1].host")); + } + + @Test + public void handlesEmptyListAndNullValue() throws Exception { + Properties result = read("emptyList = []\nnullValue = null\nvalidKey = value"); + assertEquals("", result.getProperty("emptyList")); + assertFalse(result.containsKey("nullValue")); + assertEquals("value", result.getProperty("validKey")); + } + + @Test + public void resolvesSubstitutionsAndSelfReferences() throws Exception { + Properties result = read("base = /srv\npath = ${base}/app\nenvPath = /bin\nenvPath = ${envPath}\":/usr/bin\""); + assertEquals("/srv/app", result.getProperty("path")); + assertEquals("/bin:/usr/bin", result.getProperty("envPath")); + } + + @Test + public void handlesOptionalSubstitutionAndSystemProperties() throws Exception { + String key = "owner.hocon.reader.test.prop"; + System.setProperty(key, "sysVal"); + com.typesafe.config.ConfigFactory.invalidateCaches(); + try { + Properties result = read("missingOpt = ${?NON_EXISTENT_VAR}\nsysProp = ${" + key + "}"); + assertFalse(result.containsKey("missingOpt")); + assertEquals("sysVal", result.getProperty("sysProp")); + assertNull(result.getProperty(key)); + } finally { + System.clearProperty(key); + } + } + + @Test + public void throwsIOExceptionForMissingSourceFile() { + File missingFile = new File(folder.getRoot(), "does-not-exist.conf"); + Properties result = new Properties(); + try { + HoconReader.read(result, missingFile.toURI()); + fail("Expected IOException for non-existent file"); + } catch (IOException expected) { + // expected + } + } + + @Test + public void throwsUnsupportedOperationExceptionOnParseErrorAndHidesCredentials() throws Exception { + File file = writeDocument("invalid { hocon = [ ]"); + URI uriWithCreds = URI.create("file://user:secretpassword@localhost" + file.getAbsolutePath()); + + try { + Properties result = new Properties(); + HoconReader.read(result, uriWithCreds); + fail("Expected UnsupportedOperationException for malformed document"); + } catch (UnsupportedOperationException expected) { + assertTrue(expected.getMessage().contains("could not be read as HOCON")); + assertTrue(expected.getMessage().contains("***@localhost")); + assertFalse(expected.getMessage().contains("secretpassword")); + } + } + + @Test + public void throwsUnsupportedOperationExceptionForMissingRequiredSubstitution() throws Exception { + try { + read("host = ${no.such.required.property.anywhere}"); + fail("Expected UnsupportedOperationException for unresolved required substitution"); + } catch (UnsupportedOperationException expected) { + assertTrue(expected.getMessage().contains("could not be read as HOCON")); + } + } + + private Properties read(String content) throws IOException { + File file = writeDocument(content); + Properties result = new Properties(); + HoconReader.read(result, file.toURI()); + return result; + } + + private File writeDocument(String content) throws IOException { + File file = folder.newFile("test" + documents++ + ".conf"); + try (Writer out = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) { + out.write(content); + } + return file; + } +} From 233dc108cdaeea1d57429ad9d57d9b1fda595856 Mon Sep 17 00:00:00 2001 From: matteo baccan Date: Thu, 1 Oct 2026 12:38:47 +0200 Subject: [PATCH 2/2] Build the credentialed file URI portably in HoconReaderTest Concatenating an absolute path after the authority breaks on Windows, where the path starts with a drive letter instead of a slash. --- .../java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java b/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java index e19171add..0b415c2d8 100644 --- a/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java +++ b/owner-extras/src/test/java/org/aeonbits/owner/extras/loaders/HoconReaderTest.java @@ -109,7 +109,7 @@ public void throwsIOExceptionForMissingSourceFile() { @Test public void throwsUnsupportedOperationExceptionOnParseErrorAndHidesCredentials() throws Exception { File file = writeDocument("invalid { hocon = [ ]"); - URI uriWithCreds = URI.create("file://user:secretpassword@localhost" + file.getAbsolutePath()); + URI uriWithCreds = new URI("file", "user:secretpassword", "localhost", -1, file.toURI().getPath(), null, null); try { Properties result = new Properties();