aglinxinyuan commented on code in PR #5698:
URL: https://github.com/apache/texera/pull/5698#discussion_r3408758406


##########
common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/sql/postgresql/PostgreSQLConnUtilSpec.scala:
##########
@@ -0,0 +1,226 @@
+/*
+ * 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.texera.amber.operator.source.sql.postgresql
+
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.BeforeAndAfterAll
+
+import java.lang.reflect.{InvocationHandler, Method, Proxy}
+import java.sql.{Connection, Driver, DriverManager, DriverPropertyInfo, 
SQLException}
+import java.util.Properties
+import java.util.logging.Logger
+import scala.collection.mutable.ArrayBuffer
+import scala.jdk.CollectionConverters._
+
+class PostgreSQLConnUtilSpec extends AnyFlatSpec with BeforeAndAfterAll {
+
+  // 
---------------------------------------------------------------------------
+  // Strategy — pin the JDBC URL composition (the only application-logic in
+  // this util) without a real DB.
+  //
+  // The workflow-operator test classpath DOES include the real PostgreSQL
+  // driver (transitively), and that driver eats `jdbc:postgresql:` URLs
+  // before returning a generic "The connection attempt failed." exception.
+  // So we can't rely on `DriverManager.getConnection`'s default
+  // "No suitable driver" message.
+  //
+  // Instead, we deregister every driver claiming `jdbc:postgresql:`,
+  // register a capturing driver that records each URL it is asked to open
+  // (and returns a Proxy-backed Connection so the production code can call
+  // `setReadOnly`), run the assertions, then restore the real drivers
+  // in afterAll.
+  // 
---------------------------------------------------------------------------
+
+  private object CapturingPGDriver extends Driver {
+    val seenUrls: ArrayBuffer[String] = ArrayBuffer.empty
+    val seenProps: ArrayBuffer[Properties] = ArrayBuffer.empty
+    val readOnlyCalls: ArrayBuffer[Boolean] = ArrayBuffer.empty
+
+    override def connect(url: String, info: Properties): Connection = {
+      if (!acceptsURL(url)) return null
+      seenUrls += url
+      seenProps += info
+      Proxy
+        .newProxyInstance(
+          getClass.getClassLoader,
+          Array(classOf[Connection]),
+          new InvocationHandler {
+            override def invoke(p: Any, m: Method, args: Array[AnyRef]): 
AnyRef =
+              m.getName match {
+                case "setReadOnly" =>
+                  readOnlyCalls += 
args(0).asInstanceOf[java.lang.Boolean].booleanValue()
+                  null
+                // Object methods — required so `conn != null`, 
`conn.toString`,
+                // and identity HashMap-keying work without NPE on 
auto-unboxing.
+                case "equals"       => java.lang.Boolean.valueOf(p eq args(0))
+                case "hashCode"     => 
java.lang.Integer.valueOf(System.identityHashCode(p))
+                case "toString"     => "CapturingPGDriver.StubConnection@" + 
System.identityHashCode(p)
+                case "isWrapperFor" => java.lang.Boolean.FALSE
+                case "close"        => null
+                case _              => null
+              }
+          }
+        )
+        .asInstanceOf[Connection]
+    }
+    override def acceptsURL(url: String): Boolean =
+      url != null && url.startsWith("jdbc:postgresql:")
+    override def getPropertyInfo(url: String, info: Properties): 
Array[DriverPropertyInfo] =
+      Array.empty
+    override def getMajorVersion: Int = 1
+    override def getMinorVersion: Int = 0
+    override def jdbcCompliant(): Boolean = false
+    override def getParentLogger: Logger = 
Logger.getLogger("test-pg-capturing")
+  }
+
+  // Snapshot the real PG drivers so afterAll can restore them.
+  private val savedRealDrivers: List[Driver] = ArrayBuffer.empty[Driver].toList
+

Review Comment:
   Fixed in 1c85337853 — dropped the dead `savedRealDrivers` placeholder and 
renamed the actually-used buffer back to `savedRealDrivers: 
ArrayBuffer[Driver]`. Restore logic now reads from the same buffer it wrote to.



##########
common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/sql/postgresql/PostgreSQLConnUtilSpec.scala:
##########
@@ -0,0 +1,226 @@
+/*
+ * 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.texera.amber.operator.source.sql.postgresql
+
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.BeforeAndAfterAll
+
+import java.lang.reflect.{InvocationHandler, Method, Proxy}
+import java.sql.{Connection, Driver, DriverManager, DriverPropertyInfo, 
SQLException}
+import java.util.Properties
+import java.util.logging.Logger
+import scala.collection.mutable.ArrayBuffer
+import scala.jdk.CollectionConverters._
+
+class PostgreSQLConnUtilSpec extends AnyFlatSpec with BeforeAndAfterAll {
+
+  // 
---------------------------------------------------------------------------
+  // Strategy — pin the JDBC URL composition (the only application-logic in
+  // this util) without a real DB.
+  //
+  // The workflow-operator test classpath DOES include the real PostgreSQL
+  // driver (transitively), and that driver eats `jdbc:postgresql:` URLs
+  // before returning a generic "The connection attempt failed." exception.
+  // So we can't rely on `DriverManager.getConnection`'s default
+  // "No suitable driver" message.
+  //
+  // Instead, we deregister every driver claiming `jdbc:postgresql:`,
+  // register a capturing driver that records each URL it is asked to open
+  // (and returns a Proxy-backed Connection so the production code can call
+  // `setReadOnly`), run the assertions, then restore the real drivers
+  // in afterAll.
+  // 
---------------------------------------------------------------------------
+
+  private object CapturingPGDriver extends Driver {
+    val seenUrls: ArrayBuffer[String] = ArrayBuffer.empty
+    val seenProps: ArrayBuffer[Properties] = ArrayBuffer.empty
+    val readOnlyCalls: ArrayBuffer[Boolean] = ArrayBuffer.empty
+
+    override def connect(url: String, info: Properties): Connection = {
+      if (!acceptsURL(url)) return null
+      seenUrls += url
+      seenProps += info
+      Proxy
+        .newProxyInstance(
+          getClass.getClassLoader,
+          Array(classOf[Connection]),
+          new InvocationHandler {
+            override def invoke(p: Any, m: Method, args: Array[AnyRef]): 
AnyRef =
+              m.getName match {
+                case "setReadOnly" =>
+                  readOnlyCalls += 
args(0).asInstanceOf[java.lang.Boolean].booleanValue()
+                  null
+                // Object methods — required so `conn != null`, 
`conn.toString`,
+                // and identity HashMap-keying work without NPE on 
auto-unboxing.
+                case "equals"       => java.lang.Boolean.valueOf(p eq args(0))
+                case "hashCode"     => 
java.lang.Integer.valueOf(System.identityHashCode(p))
+                case "toString"     => "CapturingPGDriver.StubConnection@" + 
System.identityHashCode(p)
+                case "isWrapperFor" => java.lang.Boolean.FALSE
+                case "close"        => null
+                case _              => null
+              }
+          }
+        )
+        .asInstanceOf[Connection]
+    }
+    override def acceptsURL(url: String): Boolean =
+      url != null && url.startsWith("jdbc:postgresql:")
+    override def getPropertyInfo(url: String, info: Properties): 
Array[DriverPropertyInfo] =
+      Array.empty
+    override def getMajorVersion: Int = 1
+    override def getMinorVersion: Int = 0
+    override def jdbcCompliant(): Boolean = false
+    override def getParentLogger: Logger = 
Logger.getLogger("test-pg-capturing")
+  }
+
+  // Snapshot the real PG drivers so afterAll can restore them.
+  private val savedRealDrivers: List[Driver] = ArrayBuffer.empty[Driver].toList
+
+  override protected def beforeAll(): Unit = {
+    super.beforeAll()
+    // Remove every other driver that accepts jdbc:postgresql: so our
+    // capturing driver is the only one DriverManager.getConnection sees.
+    val others = DriverManager.getDrivers.asScala.toList.filter { d =>
+      d != CapturingPGDriver && d.acceptsURL("jdbc:postgresql://h/d")
+    }
+    others.foreach { d =>
+      savedRealDriversBuffer += d
+      DriverManager.deregisterDriver(d)
+    }
+    DriverManager.registerDriver(CapturingPGDriver)
+  }

Review Comment:
   Fixed in 1c85337853 — added a `safeAcceptsURL` helper that swallows any 
throw from `Driver.acceptsURL` (the JDBC spec allows it to throw 
`SQLException`). The whole `beforeAll` body is also wrapped in try/catch with 
best-effort re-registration of any drivers we already deregistered before 
failing — keeps the JVM JDBC registry consistent for sibling tests on setup 
failure.



##########
common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/sql/mysql/MySQLConnUtilSpec.scala:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.texera.amber.operator.source.sql.mysql
+
+import org.scalatest.flatspec.AnyFlatSpec
+
+import java.sql.SQLException
+
+class MySQLConnUtilSpec extends AnyFlatSpec {
+
+  // 
---------------------------------------------------------------------------
+  // Strategy — same approach as PostgreSQLConnUtilSpec. The workflow-operator
+  // test classpath does not carry a MySQL driver, so 
DriverManager.getConnection
+  // throws SQLException("No suitable driver found for " + url). The URL is
+  // present in the exception message, which is what we pin on.
+  // 
---------------------------------------------------------------------------

Review Comment:
   Fixed in 1c85337853 — rewrote `MySQLConnUtilSpec` around the same 
capturing-driver pattern as `PostgreSQLConnUtilSpec`. The Proxy-backed 
`Connection` now lets the spec pin `setReadOnly(true)` on MySQL too (parity 
with the PG spec), and the spec no longer relies on a real MySQL driver being 
absent from the classpath — works in both configurations.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to