import { describe, it, expect } from 'vitest'; import crypto from 'crypto'; import { generateSign, verifySign } from '@/lib/alipay/sign'; // 生成测试用 RSA 密钥对 const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' }, }); // 提取裸 base64(去掉 PEM 头尾) const barePrivateKey = privateKey .replace(/-----BEGIN PRIVATE KEY-----/, '') .replace(/-----END PRIVATE KEY-----/, '') .replace(/\n/g, ''); const barePublicKey = publicKey .replace(/-----BEGIN PUBLIC KEY-----/, '') .replace(/-----END PUBLIC KEY-----/, '') .replace(/\n/g, ''); describe('Alipay RSA2 Sign', () => { const testParams: Record = { app_id: '2021000000000000', method: 'alipay.trade.page.pay', charset: 'utf-8', timestamp: '2026-03-05 12:00:00', version: '1.0', biz_content: '{"out_trade_no":"order-001","total_amount":"100.00"}', }; describe('generateSign', () => { it('should generate a valid RSA2 signature', () => { const sign = generateSign(testParams, privateKey); expect(sign).toBeTruthy(); expect(typeof sign).toBe('string'); // base64 格式 expect(() => Buffer.from(sign, 'base64')).not.toThrow(); }); it('should produce consistent signatures for same input', () => { const sign1 = generateSign(testParams, privateKey); const sign2 = generateSign(testParams, privateKey); expect(sign1).toBe(sign2); }); it('should filter out sign and sign_type fields', () => { const paramsWithSign = { ...testParams, sign: 'old_sign', sign_type: 'RSA2' }; const sign1 = generateSign(testParams, privateKey); const sign2 = generateSign(paramsWithSign, privateKey); expect(sign1).toBe(sign2); }); it('should filter out empty values', () => { const paramsWithEmpty = { ...testParams, empty_field: '' }; const sign1 = generateSign(testParams, privateKey); const sign2 = generateSign(paramsWithEmpty, privateKey); expect(sign1).toBe(sign2); }); it('should sort parameters alphabetically', () => { const reversed: Record = {}; const keys = Object.keys(testParams).reverse(); for (const key of keys) { reversed[key] = testParams[key]; } const sign1 = generateSign(testParams, privateKey); const sign2 = generateSign(reversed, privateKey); expect(sign1).toBe(sign2); }); }); describe('verifySign', () => { it('should verify a valid signature', () => { const sign = generateSign(testParams, privateKey); const valid = verifySign(testParams, publicKey, sign); expect(valid).toBe(true); }); it('should reject an invalid signature', () => { const valid = verifySign(testParams, publicKey, 'invalid_base64_signature'); expect(valid).toBe(false); }); it('should reject tampered params', () => { const sign = generateSign(testParams, privateKey); const tampered = { ...testParams, total_amount: '999.99' }; const valid = verifySign(tampered, publicKey, sign); expect(valid).toBe(false); }); }); describe('PEM auto-formatting', () => { it('should work with bare base64 private key (no PEM headers)', () => { const sign = generateSign(testParams, barePrivateKey); const valid = verifySign(testParams, publicKey, sign); expect(valid).toBe(true); }); it('should work with bare base64 public key (no PEM headers)', () => { const sign = generateSign(testParams, privateKey); const valid = verifySign(testParams, barePublicKey, sign); expect(valid).toBe(true); }); it('should work with both bare keys', () => { const sign = generateSign(testParams, barePrivateKey); const valid = verifySign(testParams, barePublicKey, sign); expect(valid).toBe(true); }); }); });