补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
142 行
4.4 KiB
TypeScript
142 行
4.4 KiB
TypeScript
import axios from 'axios';
|
||
import { CoolCommException } from '@cool-midway/core';
|
||
import { CrmSalesforceService } from '../../../src/modules/crm/service/salesforce';
|
||
|
||
describe('CrmSalesforceService.createLead', () => {
|
||
let service: CrmSalesforceService;
|
||
|
||
const validPayload = {
|
||
FirstName: 'Jane',
|
||
LastName: 'Doe',
|
||
Email: 'jane@acme.com',
|
||
Company: 'Acme',
|
||
Phone: '+15551234567',
|
||
Status: 'Open - Not Contacted',
|
||
};
|
||
|
||
const validInstanceUrl = 'https://myorg.my.salesforce.com';
|
||
|
||
beforeEach(() => {
|
||
service = new CrmSalesforceService();
|
||
jest.clearAllMocks();
|
||
});
|
||
|
||
afterEach(() => {
|
||
jest.restoreAllMocks();
|
||
});
|
||
|
||
it('accessToken 为空时应抛出 CoolCommException,说明未配置 Salesforce', async () => {
|
||
const postSpy = jest.spyOn(axios, 'post');
|
||
|
||
await expect(
|
||
service.createLead('', validInstanceUrl, validPayload)
|
||
).rejects.toThrow(CoolCommException);
|
||
|
||
await expect(
|
||
service.createLead(' ', validInstanceUrl, validPayload)
|
||
).rejects.toThrow(/Salesforce/);
|
||
|
||
expect(postSpy).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('instanceUrl 为空时应抛出 CoolCommException,说明未配置 Salesforce', async () => {
|
||
const postSpy = jest.spyOn(axios, 'post');
|
||
|
||
await expect(
|
||
service.createLead('sf-token', '', validPayload)
|
||
).rejects.toThrow(CoolCommException);
|
||
|
||
await expect(
|
||
service.createLead('sf-token', ' ', validPayload)
|
||
).rejects.toThrow(/Salesforce/);
|
||
|
||
expect(postSpy).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('非法 instanceUrl(非 https 或主机名不符)应抛错且不发请求', async () => {
|
||
const postSpy = jest.spyOn(axios, 'post');
|
||
|
||
await expect(
|
||
service.createLead('sf-token', 'http://myorg.my.salesforce.com', validPayload)
|
||
).rejects.toThrow(CoolCommException);
|
||
|
||
await expect(
|
||
service.createLead('sf-token', 'https://evil.example.com', validPayload)
|
||
).rejects.toThrow(CoolCommException);
|
||
|
||
await expect(
|
||
service.createLead('sf-token', 'https://127.0.0.1', validPayload)
|
||
).rejects.toThrow(CoolCommException);
|
||
|
||
expect(postSpy).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('有效时应向 Salesforce Lead API 发起 Bearer 请求并返回 id 与原始响应', async () => {
|
||
const token = 'sf-access-token-xyz';
|
||
const raw = { id: '00Qxx000001Lead', success: true, errors: [] };
|
||
|
||
const postSpy = jest.spyOn(axios, 'post').mockResolvedValueOnce({
|
||
status: 201,
|
||
data: raw,
|
||
});
|
||
|
||
const result = await service.createLead(token, validInstanceUrl, validPayload);
|
||
|
||
expect(postSpy).toHaveBeenCalledTimes(1);
|
||
const [url, body, config] = postSpy.mock.calls[0];
|
||
expect(url).toBe(
|
||
`${validInstanceUrl}/services/data/v59.0/sobjects/Lead`
|
||
);
|
||
expect(body).toEqual(validPayload);
|
||
expect(config?.headers?.Authorization).toBe(`Bearer ${token}`);
|
||
expect(config?.timeout).toBe(8000);
|
||
|
||
expect(result.id).toBe('00Qxx000001Lead');
|
||
expect(result.raw).toEqual(raw);
|
||
});
|
||
|
||
it('force.com 主机名应被允许', async () => {
|
||
const instanceUrl = 'https://na1.salesforce.com';
|
||
const forceUrl = 'https://custom.force.com';
|
||
const token = 'sf-token';
|
||
const raw = { id: '00Qxx000002', success: true };
|
||
|
||
const postSpy = jest
|
||
.spyOn(axios, 'post')
|
||
.mockResolvedValueOnce({ status: 201, data: raw })
|
||
.mockResolvedValueOnce({ status: 201, data: raw });
|
||
|
||
await service.createLead(token, instanceUrl, validPayload);
|
||
await service.createLead(token, forceUrl, validPayload);
|
||
|
||
expect(postSpy).toHaveBeenCalledTimes(2);
|
||
expect(postSpy.mock.calls[0][0]).toBe(
|
||
`${instanceUrl}/services/data/v59.0/sobjects/Lead`
|
||
);
|
||
expect(postSpy.mock.calls[1][0]).toBe(
|
||
`${forceUrl}/services/data/v59.0/sobjects/Lead`
|
||
);
|
||
});
|
||
|
||
it('对方返回 4xx 时应抛出包含状态码与错误信息的异常,不返回 success:true', async () => {
|
||
const axiosError = Object.assign(new Error('Request failed with status code 400'), {
|
||
isAxiosError: true,
|
||
response: {
|
||
status: 400,
|
||
data: [
|
||
{
|
||
message: 'Required fields are missing: [LastName]',
|
||
errorCode: 'REQUIRED_FIELD_MISSING',
|
||
},
|
||
],
|
||
},
|
||
});
|
||
|
||
jest.spyOn(axios, 'post').mockRejectedValueOnce(axiosError);
|
||
|
||
await expect(
|
||
service.createLead('sf-valid', validInstanceUrl, validPayload)
|
||
).rejects.toThrow(/400|Required fields are missing/);
|
||
});
|
||
});
|