Copilot commented on code in PR #8035:
URL: https://github.com/apache/texera/pull/8035#discussion_r3869472282


##########
amber/src/test/scala/org/apache/texera/web/service/WorkflowExecutionServiceSpec.scala:
##########
@@ -54,19 +83,155 @@ import scala.collection.mutable
   * purpose: construction must stay side-effect-free (all throwing work is in
   * `executeWorkflow`), so a future change that dereferences them during
   * construction would fail here.
+  *
+  * The suite also owns two paths that need more scaffolding than that:
+  *
+  *   - `executeWorkflow`'s compilation-failure arm, driven for real (not 
simulated
+  *     through `errorHandler`) with a CSV scan that has no file selected -- 
the
+  *     recipe `SyncExecutionResourceSpec` establishes and 
`WorkflowServiceSpec`
+  *     reuses. Compilation rejects it inside 
`LogicalPlan.resolveScanSourceOpFileName`
+  *     before `FileResolver` is reached, so no storage is involved, and the 
early
+  *     `return` stops the method before 
`ComputingUnitMaster.createAmberRuntime`,
+  *     which would build an `AmberClient` on `AmberRuntime.actorSystem` -- 
null
+  *     outside a started coordinator, and whatever another suite left behind 
inside
+  *     the shared serial JVM.
+  *
+  *   - `unsubscribeAll`, the session-teardown contract. This is what makes the
+  *     suite carry a TestKit `ActorSystem` and `MockTexeraDB`: the method 
walks the
+  *     four runtime services, and `ExecutionStatsService`'s constructor 
creates its
+  *     iceberg runtime-statistics table and stamps the URI onto a 
`workflow_executions`
+  *     row, so a non-null value for that field cannot be had more cheaply. 
There is
+  *     no second home for the test -- one spec file per source class -- and 
stubbing
+  *     the services out would leave the four teardown calls unobserved, which 
is the
+  *     entire content of the method.
+  *
+  * The runtime half of `executeWorkflow` (everything from 
`createAmberRuntime` on) is
+  * out of reach here for the reason above and is exercised by the integration 
suites.
+  *
+  * Known, pre-existing: `ExecutionStatsService` never shuts down its private
+  * `metricsPersistThread` (it does not override `unsubscribeAll`), so the 
teardown
+  * test below leaks one single-thread executor into the shared JVM, as every
+  * construction of that service already does.
+  *
+  * Three further gaps are deliberately described and NOT pinned, because a 
test that
+  * froze the current behaviour would cement it:
+  *
+  *   - Partial construction. `executeWorkflow` assigns `client` first and the 
four
+  *     services after it, and `WorkflowService` catches whatever they throw 
while
+  *     leaving the half-built service published. `unsubscribeAll` then passes 
its
+  *     `client != null` guard and NPEs on the first null service, aborting 
teardown.
+  *
+  *   - A recovery that ends in failure. `createStateEvent` carves out 
COMPLETED only,
+  *     while the flag itself is lowered only by a 
`WorkflowRecoveryStatus(false)` from
+  *     the engine, which an execution that dies mid-recovery need not ever 
send. Such an
+  *     execution reaches FAILED with the flag still raised and is announced as
+  *     "Recovering" from then on.
+  *
+  *   - `ExecutionReconfigurationService.registerWorkerCompletionCallback` 
discards the
+  *     `Disposable` its `registerCallback` returns instead of handing it to
+  *     `addSubscription`, so that one callback outlives the teardown below. 
Not this
+  *     class's defect, and not this suite's to pin.
   */
-class WorkflowExecutionServiceSpec extends AnyFlatSpec with Matchers {
+class WorkflowExecutionServiceSpec
+    extends TestKit(ActorSystem("WorkflowExecutionServiceSpec"))
+    with AnyFlatSpecLike
+    with Matchers
+    with BeforeAndAfterAll
+    with MockTexeraDB {
+
+  // Distinct from every other suite's ids: the statistics URI is derived from 
wid/eid and
+  // `createDocument` truncates whatever table already sits at it 
(ExecutionStatsServiceSpec
+  // owns 9107/9108).
+  private val testUid: Integer = 9207
+  private val testWid: Integer = 9207
+  private val testEid: Integer = 9208
+
+  /**
+    * The computing unit the fixture execution ran on. A different literal 
from `testWid` on
+    * purpose: `getLatestExecutionID(wid, cuid)` binds two bare `Integer`s 
into one predicate, so a
+    * fixture that reused one number for both would make a transposition of 
the two arguments
+    * produce byte-identical SQL.
+    */
+  private val testCuid: Integer = 9209
+
+  /** A computing unit with no executions, so the cuid leg of the predicate is 
not vacuous. */
+  private val otherCuid: Integer = 9210
+
+  override protected def beforeAll(): Unit = {
+    initializeDBAndReplaceDSLContext()
+
+    val user = new User
+    user.setUid(testUid)

Review Comment:
   `beforeAll` overrides ScalaTest lifecycle but doesn't call 
`super.beforeAll()`. If another mixed-in trait later adds its own `beforeAll`, 
this override will prevent it from running (trait-stacking contract). Call 
`super.beforeAll()` as the first statement to keep lifecycle hooks composable.



##########
amber/src/test/scala/org/apache/texera/web/service/WorkflowExecutionServiceSpec.scala:
##########
@@ -54,19 +83,155 @@ import scala.collection.mutable
   * purpose: construction must stay side-effect-free (all throwing work is in
   * `executeWorkflow`), so a future change that dereferences them during
   * construction would fail here.
+  *
+  * The suite also owns two paths that need more scaffolding than that:
+  *
+  *   - `executeWorkflow`'s compilation-failure arm, driven for real (not 
simulated
+  *     through `errorHandler`) with a CSV scan that has no file selected -- 
the
+  *     recipe `SyncExecutionResourceSpec` establishes and 
`WorkflowServiceSpec`
+  *     reuses. Compilation rejects it inside 
`LogicalPlan.resolveScanSourceOpFileName`
+  *     before `FileResolver` is reached, so no storage is involved, and the 
early
+  *     `return` stops the method before 
`ComputingUnitMaster.createAmberRuntime`,
+  *     which would build an `AmberClient` on `AmberRuntime.actorSystem` -- 
null
+  *     outside a started coordinator, and whatever another suite left behind 
inside
+  *     the shared serial JVM.
+  *
+  *   - `unsubscribeAll`, the session-teardown contract. This is what makes the
+  *     suite carry a TestKit `ActorSystem` and `MockTexeraDB`: the method 
walks the
+  *     four runtime services, and `ExecutionStatsService`'s constructor 
creates its
+  *     iceberg runtime-statistics table and stamps the URI onto a 
`workflow_executions`
+  *     row, so a non-null value for that field cannot be had more cheaply. 
There is
+  *     no second home for the test -- one spec file per source class -- and 
stubbing
+  *     the services out would leave the four teardown calls unobserved, which 
is the
+  *     entire content of the method.
+  *
+  * The runtime half of `executeWorkflow` (everything from 
`createAmberRuntime` on) is
+  * out of reach here for the reason above and is exercised by the integration 
suites.
+  *
+  * Known, pre-existing: `ExecutionStatsService` never shuts down its private
+  * `metricsPersistThread` (it does not override `unsubscribeAll`), so the 
teardown
+  * test below leaks one single-thread executor into the shared JVM, as every
+  * construction of that service already does.
+  *
+  * Three further gaps are deliberately described and NOT pinned, because a 
test that
+  * froze the current behaviour would cement it:
+  *
+  *   - Partial construction. `executeWorkflow` assigns `client` first and the 
four
+  *     services after it, and `WorkflowService` catches whatever they throw 
while
+  *     leaving the half-built service published. `unsubscribeAll` then passes 
its
+  *     `client != null` guard and NPEs on the first null service, aborting 
teardown.
+  *
+  *   - A recovery that ends in failure. `createStateEvent` carves out 
COMPLETED only,
+  *     while the flag itself is lowered only by a 
`WorkflowRecoveryStatus(false)` from
+  *     the engine, which an execution that dies mid-recovery need not ever 
send. Such an
+  *     execution reaches FAILED with the flag still raised and is announced as
+  *     "Recovering" from then on.
+  *
+  *   - `ExecutionReconfigurationService.registerWorkerCompletionCallback` 
discards the
+  *     `Disposable` its `registerCallback` returns instead of handing it to
+  *     `addSubscription`, so that one callback outlives the teardown below. 
Not this
+  *     class's defect, and not this suite's to pin.
   */
-class WorkflowExecutionServiceSpec extends AnyFlatSpec with Matchers {
+class WorkflowExecutionServiceSpec
+    extends TestKit(ActorSystem("WorkflowExecutionServiceSpec"))
+    with AnyFlatSpecLike
+    with Matchers
+    with BeforeAndAfterAll
+    with MockTexeraDB {
+
+  // Distinct from every other suite's ids: the statistics URI is derived from 
wid/eid and
+  // `createDocument` truncates whatever table already sits at it 
(ExecutionStatsServiceSpec
+  // owns 9107/9108).
+  private val testUid: Integer = 9207
+  private val testWid: Integer = 9207
+  private val testEid: Integer = 9208
+
+  /**
+    * The computing unit the fixture execution ran on. A different literal 
from `testWid` on
+    * purpose: `getLatestExecutionID(wid, cuid)` binds two bare `Integer`s 
into one predicate, so a
+    * fixture that reused one number for both would make a transposition of 
the two arguments
+    * produce byte-identical SQL.
+    */
+  private val testCuid: Integer = 9209
+
+  /** A computing unit with no executions, so the cuid leg of the predicate is 
not vacuous. */
+  private val otherCuid: Integer = 9210
+
+  override protected def beforeAll(): Unit = {
+    initializeDBAndReplaceDSLContext()
+
+    val user = new User
+    user.setUid(testUid)
+    user.setName("workflow-execution-test-user")
+    user.setEmail(s"[email protected]")
+    new UserDao(getDSLContext.configuration()).insert(user)
+
+    val workflow = new Workflow
+    workflow.setWid(testWid)
+    workflow.setName(s"workflow-execution-test-$testWid")
+    workflow.setContent("{}")
+    workflow.setDescription("")
+    workflow.setCreationTime(new SqlTimestamp(System.currentTimeMillis()))
+    workflow.setLastModifiedTime(new SqlTimestamp(System.currentTimeMillis()))
+    new WorkflowDao(getDSLContext.configuration()).insert(workflow)
+
+    val version = new WorkflowVersion
+    version.setWid(testWid)
+    version.setContent("{}")
+    version.setCreationTime(new SqlTimestamp(System.currentTimeMillis()))
+    new WorkflowVersionDao(getDSLContext.configuration()).insert(version)
+
+    val computingUnitDao = new 
WorkflowComputingUnitDao(getDSLContext.configuration())
+    List(testCuid -> "workflow-execution-test-unit", otherCuid -> 
"workflow-execution-idle-unit")
+      .foreach {
+        case (cuid, name) =>
+          val unit = new WorkflowComputingUnit
+          unit.setCuid(cuid)
+          unit.setUid(testUid)
+          unit.setName(name)
+          unit.setCreationTime(new SqlTimestamp(System.currentTimeMillis()))
+          unit.setType(WorkflowComputingUnitTypeEnum.local)
+          unit.setUri("local://test")
+          unit.setResource("{}")
+          computingUnitDao.insert(unit)
+      }
+
+    // `ExecutionStatsService`'s constructor stamps the runtime-statistics URI 
onto this row,
+    // reaching it from the workflow through workflow_version. `cuid` is set 
because
+    // `getLatestExecutionID` matches on it and a NULL cuid matches no value 
at all.
+    val execution = new WorkflowExecutions
+    execution.setEid(testEid)
+    execution.setVid(version.getVid)
+    execution.setUid(testUid)
+    execution.setCuid(testCuid)
+    execution.setStatus(0.toByte)
+    execution.setStartingTime(new SqlTimestamp(System.currentTimeMillis()))
+    execution.setBookmarked(false)
+    execution.setName("workflow-execution-test-execution")
+    execution.setEnvironmentVersion("test-env")
+    new WorkflowExecutionsDao(getDSLContext.configuration()).insert(execution)
+  }
+
+  override protected def afterAll(): Unit = {
+    try {
+      TestKit.shutdownActorSystem(system)
+      closeConnectionPool()
+    } finally super.afterAll()
+  }

Review Comment:
   `afterAll` calls `TestKit.shutdownActorSystem(system)` and then 
`closeConnectionPool()`. If shutting down the actor system throws (e.g., 
timeout), the DB connection pool will never be closed, leaving resources pinned 
for the remainder of the sbt JVM. Wrap the DB cleanup in a `finally` so it 
always runs.



-- 
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