smoke-cost-estimate.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. 'use strict';
  2. const assert = require('node:assert/strict');
  3. const { buildWorkflowPlans } = require('../mcp/src/features/voc-cost/workflow-planner');
  4. const { checkBudget } = require('../mcp/src/core/budget-guard');
  5. const { runVocCostEstimate } = require('../mcp/src/tools/voc-cost-estimate-run');
  6. const { runVocUsageReport } = require('../mcp/src/tools/voc-usage-report-run');
  7. const fs = require('fs');
  8. const os = require('os');
  9. const path = require('path');
  10. async function main() {
  11. const plans = buildWorkflowPlans({ workflowType: 'xhs_account_monitor', accountCount: 280, unresolvedAccountCount: 105 });
  12. assert.equal(plans.length, 3);
  13. assert.equal(plans[0].operations.find(item => item.path.includes('search_user')).count, 105);
  14. assert.equal(plans[0].operations.find(item => item.path.includes('get_user_note')).count, 280);
  15. const trendPlans = buildWorkflowPlans({ workflowType: 'douyin_trend_report', keywordCount: 25, videosPerKeyword: 2, maxCommentPages: 0 });
  16. assert.equal(trendPlans.length, 3);
  17. assert.equal(trendPlans[0].workflowType, 'douyin_trend_report');
  18. assert.equal(trendPlans[0].operations.find(item => item.path.includes('fetch_general_search')).count, 25);
  19. assert.equal(trendPlans[0].operations.some(item => item.path.includes('fetch_video_comments')), false);
  20. const guard = checkBudget({ estimate: { amountCny: 100, quota: 1000 }, balance: { availableCny: 100 }, budgetLimitCny: 110, nextCostCny: 90 });
  21. assert.equal(guard.status, 'warning');
  22. const blocked = checkBudget({ estimate: { amountCny: 100 }, budgetLimitCny: 50, nextCostCny: 60 });
  23. assert.equal(blocked.status, 'blocked');
  24. const oldFetch = global.fetch;
  25. global.fetch = async (_url, options) => {
  26. const body = JSON.parse(options.body);
  27. if (Array.isArray(body.include) && body.include.includes('usage')) {
  28. return new Response(JSON.stringify({ code: 200, data: { usage: { quality: 'exact', totals: { calls: 3, billedCalls: 2, quota: 120, costCny: 0.24 }, groups: [{ endpoint: 'xhs.search', calls: 3, billedCalls: 2, quota: 120, costCny: 0.24 }] } } }), { status: 200 });
  29. }
  30. return new Response(JSON.stringify({ code: 200, data: { balance: { availableQuota: 100000 }, catalog: [], estimate: { quota: 100, amountCny: 1, sufficient: true, operations: body.operations.map(item => ({ model: item.model, path: item.path, count: item.count, quotaPerCall: 1, quota: item.count, priceCnyPerCall: 0.01, amountCny: item.count * 0.01 })) }, balanceUrl: null } }), { status: 200 });
  31. };
  32. const estimate = await runVocCostEstimate({ newapiToken: 'sk-test', baseUrl: 'https://billing.test', workflowType: 'tmall_listing_audit', productCount: 134 });
  33. assert.equal(estimate.status, 'ok');
  34. assert.equal(estimate.data.plans.length, 3);
  35. assert.equal(estimate.data.plans[0].assumptions.shopListPages, 5);
  36. assert.doesNotMatch(JSON.stringify(estimate), /sk-test/);
  37. const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'voc-usage-smoke-'));
  38. const usagePath = path.join(tmp, 'usage.json');
  39. fs.writeFileSync(usagePath, JSON.stringify({ usageTraceId: 'usage_smoke', usage: { quality: 'exact', totals: { calls: 2, billedCalls: 2, quota: 20, costCny: 0.2 }, items: [{ path: 'xhs.search', calls: 2, quota: 20, costCny: 0.2 }] } }));
  40. const usage = await runVocUsageReport({ reportPath: usagePath });
  41. assert.equal(usage.status, 'ok');
  42. assert.equal(usage.summary.costCny, 0.2);
  43. global.fetch = oldFetch;
  44. console.log('cost estimate and usage report smoke passed');
  45. }
  46. main().catch(error => {
  47. console.error(error);
  48. process.exitCode = 1;
  49. });