Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions extensions/pvaPublish/src/node/publish.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { publish } from './publish';

const mockFetch = jest.fn();
jest.mock('node-fetch', () => {
return async (...args) => await mockFetch(args);
});

const mockPublishConfig: any = {
profileName: 'Publish to Power Virtual Agents',
baseUrl: 'https://bots.int.customercareintelligence.net/',
botId: 'myBotId',
envId: 'myEnvId',
tenantId: 'myTenantId',
deleteMissingDependencies: false,
};
const mockBotProject: any = {
exportToZip: jest.fn((options, cb) => {
const archive = {
on: jest.fn((eventName, handler) => {
if (eventName === 'end') {
handler();
}
}),
pipe: jest.fn(),
unpipe: jest.fn(),
};
cb(archive);
}),
};
const mockGetAccessToken = jest.fn().mockResolvedValue('accessToken');

beforeEach(() => {
mockFetch.mockClear();
});

describe('publish()', () => {
it('should successfully start a publish job', async () => {
const mockDiagnostics = [
{
message: 'This is a log message from PVA',
},
{
message: 'This is a second log message from PVA',
},
];
mockFetch.mockResolvedValueOnce({
status: 202,
json: jest.fn().mockResolvedValue({
comment: 'testing',
importedContentEtag: 'W/"version"',
operationId: 'operationId',
diagnostics: mockDiagnostics,
lastUpdateTimeUtc: Date.now(),
state: 'Validating',
}),
});
const result = await publish(
mockPublishConfig,
mockBotProject,
{ comment: 'testing' },
undefined,
mockGetAccessToken
);

expect(result.status).toBe(202);
const innerResult = result.result;
expect(innerResult.message).toBe('Validating bot assets...');
expect(innerResult.comment).toBe('testing');
expect(innerResult.eTag).toBe('W/"version"');
expect(innerResult.log).toEqual(
mockDiagnostics.map((diag) => `---\n${JSON.stringify(diag, null, 2)}\n---\n`).join('\n')
);
expect(innerResult.id).toBe('operationId');
expect(innerResult.action).toEqual(null);
});

it('should surface the status message from PVA if a 202 is not returned', async () => {
mockFetch.mockResolvedValueOnce({
status: 502,
text: jest.fn().mockResolvedValue('Bad Gateway: The service might be down temporarily.'),
});
const result = await publish(
mockPublishConfig,
mockBotProject,
{ comment: 'testing' },
undefined,
mockGetAccessToken
);

expect(result.status).toBe(502);
expect(result.result.message).toBe('Bad Gateway: The service might be down temporarily.');
});

it('should surface a 500 and th error message if the publish code throws', async () => {
mockFetch.mockResolvedValueOnce({
status: 202,
json: jest.fn().mockRejectedValueOnce(new Error('Invalid JSON at position 0: "<"')),
});
const result = await publish(
mockPublishConfig,
mockBotProject,
{ comment: 'testing' },
undefined,
mockGetAccessToken
);

expect(result.status).toBe(500);
expect(result.result.message).toBe('Invalid JSON at position 0: "<"');
});
});
40 changes: 27 additions & 13 deletions extensions/pvaPublish/src/node/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,37 @@ export const publish = async (
'If-Match': project.eTag,
},
});
const job: PVAPublishJob = await res.json();
logger.log('Publish job started: %O', job);
if (res.status === 202) {
const job: PVAPublishJob = await res.json();
logger.log('Publish job started: %O', job);

// transform the PVA job to a publish response
const result = xformJobToResult(job);
// transform the PVA job to a publish response
const result = xformJobToResult(job);

// add to publish history
const botProjectId = project.id;
ensurePublishProfileHistory(botProjectId, profileName);
publishHistory[botProjectId][profileName].unshift(result);
// add to publish history
const botProjectId = project.id;
ensurePublishProfileHistory(botProjectId, profileName);
publishHistory[botProjectId][profileName].unshift(result);

logger.log('Publish call successful.');
logger.log('Publish call successful.');

return {
status: result.status,
result,
};
return {
status: result.status,
result,
};
} else {
// otherwise we should surface the error in the UI
let errorText = res.statusText;
if (res && res.text) {
errorText = await res.text();
}
return {
status: res.status,
result: {
message: errorText,
},
};
}
} catch (e) {
return {
status: 500,
Expand Down