EU-Utility/plugins/integration_tests/scripts/api_client_test.js

356 lines
11 KiB
JavaScript

/**
* EU-Utility External API Test Client (JavaScript/Node.js)
* =========================================================
*
* Example JavaScript client for testing EU-Utility external API.
* Supports REST API and webhook integrations.
*
* Usage:
* node api_client_test.js --help
* node api_client_test.js health
* node api_client_test.js status
* node api_client_test.js notify "Test Title" "Test Message"
* node api_client_test.js search "ArMatrix"
* node api_client_test.js webhook --url <discord_webhook_url>
*/
const http = require('http');
const https = require('https');
const { URL } = require('url');
class EUUtilityClient {
constructor(host = '127.0.0.1', port = 8080, apiKey = null) {
this.host = host;
this.port = port;
this.apiKey = apiKey;
this.baseUrl = `http://${host}:${port}`;
}
_request(method, endpoint, data = null) {
return new Promise((resolve, reject) => {
const url = new URL(endpoint, this.baseUrl);
const options = {
method: method,
headers: {
'Content-Type': 'application/json'
}
};
if (this.apiKey) {
options.headers['X-API-Key'] = this.apiKey;
}
const req = http.request(url, options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
try {
const data = responseData ? JSON.parse(responseData) : null;
resolve({
status: res.statusCode,
data: data
});
} catch (e) {
resolve({
status: res.statusCode,
data: responseData
});
}
});
});
req.on('error', (error) => {
reject(error);
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
async healthCheck() {
return this._request('GET', '/health');
}
async getStatus() {
return this._request('GET', '/api/v1/status');
}
async sendNotification(title, message, type = 'info') {
return this._request('POST', '/api/v1/notify', {
title: title,
message: message,
type: type
});
}
async searchNexus(query, entityType = 'items') {
return this._request('GET', `/api/v1/search?q=${encodeURIComponent(query)}&type=${entityType}`);
}
async recordLoot(value, mob, items = []) {
return this._request('POST', '/api/v1/loot', {
value: value,
mob: mob,
items: items,
timestamp: Date.now()
});
}
async getLootSession() {
return this._request('GET', '/api/v1/loot/session');
}
}
async function sendDiscordWebhook(webhookUrl, content, embeds = null) {
return new Promise((resolve, reject) => {
const payload = { content: content };
if (embeds) {
payload.embeds = embeds;
}
const url = new URL(webhookUrl);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const req = https.request(url, options, (res) => {
resolve(res.statusCode >= 200 && res.statusCode < 300);
});
req.on('error', (error) => {
reject(error);
});
req.write(JSON.stringify(payload));
req.end();
});
}
function validateWebhookPayload(payload) {
if (typeof payload !== 'object' || payload === null) {
return { valid: false, error: 'Payload must be an object' };
}
if (!payload.content && !payload.embeds) {
return { valid: false, error: 'Payload must contain content or embeds' };
}
if (payload.content && payload.content.length > 2000) {
return { valid: false, error: 'Content exceeds 2000 character limit' };
}
if (payload.embeds) {
if (!Array.isArray(payload.embeds)) {
return { valid: false, error: 'Embeds must be an array' };
}
if (payload.embeds.length > 10) {
return { valid: false, error: 'Maximum 10 embeds allowed' };
}
}
return { valid: true, error: null };
}
async function testAllEndpoints(client) {
console.log('Testing EU-Utility External API...');
console.log('=' .repeat(50));
const tests = [
['Health Check', () => client.healthCheck()],
['Get Status', () => client.getStatus()],
['Send Notification', () => client.sendNotification('Test', 'Hello from JS client')],
['Search Nexus', () => client.searchNexus('ArMatrix')],
['Record Loot', () => client.recordLoot(50.25, 'Atrox Young', ['Animal Oil'])],
['Get Loot Session', () => client.getLootSession()],
];
for (const [name, testFunc] of tests) {
try {
console.log(`\n${name}...`);
const result = await testFunc();
const status = result.status >= 200 && result.status < 300 ? '✅' : '❌';
console.log(`${status} Status: ${result.status}`);
console.log(` Response:`, result.data);
} catch (error) {
console.log(`❌ Error: ${error.message}`);
}
}
console.log('\n' + '='.repeat(50));
console.log('Tests completed');
}
// CLI handling
function printHelp() {
console.log(`
EU-Utility External API Test Client (JavaScript)
Usage:
node api_client_test.js [options] <command>
Options:
--host <host> API host (default: 127.0.0.1)
--port <port> API port (default: 8080)
--api-key <key> API key for authentication
Commands:
health Check API health
status Get EU-Utility status
notify <title> <msg> Send notification
search <query> Search Nexus
loot <value> <mob> Record loot
global <value> <mob> Record global
webhook --url <url> Send Discord webhook
test Test all endpoints
validate <payload> Validate webhook payload
Examples:
node api_client_test.js health
node api_client_test.js notify "Test" "Hello World"
node api_client_test.js search "ArMatrix"
node api_client_test.js webhook --url https://discord.com/api/webhooks/...
`);
}
async function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
printHelp();
return;
}
// Parse options
let host = '127.0.0.1';
let port = 8080;
let apiKey = null;
const hostIndex = args.indexOf('--host');
if (hostIndex !== -1) {
host = args[hostIndex + 1];
args.splice(hostIndex, 2);
}
const portIndex = args.indexOf('--port');
if (portIndex !== -1) {
port = parseInt(args[portIndex + 1]);
args.splice(portIndex, 2);
}
const keyIndex = args.indexOf('--api-key');
if (keyIndex !== -1) {
apiKey = args[keyIndex + 1];
args.splice(keyIndex, 2);
}
const command = args[0];
const client = new EUUtilityClient(host, port, apiKey);
try {
switch (command) {
case 'health': {
const result = await client.healthCheck();
console.log(JSON.stringify(result, null, 2));
break;
}
case 'status': {
const result = await client.getStatus();
console.log(JSON.stringify(result, null, 2));
break;
}
case 'notify': {
if (args.length < 3) {
console.error('Usage: notify <title> <message>');
process.exit(1);
}
const result = await client.sendNotification(args[1], args[2]);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'search': {
if (args.length < 2) {
console.error('Usage: search <query>');
process.exit(1);
}
const result = await client.searchNexus(args[1]);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'loot': {
if (args.length < 3) {
console.error('Usage: loot <value> <mob>');
process.exit(1);
}
const result = await client.recordLoot(parseFloat(args[1]), args[2]);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'global': {
if (args.length < 3) {
console.error('Usage: global <value> <mob>');
process.exit(1);
}
const result = await client.recordLoot(parseFloat(args[1]), args[2]);
console.log(JSON.stringify(result, null, 2));
break;
}
case 'webhook': {
const urlIndex = args.indexOf('--url');
if (urlIndex === -1 || !args[urlIndex + 1]) {
console.error('Usage: webhook --url <webhook_url>');
process.exit(1);
}
const contentIndex = args.indexOf('--content');
const content = contentIndex !== -1 ? args[contentIndex + 1] : 'Test from EU-Utility';
const success = await sendDiscordWebhook(args[urlIndex + 1], content);
console.log(success ? '✅ Sent' : '❌ Failed');
break;
}
case 'test': {
await testAllEndpoints(client);
break;
}
case 'validate': {
if (args.length < 2) {
console.error('Usage: validate <json_payload>');
process.exit(1);
}
try {
const payload = JSON.parse(args[1]);
const validation = validateWebhookPayload(payload);
console.log(validation.valid ? '✅ Valid' : `${validation.error}`);
} catch (e) {
console.error(`❌ Invalid JSON: ${e.message}`);
}
break;
}
default:
console.error(`Unknown command: ${command}`);
printHelp();
process.exit(1);
}
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
main();