Ctrl K
ComunidadGuías…

test/cloud-catalog-backend-boundary.test.js

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

Archivo original ↗Preguntar a KiBot
import { afterEach, beforeEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { config } from '../src/config.js';
import {
  __resetModelCatalogState,
  __setModelCatalogDeps,
  __waitForModelCatalogSync,
  addAccountByKey,
  configureBindHost,
  getDroughtSummary,
  removeAccount,
} from '../src/auth.js';
import { handleDashboardApi } from '../src/dashboard/api.js';
import { handleModels } from '../src/handlers/models.js';
import { _resetRuntimeConfigForTests } from '../src/runtime-config.js';
import {
  MODELS,
  mergeCloudModels,
  setActiveCloudCatalogAccounts,
} from '../src/models.js';

const CASCADE_FREE_KEY = 'gemini-2.5-flash';
const CASCADE_ONLY_KEY = 'claude-4-sonnet';
// The Connect-namespace counterpart of CASCADE_FREE_KEY. Written literally rather
// than imported from FREE_REACHABLE_SELECTORS on purpose: importing the set the
// implementation reads would make this assertion pass no matter what that set
// contains, which is the "guard satisfied by its own source" antipattern.
const CONNECT_FREE_SELECTOR = 'swe-1-6-slow';
const ACCOUNT_ID = 'catalog-backend-boundary';
const ORIGINAL_ALLOW_NO_AUTH = process.env.DASHBOARD_ALLOW_NO_AUTH;
const ORIGINAL_DEVIN_CONNECT = process.env.DEVIN_CONNECT;
const ORIGINAL_DASHBOARD_PASSWORD = config.dashboardPassword;
const ORIGINAL_API_KEY = config.apiKey;
const createdAccountIds = [];

function fakeRes() {
  return {
    statusCode: 0,
    body: '',
    writeHead(status) { this.statusCode = status; },
    end(chunk) { this.body += chunk ? String(chunk) : ''; },
    json() { return this.body ? JSON.parse(this.body) : null; },
  };
}

function localReq(path) {
  return {
    url: `/dashboard/api${path}`,
    headers: {},
    socket: { remoteAddress: '127.0.0.1' },
  };
}

function syncCascadeOnlyCatalog() {
  setActiveCloudCatalogAccounts([ACCOUNT_ID]);
  const configs = [{ modelUid: MODELS[CASCADE_ONLY_KEY].modelUid }];
  // These direct calls represent separate production sync rounds: the first
  // quarantines a small snapshot and the second confirms it after a delay.
  mergeCloudModels(configs, { accountId: ACCOUNT_ID });
  mergeCloudModels(configs, { accountId: ACCOUNT_ID });
}

beforeEach(() => {
  _resetRuntimeConfigForTests();
  __resetModelCatalogState();
  __setModelCatalogDeps(null);
  delete process.env.DEVIN_CONNECT;
  process.env.DASHBOARD_ALLOW_NO_AUTH = '1';
  config.dashboardPassword = '';
  config.apiKey = '';
  configureBindHost('127.0.0.1');
});

afterEach(async () => {
  for (const accountId of createdAccountIds.splice(0)) removeAccount(accountId);
  await __waitForModelCatalogSync();
  __resetModelCatalogState();
  __setModelCatalogDeps(null);
  _resetRuntimeConfigForTests();
  if (ORIGINAL_DEVIN_CONNECT === undefined) delete process.env.DEVIN_CONNECT;
  else process.env.DEVIN_CONNECT = ORIGINAL_DEVIN_CONNECT;
  if (ORIGINAL_ALLOW_NO_AUTH === undefined) delete process.env.DASHBOARD_ALLOW_NO_AUTH;
  else process.env.DASHBOARD_ALLOW_NO_AUTH = ORIGINAL_ALLOW_NO_AUTH;
  config.dashboardPassword = ORIGINAL_DASHBOARD_PASSWORD;
  config.apiKey = ORIGINAL_API_KEY;
  configureBindHost('0.0.0.0');
});

describe('Cascade cloud catalog backend boundary', () => {
  it('does not narrow DEVIN_CONNECT model listings', () => {
    const connectEnv = { DEVIN_CONNECT: '1' };
    const baseline = handleModels(connectEnv).data.map((model) => model.id);

    syncCascadeOnlyCatalog();

    assert.deepEqual(
      handleModels(connectEnv).data.map((model) => model.id),
      baseline,
      'a Cascade-only allowlist must not empty or narrow the Connect model view',
    );
  });

  it('does not narrow Dashboard model views in DEVIN_CONNECT mode', async () => {
    process.env.DEVIN_CONNECT = '1';

    const baselineModelsRes = fakeRes();
    await handleDashboardApi('GET', '/models', {}, localReq('/models'), baselineModelsRes);
    const baselineTierRes = fakeRes();
    await handleDashboardApi('GET', '/tier-access', {}, localReq('/tier-access'), baselineTierRes);

    syncCascadeOnlyCatalog();

    const modelsRes = fakeRes();
    await handleDashboardApi('GET', '/models', {}, localReq('/models'), modelsRes);
    assert.deepEqual(
      modelsRes.json().models.map((model) => model.id),
      baselineModelsRes.json().models.map((model) => model.id),
    );

    const tierRes = fakeRes();
    await handleDashboardApi('GET', '/tier-access', {}, localReq('/tier-access'), tierRes);
    assert.deepEqual(tierRes.json().allModels, baselineTierRes.json().allModels);
  });

  it('does not delay the independent Connect catalog behind Cascade confirmation', async () => {
    const runId = Date.now().toString(36);
    const apiKey = `catalog-connect-independent-${runId}`;
    let cascadeRequests = 0;
    let connectRequests = 0;
    let connectRows = null;
    let scheduledRetry;
    __setModelCatalogDeps({
      scheduleCatalogRetry: (retry) => {
        scheduledRetry = retry;
        return () => {};
      },
      getCascadeModelConfigs: async () => {
        cascadeRequests += 1;
        return { configs: [{ modelUid: MODELS[CASCADE_ONLY_KEY].modelUid }] };
      },
      fetchConnectCatalog: async ({ token }) => {
        connectRequests += 1;
        assert.equal(token, apiKey);
        return [{ selector: 'swe-1-6-slow', provider: 'windsurf' }];
      },
      setLiveCatalogSelectors: (rows) => {
        connectRows = rows;
      },
    });

    const account = addAccountByKey(apiKey, 'catalog-connect-independent');
    createdAccountIds.push(account.id);
    await __waitForModelCatalogSync();

    assert.equal(cascadeRequests, 1);
    assert.equal(typeof scheduledRetry, 'function');
    assert.equal(connectRequests, 1);
    assert.deepEqual(connectRows, [{ selector: 'swe-1-6-slow', provider: 'windsurf' }]);
  });

  it('does not expose the Cascade drought fail-open warning in DEVIN_CONNECT mode', async () => {
    process.env.DEVIN_CONNECT = '1';
    const runId = Date.now().toString(36);
    const apiKey = `catalog-connect-drought-${runId}`;
    let scheduledRetry;
    __setModelCatalogDeps({
      disableConnectSync: true,
      scheduleCatalogRetry: (retry) => {
        scheduledRetry = retry;
        return () => {};
      },
      getCascadeModelConfigs: async () => ({
        configs: [{ modelUid: MODELS[CASCADE_ONLY_KEY].modelUid }],
      }),
    });

    const account = addAccountByKey(apiKey, 'catalog-connect-drought');
    createdAccountIds.push(account.id);
    account.credits = { weeklyPercent: 0, dailyPercent: 0 };
    await __waitForModelCatalogSync();
    scheduledRetry();
    await __waitForModelCatalogSync();

    const summary = getDroughtSummary();
    assert.equal(summary.drought, true);
    assert.equal(summary.restrictionFailOpen, false);
    // The point of this assertion is that the free list is NON-EMPTY, which is
    // what keeps restrictionFailOpen false. It used to check for the Cascade key
    // because MODELS was the only namespace getDroughtSummary knew about.
    //
    // Since #234 the list resolves per active backend, and this test runs with
    // DEVIN_CONNECT=1 — so the correct expectation is the Connect free list.
    // Asserting the Cascade key here would pin the very defect #234 fixes:
    // connect cannot route to gemini-2.5-flash, so offering it as the drought
    // fallback sends clients at a model that returns an error.
    assert.ok(summary.freeTierModels.length > 0, 'a non-empty free list is what avoids fail-open');
    assert.ok(
      summary.freeTierModels.includes(CONNECT_FREE_SELECTOR),
      'in DEVIN_CONNECT mode the drought fallback must be a connect-reachable selector',
    );
    assert.ok(
      !summary.freeTierModels.includes(CASCADE_FREE_KEY),
      'the Cascade free model is not reachable through connect and must not be offered',
    );
  });
});