Files
pocket-id/backend/internal/usersignup/testing_e2etest.go
Claude 7c59dcdb82 refactor: use actors for one-time access and signup tokens
Move one-time access tokens and signup tokens onto the Francis actor
framework, so their state is coordinated cluster-wide and expired entries
are purged without the periodic db_cleanup job.

One-time access tokens:
- Each token is its own actor, keyed by the token value, storing the user
  ID and optional device token in actor state with a TTL matching the
  token's expiration. Expired state is purged automatically.
- Exchange consumes the token by invoking the actor (atomic validate +
  delete) outside of any DB transaction, then loads the user and issues an
  access token. On a later failure it compensates by restoring the token
  (best-effort, using a non-cancelable context).
- The one_time_access_tokens table is dropped; the CLI writes token state
  through a minimal, non-running actor host.

Signup tokens:
- A singleton actor holds every signup token in its state and keeps a
  single cleanup alarm scheduled for the earliest expiration; when it
  fires it purges expired tokens and reschedules. Listing uses Peek.
- Sign-up consumes a token by invoking the actor to atomically increment
  its usage count, performs user creation in a transaction, and, on
  failure, compensates by releasing the token (best-effort).
- Existing tokens are migrated from the database into the actor state on
  first startup; the tables are retained only for that migration.

Also removes ClearOneTimeAccessTokens and ClearSignupTokens, and lets the
importer skip tables no longer present in the schema.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBMz9v9s8S8RPYNWzshkBu
2026-07-20 21:12:46 +00:00

49 lines
1.3 KiB
Go

//go:build e2etest
package usersignup
import (
"context"
"fmt"
"time"
"github.com/italypaleale/francis/actor"
"github.com/italypaleale/francis/host/local"
)
// SignupTokenSeed describes a signup token to seed into the actor state, used by E2E test setup.
type SignupTokenSeed struct {
ID string
Token string
ExpiresAt time.Time
UsageLimit int
UsageCount int
UserGroupIDs []string
CreatedAt time.Time
}
// SeedSignupTokens replaces the signup token singleton actor's state with the given tokens.
// It's intended for E2E test setup, where fixture tokens need to be created with exact values.
func SeedSignupTokens(ctx context.Context, actors *local.Host, seeds []SignupTokenSeed) error {
tokens := make([]storedSignupToken, len(seeds))
for i, s := range seeds {
tokens[i] = storedSignupToken{
ID: s.ID,
Token: s.Token,
ExpiresAt: s.ExpiresAt,
UsageLimit: s.UsageLimit,
UsageCount: s.UsageCount,
UserGroupIDs: s.UserGroupIDs,
CreatedAt: s.CreatedAt,
}
}
_, err := actors.Service().Invoke(ctx, SignupTokenActorType, actor.SingletonActorID, signupTokenMethodReplace, signupTokenReplaceRequest{
Tokens: tokens,
})
if err != nil {
return fmt.Errorf("failed to seed signup tokens into actor: %w", err)
}
return nil
}