fix(api): 消费 op-login 请求时要求已审批,fake 同步
This commit is contained in:
3 files changed
+15
-6
No files matched your search
@@ -1734,13 +1734,13 @@ func (f *fakeRepo) OpLoginRequestByID(_ context.Context, id string) (*OpLoginReq
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConsumeOpLoginRequest stamps consumed on an unconsumed, unexpired request (the
|
// ConsumeOpLoginRequest stamps consumed on an approved, unconsumed, unexpired
|
||||||
// finish path's single-use guard), mirroring the PG zero-rows-else UPDATE. The
|
// request (the finish path's single-use guard), mirroring the PG zero-rows-else
|
||||||
// approval gate is read by the handler BEFORE this call, so consume only checks
|
// UPDATE. The handler reads the approval first too; the store refuses a pending
|
||||||
// consumed_at and expiry (exactly as PG does).
|
// request on its own so the single-use guard never depends on that read.
|
||||||
func (f *fakeRepo) ConsumeOpLoginRequest(_ context.Context, id string, now time.Time) error {
|
func (f *fakeRepo) ConsumeOpLoginRequest(_ context.Context, id string, now time.Time) error {
|
||||||
r, ok := f.opLogins[id]
|
r, ok := f.opLogins[id]
|
||||||
if !ok || r.consumed || !r.expiresAt.After(now) {
|
if !ok || r.status != "approved" || r.consumed || !r.expiresAt.After(now) {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
r.consumed = true
|
r.consumed = true
|
||||||
|
|||||||
@@ -2948,7 +2948,7 @@ func (p *PGRepo) ApproveOpLogin(ctx context.Context, id, approverUserID string,
|
|||||||
func (p *PGRepo) ConsumeOpLoginRequest(ctx context.Context, id string, now time.Time) error {
|
func (p *PGRepo) ConsumeOpLoginRequest(ctx context.Context, id string, now time.Time) error {
|
||||||
res, err := p.db.ExecContext(ctx,
|
res, err := p.db.ExecContext(ctx,
|
||||||
`UPDATE op_login_requests SET consumed_at = $2
|
`UPDATE op_login_requests SET consumed_at = $2
|
||||||
WHERE id = $1 AND consumed_at IS NULL AND expires_at > $2`,
|
WHERE id = $1 AND approved_at IS NOT NULL AND consumed_at IS NULL AND expires_at > $2`,
|
||||||
id, now)
|
id, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1260,6 +1260,15 @@ func TestOpLoginStateMachine(t *testing.T) {
|
|||||||
t.Fatal("fresh pending request missing from the list")
|
t.Fatal("fresh pending request missing from the list")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A pending request is not a ticket: the store refuses to consume it on its own,
|
||||||
|
// whatever the caller checked, and the refusal leaves it pending and unconsumed.
|
||||||
|
if err := repo.ConsumeOpLoginRequest(ctx, id, now); !errors.Is(err, api.ErrNotFound) {
|
||||||
|
t.Fatalf("consume pending = %v, want ErrNotFound", err)
|
||||||
|
}
|
||||||
|
if req, err := repo.OpLoginRequestByID(ctx, id); err != nil || req.Status != "pending" || req.Consumed {
|
||||||
|
t.Fatalf("after refused consume = %+v, %v; want pending+unconsumed", req, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Approve -> consume -> single use; second approve/consume are ErrNotFound.
|
// Approve -> consume -> single use; second approve/consume are ErrNotFound.
|
||||||
if err := repo.ApproveOpLogin(ctx, id, approver.ID, now); err != nil {
|
if err := repo.ApproveOpLogin(ctx, id, approver.ID, now); err != nil {
|
||||||
t.Fatalf("ApproveOpLogin: %v", err)
|
t.Fatalf("ApproveOpLogin: %v", err)
|
||||||
|
|||||||
Reference in new issue
Block a user