diff --git a/CHANGELOG.md b/CHANGELOG.md index 463e9401..bad5c336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [9.4.1] +- Fixes env var reading with specific types - Updates dependencies for testing ## [9.4.0] diff --git a/build.gradle b/build.gradle index f1cc9f0a..5251cbe7 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,8 @@ tasks.register('generateMetaInf') { build.dependsOn(generateMetaInf) test { - jvmArgs '-Djava.security.egd=file:/dev/urandom' + jvmArgs '-Djava.security.egd=file:/dev/urandom', + '--add-opens=java.base/java.util=ALL-UNNAMED' minHeapSize = "128m" // initial heap size maxHeapSize = "1024m" // maximum heap size testLogging { diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 28cea0ee..3981bf69 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -863,18 +863,24 @@ public void updateConfigJsonFromEnv(JsonObject configJson) { .replace("\\\\", "\\"); } + // Empty/unset stringValue is already handled above (null/empty check), + // so boxed types (Integer, Long, etc.) can safely share branches + // with their primitive counterparts. if (field.getType().equals(String.class)) { configJson.addProperty(field.getName(), stringValue); - } else if (field.getType().equals(int.class)) { + } else if (field.getType().equals(int.class) || field.getType().equals(Integer.class)) { configJson.addProperty(field.getName(), Integer.parseInt(stringValue)); - } else if (field.getType().equals(long.class)) { + } else if (field.getType().equals(long.class) || field.getType().equals(Long.class)) { configJson.addProperty(field.getName(), Long.parseLong(stringValue)); - } else if (field.getType().equals(boolean.class)) { + } else if (field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)) { configJson.addProperty(field.getName(), Boolean.parseBoolean(stringValue)); - } else if (field.getType().equals(float.class)) { + } else if (field.getType().equals(float.class) || field.getType().equals(Float.class)) { configJson.addProperty(field.getName(), Float.parseFloat(stringValue)); - } else if (field.getType().equals(double.class)) { + } else if (field.getType().equals(double.class) || field.getType().equals(Double.class)) { configJson.addProperty(field.getName(), Double.parseDouble(stringValue)); + } else { + throw new RuntimeException( + "Unknown field type " + field.getType().getName() + " for config field " + field.getName()); } } } diff --git a/src/test/java/io/supertokens/storage/postgresql/test/EnvConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/EnvConfigTest.java new file mode 100644 index 00000000..28e72fd3 --- /dev/null +++ b/src/test/java/io/supertokens/storage/postgresql/test/EnvConfigTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * 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 io.supertokens.storage.postgresql.test; + +import com.google.gson.JsonObject; +import io.supertokens.storage.postgresql.Start; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.Map; + +import static org.junit.Assert.*; + +public class EnvConfigTest { + + private static void setEnv(String key, String value) { + try { + Map env = System.getenv(); + Class cl = env.getClass(); + Field field = cl.getDeclaredField("m"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + Map writableEnv = (Map) field.get(env); + writableEnv.put(key, value); + } catch (Exception e) { + throw new IllegalStateException("Failed to set environment variable", e); + } + } + + private static void removeEnv(String key) { + try { + Map env = System.getenv(); + Class cl = env.getClass(); + Field field = cl.getDeclaredField("m"); + field.setAccessible(true); + @SuppressWarnings("unchecked") + Map writableEnv = (Map) field.get(env); + writableEnv.remove(key); + } catch (Exception e) { + throw new IllegalStateException("Failed to remove environment variable", e); + } + } + + @Test + public void testUpdateConfigJsonFromEnvHandlesBoxedTypes() { + // Regression test: Integer fields like postgresql_minimum_idle_connections + // were silently dropped because updateConfigJsonFromEnv only checked + // primitive types (int.class) not boxed types (Integer.class). + Start start = new Start(); + try { + setEnv("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS", "5"); + + JsonObject configJson = new JsonObject(); + start.updateConfigJsonFromEnv(configJson); + + assertTrue("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS env var should be loaded into configJson", + configJson.has("postgresql_minimum_idle_connections")); + assertEquals(5, configJson.get("postgresql_minimum_idle_connections").getAsInt()); + } finally { + removeEnv("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS"); + } + } + + @Test + public void testUpdateConfigJsonFromEnvHandlesBoxedTypeWithZero() { + // Tests that Integer value of 0 is correctly loaded (not confused with null/unset) + Start start = new Start(); + try { + setEnv("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS", "0"); + + JsonObject configJson = new JsonObject(); + start.updateConfigJsonFromEnv(configJson); + + assertTrue("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS env var with value 0 should be loaded", + configJson.has("postgresql_minimum_idle_connections")); + assertEquals(0, configJson.get("postgresql_minimum_idle_connections").getAsInt()); + } finally { + removeEnv("POSTGRESQL_MINIMUM_IDLE_CONNECTIONS"); + } + } +}