test(09-05): add failing tests for scoped record lifecycle

- Record routes must enforce permission before ids or bodies and return D-10 envelopes
- Create, update, and delete run Before and After hooks once inside the transaction
- Out-of-scope and missing records are indistinguishable, and hook failure rolls back
This commit is contained in:
Jakub Zych
2026-09-24 19:38:23 +02:00
parent 578bdc8d5d
commit 94814d980b
5 changed files with 604 additions and 4 deletions

View File

@@ -75,10 +75,8 @@ func (crudRow) Fillable() []string { return []string{"name", "note"} }
func (crudRow) Rules() map[string]string { return crudRuleSet }
func (r *crudRow) BeforeValidate(tx *gorm.DB) error {
ctx := context.Background()
if tx != nil && tx.Statement != nil && tx.Statement.Context != nil {
ctx = tx.Statement.Context
}
ctx := txContext(tx)
recordHook(ctx, "before_validate")
if clear, _ := ctx.Value(clearNameKey{}).(bool); clear {
if r.Name == "" {
return errors.New("fill did not run before validate")
@@ -88,6 +86,71 @@ func (r *crudRow) BeforeValidate(tx *gorm.DB) error {
return nil
}
func (r *crudRow) BeforeSave(tx *gorm.DB) error {
recordHook(txContext(tx), "before_save")
return nil
}
func (r *crudRow) BeforeCreate(tx *gorm.DB) error {
recordHook(txContext(tx), "before_create")
return nil
}
func (r *crudRow) AfterCreate(tx *gorm.DB) error {
recordHook(txContext(tx), "after_create")
return nil
}
func (r *crudRow) AfterSave(tx *gorm.DB) error {
recordHook(txContext(tx), "after_save")
return nil
}
func (r *crudRow) BeforeUpdate(tx *gorm.DB) error {
recordHook(txContext(tx), "before_update")
return nil
}
func (r *crudRow) AfterUpdate(tx *gorm.DB) error {
recordHook(txContext(tx), "after_update")
return nil
}
func (r *crudRow) BeforeDelete(tx *gorm.DB) error {
ctx := txContext(tx)
recordHook(ctx, "before_delete")
if fail, _ := ctx.Value(failDeleteKey{}).(bool); fail {
return errors.New("secret-hook-boom")
}
return nil
}
func (r *crudRow) AfterDelete(tx *gorm.DB) error {
recordHook(txContext(tx), "after_delete")
return nil
}
type hookSinkKey struct{}
type failDeleteKey struct{}
func txContext(tx *gorm.DB) context.Context {
if tx != nil && tx.Statement != nil && tx.Statement.Context != nil {
return tx.Statement.Context
}
return context.Background()
}
func recordHook(ctx context.Context, name string) {
if ctx == nil {
return
}
sink, _ := ctx.Value(hookSinkKey{}).(*[]string)
if sink == nil {
return
}
*sink = append(*sink, name)
}
type crudNoRules struct {
ID uint `gorm:"column:id;primaryKey"`
Name string `gorm:"column:name"`