servchan/scripts/smoke-collab-support.js
jmcqueen c2e98d105e Initial commit: ServChan Webex bot for ServiceChannel work orders.
ServiceChannel webhook processor, proposal approval cards, attachment auto-post,
CollabSupport commands, and Docker deployment configuration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:12:08 -04:00

153 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Smoke-test CollabSupport HTTP endpoints used by ServChan.
*
* Usage:
* CS_API_BASE=https://bot.joesjavajoint.com/CollabSupport \
* SMOKE_WO_ID=356369551 SMOKE_STORE_NUM=2254 \
* node scripts/smoke-collab-support.js
*/
import 'dotenv/config';
const base = (process.env.CS_API_BASE_INTERNAL || process.env.CS_API_BASE || '').replace(/\/+$/, '');
const woId = process.env.SMOKE_WO_ID || '356369551';
const storeNum = process.env.SMOKE_STORE_NUM || '2254';
const timeoutMs = Number(process.env.SMOKE_TIMEOUT_MS) || 30_000;
const USAGE_MARKERS = [
'**Work Order Summary Usage:**',
'**Work Order History Usage:**',
'Please provide a 24 digit store number.',
];
function looksLikeUsage(text) {
return USAGE_MARKERS.some((m) => text.includes(m));
}
async function fetchCheck(label, url, { expectJson = false, validate, expectStatus } = {}) {
const started = Date.now();
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, {
signal: controller.signal,
headers: expectJson ? { Accept: 'application/json' } : undefined,
});
const elapsed = Date.now() - started;
const body = expectJson ? await res.json() : await res.text();
const issues = [];
if (expectStatus !== undefined) {
if (res.status !== expectStatus) issues.push(`expected HTTP ${expectStatus}, got ${res.status}`);
} else if (!res.ok) {
issues.push(`HTTP ${res.status}`);
}
if (expectStatus === undefined && !expectJson && typeof body === 'string' && body.trim().length === 0) {
issues.push('empty markdown body');
}
if (expectStatus === undefined && !expectJson && typeof body === 'string' && looksLikeUsage(body)) {
issues.push('looks like usage/help text, not data');
}
if (validate) {
const v = validate(body, res);
if (v) issues.push(v);
}
const pass = issues.length === 0;
console.log(`${pass ? 'PASS' : 'FAIL'} ${label}`);
console.log(` ${url}`);
console.log(`${res.status} (${elapsed}ms)${issues.length ? `${issues.join('; ')}` : ''}`);
return pass;
} catch (err) {
const elapsed = Date.now() - started;
const msg = err.name === 'AbortError' ? 'timeout' : err.message;
console.log(`FAIL ${label}`);
console.log(` ${url}`);
console.log(` → error (${elapsed}ms): ${msg}`);
return false;
} finally {
clearTimeout(timer);
}
}
async function main() {
if (!base) {
console.error('CS_API_BASE is required (e.g. https://bot.joesjavajoint.com/CollabSupport)');
process.exit(1);
}
console.log(`CollabSupport smoke test → ${base}`);
console.log(`WO=${woId} store=${storeNum}\n`);
const results = [];
results.push(await fetchCheck(
'wosummary',
`${base}/wosummary?woId=${encodeURIComponent(woId)}`,
{
validate: (body) => body.includes('Work Order Summary') ? null : 'missing summary header',
}
));
results.push(await fetchCheck(
'wohistory (summary)',
`${base}/wohistory?storeNum=${encodeURIComponent(storeNum)}`,
{
validate: (body) => body.includes('Work Order History') ? null : 'missing history header',
}
));
results.push(await fetchCheck(
'wohistory (detailed)',
`${base}/wohistory?storeNum=${encodeURIComponent(storeNum)}&mode=detailed`,
{
validate: (body) => body.includes('Work Order History') ? null : 'missing history header',
}
));
results.push(await fetchCheck(
'avStatus (summary)',
`${base}/avStatus?storeNum=${encodeURIComponent(storeNum)}`,
{
validate: (body) => (body.length > 50 ? null : 'response too short'),
}
));
results.push(await fetchCheck(
'avStatus (detailed)',
`${base}/avStatus?storeNum=${encodeURIComponent(storeNum)}&mode=detailed`,
{
validate: (body) => (body.length > 50 ? null : 'response too short'),
}
));
results.push(await fetchCheck(
'woAttachments (JSON)',
`${base}/woAttachments?woId=${encodeURIComponent(woId)}`,
{
expectJson: true,
validate: (data) => {
if (typeof data.success !== 'boolean') return 'missing success field';
if (typeof data.count !== 'number') return 'missing count field';
if (!Array.isArray(data.attachments)) return 'missing attachments array';
return null;
},
}
));
// Validation endpoint — should 400
results.push(await fetchCheck(
'wosummary invalid (expect 400)',
`${base}/wosummary?woId=abc`,
{ expectStatus: 400 }
));
const passed = results.filter(Boolean).length;
const total = results.length;
console.log(`\n${passed}/${total} checks passed`);
process.exit(passed === total ? 0 : 1);
}
main();