Skip to content

Commit

Permalink
tests: add unit test for TeamsCommunicationAdapter
Browse files Browse the repository at this point in the history
  • Loading branch information
capaj committed Jun 13, 2024
1 parent 0eacc87 commit b0deb6e
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 79 deletions.
3 changes: 1 addition & 2 deletions packages/libraries/cli/examples/single.with.mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ type Query {
test: String
last: Boolean
fooTwo: Int!
foo4: Int!
}

type Mutation {
addFooT(foo: Int!): Int!
addFooAnother(foo: Int!): Int!
addBar(bar: String): String
addTest(test: String): String
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,212 @@
import { AlertChannel } from 'packages/services/api/src/shared/entities';
import { vi } from 'vitest';
import { SchemaChangeType } from '@hive/storage';
import { ChannelConfirmationInput, SchemaChangeNotificationInput } from './common';
import { TeamsCommunicationAdapter } from './msteams';

describe('TeamsCommunicationAdapter', () => {
describe('sendSchemaChangeNotification', () => {
it('should send', async () => {
// console.log('TeamsCommunicationAdapter', TeamsCommunicationAdapter);
const adapter = new TeamsCommunicationAdapter(
it('should send schema change notification', async () => {
const logger = {
child: () => ({
debug: vi.fn(),
error: vi.fn(),
}),
};
const appBaseUrl = 'app-base-url';
const webhookUrl = 'webhook-url';
const changes = [
{
child: () => ({
debug: vi.fn(),
error: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
}),
} as any,
'app-base-url',
);
// const input = {
// event: {
// organization: {
// id: 'org-id',
// },
// project: {
// id: 'project-id',
// },
// target: {
// id: 'target-id',
// },
// changes: [],
// messages: [],
// initial: false,
// },
// integrations: {
// teams: {
// webhookUrl: 'webhook-url',
// },
// },
// };
// await expect(adapter.sendSchemaChangeNotification(input)).resolves.toBeUndefined();
id: 'id-1',
type: 'FIELD_REMOVED',
approvalMetadata: null,
criticality: 'BREAKING',
message: "Field 'addFoo' was removed from object type 'Mutation'",
meta: {
typeName: 'Mutation',
removedFieldName: 'addFoo',
isRemovedFieldDeprecated: false,
typeType: 'object type',
},
path: 'Mutation.addFoo',
isSafeBasedOnUsage: false,
reason:
'Removing a field is a breaking change. It is preferable to deprecate the field before removing it.',
usageStatistics: null,
breakingChangeSchemaCoordinate: 'Mutation.addFoo',
},
{
id: 'id-2',
type: 'FIELD_REMOVED',
approvalMetadata: null,
criticality: 'BREAKING',
message: "Field 'foo3' was removed from object type 'Query'",
meta: {
typeName: 'Query',
removedFieldName: 'foo3',
isRemovedFieldDeprecated: false,
typeType: 'object type',
},
path: 'Query.foo3',
isSafeBasedOnUsage: false,
reason:
'Removing a field is a breaking change. It is preferable to deprecate the field before removing it.',
usageStatistics: null,
breakingChangeSchemaCoordinate: 'Query.foo3',
},
{
id: 'id-3',
type: 'FIELD_ADDED',
approvalMetadata: null,
criticality: 'NON_BREAKING',
message: "Field 'addFooT' was added to object type 'Mutation'",
meta: {
typeName: 'Mutation',
addedFieldName: 'addFooT',
typeType: 'object type',
},
path: 'Mutation.addFooT',
isSafeBasedOnUsage: false,
reason: null,
usageStatistics: null,
breakingChangeSchemaCoordinate: null,
},
{
id: 'id-4',
type: 'FIELD_ADDED',
approvalMetadata: null,
criticality: 'NON_BREAKING',
message: "Field 'foo4' was added to object type 'Query'",
meta: {
typeName: 'Query',
addedFieldName: 'foo4',
typeType: 'object type',
},
path: 'Query.foo4',
isSafeBasedOnUsage: false,
reason: null,
usageStatistics: null,
breakingChangeSchemaCoordinate: null,
},
] as Array<SchemaChangeType>;
const messages = [] as string[];
const input = {
alert: {
id: 'alert-id',
type: 'SCHEMA_CHANGE_NOTIFICATIONS',
channelId: 'channel-id',
projectId: 'project-id',
organizationId: 'org-id',
createdAt: new Date().toISOString(),
targetId: 'target-id',
},
integrations: {
slack: {
token: null,
},
},
event: {
organization: {
id: 'org-id',
cleanId: 'org-clean-id',
name: '',
},
project: {
id: 'project-id',
cleanId: 'project-clean-id',
name: 'project-name',
},
target: {
id: 'target-id',
cleanId: 'target-clean-id',
name: 'target-name',
},

changes,
messages,
initial: false,
errors: [],
schema: {
id: 'schema-id',
commit: 'commit',
valid: true,
},
},
channel: {
webhookEndpoint: webhookUrl,
} as AlertChannel,
} as SchemaChangeNotificationInput;

const adapter = new TeamsCommunicationAdapter(logger as any, appBaseUrl);
const sendTeamsMessageSpy = vi.spyOn(adapter as any, 'sendTeamsMessage');

await adapter.sendSchemaChangeNotification(input);

expect(sendTeamsMessageSpy.mock.calls[0]).toMatchInlineSnapshot(`
[
webhook-url,
🐝 Hi, I found *4 changes* in project [project-name](app-base-url/org-clean-id/project-clean-id), target [target-name](app-base-url/org-clean-id/project-clean-id/target-clean-id) ([view details](app-base-url/org-clean-id/project-clean-id/target-clean-id/history/schema-id)):
### Breaking changes
Field \`addFoo\` was removed from object type \`Mutation\`
Field \`foo3\` was removed from object type \`Query\`
### Safe changes
Field \`addFooT\` was added to object type \`Mutation\`
Field \`foo4\` was added to object type \`Query\`
,
]
`);
});

// Add more test cases here if needed
});
});
describe('sendChannelConfirmation', () => {
it('should send channel confirmation', async () => {
const logger = {
child: () => ({
debug: vi.fn(),
error: vi.fn(),
}),
};
const appBaseUrl = 'app-base-url';
const webhookUrl = 'webhook-url';
const input = {
event: {
organization: {
id: 'org-id',
cleanId: 'org-clean-id',
},
project: {
id: 'project-id',
cleanId: 'project-clean-id',
name: 'project-name',
},
kind: 'created',
},
channel: {
webhookEndpoint: webhookUrl,
},
} as ChannelConfirmationInput;
const adapter = new TeamsCommunicationAdapter(logger as any, appBaseUrl);
const sendTeamsMessageSpy = vi.spyOn(adapter as any, 'sendTeamsMessage');
await adapter.sendChannelConfirmation(input);
expect(sendTeamsMessageSpy.mock.calls[0]).toMatchInlineSnapshot(`
[
webhook-url,
👋 Hi! I'm the notification 🐝.
I will send here notifications about your [project-name](app-base-url/org-clean-id/project-clean-id) project.,
]
`);

input.event.kind = 'deleted';
await adapter.sendChannelConfirmation(input);
expect(sendTeamsMessageSpy.mock.calls[1]).toMatchInlineSnapshot(`
[
webhook-url,
👋 Hi! I'm the notification 🐝.
I will no longer send here notifications about your [project-name](app-base-url/org-clean-id/project-clean-id) project.,
]
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ export class TeamsCommunicationAdapter implements CommunicationAdapter {
totalChanges,
)}* in project ${projectLink}, target ${targetLink} (${viewLink}):`;

const attachments = input.event.initial
? []
: createAttachments(input.event.changes, input.event.messages);
const attachmentsText = input.event.initial
? ''
: createAttachmentsText(input.event.changes, input.event.messages);

await this.sendTeamsMessage(webhookUrl, message, attachments);
await this.sendTeamsMessage(webhookUrl, `${message}\n\n${attachmentsText}`);
} catch (error) {
this.logger.error(`Failed to send Microsoft Teams notification`, error);
}
Expand Down Expand Up @@ -108,7 +108,7 @@ export class TeamsCommunicationAdapter implements CommunicationAdapter {
return word + (num > 1 ? 's' : '');
}

private async sendTeamsMessage(webhookUrl: string, message: string, attachments: any[] = []) {
private async sendTeamsMessage(webhookUrl: string, message: string) {
const payload = {
'@type': 'MessageCard',
'@context': 'http://schema.org/extensions',
Expand All @@ -119,7 +119,6 @@ export class TeamsCommunicationAdapter implements CommunicationAdapter {
activityTitle: 'Notification',
text: message,
markdown: true,
...(attachments.length > 0 && { attachments: attachments }),
},
],
};
Expand All @@ -138,55 +137,49 @@ export class TeamsCommunicationAdapter implements CommunicationAdapter {
}
}

function createAttachments(changes: readonly SchemaChangeType[], messages: readonly string[]) {
function createAttachmentsText(
changes: readonly SchemaChangeType[],
messages: readonly string[],
): string {
const breakingChanges = changes.filter(
change => change.criticality === CriticalityLevel.Breaking,
);
const safeChanges = changes.filter(change => change.criticality !== CriticalityLevel.Breaking);

const attachments: any[] = [];
let text = '';

if (breakingChanges.length) {
attachments.push(
renderAttachments({
color: '#E74C3B',
title: 'Breaking changes',
changes: breakingChanges,
}),
);
text += renderAttachmentsText({
color: '#E74C3B',
title: 'Breaking changes',
changes: breakingChanges,
});
}

if (safeChanges.length) {
attachments.push(
renderAttachments({
color: '#23B99A',
title: 'Safe changes',
changes: safeChanges,
}),
);
text += renderAttachmentsText({
color: '#23B99A',
title: 'Safe changes',
changes: safeChanges,
});
}

if (messages.length) {
const text = messages.map(message => slackCoderize(message)).join('\n');
attachments.push({
color: '#1C8DC7',
title: 'Other changes',
text,
});
text += `### Other changes\n${messages.map(message => slackCoderize(message)).join('\n')}\n`;
}

return attachments;
return text;
}

function renderAttachments({
function renderAttachmentsText({
changes,
title,
color,
}: {
color: string;
title: string;
changes: readonly SchemaChangeType[];
}): any {
}): string {
const text = changes
.map(change => {
let text = change.message;
Expand All @@ -198,14 +191,5 @@ function renderAttachments({
})
.join('\n');

return {
title,
text,
markdown: true,
facts: changes.map(change => ({
name: change.criticality,
value: slackCoderize(change.message),
})),
themeColor: color,
};
}
return `### ${title}\n${text}\n`;
}
7 changes: 6 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export default defineConfig({
.pathname,
},
globals: true,
exclude: [...defaultExclude, 'integration-tests', 'packages/migrations/test'],
exclude: [
...defaultExclude,
'integration-tests',
'packages/migrations/test',
'docker/.hive-dev',
],
setupFiles: ['./scripts/serializer.ts'],
},
});

0 comments on commit b0deb6e

Please sign in to comment.