Ctrl K
ComunidadGuías…

test/auth-terminal-states.test.js

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// A16 — operator terminal states are authoritative.
//
// `disabled` and `banned` are operator decisions, not health verdicts. A request
// acquired before the operator flipped the switch can report its outcome after
// it (late success/error), and background recovery (auto re-login, liveness
// probe, token refresh) can finish after the flip. None of those may promote the
// credential back into rotation: only an explicit operator reset may leave a
// terminal state. These tests pin the transition table and the post-await
// identity/status revalidation that makes the flip durable.

import { afterEach, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
  __resetReauthChecks,
  __resetReloginState,
  __setReloginDeps,
  addAccountByKey,
  getAccountInternal,
  probeAndRecoverConnectAccount,
  reLoginAccount,
  removeAccount,
  reportBanSignal,
  reportError,
  reportSuccess,
  requestReauthCheck,
} from '../src/auth.js';

let seq = 0;
const created = new Set();

function newAccount(status = 'active', label = 'terminal') {
  const account = addAccountByKey(
    `devin-session-token$terminal-${++seq}-${Date.now()}`,
    `${label}-${seq}@example.com`,
  );
  account.email = `${label}-${seq}@example.com`;
  account.method = 'email';
  if (status !== 'active') account.status = status;
  created.add(account.id);
  return account;
}

function setAutoRelogin(on) {
  if (on) process.env.DEVIN_CONNECT_AUTO_RELOGIN = '1';
  else delete process.env.DEVIN_CONNECT_AUTO_RELOGIN;
}

beforeEach(() => {
  __resetReloginState();
  __resetReauthChecks();
});

afterEach(() => {
  for (const id of created) removeAccount(id);
  created.clear();
  __setReloginDeps(null);
  __resetReloginState();
  __resetReauthChecks();
  setAutoRelogin(false);
});

describe('late outcomes cannot override operator terminal states', () => {
  for (const status of ['disabled', 'banned']) {
    it(`late success leaves a ${status} account ${status}`, () => {
      const account = newAccount(status, `late-success-${status}`);
      account.errorCount = 3;
      account._errorAt = Date.now();
      if (status === 'banned') {
        account.bannedAt = 123456;
        account.bannedReason = 'Account suspended';
      }

      reportSuccess(account.apiKey);

      const live = getAccountInternal(account.id);
      assert.equal(live.status, status, 'a late success is not an operator reset');
      if (status === 'banned') {
        assert.equal(live.bannedAt, 123456, 'ban evidence survives');
        assert.equal(live.bannedReason, 'Account suspended');
      }
    });

    it(`late errors cannot walk a ${status} account into self-healing 'error'`, () => {
      const account = newAccount(status, `late-error-${status}`);
      for (let i = 0; i < 6; i++) reportError(account.apiKey);

      const live = getAccountInternal(account.id);
      assert.equal(live.status, status);
      assert.equal(live.errorCount, 0, 'a terminal account accumulates no error budget');
    });

    it(`ban signals cannot replace an operator ${status} with 'banned'`, () => {
      const account = newAccount(status, `ban-signal-${status}`);
      reportBanSignal(account.apiKey, 'Account suspended');
      reportBanSignal(account.apiKey, 'Invalid API key');

      assert.equal(getAccountInternal(account.id).status, status);
    });
  }

  it('CONTROL: success still promotes a self-healing error account to active', () => {
    const account = newAccount('error', 'error-control');
    account.errorCount = 4;
    account.erroredAt = Date.now();

    reportSuccess(account.apiKey);

    assert.equal(getAccountInternal(account.id).status, 'active');
    assert.equal(getAccountInternal(account.id).errorCount, 0);
  });

  it('CONTROL: a real error streak still disables an active account', () => {
    const peer = newAccount('active', 'error-peer');
    assert.ok(peer);
    const account = newAccount('active', 'error-control-active');
    for (let i = 0; i < 3; i++) reportError(account.apiKey);
    assert.equal(getAccountInternal(account.id).status, 'error');
  });
});

describe('automatic re-login cannot resurrect a terminal account', () => {
  for (const status of ['disabled', 'banned']) {
    it(`does not even attempt a login for a ${status} account`, async () => {
      setAutoRelogin(true);
      let loginCalls = 0;
      const account = newAccount(status, `relogin-start-${status}`);
      const originalKey = account.apiKey;
      __setReloginDeps({
        isCredStoreEnabled: () => true,
        getCredential: () => 'pw',
        windsurfLogin: async () => {
          loginCalls++;
          return { apiKey: 'devin-session-token$FRESH' };
        },
      });

      assert.equal(await reLoginAccount(account.id), false);
      assert.equal(loginCalls, 0, 'terminal accounts are out of the recovery path');
      assert.equal(getAccountInternal(account.id).status, status);
      assert.equal(getAccountInternal(account.id).apiKey, originalKey);
    });
  }

  it('discards a login that finished after the operator disabled the account', async () => {
    setAutoRelogin(true);
    const account = newAccount('active', 'relogin-mid-disable');
    const originalKey = account.apiKey;
    __setReloginDeps({
      isCredStoreEnabled: () => true,
      getCredential: () => 'pw',
      windsurfLogin: async () => {
        account.status = 'disabled';
        return { apiKey: 'devin-session-token$FRESH' };
      },
    });

    assert.equal(await reLoginAccount(account.id), false);
    const live = getAccountInternal(account.id);
    assert.equal(live.status, 'disabled', 'status revalidated after the awaited login');
    assert.equal(live.apiKey, originalKey, 'the discarded login must not install a token');
  });

  it('discards a login that finished after the account was removed', async () => {
    setAutoRelogin(true);
    const account = newAccount('active', 'relogin-mid-remove');
    __setReloginDeps({
      isCredStoreEnabled: () => true,
      getCredential: () => 'pw',
      windsurfLogin: async () => {
        removeAccount(account.id);
        return { apiKey: 'devin-session-token$FRESH' };
      },
    });

    assert.equal(await reLoginAccount(account.id), false);
    assert.equal(getAccountInternal(account.id), null, 'a stale result cannot recreate the row');
  });

  it('discards a login that finished after the operator installed a different key', async () => {
    setAutoRelogin(true);
    const account = newAccount('active', 'relogin-mid-key');
    __setReloginDeps({
      isCredStoreEnabled: () => true,
      getCredential: () => 'pw',
      windsurfLogin: async () => {
        account.apiKey = 'devin-session-token$OPERATOR';
        return { apiKey: 'devin-session-token$FRESH' };
      },
    });

    assert.equal(await reLoginAccount(account.id), false);
    assert.equal(getAccountInternal(account.id).apiKey, 'devin-session-token$OPERATOR');
  });
});

describe('liveness probe cannot resurrect a terminal account', () => {
  it('does not probe a disabled or banned account', async () => {
    for (const status of ['disabled', 'banned']) {
      const account = newAccount(status, `probe-start-${status}`);
      let probeCalls = 0;
      __setReloginDeps({
        checkSessionLiveness: async () => {
          probeCalls++;
          return { alive: true };
        },
      });
      const res = await probeAndRecoverConnectAccount(account.id);
      assert.equal(probeCalls, 0);
      assert.equal(res.alive, false);
      assert.equal(getAccountInternal(account.id).status, status);
    }
  });

  it('discards an ALIVE verdict that arrives after the operator disabled the account', async () => {
    const account = newAccount('error', 'probe-mid-disable-alive');
    account.errorCount = 4;
    __setReloginDeps({
      checkSessionLiveness: async () => {
        account.status = 'disabled';
        return { alive: true };
      },
    });

    const res = await probeAndRecoverConnectAccount(account.id);
    assert.equal(res.alive, false);
    assert.equal(getAccountInternal(account.id).status, 'disabled');
    assert.equal(getAccountInternal(account.id).errorCount, 4);
  });

  it('discards a DEAD verdict after the operator disabled the account (no error, no re-login)', async () => {
    setAutoRelogin(true);
    const account = newAccount('active', 'probe-mid-disable-dead');
    let loginCalls = 0;
    __setReloginDeps({
      checkSessionLiveness: async () => {
        account.status = 'disabled';
        return { alive: false, code: 'UNAUTHORIZED' };
      },
      isCredStoreEnabled: () => true,
      getCredential: () => 'pw',
      windsurfLogin: async () => {
        loginCalls++;
        return { apiKey: 'devin-session-token$FRESH' };
      },
    });

    const res = await probeAndRecoverConnectAccount(account.id);
    assert.equal(res.alive, false);
    assert.equal(loginCalls, 0);
    assert.equal(getAccountInternal(account.id).status, 'disabled');
    assert.equal(getAccountInternal(account.id).errorCount, 0);
  });

  it('discards a verdict for an account removed while the probe was in flight', async () => {
    const account = newAccount('active', 'probe-mid-remove');
    __setReloginDeps({
      checkSessionLiveness: async () => {
        removeAccount(account.id);
        return { alive: true };
      },
    });

    const res = await probeAndRecoverConnectAccount(account.id);
    assert.equal(res.alive, false);
    assert.equal(getAccountInternal(account.id), null);
  });

  it('discards a verdict for a token the operator replaced mid-probe', async () => {
    const account = newAccount('error', 'probe-mid-key');
    account.errorCount = 2;
    __setReloginDeps({
      checkSessionLiveness: async () => {
        account.apiKey = 'devin-session-token$OPERATOR';
        return { alive: true };
      },
    });

    const res = await probeAndRecoverConnectAccount(account.id);
    assert.equal(res.alive, false);
    assert.equal(getAccountInternal(account.id).status, 'error');
    assert.equal(getAccountInternal(account.id).apiKey, 'devin-session-token$OPERATOR');
  });

  it('does not stamp a failed re-login verdict onto a row re-added with the same id', async () => {
    setAutoRelogin(true);
    const account = newAccount('active', 'probe-aba');
    const id = account.id;
    __setReloginDeps({
      checkSessionLiveness: async () => ({ alive: false, code: 'UNAUTHORIZED' }),
      isCredStoreEnabled: () => true,
      getCredential: () => 'pw',
      windsurfLogin: async () => {
        // Delete + re-add under the same id while the recovery RPC is pending.
        removeAccount(id);
        const replacement = addAccountByKey('devin-session-token$replacement', 'replacement@example.com');
        replacement.id = id;
        throw new Error('login failed after ABA');
      },
    });

    const res = await probeAndRecoverConnectAccount(id);
    assert.equal(res.alive, false);
    assert.equal(res.recovered, false);
    const live = getAccountInternal(id);
    assert.ok(live, 'the replacement row exists');
    assert.equal(live.status, 'active', 'the old probe must not mark the new row reauth');
  });
});

describe('reauth probe verdicts cannot be applied to a changed row', () => {
  it('discards an ALIVE verdict after a mid-probe operator disable', async () => {
    const account = newAccount('active', 'reauth-probe-disable');
    __setReloginDeps({
      checkSessionLiveness: async () => {
        account.status = 'disabled';
        return { alive: true };
      },
    });

    const res = await requestReauthCheck(account.id, { force: true });
    assert.equal(res.checked, false);
    assert.equal(res.reason, 'NOT_ELIGIBLE');
    assert.equal(getAccountInternal(account.id).status, 'disabled');
  });

  it('discards an UNAUTHORIZED verdict for a row removed mid-probe', async () => {
    const account = newAccount('active', 'reauth-probe-remove');
    __setReloginDeps({
      checkSessionLiveness: async () => {
        removeAccount(account.id);
        return { alive: false, code: 'UNAUTHORIZED' };
      },
    });

    const res = await requestReauthCheck(account.id, { force: true });
    assert.equal(res.checked, false);
    assert.equal(res.reason, 'ACCOUNT_CHANGED');
    assert.equal(getAccountInternal(account.id), null);
  });
});