'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { createPddOpenPlatformClient, normalizeCategoryRule, resolvePddOpenPlatformCredential, signPddParameters, } = require('../src/services/pddOpenPlatformClient'); test('PDD signing uses UTF-8 and JSON encodes object values before sorting', () => { const params = { type: 'pdd.test', client_id: 'cid', timestamp: 1723723200, data_type: 'JSON', payload: { title: '中文', rows: [1, 2] }, }; assert.equal( signPddParameters(params, 'secret'), '3E0C5F16D0D478A19D44950A628411DB' ); }); test('PDD credential resolver matches one shop and rejects ambiguous matches', async () => { const rows = [ { id: '1', name: '卓禾旗舰店', mallID: '10001', accessToken: 'token-a', client_id: 'client-a', secrity: 'secret-a', }, { id: '2', name: '其他旗舰店', mallID: '10002', accessToken: 'token-b', client_id: 'client-b', secrity: 'secret-b', }, ]; const credential = await resolvePddOpenPlatformCredential({ shopId: '10001', shopName: '拼多多-卓禾旗舰店', }, { rows }); assert.equal(credential.mallId, '10001'); assert.equal(credential.accessToken, 'token-a'); await assert.rejects( resolvePddOpenPlatformCredential({ shopName: '卓禾旗舰店' }, { rows: [rows[0], { ...rows[0], id: '3', mallID: '10003' }], }), (error) => error.code === 'PDD_CREDENTIAL_AMBIGUOUS' ); }); test('PDD client never logs credentials and maps whitelist failures', async () => { let sentBody; const client = createPddOpenPlatformClient({ name: 'test', clientId: 'client-id', clientSecret: 'client-secret', accessToken: 'access-token', }, { httpClient: { post: async (_url, body) => { sentBody = body; return { data: { error_response: { error_code: 20005, error_msg: 'IP白名单校验失败', request_id: 'request-1', }, }, }; }, }, }); await assert.rejects( client.getMallInfo(), (error) => error.code === 'PDD_IP_NOT_WHITELISTED' && error.pddErrorCode === 20005 ); assert.equal(sentBody.client_secret, undefined); assert.equal(sentBody.access_token, 'access-token'); assert.match(sentBody.sign, /^[A-F0-9]{32}$/); }); test('only the final draft write treats transport failures as an unknown result', async () => { const timeoutError = Object.assign(new Error('timeout'), { code: 'ETIMEDOUT' }); const client = createPddOpenPlatformClient({ name: '测试店铺', clientId: 'client-id', clientSecret: 'client-secret', accessToken: 'access-token', }, { httpClient: { post: async () => { throw timeoutError; } }, timeout: 10, }); await assert.rejects( client.getMallInfo(), (error) => error.code === 'PDD_TRANSPORT_ERROR' && error.retryable === true && error.resultUnknown === false ); await assert.rejects( client.saveGoodsDraft({ goods_name: '测试商品' }), (error) => error.code === 'PDD_DRAFT_RESULT_UNKNOWN' && error.retryable === false && error.resultUnknown === true ); }); test('category rules normalize required properties and enum values', () => { const result = normalizeCategoryRule({ cat_rule_get_response: { goods_properties_rule: { properties: [{ ref_pid: 1628, name: '环保等级', required: false, is_important: true, choose_max_num: 1, values: [{ vid: 368832, value: 'E0级' }], }], }, }, }); assert.deepEqual(result.properties[0], { refPid: 1628, name: '环保等级', required: false, important: true, chooseMaxNum: 1, valueType: 0, minValue: null, maxValue: null, valueUnit: '', showCondition: null, values: [{ vid: 368832, value: 'E0级' }], }); });