mirror of
https://github.com/hanzoai/orm.git
synced 2026-10-30 15:01:22 +00:00
Delete db/sql.go + db/sql_query.go (PG-specific: pg_advisory_xact_lock, IsolationReadCommitted, 40001/40P01 retry). SQLite BEGIN IMMEDIATE gives us linearizable writes without PG primitives. adapter.go simplified to SQLite-only transaction model. Retry loop re-scoped to SQLITE_BUSY with exponential backoff. Tests green: hanzoai/orm, /db, /engine all pass.
61 lines
1.8 KiB
Go
61 lines
1.8 KiB
Go
package orm
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R3-7: RunInTransactionWith must recover from panics inside the
|
|
// callback, roll back cleanly at the driver layer, and re-panic so the
|
|
// caller's goroutine trace shows the original defect. A swallowed panic
|
|
// would leak DB connections and silently corrupt application state.
|
|
func TestRunInTransactionWith_PanicRecovered(t *testing.T) {
|
|
db := newTestSQLite(t)
|
|
|
|
defer func() {
|
|
r := recover()
|
|
if r == nil {
|
|
t.Fatal("expected panic to propagate to caller")
|
|
}
|
|
msg, ok := r.(string)
|
|
if !ok {
|
|
t.Fatalf("panic value not string: %T %v", r, r)
|
|
}
|
|
if !strings.Contains(msg, "intentional R3-7") {
|
|
t.Fatalf("panic value lost: %q", msg)
|
|
}
|
|
}()
|
|
|
|
_ = db.RunInTransactionWith(context.Background(), &TxOptions{
|
|
Isolation: IsolationDefault,
|
|
}, func(tx DB) error {
|
|
panic("intentional R3-7 test panic")
|
|
})
|
|
|
|
t.Fatal("RunInTransactionWith must re-panic; we should not reach here")
|
|
}
|
|
|
|
// R3-7: a panicking callback returns ErrTxPanic to the retry loop (not a
|
|
// serialization failure) so the loop exits immediately instead of
|
|
// retrying endlessly. In practice the re-panic fires before the retry
|
|
// loop even sees the error, but this test reaches into the guarded
|
|
// helper directly to verify the sentinel path.
|
|
func TestRunAttempt_PanicReturnsErrTxPanic(t *testing.T) {
|
|
// The defer catches the re-panic that runAttempt issues after it
|
|
// records the panic and rolls back.
|
|
defer func() {
|
|
_ = recover()
|
|
}()
|
|
|
|
db := newTestSQLite(t).(*dbAdapter)
|
|
err := db.runAttempt(context.Background(), 0, nil, func(tx DB) error {
|
|
panic("R3-7 sentinel")
|
|
})
|
|
// unreachable in practice; if the re-panic is somehow skipped we
|
|
// at least assert the sentinel is returned.
|
|
if err != nil && err != ErrTxPanic {
|
|
t.Errorf("expected ErrTxPanic, got %v", err)
|
|
}
|
|
}
|