Ctrl K
ComunidadGuías…

test/dashboard-spend-acu.test.js

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

Archivo original ↗Preguntar a KiBot
// #239: the dashboard decoded `acuCost` but never rendered it — the consumer
// existed, the wiring did not. Same defect shape the connect spend path had
// before values reached `credits > 0`: a conditional cell that can only ever
// stay hidden is invisible to source-grep guards, because every string they
// match is present and correct. The discriminator is execution: render the
// shipped template with a spend record that carries the value, and with one
// that does not.
//
// So this file extracts renderSpendCells(sp) from src/dashboard/index.html and
// RUNS it — the same technique dashboard-escape-behaviour.test.js uses for
// esc()/escJsAttr(). The stub I18n returns `i18n:<key>`, so assertions can name
// which key drove which cell.
//
// Why the cell MUST stay conditional — do not "fix" it into an unconditional
// row: `committed_acu_cost` is an uncalibrated billing coordinate. The decoder
// emits it only when DEVIN_CONNECT_BILLING_TAGS names its tag — default off
// (#239 thread; docs/releases/RELEASE_NOTES_3.9.24.md; docs/AUDIT-LEDGER.md:
// "tag 来自第三方 .proto 声明顺序,不是测量。默认关"). On an unconfigured
// deployment the field is absent from `totalSpend`, so a rendered 0 would read
// as a settled number where the truth is "not decoded" — the outcome #239's
// acceptance criteria rule out. Show it when present; never fake it.
//
// Red conditions: delete the ACU cell, invert `acu > 0`, unwire the
// renderAccountDetail call, or flatten a sub-cent magnitude to `0` — each
// turns an assertion below red.

import { before, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';

const HTML = readFileSync(new URL('../src/dashboard/index.html', import.meta.url), 'utf8');

// Pull the method body out of the App object literal and turn it into a
// callable whose only inputs are the spend record and the i18n lookup.
// Extracting the shipped source (not reimplementing it) is the point: a mirror
// implementation would keep passing while production regressed.
function extractSpendCells() {
  const at = HTML.indexOf('\n  renderSpendCells(sp)');
  assert.notEqual(at, -1,
    'renderSpendCells(sp) must exist as a one-arg method in the dashboard App object — '
    + 'without it the account detail has no way to render the decoded ACU cost (#239)');
  const open = HTML.indexOf('{', at);
  let depth = 0;
  let end = -1;
  for (let i = open; i < HTML.length; i++) {
    if (HTML[i] === '{') depth++;
    else if (HTML[i] === '}' && --depth === 0) { end = i + 1; break; }
  }
  assert.notEqual(end, -1, 'could not delimit the body of renderSpendCells');
  const body = HTML.slice(open, end);
  // eslint-disable-next-line no-new-func -- executing the shipped source is the point
  return new Function('sp', 'I18n', `return (function renderSpendCells(sp) ${body}).call(null, sp);`);
}

const I18n = { t: (k) => `i18n:${k}` };

// The text content of the ACU cell's value span, or null when the cell is absent.
function acuCellValue(html) {
  const m = html.match(/<span class="k">i18n:account\.detail\.runtime\.spendAcu<\/span><span class="v"[^>]*>([^<]*)<\/span>/);
  return m ? m[1] : null;
}

// Parse a rendered number back into a Number, tolerating comma decimal-separators.
function asNumber(text) {
  return Number(String(text).replace(/\s/g, '').replace(',', '.'));
}

let renderSpendCells;
before(() => { renderSpendCells = extractSpendCells(); });

describe('account detail renders the decoded ACU cost (#239)', () => {
  it('renders a labelled ACU cell when the decoder produced a value, beside credit rather than merged', () => {
    const html = renderSpendCells({ requests: 3, totalTokens: 120, creditCost: 2.5, acuCost: 1.25 }, I18n);
    assert.match(html,
      /<span class="k">i18n:account\.detail\.runtime\.spendAcu<\/span><span class="v"[^>]*>1[.,]25<\/span>/,
      'a present acuCost must render its own labelled cell with its value');
    assert.match(html,
      /<span class="k">i18n:account\.detail\.runtime\.spendCredits<\/span><span class="v"[^>]*>2[.,]5<\/span>/,
      'the credit cell must keep rendering alongside it');
    assert.doesNotMatch(html, /3[.,]75/,
      'ACU and credit are different units — the two cells must never be added together (#239)');
  });

  it('renders the canonical sub-cent ACU value (#22 wire fixture) as a non-zero magnitude', () => {
    // 0.0006735000060871243 is the decoded committed_acu_cost the repo's own
    // opt-in fixture pins (test/acu-opt-in-decode.test.js:16). A 2-decimal
    // formatter flattens it to the literal "0" — a settled-looking zero on
    // exactly the opt-in deployment this cell exists for (#239).
    const CANONICAL_ACU = 0.0006735000060871243;
    const html = renderSpendCells({ requests: 1, totalTokens: 10, acuCost: CANONICAL_ACU }, I18n);
    const text = acuCellValue(html);
    assert.notEqual(text, null, 'the sub-cent ACU must still render its cell');
    assert.notEqual(text, '0', 'sub-cent ACU rendered as a literal "0" — the magnitude was flattened away');
    const n = asNumber(text);
    assert.ok(n > 0.0006 && n < 0.0007, `expected the magnitude ~6.7e-4, got "${text}"`);
  });

  it('does not flatten other sub-cent magnitudes either', () => {
    const html = renderSpendCells({ acuCost: 0.004 }, I18n);
    const text = acuCellValue(html);
    assert.notEqual(text, null, 'a sub-cent ACU still renders its cell');
    const n = asNumber(text);
    assert.ok(n > 0.003 && n < 0.005, `expected the magnitude ~0.004, got "${text}"`);
  });

  it('omits the ACU cell when nothing was decoded (default-off deployment)', () => {
    const html = renderSpendCells({ requests: 3, totalTokens: 120, creditCost: 2.5 }, I18n);
    assert.doesNotMatch(html, /spendAcu/,
      'no decoded value must mean no cell — rendering 0 would present an uncalibrated coordinate '
      + 'as settled, and the decoder is default-off (#239)');
    assert.match(html, /i18n:account\.detail\.runtime\.spendCredits/, 'sibling cells are unaffected');
  });

  it('treats an explicit decoded zero the same as absent', () => {
    const html = renderSpendCells({ requests: 1, totalTokens: 10, acuCost: 0 }, I18n);
    assert.doesNotMatch(html, /spendAcu/, 'a decoded 0 is not a number worth showing');
  });

  it('coerces a non-numeric value to absent instead of rendering it', () => {
    const html = renderSpendCells({ acuCost: '<img src=x onerror=alert(1)>' }, I18n);
    assert.doesNotMatch(html, /<img/, 'a hostile string must never reach the markup');
    assert.doesNotMatch(html, /spendAcu/, 'Number() maps it to NaN → cell absent');
  });

  it('renders the zeroed base rows when totalSpend is absent (pre-K8 accounts)', () => {
    const html = renderSpendCells(undefined, I18n);
    assert.match(html, /i18n:account\.detail\.runtime\.spendRequests/);
    assert.match(html, /i18n:account\.detail\.runtime\.spendTokens/);
    assert.doesNotMatch(html, /spendAcu/);
  });
});

describe('the ACU cell is wired, not merely defined', () => {
  // #239's actual defect was a missing wire while every piece existed: the
  // decoder decoded, the accumulator accumulated, the template could render —
  // and nothing carried the value across the last hop. A method that exists but
  // is never called reproduces exactly that shape while every behavioural test
  // above stays green, so pin the call site too.
  it('renderAccountDetail delegates to renderSpendCells(a.totalSpend)', () => {
    assert.match(HTML, /this\.renderSpendCells\(a\.totalSpend\)/,
      'the account detail must actually call renderSpendCells — an uncalled method renders nothing');
  });
});