test(09-05): add failing tests for deterministic bulk delete

- Empty selections are 422 and duplicates run once in primary-key order
- A completed retry and an all-absent selection delete nothing and skip hooks
- Mixed, hook, cancel, and concurrent requests keep the batch atomic
This commit is contained in:
Jakub Zych
2026-09-24 19:43:15 +02:00
parent e3e1c2546e
commit 247c3235f6
5 changed files with 300 additions and 2 deletions

View File

@@ -7,10 +7,10 @@ import (
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
"testing/fstest"
"time"
"gorm.io/gorm"
)
@@ -118,10 +118,23 @@ func (r *crudRow) AfterUpdate(tx *gorm.DB) error {
func (r *crudRow) BeforeDelete(tx *gorm.DB) error {
ctx := txContext(tx)
if _, ok := ctx.Value(bulkSlowKey{}).(bool); ok {
time.Sleep(300 * time.Millisecond)
}
recordHook(ctx, "before_delete")
appendDeletedID(ctx, r.ID)
if fail, _ := ctx.Value(failDeleteKey{}).(bool); fail {
return errors.New("secret-hook-boom")
}
if ids, ok := ctx.Value(failIDsKey{}).(map[uint]bool); ok && ids[r.ID] {
return errors.New("secret-hook-boom")
}
if id, ok := ctx.Value(cancelOnKey{}).(uint); ok && r.ID == id {
if cancel, ok := ctx.Value(cancelKey{}).(context.CancelFunc); ok {
cancel()
}
return context.Canceled
}
return nil
}
@@ -132,6 +145,13 @@ func (r *crudRow) AfterDelete(tx *gorm.DB) error {
type hookSinkKey struct{}
type failDeleteKey struct{}
type failIDsKey struct{}
type idSinkKey struct{}
type bulkSlowKey struct{}
type cancelOnKey struct{}
type cancelKey struct{}
var hookMu sync.Mutex
func txContext(tx *gorm.DB) context.Context {
if tx != nil && tx.Statement != nil && tx.Statement.Context != nil {
@@ -144,6 +164,8 @@ func recordHook(ctx context.Context, name string) {
if ctx == nil {
return
}
hookMu.Lock()
defer hookMu.Unlock()
sink, _ := ctx.Value(hookSinkKey{}).(*[]string)
if sink == nil {
return
@@ -151,6 +173,19 @@ func recordHook(ctx context.Context, name string) {
*sink = append(*sink, name)
}
func appendDeletedID(ctx context.Context, id uint) {
if ctx == nil {
return
}
hookMu.Lock()
defer hookMu.Unlock()
sink, _ := ctx.Value(idSinkKey{}).(*[]uint)
if sink == nil {
return
}
*sink = append(*sink, id)
}
type crudNoRules struct {
ID uint `gorm:"column:id;primaryKey"`
Name string `gorm:"column:name"`