477 lines
18 KiB
JavaScript
477 lines
18 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
|
|
const GenealogyApi = require('../utils/ApiClient.js');
|
|
|
|
function createClient() {
|
|
return GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: {
|
|
getItem() { return null; },
|
|
setItem() {},
|
|
removeItem() {}
|
|
},
|
|
axiosInstance: {
|
|
request() {
|
|
return Promise.resolve({ data: { code: 200, data: {} } });
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
test('API client exposes latest document-defined PC operations', () => {
|
|
const client = createClient();
|
|
const allowed = [
|
|
'clientId', 'tenantId', 'tokenKey', 'getToken', 'setToken', 'clearToken', 'buildApiUrl',
|
|
'register', 'login', 'loginBySms', 'sendSmsCode',
|
|
'currentProfile', 'updateProfile', 'changePassword', 'resetPassword', 'changePhone',
|
|
'deactivateAccount', 'logout', 'uploadFile', 'initResumableUpload', 'uploadResumableChunk',
|
|
'completeResumableUpload', 'createFileReference', 'deleteFileReference',
|
|
'captchaRequirement', 'captchaChallenge', 'captchaVerify',
|
|
'regionChildren', 'regionPath', 'regionSearch', 'regionDetail',
|
|
'feeds', 'feedsPage', 'feedDetail', 'createFeed', 'updateFeed', 'deleteFeed',
|
|
'likeFeed', 'unlikeFeed', 'feedComments', 'feedCommentsPage', 'createFeedComment',
|
|
'feedCommentReplies', 'feedCommentRepliesPage', 'deleteFeedComment',
|
|
'generationPoems', 'generationPoemsManagement', 'createGenerationPoem', 'updateGenerationPoem',
|
|
'previewGenerationPoems', 'saveGenerationPoems',
|
|
'lineagePersons', 'lineagePersonsPage', 'lineagePersonOptions', 'lineageTree',
|
|
'createLineagePerson', 'lineagePersonDetail', 'updateLineagePerson', 'disableLineagePerson',
|
|
'createLineageChild', 'createLineageParent', 'createLineageSibling', 'createLineageSpouse'
|
|
].sort();
|
|
|
|
assert.deepEqual(Object.keys(client).sort(), allowed);
|
|
});
|
|
|
|
test('password login stores only the returned token', async () => {
|
|
let stored = '';
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: {
|
|
getItem() { return stored; },
|
|
setItem(_, value) { stored = value; },
|
|
removeItem() { stored = ''; }
|
|
},
|
|
axiosInstance: {
|
|
request(config) {
|
|
assert.equal(config.method, 'post');
|
|
assert.equal(config.url, '/genealogy/pc/auth/login');
|
|
assert.equal(config.headers.Authorization, undefined);
|
|
return Promise.resolve({ data: { code: 200, data: { accessToken: 'access-token' } } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.login({ phone: '13800000000', password: 'hash' });
|
|
|
|
assert.equal(client.getToken(), 'access-token');
|
|
});
|
|
|
|
test('sending an SMS code uses the latest documented request fields', async () => {
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
assert.equal(config.url, '/genealogy/pc/auth/sms/code');
|
|
assert.deepEqual(config.data, {
|
|
grantType: 'sms',
|
|
tenantId: '000000',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
|
phone: '13800000000',
|
|
sceneCode: 'PC_SMS_LOGIN',
|
|
validToken: 'captcha-ticket'
|
|
});
|
|
return Promise.resolve({ data: { code: 200, data: null } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.sendSmsCode({
|
|
phone: '13800000000',
|
|
sceneCode: 'PC_SMS_LOGIN',
|
|
validToken: 'captcha-ticket'
|
|
});
|
|
});
|
|
|
|
test('password reset adds the documented grant, tenant and client fields', async () => {
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
assert.equal(config.method, 'put');
|
|
assert.equal(config.url, '/genealogy/pc/auth/password/reset');
|
|
assert.equal(config.headers.Authorization, undefined);
|
|
assert.deepEqual(config.data, {
|
|
grantType: 'password',
|
|
tenantId: '000000',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
|
phone: '13800000000',
|
|
smsCode: '123456',
|
|
newPassword: 'password-md5'
|
|
});
|
|
return Promise.resolve({ data: { code: 200, data: null } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.resetPassword({
|
|
phone: '13800000000',
|
|
smsCode: '123456',
|
|
newPassword: 'password-md5'
|
|
});
|
|
});
|
|
|
|
test('registration and SMS login use the PC auth endpoints with SMS fields', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
if (config.url.endsWith('/login/sms')) {
|
|
return Promise.resolve({ data: { code: 200, data: { access_token: 'sms-token' } } });
|
|
}
|
|
return Promise.resolve({ data: { code: 200, data: null } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.register({
|
|
phone: '13800000000',
|
|
smsCode: '123456',
|
|
nickName: '小李',
|
|
password: 'password-md5'
|
|
});
|
|
await client.loginBySms({ phone: '13800000000', smsCode: '123456' });
|
|
|
|
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
|
['post', '/genealogy/pc/auth/register', {
|
|
grantType: 'password',
|
|
registerSource: 'PC',
|
|
tenantId: '000000',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
|
phone: '13800000000',
|
|
smsCode: '123456',
|
|
nickName: '小李',
|
|
password: 'password-md5'
|
|
}],
|
|
['post', '/genealogy/pc/auth/login/sms', {
|
|
grantType: 'sms',
|
|
tenantId: '000000',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
|
phone: '13800000000',
|
|
smsCode: '123456'
|
|
}]
|
|
]);
|
|
});
|
|
|
|
test('phone change and account deactivation retain their PC request shapes', async () => {
|
|
let stored = 'access-token';
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: {
|
|
getItem() { return stored; },
|
|
setItem(_, value) { stored = value; },
|
|
removeItem() { stored = ''; }
|
|
},
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: null } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.changePhone({ phone: '13900000000', smsCode: '123456' });
|
|
await client.deactivateAccount({ smsCode: '654321' });
|
|
|
|
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
|
['put', '/genealogy/pc/auth/phone', {
|
|
phone: '13900000000',
|
|
smsCode: '123456',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f'
|
|
}],
|
|
['post', '/genealogy/pc/auth/account/deactivate', {
|
|
smsCode: '654321',
|
|
clientId: 'ced7e5f0498645c6ec642dcf450b036f'
|
|
}]
|
|
]);
|
|
assert.equal(stored, '');
|
|
});
|
|
|
|
test('region methods use PC paths and retain the logged-in authorization header', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: [] } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.regionChildren('0');
|
|
await client.regionPath('11');
|
|
await client.regionSearch({ keyword: '北京', limit: 20 });
|
|
await client.regionDetail('110101');
|
|
|
|
assert.deepEqual(calls.map((config) => [
|
|
config.method,
|
|
config.url,
|
|
config.params,
|
|
config.headers.Authorization
|
|
]), [
|
|
['get', '/genealogy/region/children', { parentCode: '0' }, 'Bearer access-token'],
|
|
['get', '/genealogy/region/path/11', undefined, 'Bearer access-token'],
|
|
['get', '/genealogy/region/search', { keyword: '北京', limit: 20 }, 'Bearer access-token'],
|
|
['get', '/genealogy/region/110101', undefined, 'Bearer access-token']
|
|
]);
|
|
});
|
|
|
|
test('remaining PC file methods use the documented paths, request forms and login header', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: {} } });
|
|
}
|
|
}
|
|
});
|
|
const chunk = new Blob(['part'], { type: 'application/octet-stream' });
|
|
|
|
await client.initResumableUpload({
|
|
fileName: 'video.mp4',
|
|
fileSize: 8388608,
|
|
fileMd5: 'file-md5',
|
|
chunkSize: 4194304,
|
|
totalChunks: 2,
|
|
contentType: 'video/mp4',
|
|
bizType: 'video',
|
|
usageScene: 'family_video'
|
|
});
|
|
await client.uploadResumableChunk({
|
|
uploadId: 'UPLOAD202607240001',
|
|
chunkIndex: 0,
|
|
chunkMd5: 'chunk-md5',
|
|
file: chunk
|
|
});
|
|
await client.completeResumableUpload({
|
|
uploadId: 'UPLOAD202607240001',
|
|
fileMd5: 'file-md5',
|
|
fileSize: 8388608
|
|
});
|
|
await client.createFileReference({
|
|
bizType: 'family_feed',
|
|
bizTable: 'gen_family_feed',
|
|
bizId: '900013001',
|
|
bizField: 'media_oss_ids',
|
|
ossId: '2060000000000000000'
|
|
});
|
|
await client.deleteFileReference({
|
|
bizTable: 'gen_family_feed',
|
|
bizId: '900013001',
|
|
bizField: 'media_oss_ids'
|
|
});
|
|
|
|
assert.deepEqual(calls.map((config) => [
|
|
config.method,
|
|
config.url,
|
|
config.params,
|
|
config.headers.Authorization
|
|
]), [
|
|
['post', '/genealogy/pc/files/resumable/init', undefined, 'Bearer access-token'],
|
|
['post', '/genealogy/pc/files/resumable/chunk', undefined, 'Bearer access-token'],
|
|
['post', '/genealogy/pc/files/resumable/complete', undefined, 'Bearer access-token'],
|
|
['post', '/genealogy/pc/files/reference', undefined, 'Bearer access-token'],
|
|
['delete', '/genealogy/pc/files/reference', {
|
|
bizTable: 'gen_family_feed',
|
|
bizId: '900013001',
|
|
bizField: 'media_oss_ids'
|
|
}, 'Bearer access-token']
|
|
]);
|
|
assert.deepEqual(calls[0].data, {
|
|
fileName: 'video.mp4',
|
|
fileSize: 8388608,
|
|
fileMd5: 'file-md5',
|
|
chunkSize: 4194304,
|
|
totalChunks: 2,
|
|
contentType: 'video/mp4',
|
|
bizType: 'video',
|
|
usageScene: 'family_video'
|
|
});
|
|
assert.equal(calls[1].data.get('uploadId'), 'UPLOAD202607240001');
|
|
assert.equal(calls[1].data.get('chunkIndex'), '0');
|
|
assert.equal(calls[1].data.get('chunkMd5'), 'chunk-md5');
|
|
assert.equal(calls[1].data.get('file').name, 'blob');
|
|
assert.deepEqual(calls[2].data, {
|
|
uploadId: 'UPLOAD202607240001',
|
|
fileMd5: 'file-md5',
|
|
fileSize: 8388608
|
|
});
|
|
assert.deepEqual(calls[3].data, {
|
|
bizType: 'family_feed',
|
|
bizTable: 'gen_family_feed',
|
|
bizId: '900013001',
|
|
bizField: 'media_oss_ids',
|
|
ossId: '2060000000000000000'
|
|
});
|
|
});
|
|
|
|
test('family feed methods use the documented paths and request bodies', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: {} } });
|
|
}
|
|
}
|
|
});
|
|
|
|
await client.createFeed(900001001, {
|
|
feedType: 'text',
|
|
feedContent: '今天上传一张老照片。',
|
|
mediaOssIds: '101,102',
|
|
status: '0'
|
|
});
|
|
await client.feedCommentsPage(900001001, 7001, { pageNum: 1, pageSize: 10 });
|
|
await client.feedCommentReplies(900001001, 7001, 8001);
|
|
await client.feedCommentRepliesPage(900001001, 7001, 8001, { pageNum: 2, pageSize: 10 });
|
|
await client.deleteFeedComment(900001001, 7001, 8001);
|
|
|
|
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params, config.data]), [
|
|
['post', '/genealogy/pc/genealogies/900001001/feeds', undefined, {
|
|
feedType: 'text',
|
|
feedContent: '今天上传一张老照片。',
|
|
mediaOssIds: '101,102',
|
|
status: '0'
|
|
}],
|
|
['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/page', { pageNum: 1, pageSize: 10 }, undefined],
|
|
['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/8001/replies', undefined, undefined],
|
|
['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/8001/replies/page', { pageNum: 2, pageSize: 10 }, undefined],
|
|
['delete', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/8001', undefined, undefined]
|
|
]);
|
|
});
|
|
|
|
test('generation poem methods use the Apifox PC paths and request bodies', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: {} } });
|
|
}
|
|
}
|
|
});
|
|
const poem = {
|
|
generationNo: 3,
|
|
generationText: '万',
|
|
description: '第三世',
|
|
sortOrder: 30,
|
|
status: '0'
|
|
};
|
|
const batch = { poemText: '德 承 家 亦', disableMissing: true };
|
|
|
|
await client.generationPoems(900001001);
|
|
await client.generationPoemsManagement(900001001);
|
|
await client.createGenerationPoem(900001001, poem);
|
|
await client.updateGenerationPoem(900001001, 2060000000000000000n, poem);
|
|
await client.previewGenerationPoems(900001001, batch);
|
|
await client.saveGenerationPoems(900001001, batch);
|
|
|
|
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
|
['get', '/genealogy/pc/genealogies/900001001/generation-poems', undefined],
|
|
['get', '/genealogy/pc/genealogies/900001001/generation-poems/management', undefined],
|
|
['post', '/genealogy/pc/genealogies/900001001/generation-poems', poem],
|
|
['put', '/genealogy/pc/genealogies/900001001/generation-poems/2060000000000000000', poem],
|
|
['post', '/genealogy/pc/genealogies/900001001/generation-poems/batch/preview', batch],
|
|
['post', '/genealogy/pc/genealogies/900001001/generation-poems/batch/save', batch]
|
|
]);
|
|
});
|
|
|
|
test('lineage person methods use the Apifox PC paths, query and LineagePersonBody', async () => {
|
|
const calls = [];
|
|
const client = GenealogyApi.createClient({
|
|
baseUrl: 'https://api.example.test',
|
|
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
|
axiosInstance: {
|
|
request(config) {
|
|
calls.push(config);
|
|
return Promise.resolve({ data: { code: 200, data: {} } });
|
|
}
|
|
}
|
|
});
|
|
const person = {
|
|
name: '李明',
|
|
sex: '1',
|
|
generation: 3,
|
|
generationName: '德',
|
|
personNo: 'L-003',
|
|
aliasName: '明远',
|
|
birthDate: '1990-01-01',
|
|
deathDate: null,
|
|
biography: '人物简介'
|
|
};
|
|
const genealogyId = '2060000000000000001';
|
|
const personId = '2060000000000000002';
|
|
|
|
await client.lineagePersons(genealogyId);
|
|
await client.lineagePersonsPage(genealogyId, {
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
keyword: '李',
|
|
generation: 3,
|
|
personStatus: '0'
|
|
});
|
|
await client.lineagePersonOptions(genealogyId);
|
|
await client.lineageTree(genealogyId);
|
|
await client.createLineagePerson(genealogyId, person);
|
|
await client.lineagePersonDetail(genealogyId, personId);
|
|
await client.updateLineagePerson(genealogyId, personId, person);
|
|
await client.disableLineagePerson(genealogyId, personId);
|
|
await client.createLineageChild(genealogyId, personId, person);
|
|
await client.createLineageParent(genealogyId, personId, person);
|
|
await client.createLineageSibling(genealogyId, personId, person);
|
|
await client.createLineageSpouse(genealogyId, personId, person);
|
|
|
|
assert.deepEqual(calls.map((config) => [
|
|
config.method,
|
|
config.url,
|
|
config.params,
|
|
config.data,
|
|
config.headers.Authorization,
|
|
config.headers.clientid
|
|
]), [
|
|
['get', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['get', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/page', {
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
keyword: '李',
|
|
generation: 3,
|
|
personStatus: '0'
|
|
}, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['get', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/options', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['get', '/genealogy/pc/genealogies/2060000000000000001/lineage/tree', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['get', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['put', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['delete', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002/children', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002/parents', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002/siblings', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
|
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002/spouses', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f']
|
|
]);
|
|
});
|