Update README and Jira UAT test script

This commit is contained in:
root
2026-04-28 18:44:14 +00:00
parent caa1d539cc
commit 4f960d0866
2 changed files with 131 additions and 18 deletions

View File

@@ -44,7 +44,9 @@ function log(level, message, data) {
console.log(line);
if (data) {
const dataStr = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
console.log(' ' + dataStr.split('\n').join('\n '));
// Truncate long data to keep logs readable (HTML error pages can be 50KB+)
const truncated = dataStr.length > 2000 ? dataStr.substring(0, 2000) + '\n ... [truncated — ' + dataStr.length + ' chars total]' : dataStr;
console.log(' ' + truncated.split('\n').join('\n '));
}
}
@@ -92,21 +94,81 @@ async function testCreateIssue() {
const projectKey = jiraApi.JIRA_PROJECT_KEY;
assert(projectKey, 'JIRA_PROJECT_KEY must be set in .env');
// Discover available issue types for this project
const projRes = await jiraApi.jiraGet('/rest/api/2/project/' + encodeURIComponent(projectKey));
assert(projRes.status === 200, 'Should be able to fetch project metadata. Got HTTP ' + projRes.status + ': ' + (projRes.body || '').substring(0, 300));
const projData = JSON.parse(projRes.body);
const availableTypes = (projData.issueTypes || []).filter(t => !t.subtask);
logInfo('Available issue types:', availableTypes.map(t => t.name));
// Determine which issue type to use: configured type first, then fallback order
const configuredType = jiraApi.JIRA_ISSUE_TYPE || 'Task';
const fallbackOrder = [configuredType, 'Story', 'Task', 'Bug'];
let issueTypeName = null;
for (const candidate of fallbackOrder) {
if (availableTypes.some(t => t.name === candidate)) {
issueTypeName = candidate;
break;
}
}
// If none of the preferred types exist, use the first available non-subtask type
if (!issueTypeName && availableTypes.length > 0) {
issueTypeName = availableTypes[0].name;
}
assert(issueTypeName, 'No usable issue type found in project ' + projectKey);
if (issueTypeName !== configuredType) {
logWarn('Configured JIRA_ISSUE_TYPE "' + configuredType + '" not available — falling back to "' + issueTypeName + '"');
}
const fields = {
project: { key: projectKey },
summary: `[UAT TEST] STEAM Dashboard - CVE-2025-0001 - TestVendor - ${new Date().toISOString()}`,
issuetype: { name: jiraApi.JIRA_ISSUE_TYPE || 'Task' },
description: 'Automated UAT test issue created by STEAM Security Dashboard Jira integration test script. This issue can be safely deleted after review.'
summary: '[UAT TEST] STEAM Dashboard - CVE-2025-0001 - TestVendor - ' + new Date().toISOString(),
issuetype: { name: issueTypeName },
description: 'UAT test issue created by STEAM Security Dashboard Jira integration test script. This issue can be safely deleted after review.'
};
// Epic type requires an Epic Name field — add it if creating an Epic
if (issueTypeName === 'Epic') {
fields.customfield_10004 = fields.summary; // Epic Name (standard Jira field ID)
}
logInfo('Creating issue with fields:', { project: fields.project, summary: fields.summary, issuetype: fields.issuetype });
const result = await jiraApi.createIssue(fields);
assert(result.ok, 'Create issue should succeed. Got: ' + JSON.stringify(result));
let result = await jiraApi.createIssue(fields);
// If the first attempt fails with 400, try without description (some screens don't have it)
if (!result.ok && result.status === 400) {
const errBody = (result.body || '').substring(0, 500);
logWarn('Create failed with 400, retrying without description. Error: ' + errBody);
const retryFields = { ...fields };
delete retryFields.description;
result = await jiraApi.createIssue(retryFields);
}
// If still failing with 400 and we used Epic, try without the customfield_10004
// (Epic Name field ID varies across Jira instances)
if (!result.ok && result.status === 400 && issueTypeName === 'Epic') {
const errBody = (result.body || '').substring(0, 500);
logWarn('Epic create failed, retrying with alternate Epic Name field. Error: ' + errBody);
const retryFields = { ...fields };
delete retryFields.customfield_10004;
// Try common alternate Epic Name field IDs
retryFields.customfield_10011 = fields.summary;
result = await jiraApi.createIssue(retryFields);
}
assert(result.ok, 'Create issue should succeed. Got HTTP ' + result.status + ': ' + (result.body || '').substring(0, 500));
assert(result.data && result.data.key, 'Should return issue key');
createdIssueKey = result.data.key;
logInfo('Created issue:', { key: createdIssueKey, id: result.data.id, self: result.data.self });
logInfo('Created issue:', { key: createdIssueKey, id: result.data.id, self: result.data.self, issueType: issueTypeName });
}
// ---------------------------------------------------------------------------
@@ -117,7 +179,7 @@ async function testGetIssue() {
assert(createdIssueKey, 'Need a created issue key from previous test');
const result = await jiraApi.getIssue(createdIssueKey);
assert(result.ok, 'Get issue should succeed. Got: ' + JSON.stringify(result));
assert(result.ok, 'Get issue should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
const issue = result.data;
assert(issue.key === createdIssueKey, 'Returned key should match');
@@ -142,7 +204,7 @@ async function testUpdateIssue() {
const result = await jiraApi.updateIssue(createdIssueKey, {
summary: `[UAT TEST] STEAM Dashboard - UPDATED - ${new Date().toISOString()}`
});
assert(result.ok, 'Update issue should succeed (204). Got: ' + JSON.stringify(result));
assert(result.ok, 'Update issue should succeed (204). Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
logInfo('Updated issue summary successfully');
}
@@ -156,7 +218,7 @@ async function testAddComment() {
const commentBody = `STEAM Dashboard UAT test comment.\nTimestamp: ${new Date().toISOString()}\nThis comment was created by the automated test script.`;
const result = await jiraApi.addComment(createdIssueKey, commentBody);
assert(result.ok, 'Add comment should succeed. Got: ' + JSON.stringify(result));
assert(result.ok, 'Add comment should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
assert(result.data && result.data.id, 'Should return comment ID');
logInfo('Added comment:', { commentId: result.data.id });
@@ -171,7 +233,7 @@ async function testGetTransitions() {
assert(createdIssueKey, 'Need a created issue key from previous test');
const result = await jiraApi.getTransitions(createdIssueKey);
assert(result.ok, 'Get transitions should succeed. Got: ' + JSON.stringify(result));
assert(result.ok, 'Get transitions should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
const transitions = result.data.transitions || [];
logInfo('Available transitions:', transitions.map(t => ({ id: t.id, name: t.name, to: t.to ? t.to.name : null })));
@@ -197,7 +259,7 @@ async function testTransitionIssue(transitions) {
logInfo(`Transitioning to: ${transition.name} (id: ${transition.id})`);
const result = await jiraApi.transitionIssue(createdIssueKey, transition.id);
assert(result.ok, 'Transition should succeed (204). Got: ' + JSON.stringify(result));
assert(result.ok, 'Transition should succeed (204). Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
logInfo('Transition successful');
}
@@ -210,11 +272,12 @@ async function testJqlSearch() {
const projectKey = jiraApi.JIRA_PROJECT_KEY;
assert(projectKey, 'JIRA_PROJECT_KEY must be set');
const jql = `project = ${projectKey} AND updated >= -1h ORDER BY updated DESC`;
// Use a broad time window to ensure results even on a quiet project
const jql = `project = ${projectKey} ORDER BY updated DESC`;
logInfo('Searching with JQL:', jql);
const result = await jiraApi.searchIssues(jql, { maxResults: 10 });
assert(result.ok, 'Search should succeed. Got: ' + JSON.stringify(result));
assert(result.ok, 'Search should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
const data = result.data;
logInfo('Search results:', {
@@ -241,7 +304,7 @@ async function testBulkKeySearch() {
logInfo('Bulk searching keys:', keys);
const result = await jiraApi.searchIssuesByKeys(keys);
assert(result.ok, 'Bulk key search should succeed. Got: ' + JSON.stringify(result));
assert(result.ok, 'Bulk key search should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
const found = (result.data.issues || []).map(i => i.key);
logInfo('Found issues:', found);
@@ -330,7 +393,9 @@ function writeLog() {
const lines = results.map(r => {
let line = `[${r.timestamp}] ${r.level.toUpperCase().padEnd(5)} ${r.message}`;
if (r.data) {
line += '\n ' + (typeof r.data === 'string' ? r.data : JSON.stringify(r.data, null, 2)).split('\n').join('\n ');
const dataStr = (typeof r.data === 'string' ? r.data : JSON.stringify(r.data, null, 2));
const truncated = dataStr.length > 2000 ? dataStr.substring(0, 2000) + '\n ... [truncated — ' + dataStr.length + ' chars total]' : dataStr;
line += '\n ' + truncated.split('\n').join('\n ');
}
return line;
});