diff --git a/goatherd_test.go b/goatherd_test.go
index c954559830e2e6db493035421e21d9e812b1c9ae..e342e74af9dcdc1a940c5115b58235449d577396 100644
--- a/goatherd_test.go
+++ b/goatherd_test.go
@@ -70,20 +70,20 @@ func create_user_t(t *testing.T) {
     t.Run("stdin", create_and_check("-", "-"))
 }
 
-// runs two updating transactions concurrently, fails if none of them aborts
-func transaction_conflict_t(t *testing.T) {
+func interleaved_transactions_t(t *testing.T) {
     var err error
     var txs [2]*sql.Tx
     for i, _ := range txs {
         txs[i], err = db.Begin()
         t_err_fatal(t, err)
-        defer func(i int) { t.Log("rollback", i); txs[i].Rollback() }(i)
+        defer func(i int) { txs[i].Rollback() }(i)
     }
 
     var c uint64
-    for _, tx := range txs {
+    for i, tx := range txs {
         err = tx.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c)
         if transaction_failed(err) {
+            t.Log("rollback after select", i)
             return
         } else { t_err_fatal(t, err) }
     }
@@ -92,19 +92,71 @@ func transaction_conflict_t(t *testing.T) {
         t.Log("update", i)
         _, err := tx.Exec("UPDATE users SET count = $1 WHERE name = $2", c + uint64(i), username)
         if transaction_failed(err) {
+            t.Log("rollback after update", i)
             return
         } else { t_err_fatal(t, err) }
 
         t.Log("commit", i)
         err = tx.Commit()
         if transaction_failed(err) {
+            t.Log("rollback after commit", i)
             return
         } else { t_err_fatal(t, err) }
     }
 
-    t.Error("No transaction failure despite concurrent updates!")
+    t.Error("No transaction failure despite interleaved transactions!")
+}
+
+func nested_transactions_t(t *testing.T) {
+    outer, err := db.Begin()
+    t_err_fatal(t, err)
+    defer outer.Rollback()
+    inner, err := db.Begin()
+    t_err_fatal(t, err)
+    defer inner.Rollback()
+
+    var c uint64
+    t_err_fatal(t, outer.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c))
+
+    err = inner.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c)
+    if transaction_failed(err) {
+        t.Log("rollback after inner.Query")
+        return
+    } else { t_err_fatal(t, err) }
+
+    t.Log("update inner")
+    _, err = inner.Exec("UPDATE users SET count = $1 WHERE name = $2", c + 1, username)
+    if transaction_failed(err) {
+        t.Log("rollback after inner.Exec")
+        return
+    } else { t_err_fatal(t, err) }
+
+    err = inner.Commit()
+    if transaction_failed(err) {
+        t.Log("rollback after inner.Commit")
+        return
+    } else { t_err_fatal(t, err) }
+
+    t.Log("update outer")
+    _, err = outer.Exec("UPDATE users SET count = $1 WHERE name = $2", c + 1, username)
+    if transaction_failed(err) {
+        t.Log("rollback after outer.Exec")
+        return
+    } else { t_err_fatal(t, err) }
+
+    err = outer.Commit()
+    if transaction_failed(err) {
+        t.Log("rollback after outer.Commit")
+        return
+    } else { t_err_fatal(t, err) }
+
+    t.Error("No transaction failure despite nested transactions")
 }
 
+func transaction_conflict_t(t *testing.T) {
+    t.Run("interleaved_transactions_t", interleaved_transactions_t)
+    t.Run("nested_transactions_t", nested_transactions_t)
+}
 
 func check_offer_t(t *testing.T) {
     t.Run("no_such_user", func(t *testing.T) {