test.js•8.02 kB
#!/usr/bin/env node
/**
* 测试脚本 - 独立测试天气功能(不依赖 MCP 框架)
*/
/**
* 通过城市名称获取地理坐标
*/
async function geocodeCity(cityName) {
try {
const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(cityName)}&count=1&language=zh&format=json`;
const response = await fetch(url);
const data = await response.json();
if (!data.results || data.results.length === 0) {
throw new Error(`无法找到城市: ${cityName}`);
}
const result = data.results[0];
return {
name: result.name,
country: result.country,
latitude: result.latitude,
longitude: result.longitude,
timezone: result.timezone || 'auto'
};
} catch (error) {
throw new Error(`地理编码失败: ${error.message}`);
}
}
/**
* 获取7天天气预报
*/
async function getWeatherForecast(latitude, longitude, timezone = 'auto') {
try {
const params = new URLSearchParams({
latitude: latitude.toString(),
longitude: longitude.toString(),
timezone: timezone,
daily: [
'temperature_2m_max',
'temperature_2m_min',
'weathercode',
'precipitation_sum',
'rain_sum',
'snowfall_sum',
'windspeed_10m_max',
'windgusts_10m_max',
'winddirection_10m_dominant',
'sunrise',
'sunset'
].join(','),
current: [
'temperature_2m',
'relative_humidity_2m',
'apparent_temperature',
'weathercode',
'windspeed_10m',
'winddirection_10m'
].join(','),
forecast_days: '7'
});
const url = `https://api.open-meteo.com/v1/forecast?${params}`;
const response = await fetch(url);
const data = await response.json();
return formatWeatherData(data);
} catch (error) {
throw new Error(`获取天气数据失败: ${error.message}`);
}
}
function getWeatherDescription(code) {
const weatherCodes = {
0: '晴朗', 1: '基本晴朗', 2: '部分多云', 3: '阴天',
45: '有雾', 48: '雾凇',
51: '小毛毛雨', 53: '毛毛雨', 55: '大毛毛雨',
56: '冻毛毛雨', 57: '强冻毛毛雨',
61: '小雨', 63: '中雨', 65: '大雨',
66: '冻雨', 67: '强冻雨',
71: '小雪', 73: '中雪', 75: '大雪', 77: '雪粒',
80: '小阵雨', 81: '阵雨', 82: '大阵雨',
85: '小阵雪', 86: '大阵雪',
95: '雷暴', 96: '雷暴伴小冰雹', 99: '雷暴伴冰雹'
};
return weatherCodes[code] || '未知';
}
function getWindDirection(degree) {
const directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
const index = Math.round(degree / 45) % 8;
return directions[index];
}
function getComfortIndex(temp, humidity, apparentTemp) {
let comfort = '舒适';
if (apparentTemp < 0) comfort = '非常寒冷';
else if (apparentTemp < 10) comfort = '寒冷';
else if (apparentTemp < 16) comfort = '凉爽';
else if (apparentTemp < 24) comfort = '舒适';
else if (apparentTemp < 28) comfort = '温暖';
else if (apparentTemp < 32) comfort = '炎热';
else comfort = '酷热';
if (humidity > 80) comfort += ' (湿度高)';
else if (humidity < 30) comfort += ' (干燥)';
return comfort;
}
function formatWeatherData(data) {
const current = data.current;
const daily = data.daily;
const currentWeather = {
时间: current.time,
温度: `${current.temperature_2m}°C`,
体感温度: `${current.apparent_temperature}°C`,
湿度: `${current.relative_humidity_2m}%`,
天气状况: getWeatherDescription(current.weathercode),
风速: `${current.windspeed_10m} km/h`,
风向: getWindDirection(current.winddirection_10m),
舒适度: getComfortIndex(
current.temperature_2m,
current.relative_humidity_2m,
current.apparent_temperature
)
};
const forecast = daily.time.map((date, index) => {
const maxTemp = daily.temperature_2m_max[index];
const minTemp = daily.temperature_2m_min[index];
const avgTemp = (maxTemp + minTemp) / 2;
const weatherCode = daily.weathercode[index];
const precipitation = daily.precipitation_sum[index];
const rain = daily.rain_sum[index];
const snow = daily.snowfall_sum[index];
let weatherDetail = getWeatherDescription(weatherCode);
if (rain > 0) weatherDetail += ` (降雨 ${rain}mm)`;
if (snow > 0) weatherDetail += ` (降雪 ${snow}cm)`;
return {
日期: date,
最高温度: `${maxTemp}°C`,
最低温度: `${minTemp}°C`,
天气状况: weatherDetail,
降水量: `${precipitation}mm`,
降雨量: `${rain}mm`,
降雪量: `${snow}cm`,
最大风速: `${daily.windspeed_10m_max[index]} km/h`,
最大阵风: `${daily.windgusts_10m_max[index]} km/h`,
主导风向: getWindDirection(daily.winddirection_10m_dominant[index]),
日出: daily.sunrise[index],
日落: daily.sunset[index],
舒适度: getComfortIndex(avgTemp, 60, avgTemp)
};
});
return {
当前天气: currentWeather,
七天预报: forecast,
时区: data.timezone
};
}
/**
* 测试函数
*/
async function testWeather(cityName) {
console.log(`\n${'='.repeat(60)}`);
console.log(`测试城市: ${cityName}`);
console.log('='.repeat(60));
try {
// 测试地理编码
console.log('\n[1/3] 测试地理编码...');
const location = await geocodeCity(cityName);
console.log('✅ 地理编码成功');
console.log(` 城市: ${location.name}, ${location.country}`);
console.log(` 坐标: ${location.latitude}, ${location.longitude}`);
console.log(` 时区: ${location.timezone}`);
// 测试天气数据获取
console.log('\n[2/3] 获取天气数据...');
const weather = await getWeatherForecast(
location.latitude,
location.longitude,
location.timezone
);
console.log('✅ 天气数据获取成功');
// 验证数据完整性
console.log('\n[3/3] 验证数据完整性...');
// 检查当前天气
const required = ['时间', '温度', '体感温度', '湿度', '天气状况', '风速', '风向', '舒适度'];
const missing = required.filter(key => !weather.当前天气[key]);
if (missing.length > 0) {
console.log(`❌ 当前天气缺少字段: ${missing.join(', ')}`);
} else {
console.log('✅ 当前天气数据完整');
}
// 检查7天预报
if (weather.七天预报.length !== 7) {
console.log(`❌ 预报天数错误: ${weather.七天预报.length} (期望7天)`);
} else {
console.log('✅ 7天预报数据完整');
}
// 显示完整结果
console.log('\n' + '='.repeat(60));
console.log('完整天气数据:');
console.log('='.repeat(60));
console.log(JSON.stringify({
城市信息: {
名称: location.name,
国家: location.country,
坐标: `${location.latitude}, ${location.longitude}`,
时区: location.timezone
},
...weather
}, null, 2));
console.log('\n✅ 所有测试通过!');
return true;
} catch (error) {
console.error('\n❌ 测试失败:', error.message);
return false;
}
}
/**
* 运行多个测试案例
*/
async function runAllTests() {
console.log('\n🧪 开始测试 Weather MCP Server\n');
const testCases = [
'北京', // 中文城市
'Shanghai', // 英文城市
'Tokyo' // 国际城市
];
let passed = 0;
let failed = 0;
for (const city of testCases) {
const result = await testWeather(city);
if (result) {
passed++;
} else {
failed++;
}
}
console.log('\n' + '='.repeat(60));
console.log('测试总结');
console.log('='.repeat(60));
console.log(`总计: ${testCases.length} 个测试`);
console.log(`通过: ${passed} ✅`);
console.log(`失败: ${failed} ❌`);
console.log('='.repeat(60) + '\n');
}
// 执行测试
runAllTests().catch(console.error);