88import org .utplsql .cli .datasource .TestedDataSourceProvider ;
99
1010import javax .sql .DataSource ;
11+ import java .io .IOException ;
1112import java .nio .file .Files ;
1213import java .nio .file .Path ;
14+ import java .nio .file .Paths ;
1315import java .sql .Connection ;
1416import java .sql .ResultSet ;
1517import java .sql .Statement ;
18+ import java .util .Map ;
19+ import java .util .concurrent .TimeUnit ;
1620import java .util .regex .Matcher ;
1721import java .util .regex .Pattern ;
1822
1923import static org .junit .jupiter .api .Assertions .assertEquals ;
2024import static org .junit .jupiter .api .Assertions .assertTrue ;
25+ import static org .junit .jupiter .api .Assertions .fail ;
2126import static org .junit .jupiter .api .Assumptions .assumeTrue ;
2227
2328/**
2631 * <p>
2732 * The wallet, tnsnames.ora and ojdbc.properties are created on the fly from DB_URL / DB_USER / DB_PASS,
2833 * so no Oracle client tooling (mkstore/orapki) is needed.
34+ * <p>
35+ * TNS_ADMIN and ORACLE_HOME are read once per JVM and environment variables can't be changed at runtime,
36+ * so the scenarios depending on them run the CLI in a separate JVM with a controlled environment.
2937 */
3038class WalletConnectionIT {
3139
3240 private static final String TNS_ALIAS = "UTPLSQL_CLI_WALLET" ;
3341 private static final Pattern EZ_CONNECT = Pattern .compile ("^//([^:/]+)(?::(\\ d+))?/(.+)$" );
42+ private static final long SUBPROCESS_TIMEOUT_MINUTES = 3 ;
3443
3544 @ TempDir
36- static Path tnsAdmin ;
45+ static Path tempDir ;
3746
47+ private static Path tnsAdmin ;
48+ private static Path oracleHome ;
49+ private static Path emptyOracleHome ;
3850 private static String walletConnectString ;
3951
4052 @ BeforeAll
@@ -45,7 +57,7 @@ static void createWallet() throws Exception {
4557 String port = m .group (2 ) == null ? "1521" : m .group (2 );
4658 String service = m .group (3 );
4759
48- Path walletDir = Files .createDirectories (tnsAdmin .resolve ("wallet" ));
60+ Path walletDir = Files .createDirectories (tempDir .resolve ("wallet" ));
4961 char [] walletPassword = "Wallet_Pwd_123" .toCharArray ();
5062
5163 OracleWallet wallet = new OracleWallet ();
@@ -61,16 +73,26 @@ static void createWallet() throws Exception {
6173
6274 assertTrue (Files .exists (walletDir .resolve ("cwallet.sso" )), "auto-login wallet was not created" );
6375
64- Files .writeString (tnsAdmin .resolve ("tnsnames.ora" ),
65- TNS_ALIAS + " = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = " + host + ")(PORT = " + port + "))"
66- + "(CONNECT_DATA = (SERVICE_NAME = " + service + ")))\n " );
67- Files .writeString (tnsAdmin .resolve ("ojdbc.properties" ),
68- "oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY="
69- + forwardSlashes (walletDir ) + ")))\n " );
76+ String tnsnamesOra = TNS_ALIAS + " = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = " + host + ")(PORT = " + port + "))"
77+ + "(CONNECT_DATA = (SERVICE_NAME = " + service + ")))\n " ;
78+ String ojdbcProperties = "oracle.net.wallet_location=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY="
79+ + forwardSlashes (walletDir ) + ")))\n " ;
80+
81+ tnsAdmin = writeNetworkConfig (tempDir .resolve ("tns_admin" ), tnsnamesOra , ojdbcProperties );
82+ oracleHome = tempDir .resolve ("oracle_home" );
83+ writeNetworkConfig (oracleHome .resolve ("network" ).resolve ("admin" ), tnsnamesOra , ojdbcProperties );
84+ emptyOracleHome = Files .createDirectories (tempDir .resolve ("empty_oracle_home" ));
7085
7186 walletConnectString = "/@" + TNS_ALIAS + "?TNS_ADMIN=" + forwardSlashes (tnsAdmin );
7287 }
7388
89+ private static Path writeNetworkConfig (Path dir , String tnsnamesOra , String ojdbcProperties ) throws IOException {
90+ Files .createDirectories (dir );
91+ Files .writeString (dir .resolve ("tnsnames.ora" ), tnsnamesOra );
92+ Files .writeString (dir .resolve ("ojdbc.properties" ), ojdbcProperties );
93+ return dir ;
94+ }
95+
7496 private static String forwardSlashes (Path path ) {
7597 return path .toAbsolutePath ().toString ().replace ('\\' , '/' );
7698 }
@@ -98,4 +120,53 @@ void runCommandWithWallet() {
98120
99121 assertEquals (0 , result );
100122 }
123+
124+ @ Test
125+ void runCommandWithTnsAdminEnvironmentVariable () throws Exception {
126+ assertCliConnectsWithWallet (Map .of ("TNS_ADMIN" , tnsAdmin .toString ()));
127+ }
128+
129+ @ Test
130+ void runCommandWithOracleHomeFallback () throws Exception {
131+ assertCliConnectsWithWallet (Map .of ("ORACLE_HOME" , oracleHome .toString ()));
132+ }
133+
134+ @ Test
135+ void tnsAdminEnvironmentVariableTakesPrecedenceOverOracleHome () throws Exception {
136+ // Fails if ORACLE_HOME/network/admin (without tnsnames.ora) is used instead of TNS_ADMIN
137+ assertCliConnectsWithWallet (Map .of (
138+ "TNS_ADMIN" , tnsAdmin .toString (),
139+ "ORACLE_HOME" , emptyOracleHome .toString ()));
140+ }
141+
142+ /**
143+ * Runs {@code utplsql run /@<TNS alias>} in a separate JVM with TNS_ADMIN and ORACLE_HOME
144+ * replaced by the given environment
145+ */
146+ private void assertCliConnectsWithWallet (Map <String , String > environment ) throws Exception {
147+ Path java = Paths .get (System .getProperty ("java.home" ), "bin" , "java" );
148+ ProcessBuilder processBuilder = new ProcessBuilder (
149+ java .toString (), "-cp" , System .getProperty ("java.class.path" ),
150+ Cli .class .getName (), "run" , "/@" + TNS_ALIAS ,
151+ "-f=ut_documentation_reporter" , "-s" , "--failure-exit-code=0" );
152+
153+ processBuilder .environment ().remove ("TNS_ADMIN" );
154+ processBuilder .environment ().remove ("ORACLE_HOME" );
155+ processBuilder .environment ().putAll (environment );
156+
157+ Path output = Files .createTempFile (tempDir , "cli-output" , ".log" );
158+ processBuilder .redirectErrorStream (true );
159+ processBuilder .redirectOutput (output .toFile ());
160+
161+ Process process = processBuilder .start ();
162+ if (!process .waitFor (SUBPROCESS_TIMEOUT_MINUTES , TimeUnit .MINUTES )) {
163+ process .destroyForcibly ();
164+ fail ("CLI did not finish within " + SUBPROCESS_TIMEOUT_MINUTES + " minutes. Output:\n " + Files .readString (output ));
165+ }
166+
167+ String cliOutput = Files .readString (output );
168+ assertEquals (0 , process .exitValue (), () -> "CLI failed with environment " + environment + ". Output:\n " + cliOutput );
169+ assertTrue (cliOutput .contains ("Use connection string jdbc:oracle:thin:/@" + TNS_ALIAS ),
170+ () -> "Expected wallet connection with environment " + environment + ". Output:\n " + cliOutput );
171+ }
101172}
0 commit comments