feat(api): 完善家谱系统API客户端契约
- 实现家谱管理相关方法,包括创建、详情、概览、我的家谱和选项查询 - 添加家谱加入申请功能,支持申请、审核、取消和待审核列表操作 - 集成通知详情获取方法和通知ID安全验证机制 - 完善功德记录、谱文、相册、视频、祭祀活动的完整CRUD操作契约 - 实现家谱成员管理功能,包含成员列表、更新、移除和转让所有者操作 - 优化路径ID验证逻辑,拒绝不安全的数值ID并提供明确错误提示 - 更新测试用例以验证所有新增API方法的路径和请求体白名单机制
This commit is contained in:
@@ -29,8 +29,10 @@ test('API client exposes latest document-defined PC operations', () => {
|
||||
'completeResumableUpload',
|
||||
'captchaRequirement', 'captchaChallenge', 'captchaVerify', 'captchaChallengeUrl', 'captchaVerifyUrl',
|
||||
'regionChildren', 'regionPath', 'regionSearch', 'regionDetail',
|
||||
'genealogyQuota',
|
||||
'notifications', 'unreadNotificationCount', 'markNotificationRead', 'markAllNotificationsRead',
|
||||
'genealogiesMine', 'genealogyOptions', 'genealogyDetail', 'genealogyOverview', 'genealogyQuota',
|
||||
'createGenealogy', 'applyToGenealogy', 'myGenealogyJoinApplies',
|
||||
'pendingGenealogyJoinApplies', 'auditGenealogyJoinApply', 'cancelGenealogyJoinApply',
|
||||
'notifications', 'notificationDetail', 'unreadNotificationCount', 'markNotificationRead', 'markAllNotificationsRead',
|
||||
'feeds', 'feedsPage', 'feedDetail', 'createFeed', 'updateFeed', 'deleteFeed',
|
||||
'likeFeed', 'unlikeFeed', 'feedComments', 'feedCommentsPage', 'createFeedComment',
|
||||
'feedCommentReplies', 'feedCommentRepliesPage', 'deleteFeedComment',
|
||||
@@ -41,9 +43,16 @@ test('API client exposes latest document-defined PC operations', () => {
|
||||
'createLineageChild', 'createLineageParent', 'createLineageSibling', 'createLineageSpouse',
|
||||
'growthRecords', 'createGrowthRecord', 'growthRecordDetail', 'updateGrowthRecord', 'deleteGrowthRecord',
|
||||
'relativeRecords', 'createRelativeRecord', 'relativeRecordDetail', 'updateRelativeRecord', 'deleteRelativeRecord',
|
||||
'memos', 'createMemo', 'memoDetail', 'updateMemo', 'deleteMemo', 'deleteMeritRecord',
|
||||
'deleteArticle', 'deleteAlbum', 'deleteAlbumPhoto', 'deleteVideo', 'deleteCeremony', 'deleteCeremonyGift',
|
||||
'replaceCeremonyInvitees', 'ceremonyInvitations', 'respondCeremonyInvitation', 'myCeremonyInvitations'
|
||||
'memos', 'createMemo', 'memoDetail', 'updateMemo', 'deleteMemo',
|
||||
'meritRecords', 'createMeritRecord', 'meritRecordDetail', 'updateMeritRecord', 'deleteMeritRecord',
|
||||
'articles', 'articleDetail', 'createArticle', 'updateArticle', 'deleteArticle',
|
||||
'albums', 'createAlbum', 'updateAlbum', 'albumPhotos', 'createAlbumPhoto', 'deleteAlbum', 'deleteAlbumPhoto',
|
||||
'videos', 'createVideo', 'videoDetail', 'updateVideo', 'deleteVideo',
|
||||
'ceremonies', 'ceremonyDetail', 'createCeremony', 'updateCeremony',
|
||||
'ceremonyGifts', 'createCeremonyGift', 'deleteCeremony', 'deleteCeremonyGift',
|
||||
'replaceCeremonyInvitees', 'ceremonyInvitations', 'respondCeremonyInvitation', 'myCeremonyInvitations',
|
||||
'genealogyMembers', 'genealogyMemberOptions', 'updateGenealogyMember',
|
||||
'removeGenealogyMember', 'leaveGenealogy', 'transferGenealogyOwner'
|
||||
].sort();
|
||||
|
||||
assert.deepEqual(Object.keys(client).sort(), allowed);
|
||||
@@ -72,19 +81,19 @@ test('password login stores access_token from the real AppLoginVo response', asy
|
||||
deviceType: 'pc',
|
||||
userType: 'app_user',
|
||||
profile: {
|
||||
userId: '2062179707935264769',
|
||||
userId: '9007199254740993001',
|
||||
tenantId: '000000',
|
||||
userNo: 'U2062179707910225920',
|
||||
phone: '19181970173',
|
||||
nickName: '叶子',
|
||||
userNo: 'U9007199254740993001',
|
||||
phone: '13800000000',
|
||||
nickName: '测试用户',
|
||||
realName: '',
|
||||
avatar: null,
|
||||
sex: '2',
|
||||
birthday: null,
|
||||
email: '',
|
||||
registerSource: 'h5',
|
||||
loginIp: '112.45.165.24',
|
||||
loginDate: '2026-07-28 11:06:09',
|
||||
loginIp: '127.0.0.1',
|
||||
loginDate: '2026-01-01 00:00:00',
|
||||
status: '0',
|
||||
clientKey: 'web_pc',
|
||||
deviceType: 'pc'
|
||||
@@ -497,6 +506,113 @@ test('genealogy quota uses the current PC path without manufacturing a genealogy
|
||||
await client.genealogyQuota();
|
||||
});
|
||||
|
||||
test('genealogy context methods obtain IDs from the real PC list and detail paths', 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.genealogiesMine();
|
||||
await client.genealogyOptions({ keyword: '汤氏' });
|
||||
await client.genealogyDetail('9007199254740993002');
|
||||
await client.genealogyOverview('9007199254740993002');
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params]), [
|
||||
['get', '/genealogy/pc/genealogies/mine', undefined],
|
||||
['get', '/genealogy/pc/genealogies/options', { keyword: '汤氏' }],
|
||||
['get', '/genealogy/pc/genealogies/9007199254740993002', undefined],
|
||||
['get', '/genealogy/pc/genealogies/9007199254740993002/overview', undefined]
|
||||
]);
|
||||
});
|
||||
|
||||
test('genealogy lifecycle methods use PC paths and strict 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 genealogyId = '2062179707935264769';
|
||||
const applyId = '2062179707935264770';
|
||||
|
||||
await client.createGenealogy({
|
||||
genealogyName: '汤氏家谱',
|
||||
surname: '汤',
|
||||
regionCode: '511622',
|
||||
ancestralHall: '汤氏宗祠',
|
||||
originPlace: '四川',
|
||||
addressDetail: '沿口镇',
|
||||
coverOssId: '2062179707935264771',
|
||||
intro: '家谱简介',
|
||||
visibility: '1',
|
||||
joinMode: '1',
|
||||
legacyField: 'must-drop'
|
||||
});
|
||||
await client.applyToGenealogy(genealogyId, {
|
||||
applicantName: '申请人',
|
||||
phone: '19100000000',
|
||||
relationDesc: '族亲',
|
||||
applyReason: '申请加入',
|
||||
inviterUserId: 'must-drop'
|
||||
});
|
||||
await client.myGenealogyJoinApplies();
|
||||
await client.pendingGenealogyJoinApplies(genealogyId);
|
||||
await client.auditGenealogyJoinApply(genealogyId, applyId, {
|
||||
status: '1',
|
||||
auditRemark: '资料一致',
|
||||
legacyField: 'must-drop'
|
||||
});
|
||||
await client.cancelGenealogyJoinApply(applyId);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['post', '/genealogy/pc/genealogies', {
|
||||
genealogyName: '汤氏家谱',
|
||||
surname: '汤',
|
||||
regionCode: '511622',
|
||||
ancestralHall: '汤氏宗祠',
|
||||
originPlace: '四川',
|
||||
addressDetail: '沿口镇',
|
||||
coverOssId: '2062179707935264771',
|
||||
intro: '家谱简介',
|
||||
visibility: '1',
|
||||
joinMode: '1'
|
||||
}],
|
||||
['post', '/genealogy/pc/genealogies/2062179707935264769/join-applies', {
|
||||
applicantName: '申请人',
|
||||
phone: '19100000000',
|
||||
relationDesc: '族亲',
|
||||
applyReason: '申请加入'
|
||||
}],
|
||||
['get', '/genealogy/pc/genealogies/join-applies/mine', undefined],
|
||||
['get', '/genealogy/pc/genealogies/2062179707935264769/join-applies/pending', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2062179707935264769/join-applies/2062179707935264770/audit', {
|
||||
status: '1',
|
||||
auditRemark: '资料一致'
|
||||
}],
|
||||
['delete', '/genealogy/pc/genealogies/join-applies/2062179707935264770', undefined]
|
||||
]);
|
||||
assert.throws(
|
||||
() => client.pendingGenealogyJoinApplies(Number.MAX_SAFE_INTEGER + 1),
|
||||
/家谱编号/
|
||||
);
|
||||
assert.throws(
|
||||
() => client.cancelGenealogyJoinApply(Number.MAX_SAFE_INTEGER + 1),
|
||||
/申请编号/
|
||||
);
|
||||
});
|
||||
|
||||
test('notification methods use the current PC list, unread and read paths', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
@@ -510,13 +626,15 @@ test('notification methods use the current PC list, unread and read paths', asyn
|
||||
}
|
||||
});
|
||||
|
||||
await client.notifications({ readStatus: '0' });
|
||||
await client.notifications({ readStatus: '0', pageNum: 99 });
|
||||
await client.notificationDetail('2060000000000000001');
|
||||
await client.unreadNotificationCount();
|
||||
await client.markNotificationRead('2060000000000000001');
|
||||
await client.markAllNotificationsRead();
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params, config.data]), [
|
||||
['get', '/genealogy/pc/notifications', { readStatus: '0' }, undefined],
|
||||
['get', '/genealogy/pc/notifications/2060000000000000001', undefined, undefined],
|
||||
['get', '/genealogy/pc/notifications/unread-count', undefined, undefined],
|
||||
['post', '/genealogy/pc/notifications/2060000000000000001/read', undefined, undefined],
|
||||
['post', '/genealogy/pc/notifications/read-all', undefined, undefined]
|
||||
@@ -527,6 +645,24 @@ test('notification methods use the current PC list, unread and read paths', asyn
|
||||
});
|
||||
});
|
||||
|
||||
test('notification methods reject unsafe numeric path IDs before sending a request', async () => {
|
||||
let requestCount = 0;
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request() {
|
||||
requestCount += 1;
|
||||
return Promise.resolve({ data: { code: 200, data: null } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.throws(() => client.notificationDetail(Number.MAX_SAFE_INTEGER + 1));
|
||||
assert.throws(() => client.markNotificationRead(Number.MAX_SAFE_INTEGER + 1));
|
||||
assert.equal(requestCount, 0);
|
||||
});
|
||||
|
||||
test('family feed methods use the documented paths and request bodies', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
@@ -785,22 +921,54 @@ test('memo methods use the Apifox PC paths and documented request fields', async
|
||||
]);
|
||||
});
|
||||
|
||||
test('merit record deletion uses the only documented PC merit operation', async () => {
|
||||
test('merit record methods use the complete PC CRUD paths and MeritRecordBody', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
assert.equal(config.method, 'delete');
|
||||
assert.equal(config.url, '/genealogy/pc/genealogies/2060000000000000001/merit-records/2060000000000000002');
|
||||
assert.equal(config.headers.Authorization, 'Bearer access-token');
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
return Promise.resolve({ data: { code: 200, data: null } });
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: {} } });
|
||||
}
|
||||
}
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
const meritId = '2060000000000000002';
|
||||
const record = {
|
||||
donorName: '叶明',
|
||||
meritType: 'repair',
|
||||
meritTitle: '修缮宗祠',
|
||||
meritContent: '参与宗祠修缮',
|
||||
amount: 500.5,
|
||||
meritTime: '2026-07-26 10:00:00',
|
||||
sortOrder: 1,
|
||||
status: '0'
|
||||
};
|
||||
const recordWithExtras = Object.assign({}, record, {
|
||||
appUserId: 'must-not-send',
|
||||
mediaOssIds: 'must-not-send'
|
||||
});
|
||||
|
||||
await client.deleteMeritRecord('2060000000000000001', '2060000000000000002');
|
||||
await client.meritRecords(genealogyId);
|
||||
await client.createMeritRecord(genealogyId, recordWithExtras);
|
||||
await client.meritRecordDetail(genealogyId, meritId);
|
||||
await client.updateMeritRecord(genealogyId, meritId, recordWithExtras);
|
||||
await client.deleteMeritRecord(genealogyId, meritId);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
config.data,
|
||||
config.headers.Authorization,
|
||||
config.headers.clientid
|
||||
]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/merit-records', undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/merit-records', record, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/merit-records/2060000000000000002', undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/merit-records/2060000000000000002', record, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/merit-records/2060000000000000002', undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f']
|
||||
]);
|
||||
});
|
||||
|
||||
test('remaining document-defined deletion operations use PC paths only', async () => {
|
||||
@@ -829,6 +997,244 @@ test('remaining document-defined deletion operations use PC paths only', async (
|
||||
]);
|
||||
});
|
||||
|
||||
test('article methods use the complete PC CRUD paths and ArticleBody whitelist', 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 genealogyId = '2060000000000000001';
|
||||
const articleId = '2060000000000000002';
|
||||
const input = {
|
||||
categoryId: '2060000000000000003',
|
||||
articleTitle: '家族源流',
|
||||
articleSummary: '摘要',
|
||||
coverOssId: '2060000000000000004',
|
||||
articleContent: '<p>正文</p>',
|
||||
authorName: '宗亲',
|
||||
sortOrder: 1,
|
||||
status: '0',
|
||||
appUserId: 'must-drop'
|
||||
};
|
||||
const body = {
|
||||
categoryId: '2060000000000000003',
|
||||
articleTitle: '家族源流',
|
||||
articleSummary: '摘要',
|
||||
coverOssId: '2060000000000000004',
|
||||
articleContent: '<p>正文</p>',
|
||||
authorName: '宗亲',
|
||||
sortOrder: 1,
|
||||
status: '0'
|
||||
};
|
||||
|
||||
await client.articles(genealogyId);
|
||||
await client.createArticle(genealogyId, input);
|
||||
await client.articleDetail(genealogyId, articleId);
|
||||
await client.updateArticle(genealogyId, articleId, input);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/articles', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/articles', body],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/articles/2060000000000000002', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/articles/2060000000000000002', body]
|
||||
]);
|
||||
});
|
||||
|
||||
test('album methods use the complete PC album and photo contracts', 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 genealogyId = '2060000000000000001';
|
||||
const albumId = '2060000000000000002';
|
||||
const albumInput = {
|
||||
albumName: '祠堂旧影',
|
||||
albumDesc: '历史照片',
|
||||
coverOssId: '2060000000000000003',
|
||||
sortOrder: 2,
|
||||
status: '0',
|
||||
photoCount: 99
|
||||
};
|
||||
const albumBody = {
|
||||
albumName: '祠堂旧影',
|
||||
albumDesc: '历史照片',
|
||||
coverOssId: '2060000000000000003',
|
||||
sortOrder: 2,
|
||||
status: '0'
|
||||
};
|
||||
const photoInput = {
|
||||
ossId: '2060000000000000004',
|
||||
photoTitle: '合影',
|
||||
photoDesc: '祭祖合影',
|
||||
photographer: '宗亲',
|
||||
shootTime: '2026-07-29 10:30:00',
|
||||
sortOrder: 3,
|
||||
status: '0',
|
||||
albumId: 'must-drop'
|
||||
};
|
||||
const photoBody = {
|
||||
ossId: '2060000000000000004',
|
||||
photoTitle: '合影',
|
||||
photoDesc: '祭祖合影',
|
||||
photographer: '宗亲',
|
||||
shootTime: '2026-07-29 10:30:00',
|
||||
sortOrder: 3,
|
||||
status: '0'
|
||||
};
|
||||
|
||||
await client.albums(genealogyId);
|
||||
await client.createAlbum(genealogyId, albumInput);
|
||||
await client.updateAlbum(genealogyId, albumId, albumInput);
|
||||
await client.albumPhotos(genealogyId, albumId);
|
||||
await client.createAlbumPhoto(genealogyId, albumId, photoInput);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/albums', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/albums', albumBody],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/albums/2060000000000000002', albumBody],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/albums/2060000000000000002/photos', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/albums/2060000000000000002/photos', photoBody]
|
||||
]);
|
||||
});
|
||||
|
||||
test('ceremony admin methods use PC activity and gift contracts', 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 genealogyId = '2060000000000000001';
|
||||
const ceremonyId = '2060000000000000002';
|
||||
const ceremonyInput = {
|
||||
ceremonyType: 'ancestor',
|
||||
ceremonyTitle: '清明祭祖',
|
||||
ceremonyDesc: '宗亲祭祖',
|
||||
ceremonyTime: '2026-04-05 09:00:00',
|
||||
location: '祠堂',
|
||||
locationAddress: '宗祠路 1 号',
|
||||
longitude: 104.1,
|
||||
latitude: 30.6,
|
||||
coverOssId: '2060000000000000003',
|
||||
sortOrder: 1,
|
||||
status: '0',
|
||||
giftCount: 10
|
||||
};
|
||||
const ceremonyBody = {
|
||||
ceremonyType: 'ancestor',
|
||||
ceremonyTitle: '清明祭祖',
|
||||
ceremonyDesc: '宗亲祭祖',
|
||||
ceremonyTime: '2026-04-05 09:00:00',
|
||||
location: '祠堂',
|
||||
locationAddress: '宗祠路 1 号',
|
||||
longitude: 104.1,
|
||||
latitude: 30.6,
|
||||
coverOssId: '2060000000000000003',
|
||||
sortOrder: 1,
|
||||
status: '0'
|
||||
};
|
||||
const giftInput = {
|
||||
giverName: '宗亲',
|
||||
giftAmount: 88.5,
|
||||
giftMessage: '敬献',
|
||||
status: 'must-drop'
|
||||
};
|
||||
const giftBody = { giverName: '宗亲', giftAmount: 88.5, giftMessage: '敬献' };
|
||||
|
||||
await client.ceremonies(genealogyId);
|
||||
await client.createCeremony(genealogyId, ceremonyInput);
|
||||
await client.ceremonyDetail(genealogyId, ceremonyId);
|
||||
await client.updateCeremony(genealogyId, ceremonyId, ceremonyInput);
|
||||
await client.ceremonyGifts(genealogyId, ceremonyId);
|
||||
await client.createCeremonyGift(genealogyId, ceremonyId, giftInput);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/ceremonies', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/ceremonies', ceremonyBody],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002', ceremonyBody],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002/gifts', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002/gifts', giftBody]
|
||||
]);
|
||||
});
|
||||
|
||||
test('genealogy member methods use PC member paths and body whitelists', 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 genealogyId = '2060000000000000001';
|
||||
const memberId = '2060000000000000002';
|
||||
const memberInput = {
|
||||
memberName: '族员甲',
|
||||
relationName: '侄',
|
||||
roleType: 'editor',
|
||||
lineagePersonId: '2060000000000000003',
|
||||
appUserId: 'must-drop'
|
||||
};
|
||||
const memberBody = {
|
||||
memberName: '族员甲',
|
||||
relationName: '侄',
|
||||
roleType: 'editor',
|
||||
lineagePersonId: '2060000000000000003'
|
||||
};
|
||||
|
||||
await client.genealogyMembers(genealogyId);
|
||||
await client.genealogyMemberOptions(genealogyId);
|
||||
await client.updateGenealogyMember(genealogyId, memberId, memberInput);
|
||||
await client.removeGenealogyMember(genealogyId, memberId);
|
||||
await client.leaveGenealogy(genealogyId);
|
||||
await client.transferGenealogyOwner(genealogyId, { targetMemberId: memberId, roleType: 'must-drop' });
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/members', undefined, undefined],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/members/options', undefined, undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/members/2060000000000000002', undefined, memberBody],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/members/2060000000000000002', undefined, undefined],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/members/me', undefined, undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/members/owner-transfer', undefined, {
|
||||
targetMemberId: memberId
|
||||
}]
|
||||
]);
|
||||
});
|
||||
|
||||
test('video methods use the complete PC CRUD paths and VideoBody', 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 genealogyId = '2060000000000000001';
|
||||
const videoId = '2060000000000000002';
|
||||
const body = {
|
||||
videoTitle: '家族活动记录',
|
||||
videoDesc: '清明祭祖活动视频',
|
||||
coverOssId: '2060000000000000003',
|
||||
videoOssId: '2060000000000000004',
|
||||
durationSeconds: 180,
|
||||
sortOrder: 1,
|
||||
status: '0'
|
||||
};
|
||||
|
||||
await client.videos(genealogyId);
|
||||
await client.createVideo(genealogyId, body);
|
||||
await client.videoDetail(genealogyId, videoId);
|
||||
await client.updateVideo(genealogyId, videoId, body);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/videos', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/videos', body],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/videos/2060000000000000002', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/videos/2060000000000000002', body]
|
||||
]);
|
||||
});
|
||||
|
||||
test('ceremony invitation methods use the current PC invitation contracts', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
@@ -839,7 +1245,7 @@ test('ceremony invitation methods use the current PC invitation contracts', asyn
|
||||
const genealogyId = '2060000000000000001';
|
||||
const ceremonyId = '2060000000000000002';
|
||||
|
||||
await client.replaceCeremonyInvitees(genealogyId, ceremonyId, { inviteeUserIds: ['900000001', '900000002'] });
|
||||
await client.replaceCeremonyInvitees(genealogyId, ceremonyId, ['900000001', '900000002']);
|
||||
await client.ceremonyInvitations(genealogyId, ceremonyId);
|
||||
await client.respondCeremonyInvitation(genealogyId, ceremonyId, { inviteStatus: 'ACCEPTED' });
|
||||
await client.myCeremonyInvitations();
|
||||
|
||||
Reference in New Issue
Block a user