billing-client-smoke.js 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. 'use strict';
  2. const assert = require('node:assert/strict');
  3. const { queryBilling } = require('../mcp/src/core/billing-client');
  4. const { requestPaymentQr } = require('../mcp/src/core/recharge-client');
  5. async function main() {
  6. const originalFetch = global.fetch;
  7. let billingCalls = 0;
  8. global.fetch = async (url, options) => {
  9. if (/\/api\/fmode\/billing\/query$/.test(String(url))) {
  10. billingCalls += 1;
  11. assert.equal(options.method, 'POST');
  12. assert.match(options.headers.Authorization, /^Bearer sk-test$/);
  13. return new Response(JSON.stringify({
  14. code: 200,
  15. data: {
  16. balance: { availableQuota: 1000 },
  17. balanceUrl: billingCalls > 1 ? 'https://app.fmode.cn/dev/studio/balance/?token=r%3Asession-test' : null,
  18. estimate: {
  19. quota: 2000,
  20. amountCny: 1,
  21. shortfallQuota: 1000,
  22. suggestedRechargeCny: 1.1,
  23. sufficient: false,
  24. operations: []
  25. },
  26. catalog: []
  27. }
  28. }), { status: 200, headers: { 'content-type': 'application/json' } });
  29. }
  30. assert.match(String(url), /\/parse\/functions\/pay_code2$/);
  31. assert.equal(options.method, 'POST');
  32. assert.match(options.headers['X-Parse-Session-Token'], /^r:/);
  33. return new Response(JSON.stringify({ code_url: ['weixin://pay/query-test'], nonce_str: 'nonce-query' }), { status: 200 });
  34. };
  35. const result = await queryBilling({
  36. newapiToken: 'sk-test',
  37. baseUrl: 'https://billing.example.test',
  38. operations: [{ model: 'voc-social', count: 1 }]
  39. });
  40. assert.equal(result.status, 'ok');
  41. if (result.recharge) {
  42. assert.equal(result.recharge.paymentMode, 'balance_url_and_payment_request');
  43. assert.equal(Object.prototype.hasOwnProperty.call(result.recharge.paymentRequest, 'headers'), false);
  44. assert.doesNotMatch(JSON.stringify(result), /r:[A-Za-z0-9_-]{8,}/);
  45. }
  46. const qrResult = await queryBilling({
  47. newapiToken: 'sk-test',
  48. sessionToken: 'r:session-test',
  49. baseUrl: 'https://billing.example.test',
  50. createPaymentQr: true,
  51. operations: [{ model: 'voc-social', count: 1 }]
  52. });
  53. assert.equal(qrResult.status, 'ok');
  54. assert.equal(qrResult.recharge.paymentMode, 'qr_and_balance_url');
  55. assert.equal(qrResult.recharge.qrCodeUrl, 'weixin://pay/query-test');
  56. assert.equal(qrResult.recharge.paymentRequest.nonceStr, 'nonce-query');
  57. assert.doesNotMatch(JSON.stringify(qrResult.recharge.paymentRequest), /r:[A-Za-z0-9_-]{8,}/);
  58. const qr = await requestPaymentQr({
  59. amount: 2,
  60. sessionToken: 'r:session-test',
  61. fetchImpl: async (_url, options) => {
  62. assert.match(options.headers['X-Parse-Session-Token'], /^r:/);
  63. return new Response(JSON.stringify({ code_url: ['weixin://pay/test'], nonce_str: 'nonce' }), { status: 200 });
  64. }
  65. });
  66. assert.equal(qr.qrCodeUrl, 'weixin://pay/test');
  67. assert.equal(qr.amountCny, 2);
  68. global.fetch = originalFetch;
  69. console.log('billing-client smoke passed');
  70. }
  71. main().catch(error => {
  72. console.error(error);
  73. process.exitCode = 1;
  74. });