@Test
  public void exploreMultipleExternalCatalog_ListTablesException() throws TException, IOException {
    createTable();
    ServerCatalog originalCatalog = catalogManager().getServerCatalog(TEST_CATALOG_NAME);

    // test list tables
    List<TableRuntimeMeta> tableRuntimeMetaListAfterAddTable = persistency.getTableRuntimeMetas();
    Assert.assertEquals(1, tableRuntimeMetaListAfterAddTable.size());

    MockitoAnnotations.openMocks(this);
    when(externalCatalog.name()).thenReturn(TEST_CATALOG_NAME);
    when(externalCatalog.toString()).thenReturn(originalCatalog.toString());
    when(externalCatalog.getMetadata()).thenReturn(originalCatalog.getMetadata());
    when(externalCatalog.listDatabases()).thenReturn(originalCatalog.listDatabases());
    when(externalCatalog.listTables(TEST_DB_NAME))
        .thenThrow(new RuntimeException("List tables in " + TEST_DB_NAME + " error"));

    catalogManager().setServerCatalog(externalCatalog);

    // create a new catalog new_catalog with a new db and a new table new_table
    String newCatalogName = "new_catalog";
    String newDbName = "new_db";
    String newTableName = "new_table";
    UnifiedCatalog newCatalog = createNewCatalogTable(newCatalogName, newDbName, newTableName);

    // This should not throw an exception, and log an error message "TableExplorer error when
    // explore table runtimes for catalog:test_catalog". Meanwhile, the newly added catalog can be
    // synchronized normally
    // without being affected.
    tableService().exploreTableRuntimes();

    // test tableRuntime of test_table not removed and tableRuntime of new_table added
    List<TableRuntimeMeta> tableRuntimeMetaListAfterExploreCatalog =
        persistency.getTableRuntimeMetas();
    Assert.assertEquals(2, tableRuntimeMetaListAfterExploreCatalog.size());
    Assert.assertTrue(
        tableRuntimeMetaListAfterExploreCatalog.stream()
            .anyMatch(tableRuntimeMeta -> tableRuntimeMeta.getTableName().equals(TEST_TABLE_NAME)));
    Assert.assertTrue(
        tableRuntimeMetaListAfterExploreCatalog.stream()
            .anyMatch(tableRuntimeMeta -> tableRuntimeMeta.getTableName().equals(newTableName)));

    // test tableRuntime of new_table removed
    disposeNewCatalogTable(newCatalog, newCatalogName, newDbName, newTableName);
    // This should not throw an exception, but log an error message "TableExplorer error when
    // explore table runtimes for catalog:test_catalog". Meanwhile, the removed catalog and
    // tableRuntime of new_table can
    // be synchronized normally
    // without being affected.
    tableService().exploreTableRuntimes();

    List<TableRuntimeMeta> tableRuntimeMetaListAfterDisposeNewCatalog =
        persistency.getTableRuntimeMetas();
    Assert.assertEquals(1, tableRuntimeMetaListAfterDisposeNewCatalog.size());
    Assert.assertTrue(
        tableRuntimeMetaListAfterExploreCatalog.stream()
            .anyMatch(tableRuntimeMeta -> tableRuntimeMeta.getTableName().equals(TEST_TABLE_NAME)));
    Assert.assertFalse(
        tableRuntimeMetaListAfterDisposeNewCatalog.stream()
            .anyMatch(tableRuntimeMeta -> tableRuntimeMeta.getTableName().equals(newTableName)));

    catalogManager().setServerCatalog(originalCatalog);
    dropTable();
    dropDatabase();
  }
Test synchronization in the anomaly scenario where the tableIdentifier is remained in
persistence and the tableRuntime is remained in memory but iceberg table does not exist.
amoro-ams/src/test/java/org/apache/amoro/server/table/TestSyncTableOfExternalCatalog.java