feat: sync account freeze lifecycle
This commit is contained in:
parent
76bfc5100f
commit
47fcf0ea41
40 changed files with 1363 additions and 196 deletions
109
internal/store/postgres/admin_freeze_integration_test.go
Normal file
109
internal/store/postgres/admin_freeze_integration_test.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"telesrv/deploy"
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func TestAccountFreezeMigrationAndStoreRoundTrip(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
tx, err := pool.Begin(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
|
||||
downSQL, err := deploy.Migrations.ReadFile("migrations/0088_account_freeze_state.down.sql")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
upSQL, err := deploy.Migrations.ReadFile("migrations/0088_account_freeze_state.up.sql")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec(ctx, string(downSQL)); err != nil {
|
||||
t.Fatalf("roll schema back to legacy restriction shape: %v", err)
|
||||
}
|
||||
|
||||
const (
|
||||
frozenUserID = int64(1999999881)
|
||||
activeUserID = int64(1999999882)
|
||||
)
|
||||
for _, user := range []struct {
|
||||
id int64
|
||||
phone string
|
||||
}{{frozenUserID, "1999999881"}, {activeUserID, "1999999882"}} {
|
||||
if _, err := tx.Exec(ctx, `
|
||||
INSERT INTO users (id, access_hash, phone, first_name)
|
||||
VALUES ($1, $1, $2, 'Freeze migration test')`, user.id, user.phone); err != nil {
|
||||
t.Fatalf("insert migration user %d: %v", user.id, err)
|
||||
}
|
||||
}
|
||||
legacyUpdatedAt := time.Date(2026, 7, 1, 12, 0, 0, 0, time.UTC)
|
||||
if _, err := tx.Exec(ctx, `
|
||||
INSERT INTO account_send_restrictions (user_id, frozen, reason, actor, command_id, updated_at)
|
||||
VALUES ($1, true, 'legacy freeze', 'ops', 'legacy-freeze', $2)`, frozenUserID, legacyUpdatedAt); err != nil {
|
||||
t.Fatalf("insert legacy restriction: %v", err)
|
||||
}
|
||||
if _, err := tx.Exec(ctx, string(upSQL)); err != nil {
|
||||
t.Fatalf("apply account freeze migration: %v", err)
|
||||
}
|
||||
|
||||
store := NewAdminStore(tx)
|
||||
migrated, found, err := store.GetAccountFreeze(ctx, frozenUserID)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("GetAccountFreeze migrated = %+v found=%v err=%v", migrated, found, err)
|
||||
}
|
||||
if !migrated.Frozen || !migrated.Since.Equal(legacyUpdatedAt) ||
|
||||
!migrated.Until.Equal(legacyUpdatedAt.Add(7*24*time.Hour)) || migrated.AppealURL != "https://t.me/SpamBot" {
|
||||
t.Fatalf("migrated freeze = %+v", migrated)
|
||||
}
|
||||
|
||||
since := time.Date(2026, 7, 15, 2, 0, 0, 0, time.UTC)
|
||||
want := domain.AccountFreeze{
|
||||
UserID: activeUserID,
|
||||
Frozen: true,
|
||||
Since: since,
|
||||
Until: since.Add(48 * time.Hour),
|
||||
AppealURL: "https://appeals.example.test/users/1999999882",
|
||||
Reason: "abuse review",
|
||||
Actor: "ops",
|
||||
CommandID: "freeze-round-trip",
|
||||
}
|
||||
if _, err := store.SetAccountFreeze(ctx, want); err != nil {
|
||||
t.Fatalf("SetAccountFreeze active: %v", err)
|
||||
}
|
||||
got, found, err := store.GetAccountFreeze(ctx, activeUserID)
|
||||
if err != nil || !found || !got.Frozen || !got.Since.Equal(want.Since) ||
|
||||
!got.Until.Equal(want.Until) || got.AppealURL != want.AppealURL {
|
||||
t.Fatalf("active round trip = %+v found=%v err=%v", got, found, err)
|
||||
}
|
||||
if _, err := store.SetAccountFreeze(ctx, domain.AccountFreeze{
|
||||
UserID: activeUserID, Reason: "appeal accepted", Actor: "ops", CommandID: "unfreeze-round-trip",
|
||||
}); err != nil {
|
||||
t.Fatalf("SetAccountFreeze inactive: %v", err)
|
||||
}
|
||||
got, found, err = store.GetAccountFreeze(ctx, activeUserID)
|
||||
if err != nil || !found || got.Frozen || !got.Since.IsZero() || !got.Until.IsZero() || got.AppealURL != "" {
|
||||
t.Fatalf("inactive round trip = %+v found=%v err=%v", got, found, err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(ctx, "SAVEPOINT invalid_freeze"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, invalidErr := tx.Exec(ctx, `
|
||||
UPDATE account_restrictions
|
||||
SET frozen = true, frozen_since = NULL, frozen_until = NULL, appeal_url = ''
|
||||
WHERE user_id = $1`, activeUserID)
|
||||
if invalidErr == nil {
|
||||
t.Fatal("database accepted an active freeze without client-visible state")
|
||||
}
|
||||
if _, err := tx.Exec(ctx, "ROLLBACK TO SAVEPOINT invalid_freeze"); err != nil {
|
||||
t.Fatalf("rollback invalid freeze savepoint: %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue