function getBudgetAssertionResults(auditResults) {
  /** @type {LHCI.AssertResults.AssertionResultNoURL[]} */
  const results = [];
  /** @type {Set<string>} */
  const resultsKeys = new Set();

  for (const auditResult of auditResults) {
    if (!auditResult) continue;
    if (!auditResult.details || !auditResult.details.items) continue;

    for (const budgetRow of auditResult.details.items) {
      const sizeKey = `${budgetRow.resourceType}.size`;
      const countKey = `${budgetRow.resourceType}.count`;

      if (budgetRow.sizeOverBudget && !resultsKeys.has(sizeKey)) {
        const actual = budgetRow.size || budgetRow.transferSize;
        const expected = actual - budgetRow.sizeOverBudget;
        results.push(...getAssertionResultsForBudgetRow(sizeKey, actual, expected));
        resultsKeys.add(sizeKey);
      }

      if (budgetRow.countOverBudget && !resultsKeys.has(countKey)) {
        const actual = budgetRow.requestCount;
        const overBudgetMatch = budgetRow.countOverBudget.match(/\d+/);
        if (!overBudgetMatch) continue;
        const overBudget = Number(overBudgetMatch[0]) || 0;
        const expected = actual - overBudget;
        results.push(...getAssertionResultsForBudgetRow(countKey, actual, expected));
        resultsKeys.add(countKey);
      }
    }
  }

  return results;
}
Budgets are somewhat unique in that they are already asserted at collection time by Lighthouse.
We won't use any of our fancy logic here and we just want to pass on whatever Lighthouse found
by creating fake individual audit results to assert against for each individual table row
(de-duped by "<resource type>.<property>").

@param {LH.AuditResult[]} auditResults
@return {LHCI.AssertResults.AssertionResultNoURL[]}