Testing failure conditions is challenging. Transactions end either by being committed or aborted. System state must be the same as if the transaction had never existed. Must abort any transactions that depend on the outcome of the aborting transaction. If you say you've committed, no actions should be able to move the system to a state not containing the transaction.
All operations much be forever persistent in the database.
The simplest way to support transaction semantics is to require that each transaction run to completion before the next one begins. This is sub-optimal. Call the sequence of operations actually performed a schedule. Any schedule that corresponds to a serial schedule is correct. For our purposes, we can assume that the only operations performed by a transaction are read and write.