342 lines
8.8 KiB
JavaScript
342 lines
8.8 KiB
JavaScript
|
|
/**
|
|||
|
|
* 使用者資訊相關 API 場景模組
|
|||
|
|
*
|
|||
|
|
* 此模組提供可重複使用的使用者資訊相關場景,包括:
|
|||
|
|
* - 取得使用者資訊
|
|||
|
|
* - 更新使用者資訊
|
|||
|
|
* - 修改密碼
|
|||
|
|
* - 驗證碼流程(email/phone)
|
|||
|
|
*
|
|||
|
|
* 使用方式:
|
|||
|
|
* import { getUserInfo, updateUserInfo } from './scenarios/apis/user.js';
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import http from 'k6/http';
|
|||
|
|
import { check } from 'k6';
|
|||
|
|
import { Rate, Trend } from 'k6/metrics';
|
|||
|
|
|
|||
|
|
// 可選的自定義指標
|
|||
|
|
const getUserInfoSuccessRate = new Rate('user_get_info_success');
|
|||
|
|
const updateUserInfoSuccessRate = new Rate('user_update_info_success');
|
|||
|
|
const getUserInfoDuration = new Trend('user_get_info_duration');
|
|||
|
|
const updateUserInfoDuration = new Trend('user_update_info_duration');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 取得當前登入的使用者資訊
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - Access Token
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 使用者資訊結果
|
|||
|
|
*/
|
|||
|
|
export function getUserInfo(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken) {
|
|||
|
|
throw new Error('accessToken is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/user/me`;
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${accessToken}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'user_get_info',
|
|||
|
|
api: 'user',
|
|||
|
|
method: 'get_user_info',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.get(url, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'get user info status is 200': (r) => r.status === 200,
|
|||
|
|
'get user info has uid': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
return body.uid && body.uid.length > 0;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
'get user info has user_status': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
return body.user_status !== undefined;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}, { name: 'get_user_info_checks' });
|
|||
|
|
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.getUserInfoSuccessRate?.add(success);
|
|||
|
|
customMetrics.getUserInfoDuration?.add(duration);
|
|||
|
|
} else {
|
|||
|
|
getUserInfoSuccessRate.add(success);
|
|||
|
|
getUserInfoDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = null;
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse get user info response:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新當前登入的使用者資訊
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - Access Token
|
|||
|
|
* @param {Object} options.updateData - 要更新的資料(可選欄位)
|
|||
|
|
* @param {Object} options.customMetrics - 自定義指標對象(可選)
|
|||
|
|
* @returns {Object} 更新結果
|
|||
|
|
*/
|
|||
|
|
export function updateUserInfo(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
updateData = {},
|
|||
|
|
customMetrics = null,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken) {
|
|||
|
|
throw new Error('accessToken is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/user/me`;
|
|||
|
|
const payload = JSON.stringify(updateData);
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${accessToken}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'user_update_info',
|
|||
|
|
api: 'user',
|
|||
|
|
method: 'update_user_info',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startTime = Date.now();
|
|||
|
|
const res = http.put(url, payload, params);
|
|||
|
|
const duration = Date.now() - startTime;
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'update user info status is 200': (r) => r.status === 200,
|
|||
|
|
'update user info returns updated data': (r) => {
|
|||
|
|
try {
|
|||
|
|
const body = JSON.parse(r.body);
|
|||
|
|
return body.uid && body.uid.length > 0;
|
|||
|
|
} catch {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}, { name: 'update_user_info_checks' });
|
|||
|
|
|
|||
|
|
if (customMetrics) {
|
|||
|
|
customMetrics.updateUserInfoSuccessRate?.add(success);
|
|||
|
|
customMetrics.updateUserInfoDuration?.add(duration);
|
|||
|
|
} else {
|
|||
|
|
updateUserInfoSuccessRate.add(success);
|
|||
|
|
updateUserInfoDuration.add(duration);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = null;
|
|||
|
|
if (res.status === 200) {
|
|||
|
|
try {
|
|||
|
|
result = JSON.parse(res.body);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('Failed to parse update user info response:', e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
response: result,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 修改當前登入使用者的密碼
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - Access Token
|
|||
|
|
* @param {string} options.currentPassword - 當前密碼
|
|||
|
|
* @param {string} options.newPassword - 新密碼
|
|||
|
|
* @returns {Object} 修改結果
|
|||
|
|
*/
|
|||
|
|
export function updatePassword(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
currentPassword,
|
|||
|
|
newPassword,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken || !currentPassword || !newPassword) {
|
|||
|
|
throw new Error('accessToken, currentPassword, and newPassword are required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/user/me/password`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
current_password: currentPassword,
|
|||
|
|
new_password: newPassword,
|
|||
|
|
new_password_confirm: newPassword,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${accessToken}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'user_update_password',
|
|||
|
|
api: 'user',
|
|||
|
|
method: 'update_password',
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.put(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'update password status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'update_password_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 請求發送驗證碼(用於驗證 email/phone)
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - Access Token
|
|||
|
|
* @param {string} options.purpose - 驗證目的(email_verification/phone_verification)
|
|||
|
|
* @returns {Object} 請求結果
|
|||
|
|
*/
|
|||
|
|
export function requestVerificationCode(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
purpose = 'email_verification',
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken) {
|
|||
|
|
throw new Error('accessToken is required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!['email_verification', 'phone_verification'].includes(purpose)) {
|
|||
|
|
throw new Error('purpose must be email_verification or phone_verification');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/user/me/verifications`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
purpose: purpose,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${accessToken}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'user_request_verification_code',
|
|||
|
|
api: 'user',
|
|||
|
|
method: 'request_verification_code',
|
|||
|
|
purpose: purpose,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.post(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'request verification code status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'request_verification_code_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 提交驗證碼以完成驗證
|
|||
|
|
* @param {Object} options - 配置選項
|
|||
|
|
* @param {string} options.baseUrl - API 基礎 URL
|
|||
|
|
* @param {string} options.accessToken - Access Token
|
|||
|
|
* @param {string} options.purpose - 驗證目的(email_verification/phone_verification)
|
|||
|
|
* @param {string} options.verifyCode - 驗證碼
|
|||
|
|
* @returns {Object} 提交結果
|
|||
|
|
*/
|
|||
|
|
export function submitVerificationCode(options = {}) {
|
|||
|
|
const {
|
|||
|
|
baseUrl = __ENV.BASE_URL || 'https://localhost:8888',
|
|||
|
|
accessToken,
|
|||
|
|
purpose = 'email_verification',
|
|||
|
|
verifyCode,
|
|||
|
|
} = options;
|
|||
|
|
|
|||
|
|
if (!accessToken || !verifyCode) {
|
|||
|
|
throw new Error('accessToken and verifyCode are required');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!['email_verification', 'phone_verification'].includes(purpose)) {
|
|||
|
|
throw new Error('purpose must be email_verification or phone_verification');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const url = `${baseUrl}/api/v1/user/me/verifications`;
|
|||
|
|
const payload = JSON.stringify({
|
|||
|
|
purpose: purpose,
|
|||
|
|
verify_code: verifyCode,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const params = {
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${accessToken}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
tags: {
|
|||
|
|
name: 'user_submit_verification_code',
|
|||
|
|
api: 'user',
|
|||
|
|
method: 'submit_verification_code',
|
|||
|
|
purpose: purpose,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = http.put(url, payload, params);
|
|||
|
|
|
|||
|
|
const success = check(res, {
|
|||
|
|
'submit verification code status is 200': (r) => r.status === 200,
|
|||
|
|
}, { name: 'submit_verification_code_checks' });
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
success,
|
|||
|
|
status: res.status,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|