test: RequestUser, EndRequestSession, AvatarURL and user.Deactivating
- TestRequestUser: bearer, token and auth_token cookies in guard order; expired, foreign, blacklisted, revoked by tokens_valid_after and deactivated tokens are anonymous; only infrastructure failures error - TestEndRequestSession (no database): jti blacklisted until the token expires, forget cookie with the configured Domain and Path, best effort - TestAvatarURL: thumbnail, broken-image and storage-failure fallbacks, gravatar with the size, the user payload's avatar_url unchanged - TestDeactivatingEvent: one event per newly deactivated user inside the bulk action's transaction, a listener error rolls the action back
This commit is contained in:
110
deactivating_event_test.go
Normal file
110
deactivating_event_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/sm-user-plugin/classes"
|
||||
"git.golem15.com/golem15/sm-user-plugin/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TestDeactivatingEvent pins user.Deactivating (D-05): the admin deactivate
|
||||
// bulk action fires one event per newly deactivated user inside the
|
||||
// action's transaction, already deactivated users fire none, a listener
|
||||
// error rolls the whole action back, and the event name is the constant.
|
||||
func TestDeactivatingEvent(t *testing.T) {
|
||||
if classes.DeactivatingEventName != "user.Deactivating" {
|
||||
t.Fatalf("DeactivatingEventName = %q", classes.DeactivatingEventName)
|
||||
}
|
||||
env := newAdminEnv(t)
|
||||
token := env.admin(t, permAccessUsers)
|
||||
|
||||
var mu sync.Mutex
|
||||
var fired []uint
|
||||
var failFor uint
|
||||
var txSawDeactivation = map[uint]bool{}
|
||||
env.app.Events.Listen[*classes.DeactivatingEvent]("test.deactivating", func(ctx context.Context, e *classes.DeactivatingEvent) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if e.User == nil || e.Tx == nil {
|
||||
return errors.New("event without user or transaction")
|
||||
}
|
||||
fired = append(fired, e.User.ID)
|
||||
// Inside the action's transaction the row is already deactivated.
|
||||
var deleted int64
|
||||
if err := e.Tx.Session(&gorm.Session{NewDB: true}).Unscoped().Model(&models.User{}).
|
||||
Where("id = ? AND deleted_at IS NOT NULL", e.User.ID).Count(&deleted).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
txSawDeactivation[e.User.ID] = deleted == 1
|
||||
if e.User.ID == failFor {
|
||||
return errors.New("listener refuses")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
reset := func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fired, txSawDeactivation = nil, map[uint]bool{}
|
||||
}
|
||||
|
||||
a, b := env.seedUser(t, "deact-a", nil), env.seedUser(t, "deact-b", nil)
|
||||
if rec := env.bulk(t, token, "deactivate", a, b); rec.Code != http.StatusOK {
|
||||
t.Fatalf("deactivate: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
mu.Lock()
|
||||
if len(fired) != 2 || !txSawDeactivation[a.ID] || !txSawDeactivation[b.ID] {
|
||||
t.Fatalf("events %v, seen in the transaction %v", fired, txSawDeactivation)
|
||||
}
|
||||
mu.Unlock()
|
||||
for _, u := range []*models.User{a, b} {
|
||||
if row := env.reload(t, u.ID); row == nil || !row.DeletedAt.Valid {
|
||||
t.Fatalf("user %d not deactivated", u.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Already deactivated users fire nothing; a mixed selection fires only
|
||||
// for the newly deactivated one.
|
||||
reset()
|
||||
c := env.seedUser(t, "deact-c", nil)
|
||||
if rec := env.bulk(t, token, "deactivate", a, c); rec.Code != http.StatusOK {
|
||||
t.Fatalf("mixed deactivate: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
mu.Lock()
|
||||
if len(fired) != 1 || fired[0] != c.ID {
|
||||
t.Fatalf("mixed selection fired %v, want only %d", fired, c.ID)
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
// A listener error rolls the whole action back.
|
||||
reset()
|
||||
d, e := env.seedUser(t, "deact-d", nil), env.seedUser(t, "deact-e", nil)
|
||||
failFor = e.ID
|
||||
if rec := env.bulk(t, token, "deactivate", d, e); rec.Code < 400 {
|
||||
t.Fatalf("deactivate with a refusing listener: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
failFor = 0
|
||||
for _, u := range []*models.User{d, e} {
|
||||
if row := env.reload(t, u.ID); row == nil || row.DeletedAt.Valid {
|
||||
t.Fatalf("user %d deactivated although a listener refused", u.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Other bulk actions fire nothing.
|
||||
reset()
|
||||
if rec := env.bulk(t, token, "restore", a); rec.Code != http.StatusOK {
|
||||
t.Fatalf("restore: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if rec := env.bulk(t, token, "activate", d); rec.Code != http.StatusOK {
|
||||
t.Fatalf("activate: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
mu.Lock()
|
||||
if len(fired) != 0 {
|
||||
t.Fatalf("restore and activate fired %v", fired)
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
Reference in New Issue
Block a user