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,
|
||
};
|
||
}
|
||
|