Ctrl K
ComunidadGuías…

test/dashboard-devin-read.test.js

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

Archivo original ↗Preguntar a KiBot
import { afterEach, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import {
  ReadError,
  accountRef,
  cacheKey,
  peekCached,
  readCached,
  readDevinJson,
  redactUpstreamText,
  resolveOrgId,
  __resetReadCache,
  __setFetchImplForTest,
  __setNowForTest,
} from '../src/dashboard/devin-read.js';

function jsonResponse(value, { status = 200, headers = {} } = {}) {
  return {
    ok: status >= 200 && status < 300,
    status,
    headers: { get: (name) => headers[String(name).toLowerCase()] ?? null },
    text: async () => (typeof value === 'string' ? value : JSON.stringify(value)),
  };
}

function errorResponse(status) {
  return jsonResponse({ detail: 'nope' }, { status });
}

const originalOrg = process.env.DEVIN_BACKEND_ORG_ID;

afterEach(() => {
  __setFetchImplForTest(null);
  __setNowForTest(null);
  __resetReadCache();
  if (originalOrg === undefined) delete process.env.DEVIN_BACKEND_ORG_ID;
  else process.env.DEVIN_BACKEND_ORG_ID = originalOrg;
});

describe('readDevinJson', () => {
  it('is GET-only: no method/body parameter, redirect blocked, no Content-Type', async () => {
    const calls = [];
    const out = await readDevinJson({
      token: 'tok',
      path: '/org-abc123/billing/status',
      orgId: 'org-abc123',
      fetchImpl: async (url, init) => {
        calls.push({ url, init });
        return jsonResponse({ plan_slug: 'pro' });
      },
    });
    assert.equal(out.plan_slug, 'pro');
    assert.equal(calls.length, 1);
    assert.equal(calls[0].init.method, 'GET');
    assert.equal('body' in calls[0].init, false);
    assert.equal(calls[0].init.redirect, 'error');
    assert.equal(calls[0].init.headers.Authorization, 'Bearer tok');
    assert.equal(calls[0].init.headers.Accept, 'application/json');
    assert.match(calls[0].init.headers['User-Agent'], /^WindsurfAPI\//);
    assert.equal(calls[0].init.headers['x-cog-org-id'], 'org-abc123');
    assert.equal('Content-Type' in calls[0].init.headers, false);
    assert.equal(calls[0].url, 'https://app.devin.ai/api/org-abc123/billing/status');
  });

  it('omits x-cog-org-id on global paths', async () => {
    let seen;
    await readDevinJson({
      token: 'tok',
      path: '/users/spending-state',
      fetchImpl: async (url, init) => { seen = init; return jsonResponse({ state: 'active' }); },
    });
    assert.equal('x-cog-org-id' in seen.headers, false);
  });

  it('rejects absolute URLs, protocol-relative paths and header injection before fetch', async () => {
    let called = 0;
    const fetchImpl = async () => { called++; return jsonResponse({}); };
    for (const path of ['https://evil.test/x', '//evil.test/x', '/x\r\nHost: evil', 'x/no-leading-slash']) {
      await assert.rejects(
        readDevinJson({ token: 'tok', path, fetchImpl }),
        (err) => err instanceof ReadError && err.kind === 'bad_path',
        `path ${JSON.stringify(path)} must be rejected`,
      );
    }
    assert.equal(called, 0, 'no fetch may happen for a bad path');
  });

  it('maps upstream statuses to error kinds', async () => {
    const cases = [
      [401, 'unauthorized'],
      [403, 'forbidden'],
      [404, 'not_found'],
      [429, 'rate_limited'],
      [500, 'upstream'],
      [302, 'redirect_blocked'],
    ];
    for (const [status, kind] of cases) {
      await assert.rejects(
        readDevinJson({ token: 'tok', path: '/x', fetchImpl: async () => errorResponse(status) }),
        (err) => err instanceof ReadError && err.kind === kind && err.status === status,
        `status ${status}`,
      );
    }
  });

  it('maps timeout, malformed and too-large bodies', async () => {
    await assert.rejects(
      readDevinJson({
        token: 'tok',
        path: '/x',
        fetchImpl: async () => { throw Object.assign(new Error('aborted'), { name: 'TimeoutError' }); },
      }),
      (err) => err.kind === 'timeout',
    );
    await assert.rejects(
      readDevinJson({ token: 'tok', path: '/x', fetchImpl: async () => jsonResponse('<html>not json') }),
      (err) => err.kind === 'malformed',
    );
    await assert.rejects(
      readDevinJson({
        token: 'tok',
        path: '/x',
        maxBytes: 8,
        fetchImpl: async () => jsonResponse({ long: 'aaaaaaaaaaaaaaaa' }),
      }),
      (err) => err.kind === 'too_large',
    );
  });

  it('keeps the timeout armed while a streamed body is read', async () => {
    let signal;
    await assert.rejects(
      readDevinJson({
        token: 'tok',
        path: '/x',
        timeoutMs: 20,
        fetchImpl: async (_url, init) => {
          signal = init.signal;
          return new Response(new ReadableStream({
            start(controller) {
              const timer = setTimeout(() => {
                controller.enqueue(new TextEncoder().encode('{}'));
                controller.close();
              }, 120);
              signal.addEventListener('abort', () => {
                clearTimeout(timer);
                controller.error(Object.assign(new Error('aborted'), { name: 'AbortError' }));
              }, { once: true });
            },
          }));
        },
      }),
      (err) => err.kind === 'timeout',
    );
    assert.equal(signal.aborted, true);
  });

  it('aborts an oversized streamed body before reading all of it', async () => {
    let chunks = 0;
    await assert.rejects(
      readDevinJson({
        token: 'tok',
        path: '/x',
        maxBytes: 32,
        fetchImpl: async () => new Response(new ReadableStream({
          pull(controller) {
            chunks++;
            controller.enqueue(new Uint8Array(16).fill(97));
            if (chunks === 8) controller.close();
          },
        }, { highWaterMark: 0 })),
      }),
      (err) => err.kind === 'too_large',
    );
    assert.ok(chunks <= 3, `read ${chunks} chunks before enforcing the cap`);
  });

  it('requires a token and a valid org header when one is supplied', async () => {
    await assert.rejects(readDevinJson({ token: '', path: '/x' }), (err) => err.kind === 'no_account');
    await assert.rejects(
      readDevinJson({ token: 'tok', path: '/x', orgId: 'not-an-org' }),
      (err) => err.kind === 'bad_org',
    );
  });
});

describe('resolveOrgId', () => {
  it('uses GET /users/current-membership first, never post-auth', async () => {
    const calls = [];
    const org = await resolveOrgId({
      token: 'tok',
      fetchImpl: async (url, init) => {
        calls.push({ url, method: init.method });
        if (url.endsWith('/users/current-membership')) {
          return jsonResponse({ org: { org_id: 'org-primary1' }, user: { email: 'x@example.test' } });
        }
        throw new Error(`unexpected ${url}`);
      },
    });
    assert.equal(org, 'org-primary1');
    assert.equal(calls.length, 1);
    assert.equal(calls[0].method, 'GET');
    assert.ok(calls.every((c) => !c.url.includes('/users/post-auth')));
  });

  it('falls back to GET /organizations and picks the primary org', async () => {
    const calls = [];
    const org = await resolveOrgId({
      token: 'tok',
      fetchImpl: async (url) => {
        calls.push(url);
        if (url.endsWith('/users/current-membership')) return errorResponse(404);
        if (url.endsWith('/organizations')) {
          return jsonResponse([{ org_id: 'org-a' }, { org_id: 'org-b', is_primary_org: true }]);
        }
        throw new Error(`unexpected ${url}`);
      },
    });
    assert.equal(org, 'org-b');
    assert.deepEqual(calls.map((u) => u.split('/api')[1]), ['/users/current-membership', '/organizations']);
  });

  it('uses the single org when no primary is flagged', async () => {
    const org = await resolveOrgId({
      token: 'tok',
      fetchImpl: async (url) => {
        if (url.endsWith('/users/current-membership')) return jsonResponse({});
        if (url.endsWith('/organizations')) return jsonResponse([{ org_id: 'org-only1' }]);
        throw new Error(`unexpected ${url}`);
      },
    });
    assert.equal(org, 'org-only1');
  });

  it('refuses to guess between multiple orgs with no primary', async () => {
    await assert.rejects(
      resolveOrgId({
        token: 'tok',
        fetchImpl: async (url) => {
          if (url.endsWith('/users/current-membership')) return jsonResponse({});
          if (url.endsWith('/organizations')) return jsonResponse([{ org_id: 'org-a' }, { org_id: 'org-b' }]);
          throw new Error(`unexpected ${url}`);
        },
      }),
      (err) => err.kind === 'org_unresolved',
    );
  });

  it('does not fall back when the token itself is rejected', async () => {
    const calls = [];
    await assert.rejects(
      resolveOrgId({
        token: 'tok',
        fetchImpl: async (url) => { calls.push(url); return errorResponse(401); },
      }),
      (err) => err.kind === 'unauthorized',
    );
    assert.equal(calls.length, 1, 'a 401 must not be retried against /organizations');
  });

  it('honours DEVIN_BACKEND_ORG_ID without any fetch', async () => {
    process.env.DEVIN_BACKEND_ORG_ID = 'org-fromenv';
    let called = 0;
    const org = await resolveOrgId({ token: 'tok', fetchImpl: async () => { called++; return jsonResponse({}); } });
    assert.equal(org, 'org-fromenv');
    assert.equal(called, 0);
  });
});

describe('readCached', () => {
  it('serves fresh hits, refetches after TTL, and supports force', async () => {
    let now = 1_000_000;
    __setNowForTest(() => now);
    let loads = 0;
    const load = async () => { loads++; return { n: loads }; };

    const first = await readCached('k', 30_000, load);
    assert.equal(first.cached, false);
    assert.equal(first.value.n, 1);

    now += 29_000;
    const hit = await readCached('k', 30_000, load);
    assert.equal(hit.cached, true);
    assert.equal(hit.value.n, 1);
    assert.equal(loads, 1);

    now += 2_000;
    const miss = await readCached('k', 30_000, load);
    assert.equal(miss.cached, false);
    assert.equal(miss.value.n, 2);

    now += 1_000;
    const forced = await readCached('k', 30_000, load, { force: true });
    assert.equal(forced.cached, false);
    assert.equal(loads, 3);
  });

  it('single-flights concurrent loads', async () => {
    let loads = 0;
    let release;
    const gate = new Promise((resolve) => { release = resolve; });
    const load = async () => { loads++; await gate; return { ok: 1 }; };
    const a = readCached('single', 30_000, load);
    const b = readCached('single', 30_000, load);
    release();
    const [ra, rb] = await Promise.all([a, b]);
    assert.equal(loads, 1);
    assert.equal(ra.value.ok, 1);
    assert.equal(rb.value.ok, 1);
  });

  it('serves the last good value as stale on transient errors, but never on 403', async () => {
    let now = 2_000_000;
    __setNowForTest(() => now);
    await readCached('stale', 30_000, async () => ({ plan: 'pro' }));
    now += 31_000;

    const stale = await readCached('stale', 30_000, async () => {
      throw new ReadError('timeout', { status: null });
    });
    assert.equal(stale.ok, true);
    assert.equal(stale.stale, true);
    assert.equal(stale.value.plan, 'pro');

    const forbidden = await readCached('stale', 30_000, async () => {
      throw new ReadError('forbidden', { status: 403 });
    });
    assert.equal(forbidden.ok, false);
    assert.equal(forbidden.error, 'forbidden');
    assert.equal('value' in forbidden, false);
  });

  it('drops the stale value after the max-stale cap', async () => {
    let now = 3_000_000;
    __setNowForTest(() => now);
    await readCached('old', 30_000, async () => ({ plan: 'pro' }));
    now += 31 * 60_000;
    const out = await readCached('old', 30_000, async () => {
      throw new ReadError('upstream', { status: 502 });
    });
    assert.equal(out.ok, false);
    assert.equal(out.error, 'upstream');
  });

  it('caps the cache at 200 entries, evicting the oldest insertion', async () => {
    for (let i = 0; i < 201; i += 1) {
      await readCached(`cap-${i}`, 30_000, async () => ({ i }));
    }
    assert.equal(peekCached('cap-0'), null, 'the oldest entry is evicted');
    assert.equal(peekCached('cap-200').value.i, 200);
  });
});

describe('cache keys, peek and redaction', () => {
  it('never uses the raw token as a cache key and accountRef is a hash', () => {
    const token = ['super', 'secret', 'token'].join('-');
    const key = cacheKey({ accountId: 'acct-test', token, name: 'status' });
    assert.equal(key.includes(token), false);
    assert.match(key, /^acct-test:[0-9a-f]{12}:status$/);
    assert.match(accountRef('acct-test'), /^acct-[0-9a-f]{8}$/);
  });

  it('peekCached is read-only and reports freshness', async () => {
    let now = 4_000_000;
    __setNowForTest(() => now);
    assert.equal(peekCached('peek'), null);
    await readCached('peek', 30_000, async () => ({ v: 1 }));
    assert.equal(peekCached('peek').value.v, 1);
    assert.equal(peekCached('peek').stale, false);
    now += 31_000;
    assert.equal(peekCached('peek').stale, true);
  });

  it('redacts credentials, org/user/acct ids and email from free text', () => {
    const raw = 'contact org-test123 / user-test456 / acct_abcdef123 / user@example.test '
      + 'auth1_abcdefgh devin-session-token$abcdef';
    const out = redactUpstreamText(raw);
    for (const secret of ['org-test123', 'user-test456', 'acct_abcdef123', 'user@example.test', 'auth1_abcdefgh', 'devin-session-token$abcdef']) {
      assert.equal(out.includes(secret), false, `must redact ${secret}`);
    }
    assert.ok(redactUpstreamText('x'.repeat(1000)).length <= 200);
  });
});

describe('read-path write scan', () => {
  const here = dirname(fileURLToPath(import.meta.url));
  const modules = [
    'devin-read.js',
    'billing-health.js',
    'model-reconcile.js',
    'quota-burn-down.js',
    'usage.js',
    'catalog-diff.js',
    'pool-events.js',
  ];
  const FORBIDDEN = [
    'probePostAuth',
    'saveAccounts',
    'setAccountStatus',
    'refreshCredits',
    'setRuntimeApiKey',
    'setRuntimeDashboardPassword',
    'writeJsonAtomic',
    'writeFileSync',
    "method: 'POST'",
    'method: "POST"',
  ];

  it('never contains a write call, a post-auth probe or a local persistence call', () => {
    for (const name of modules) {
      const source = readFileSync(join(here, '..', 'src', 'dashboard', name), 'utf8');
      for (const token of FORBIDDEN) {
        assert.equal(source.includes(token), false, `${name} must not contain ${token}`);
      }
      assert.equal(source.includes("'/users/post-auth'"), false, `${name} must not call post-auth`);
    }
  });
});