zhangshenghang commented on code in PR #10925:
URL: https://github.com/apache/seatunnel/pull/10925#discussion_r3675124598


##########
seatunnel-e2e/seatunnel-connector-v2-e2e/connector-file-ftp-e2e/src/test/java/org/apache/seatunnel/e2e/connector/file/ftp/FtpFileIT.java:
##########
@@ -343,72 +350,162 @@ public void 
testFtpBinaryUpdateModeContinuousDiscoveryWithNonRecursiveScan(
                                 throw new RuntimeException(e);
                             }
                         });
-
-        putFtpFile("/tmp/seatunnel/continuous/src/root.bin", "root");
-        putFtpFile("/tmp/seatunnel/continuous/src/subdir/nested.bin", 
"nested");
-
-        Awaitility.await()
-                .atMost(60, TimeUnit.SECONDS)
-                .untilAsserted(
-                        () ->
-                                Assertions.assertEquals(
-                                        "root",
-                                        
readFtpFile("/tmp/seatunnel/continuous/dst/root.bin")));
-
-        Thread.sleep(3000);
-        
Assertions.assertFalse(isFtpFileExists("/tmp/seatunnel/continuous/dst/subdir/nested.bin"));
-
-        Container.ExecResult cancelResult = container.cancelJob(jobId);
-        Assertions.assertEquals(0, cancelResult.getExitCode(), 
cancelResult.getStderr());
-
-        Container.ExecResult execResult;
+        Throwable testFailure = null;
         try {
-            execResult = jobFuture.get(120, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            throw new RuntimeException("Wait continuous job exit failed.", e);
+            putFtpFile("/tmp/seatunnel/continuous/src/root.bin", "root");
+            putFtpFile("/tmp/seatunnel/continuous/src/subdir/nested.bin", 
"nested");
+
+            Awaitility.await()
+                    .atMost(120, TimeUnit.SECONDS)
+                    .untilAsserted(
+                            () ->
+                                    Assertions.assertEquals(
+                                            "root",
+                                            
readFtpFile("/tmp/seatunnel/continuous/dst/root.bin")));
+
+            Thread.sleep(3000);
+            Assertions.assertFalse(
+                    
isFtpFileExists("/tmp/seatunnel/continuous/dst/subdir/nested.bin"));
+        } catch (Throwable failure) {
+            testFailure = failure;
+            throw failure;
+        } finally {
+            cleanupContinuousJob(
+                    container,
+                    jobId,
+                    jobFuture,
+                    ftpHomeDir + "/tmp/seatunnel/continuous",
+                    testFailure);
         }
-        Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
-
-        deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/continuous");
     }
 
+    /**
+     * Verifies non-recursive FTP distcp updates root files without 
overwriting nested files.
+     *
+     * <p>The stale nested destination file must remain unchanged after the 
job finishes.
+     */
     @TestTemplate
     public void 
testFtpBinaryUpdateModeDistcpWithNonRecursiveScan(TestContainer container)
             throws IOException, InterruptedException {
         resetUpdateTestPath();
-        putFtpFile("/tmp/seatunnel/update/src/root.bin", "root-updated-v2");
-        putFtpFile("/tmp/seatunnel/update/src/subdir/nested.bin", 
"nest-updated-v2");
-        putFtpFile("/tmp/seatunnel/update/dst/root.bin", "root-stale-v1");
-        putFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin", 
"nest-stale-v1");
-
-        Container.ExecResult execResult =
-                
container.executeJob("/text/ftp_binary_update_non_recursive_distcp.conf");
-        Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
-        Assertions.assertEquals(
-                "root-updated-v2", 
readFtpFile("/tmp/seatunnel/update/dst/root.bin"));
-        Assertions.assertEquals(
-                "nest-stale-v1", 
readFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin"));
-
-        deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/update");
+        try {
+            putFtpFile("/tmp/seatunnel/update/src/root.bin", 
"root-updated-v2");
+            putFtpFile("/tmp/seatunnel/update/src/subdir/nested.bin", 
"nest-updated-v2");
+            putFtpFile("/tmp/seatunnel/update/dst/root.bin", "root-stale-v1");
+            putFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin", 
"nest-stale-v1");
+
+            Container.ExecResult execResult =
+                    
container.executeJob("/text/ftp_binary_update_non_recursive_distcp.conf");
+            Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
+            Assertions.assertEquals(
+                    "root-updated-v2", 
readFtpFile("/tmp/seatunnel/update/dst/root.bin"));
+            Assertions.assertEquals(
+                    "nest-stale-v1", 
readFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin"));
+        } finally {
+            deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/update");
+        }
     }
 
+    /**
+     * Verifies strict checksum mode keeps nested FTP files untouched during 
non-recursive scans.
+     */
     @TestTemplate
     public void 
testFtpBinaryUpdateModeStrictChecksumSkipsNestedChangesWithNonRecursiveScan(
             TestContainer container) throws IOException, InterruptedException {
         resetUpdateTestPath();
-        putFtpFile("/tmp/seatunnel/update/src/root.bin", "root-same-v1");
-        putFtpFile("/tmp/seatunnel/update/src/subdir/nested.bin", 
"nest-new-v1");
-        putFtpFile("/tmp/seatunnel/update/dst/root.bin", "root-same-v1");
-        putFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin", 
"nest-old-v1");
+        try {
+            putFtpFile("/tmp/seatunnel/update/src/root.bin", "root-same-v1");
+            putFtpFile("/tmp/seatunnel/update/src/subdir/nested.bin", 
"nest-new-v1");
+            putFtpFile("/tmp/seatunnel/update/dst/root.bin", "root-same-v1");
+            putFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin", 
"nest-old-v1");
+
+            Container.ExecResult execResult =
+                    container.executeJob(
+                            
"/text/ftp_binary_update_non_recursive_strict_checksum.conf");
+            Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
+            Assertions.assertEquals(
+                    "root-same-v1", 
readFtpFile("/tmp/seatunnel/update/dst/root.bin"));
+            Assertions.assertEquals(
+                    "nest-old-v1", 
readFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin"));
+        } finally {
+            deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/update");
+        }
+    }
 
-        Container.ExecResult execResult =
-                
container.executeJob("/text/ftp_binary_update_non_recursive_strict_checksum.conf");
-        Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
-        Assertions.assertEquals("root-same-v1", 
readFtpFile("/tmp/seatunnel/update/dst/root.bin"));
-        Assertions.assertEquals(
-                "nest-old-v1", 
readFtpFile("/tmp/seatunnel/update/dst/subdir/nested.bin"));
+    /**
+     * Continuous discovery is only considered stopped after the engine 
reaches CANCELED and the
+     * submit command exits, so cleanup regressions still fail this E2E test.
+     */
+    private void assertContinuousJobStopsAfterCancel(
+            TestContainer container,
+            String jobId,
+            CompletableFuture<Container.ExecResult> jobFuture)
+            throws IOException, InterruptedException {
+        Awaitility.await()
+                .atMost(30, TimeUnit.SECONDS)
+                .untilAsserted(
+                        () -> {
+                            Container.ExecResult cancelResult = 
container.cancelJob(jobId);
+                            Assertions.assertEquals(
+                                    0, cancelResult.getExitCode(), 
cancelResult.getStderr());
+                        });
 
-        deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/update");
+        Awaitility.await()
+                .atMost(30, TimeUnit.SECONDS)
+                .untilAsserted(
+                        () ->
+                                Assertions.assertEquals(
+                                        "CANCELED",
+                                        container.getJobStatus(jobId),
+                                        "Continuous job should be canceled 
before the test exits."));
+
+        Awaitility.await()
+                .atMost(180, TimeUnit.SECONDS)
+                .pollInterval(2, TimeUnit.SECONDS)
+                .until(jobFuture::isDone);
+
+        try {
+            Container.ExecResult execResult = jobFuture.get(30, 
TimeUnit.SECONDS);
+            Assertions.assertEquals(0, execResult.getExitCode(), 
execResult.getStderr());
+        } catch (Exception e) {
+            throw new RuntimeException("Wait continuous job exit failed.", e);
+        }
+    }
+
+    /**
+     * Stops a continuous job before deleting its shared test path, without 
hiding the original test
+     * failure when cleanup also fails.
+     */
+    private void cleanupContinuousJob(

Review Comment:
   **P2 — `cleanupContinuousJob` exception handling**
   
   ```java
   if (testFailure != null) {
       testFailure.addSuppressed(cleanupFailure);
   } else {
       throw cleanupFailure;
   }
   ```
   
   If `cleanupContinuousJob` itself is the first failure (i.e. the test body 
succeeded but cleanup threw), we throw `cleanupFailure` and lose the original 
`testFailure`. Here `testFailure` is null in that case so it's actually fine — 
but please consider using `assertThrows`-style logic and double-check that 
`Throwable#addSuppressed` is reachable when the cleanup is called from 
`finally` (it is, but only when `testFailure` is non-null, i.e. the test body 
threw first). That matches the comment ("without hiding the original test 
failure"), but worth a unit test that exercises both branches.
   
   Also, `assertContinuousJobStopsAfterCancel` waits up to 30s + 30s + 180s = 4 
minutes per continuous discovery test. With 4 such tests per suite on FTP and 4 
on SFTP, cleanup alone can add ~32 minutes in the worst case. If that becomes a 
problem in CI, consider lowering the 180s timeout.



##########
seatunnel-e2e/seatunnel-connector-v2-e2e/connector-file-ftp-e2e/src/test/java/org/apache/seatunnel/e2e/connector/file/ftp/FtpFileIT.java:
##########
@@ -765,6 +884,38 @@ private void ensureReadJsonInputFile() throws IOException, 
InterruptedException
         Assertions.assertEquals(0, chmodResult.getExitCode(), 
chmodResult.getStderr());
     }
 
+    /**
+     * The multiple-table FTP JSON test reads two logical tables from two 
table configs. Give each
+     * table its own physical source file so one reader never depends on a 
file already consumed by
+     * the other table path.
+     */
+    private void ensureMultipleTableJsonInputFiles() throws IOException, 
InterruptedException {

Review Comment:
   **P3 — `ensureMultipleTableJsonInputFiles` deletes shared dir, may affect 
later runs**
   
   ```java
   deleteFileFromContainer(ftpHomeDir + "/tmp/seatunnel/read/json");
   ```
   
   This is fine because the FTP container is per-suite (`@BeforeAll` starts 
one), but if a future test adds a sibling read under 
`tmp/seatunnel/read/json/...` between the multiple-table run and `tearDown`, 
the data will be wiped. Worth a Javadoc note that the helper owns 
`tmp/seatunnel/read/json` for the lifetime of the suite.



##########
seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java:
##########
@@ -416,10 +419,17 @@ public void testSinkRocketMqMessageTag(TestContainer 
container)
         Assertions.assertEquals(tag, data.get(key).getTag());
     }
 
-    public void testRocketMqGroupOffsetsToConsole(TestContainer container)
+    /**
+     * Uses isolated topic and consumer-group names so template invocations 
cannot share offsets.
+     */
+    public void testRocketMqGroupOffsetsToConsole(

Review Comment:
   **P3 — `testRocketMqGroupOffsetsToConsole` is `public` and `test`-prefixed 
but not a `@TestTemplate`**
   
   ```java
   public void testRocketMqGroupOffsetsToConsole(TestContainer container, 
String topicName, String consumerGroup)
   ```
   
   This is a helper invoked only by `testSourceRocketMqStartConfig` (line 400). 
The `test` prefix and `public` visibility are misleading. Recommend either:
   
   1. Rename to `runRocketMqGroupOffsetsToConsole(...)` (or 
`executeRocketMqGroupOffsetsToConsole`), or
   2. Make it `private` (preferred, since there's only one caller).



##########
seatunnel-e2e/seatunnel-connector-v2-e2e/connector-rocketmq-e2e/src/test/java/org/apache/seatunnel/e2e/connector/rocketmq/RocketMqIT.java:
##########
@@ -806,6 +837,15 @@ private long getTopicMaxOffset(String topicName) {
         }
     }
 
+    /**
+     * Returns a collision-resistant suffix for broker resources shared by 
template invocations.
+     *
+     * @return UUID text without separators, suitable for topic and 
consumer-group names
+     */
+    private String uniqueTestSuffix() {

Review Comment:
   **P3 — `uniqueTestSuffix()` collision risk is very low but worth noting**
   
   UUID without hyphens gives 32 hex chars (~128 bits). With one call per test 
invocation the chance of collision is negligible, but two callers in the same 
test (`testSourceRocketMqStartConfig` and `testSourceRocketMqRestore`) each 
call it once, so a single suite has at most 2 topics × 2 groups = 4 resources. 
Fine.
   
   Just confirm that `createTopic` (or RocketMQ auto-create) tolerates 
concurrent creation — if not, consider synchronizing the UUID generation.



##########
seatunnel-e2e/seatunnel-connector-v2-e2e/connector-cdc-mysql-e2e/src/test/java/org/apache/seatunnel/connectors/seatunnel/cdc/mysql/MysqlCDCWithFlinkSchemaChangeIT.java:
##########
@@ -68,7 +68,7 @@ public class MysqlCDCWithFlinkSchemaChangeIT extends 
TestSuiteBase implements Te
      * Flink schema evolution can restart after XA recover/rollback on loaded 
CI runners, so these
      * assertions need enough time for the job to recover and replay the 
schema-change event.
      */
-    private static final long SCHEMA_EVOLUTION_ASSERT_TIMEOUT_MILLIS = 
300_000L;
+    private static final long SCHEMA_EVOLUTION_ASSERT_TIMEOUT_MILLIS = 
600_000L;
 
     private static final long STRUCTURE_AND_DATA_ASSERT_TIMEOUT_MILLIS = 
300_000L;
     private static final int MAX_TIMESTAMP_DRIFT_SECONDS = 60;

Review Comment:
   **P3 — `MAX_TIMESTAMP_DRIFT_SECONDS = 60` may be too lenient**
   
   ```java
   private static final int MAX_TIMESTAMP_DRIFT_SECONDS = 60;
   ```
   
   `add_column4` defaults to `current_timestamp()`. A 60-second drift between 
source and sink is large enough to hide real CDC lag issues. Suggest 10-20 
seconds once the CI is stable.



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