### Question:
TrimService { public void trimDerivedTables(int height, boolean async) { log.debug("TRIM: trimDerivedTables on height={}, async={}", height, async); TransactionalDataSource dataSource = dbManager.getDataSource(); boolean inTransaction = dataSource.isInTransaction(); lock.lock(); try { try { if (!inTransaction) { dataSource.begin(); } long startTime = System.currentTimeMillis(); doTrimDerivedTablesOnBlockchainHeight(height, async); dataSource.commit(!inTransaction); log.info("Total trim time: {} ms on height '{}', InTr?=('{}')", (System.currentTimeMillis() - startTime), height, inTransaction); } catch (Exception e) { log.warn(e.toString(), e); dataSource.rollback(!inTransaction); throw e; } } finally { lock.unlock(); } } @Inject  TrimService(DatabaseManager databaseManager,
                       DerivedTablesRegistry derivedDbTablesRegistry,
                       GlobalSync globalSync,
                       TimeService timeService,
                       Event<TrimData> trimEvent,
                       Event<TrimConfig> trimConfigEvent,
                       TrimDao trimDao,
                       @Property(value = "apl.maxRollback", defaultValue = "720") int maxRollback
    ); int getLastTrimHeight(); void init(int height, int shardInitialBlockHeight); void trimDerivedTables(int height, boolean async); void doTrimDerivedTablesOnBlockchainHeight(int blockchainHeight, boolean async); void resetTrim(); void resetTrim(int height); int doTrimDerivedTablesOnHeightLocked(int height, boolean isSharding); void updateTrimConfig(boolean enableTrim, boolean clearQueue); boolean isTrimming(); void waitTrimming();  }### Answer:
@Test void testTrimDerivedTablesInOuterTransaction() { databaseManager.getDataSource().begin(); doReturn(new TrimEntry(1L, 5000, false)).when(trimDao).save(new TrimEntry(null, 5000, false)); Event firedEvent = mock(Event.class); doReturn(firedEvent).when(event).select(new AnnotationLiteral<TrimEvent>() { }); doReturn(List.of(derivedTable)).when(registry).getDerivedTables(); doReturn(3500).when(timeService).getEpochTime(); trimService.trimDerivedTables(5000, true); assertTrue(databaseManager.getDataSource().isInTransaction()); verify(derivedTable).trim(4000, false); verify(firedEvent).fireAsync(new TrimData(4000, 5000, 0)); }
@Test void testTrimDerivedTablesWithException() { doThrow(new RuntimeException()).when(trimDao).save(new TrimEntry(null, 6000, false)); TransactionalDataSource dataSource = spy(databaseManager.getDataSource()); doReturn(dataSource).when(databaseManager).getDataSource(); assertThrows(RuntimeException.class, () -> trimService.trimDerivedTables(6000, false)); assertFalse(dataSource.isInTransaction()); verify(dataSource).begin(); verify(dataSource).rollback(true); verifyZeroInteractions(derivedTable, event); }