aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
768 lines • 95.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable import/order */
const cxschema = require("@aws-cdk/cloud-assembly-schema");
const AWS = require("aws-sdk-mock");
const load_balancers_1 = require("../../lib/context-providers/load-balancers");
const mock_sdk_1 = require("../util/mock-sdk");
AWS.setSDK(require.resolve('aws-sdk'));
const mockSDK = new mock_sdk_1.MockSdkProvider();
afterEach(done => {
AWS.restore();
done();
});
describe('utilities', () => {
test('describeTags yields tags by chunk', async () => {
const resourceTags = {};
for (const resourceArn of [...Array(100)].map((_, i) => `arn:load-balancer-${i}`)) {
resourceTags[resourceArn] = {
ResourceArn: resourceArn,
Tags: [
{ Key: 'name', Value: resourceArn },
],
};
}
;
AWS.mock('ELBv2', 'describeTags', (_params, cb) => {
expect(_params.ResourceArns.length).toBeLessThanOrEqual(20);
cb(null, {
TagDescriptions: _params.ResourceArns.map(resourceArn => ({
ResourceArn: resourceArn,
Tags: [
{ Key: 'name', Value: resourceArn },
],
})),
});
});
const elbv2 = (await mockSDK.forEnvironment()).sdk.elbv2();
const resourceTagsOut = {};
for await (const tagDescription of (0, load_balancers_1.describeTags)(elbv2, Object.keys(resourceTags))) {
resourceTagsOut[tagDescription.ResourceArn] = tagDescription;
}
expect(resourceTagsOut).toEqual(resourceTags);
});
test('describeListenersByLoadBalancerArn traverses pages', async () => {
// arn:listener-0, arn:listener-1, ..., arn:listener-99
const listenerArns = [...Array(100)].map((_, i) => `arn:listener-${i}`);
expect(listenerArns[0]).toEqual('arn:listener-0');
AWS.mock('ELBv2', 'describeListeners', (_params, cb) => {
const start = parseInt(_params.Marker ?? '0');
const end = start + 10;
const slice = listenerArns.slice(start, end);
cb(null, {
Listeners: slice.map(arn => ({
ListenerArn: arn,
})),
NextMarker: end < listenerArns.length ? end.toString() : undefined,
});
});
const elbv2 = (await mockSDK.forEnvironment()).sdk.elbv2();
const listenerArnsFromPages = Array();
for await (const listener of (0, load_balancers_1.describeListenersByLoadBalancerArn)(elbv2, ['arn:load-balancer'])) {
listenerArnsFromPages.push(listener.ListenerArn);
}
expect(listenerArnsFromPages).toEqual(listenerArns);
});
test('describeLoadBalancers traverses pages', async () => {
const loadBalancerArns = [...Array(100)].map((_, i) => `arn:load-balancer-${i}`);
expect(loadBalancerArns[0]).toEqual('arn:load-balancer-0');
AWS.mock('ELBv2', 'describeLoadBalancers', (_params, cb) => {
const start = parseInt(_params.Marker ?? '0');
const end = start + 10;
const slice = loadBalancerArns.slice(start, end);
cb(null, {
LoadBalancers: slice.map(loadBalancerArn => ({
LoadBalancerArn: loadBalancerArn,
})),
NextMarker: end < loadBalancerArns.length ? end.toString() : undefined,
});
});
const elbv2 = (await mockSDK.forEnvironment()).sdk.elbv2();
const loadBalancerArnsFromPages = (await (0, load_balancers_1.describeLoadBalancers)(elbv2, {})).map(l => l.LoadBalancerArn);
expect(loadBalancerArnsFromPages).toEqual(loadBalancerArns);
});
describe('tagsMatch', () => {
test('all tags match', () => {
const tagDescription = {
ResourceArn: 'arn:whatever',
Tags: [{ Key: 'some', Value: 'tag' }],
};
const requiredTags = [
{ key: 'some', value: 'tag' },
];
expect((0, load_balancers_1.tagsMatch)(tagDescription, requiredTags)).toEqual(true);
});
test('extra tags match', () => {
const tagDescription = {
ResourceArn: 'arn:whatever',
Tags: [
{ Key: 'some', Value: 'tag' },
{ Key: 'other', Value: 'tag2' },
],
};
const requiredTags = [
{ key: 'some', value: 'tag' },
];
expect((0, load_balancers_1.tagsMatch)(tagDescription, requiredTags)).toEqual(true);
});
test('no tags matches no tags', () => {
const tagDescription = {
ResourceArn: 'arn:whatever',
Tags: [],
};
expect((0, load_balancers_1.tagsMatch)(tagDescription, [])).toEqual(true);
});
test('one tag matches of several', () => {
const tagDescription = {
ResourceArn: 'arn:whatever',
Tags: [{ Key: 'some', Value: 'tag' }],
};
const requiredTags = [
{ key: 'some', value: 'tag' },
{ key: 'other', value: 'value' },
];
expect((0, load_balancers_1.tagsMatch)(tagDescription, requiredTags)).toEqual(false);
});
test('undefined tag does not error', () => {
const tagDescription = {
ResourceArn: 'arn:whatever',
Tags: [{ Key: 'some' }],
};
const requiredTags = [
{ key: 'some', value: 'tag' },
{ key: 'other', value: 'value' },
];
expect((0, load_balancers_1.tagsMatch)(tagDescription, requiredTags)).toEqual(false);
});
});
});
describe('load balancer context provider plugin', () => {
test('errors when no matches are found', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerArn: 'arn:load-balancer1',
})).rejects.toThrow(/No load balancers found/i);
});
test('errors when multiple load balancers match', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [
{
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer1',
DNSName: 'dns1.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
{
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer2',
DNSName: 'dns2.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
],
describeTagsExpected: { ResourceArns: ['arn:load-balancer1', 'arn:load-balancer2'] },
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer1',
Tags: [
{ Key: 'some', Value: 'tag' },
],
},
{
ResourceArn: 'arn:load-balancer2',
Tags: [
{ Key: 'some', Value: 'tag' },
],
},
],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [
{ key: 'some', value: 'tag' },
],
})).rejects.toThrow(/Multiple load balancers found/i);
});
test('looks up by arn', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerContextProviderPlugin(mockSDK);
mockALBLookup({
describeLoadBalancersExpected: { LoadBalancerArns: ['arn:load-balancer1'] },
loadBalancers: [
{
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer1',
DNSName: 'dns.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
],
});
// WHEN
const result = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerArn: 'arn:load-balancer1',
});
// THEN
expect(result.ipAddressType).toEqual('ipv4');
expect(result.loadBalancerArn).toEqual('arn:load-balancer1');
expect(result.loadBalancerCanonicalHostedZoneId).toEqual('Z1234');
expect(result.loadBalancerDnsName).toEqual('dns.example.com');
expect(result.securityGroupIds).toEqual(['sg-1234']);
expect(result.vpcId).toEqual('vpc-1234');
});
test('looks up by tags', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [
{
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer1',
DNSName: 'dns1.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
{
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer2',
DNSName: 'dns2.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
],
describeTagsExpected: { ResourceArns: ['arn:load-balancer1', 'arn:load-balancer2'] },
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer1',
Tags: [
{ Key: 'some', Value: 'tag' },
],
},
{
ResourceArn: 'arn:load-balancer2',
Tags: [
{ Key: 'some', Value: 'tag' },
{ Key: 'second', Value: 'tag2' },
],
},
],
});
// WHEN
const result = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [
{ key: 'some', value: 'tag' },
{ key: 'second', value: 'tag2' },
],
});
expect(result.loadBalancerArn).toEqual('arn:load-balancer2');
});
test('filters by type', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [
{
IpAddressType: 'ipv4',
Type: 'network',
LoadBalancerArn: 'arn:load-balancer1',
DNSName: 'dns1.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
},
{
IpAddressType: 'ipv4',
Type: 'application',
LoadBalancerArn: 'arn:load-balancer2',
DNSName: 'dns2.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
},
],
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer1',
Tags: [{ Key: 'some', Value: 'tag' }],
},
{
ResourceArn: 'arn:load-balancer2',
Tags: [{ Key: 'some', Value: 'tag' }],
},
],
});
// WHEN
const loadBalancer = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerTags: [{ key: 'some', value: 'tag' }],
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
});
expect(loadBalancer.loadBalancerArn).toEqual('arn:load-balancer2');
});
});
describe('load balancer listener context provider plugin', () => {
test('errors when no associated load balancers match', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [{ key: 'some', value: 'tag' }],
})).rejects.toThrow(/No associated load balancers found/i);
});
test('errors when no listeners match', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [
{
LoadBalancerArn: 'arn:load-balancer',
Type: 'application',
},
],
listeners: [
{
LoadBalancerArn: 'arn:load-balancer',
ListenerArn: 'arn:listener',
Port: 80,
Protocol: 'HTTP',
},
],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerArn: 'arn:load-balancer',
listenerPort: 443,
listenerProtocol: cxschema.LoadBalancerListenerProtocol.HTTPS,
})).rejects.toThrow(/No load balancer listeners found/i);
});
test('errors when multiple listeners match', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
loadBalancers: [
{
LoadBalancerArn: 'arn:load-balancer',
Type: 'application',
},
{
LoadBalancerArn: 'arn:load-balancer2',
Type: 'application',
},
],
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer',
Tags: [{ Key: 'some', Value: 'tag' }],
},
{
ResourceArn: 'arn:load-balancer2',
Tags: [{ Key: 'some', Value: 'tag' }],
},
],
listeners: [
{
LoadBalancerArn: 'arn:load-balancer',
ListenerArn: 'arn:listener',
Port: 80,
Protocol: 'HTTP',
},
{
LoadBalancerArn: 'arn:load-balancer2',
ListenerArn: 'arn:listener2',
Port: 80,
Protocol: 'HTTP',
},
],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [{ key: 'some', value: 'tag' }],
listenerPort: 80,
listenerProtocol: cxschema.LoadBalancerListenerProtocol.HTTP,
})).rejects.toThrow(/Multiple load balancer listeners/i);
});
test('looks up by listener arn', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
describeListenersExpected: { ListenerArns: ['arn:listener-arn'] },
listeners: [
{
ListenerArn: 'arn:listener-arn',
LoadBalancerArn: 'arn:load-balancer-arn',
Port: 999,
},
],
describeLoadBalancersExpected: { LoadBalancerArns: ['arn:load-balancer-arn'] },
loadBalancers: [
{
LoadBalancerArn: 'arn:load-balancer-arn',
SecurityGroups: ['sg-1234', 'sg-2345'],
Type: 'application',
},
],
});
// WHEN
const listener = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
listenerArn: 'arn:listener-arn',
});
// THEN
expect(listener.listenerArn).toEqual('arn:listener-arn');
expect(listener.listenerPort).toEqual(999);
expect(listener.securityGroupIds).toEqual(['sg-1234', 'sg-2345']);
});
test('looks up by associated load balancer arn', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
describeLoadBalancersExpected: { LoadBalancerArns: ['arn:load-balancer-arn1'] },
loadBalancers: [
{
LoadBalancerArn: 'arn:load-balancer-arn1',
SecurityGroups: ['sg-1234'],
Type: 'application',
},
],
describeListenersExpected: { LoadBalancerArn: 'arn:load-balancer-arn1' },
listeners: [
{
// This one
ListenerArn: 'arn:listener-arn1',
LoadBalancerArn: 'arn:load-balancer-arn1',
Port: 80,
},
],
});
// WHEN
const listener = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerArn: 'arn:load-balancer-arn1',
});
// THEN
expect(listener.listenerArn).toEqual('arn:listener-arn1');
expect(listener.listenerPort).toEqual(80);
expect(listener.securityGroupIds).toEqual(['sg-1234']);
});
test('looks up by associated load balancer tags', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
describeLoadBalancersExpected: { LoadBalancerArns: undefined },
loadBalancers: [
{
// This one should have the wrong tags
LoadBalancerArn: 'arn:load-balancer-arn1',
SecurityGroups: ['sg-1234', 'sg-2345'],
Type: 'application',
},
{
// Expecting this one
LoadBalancerArn: 'arn:load-balancer-arn2',
SecurityGroups: ['sg-3456', 'sg-4567'],
Type: 'application',
},
],
describeTagsExpected: { ResourceArns: ['arn:load-balancer-arn1', 'arn:load-balancer-arn2'] },
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer-arn1',
Tags: [],
},
{
// Expecting this one
ResourceArn: 'arn:load-balancer-arn2',
Tags: [
{ Key: 'some', Value: 'tag' },
],
},
],
describeListenersExpected: { LoadBalancerArn: 'arn:load-balancer-arn2' },
listeners: [
{
// This one
ListenerArn: 'arn:listener-arn1',
LoadBalancerArn: 'arn:load-balancer-arn2',
Port: 80,
},
{
ListenerArn: 'arn:listener-arn2',
LoadBalancerArn: 'arn:load-balancer-arn2',
Port: 999,
},
],
});
// WHEN
const listener = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [
{ key: 'some', value: 'tag' },
],
listenerPort: 999,
});
// THEN
expect(listener.listenerArn).toEqual('arn:listener-arn2');
expect(listener.listenerPort).toEqual(999);
expect(listener.securityGroupIds).toEqual(['sg-3456', 'sg-4567']);
});
test('looks up by listener port and proto', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
AWS.mock('ELBv2', 'describeLoadBalancers', (_params, cb) => {
expect(_params).toEqual({});
cb(null, {
LoadBalancers: [
{
// Shouldn't have any matching listeners
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer1',
DNSName: 'dns1.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-1234'],
VpcId: 'vpc-1234',
Type: 'application',
},
{
// Should have a matching listener
IpAddressType: 'ipv4',
LoadBalancerArn: 'arn:load-balancer2',
DNSName: 'dns2.example.com',
CanonicalHostedZoneId: 'Z1234',
SecurityGroups: ['sg-2345'],
VpcId: 'vpc-1234',
Type: 'application',
},
],
});
});
AWS.mock('ELBv2', 'describeTags', (_params, cb) => {
cb(null, {
TagDescriptions: [
{
ResourceArn: 'arn:load-balancer1',
Tags: [{ Key: 'some', Value: 'tag' }],
},
{
ResourceArn: 'arn:load-balancer2',
Tags: [{ Key: 'some', Value: 'tag' }],
},
],
});
});
AWS.mock('ELBv2', 'describeListeners', (params, cb) => {
if (params.LoadBalancerArn === 'arn:load-balancer1') {
cb(null, {
Listeners: [
{
// Wrong port, wrong protocol => no match
ListenerArn: 'arn:listener-arn1',
LoadBalancerArn: 'arn:load-balancer1',
Protocol: 'HTTP',
Port: 80,
},
{
// Wrong protocol, right port => no match
ListenerArn: 'arn:listener-arn3',
LoadBalancerArn: 'arn:load-balancer1',
Protocol: 'HTTPS',
Port: 443,
},
{
// Wrong port, right protocol => no match
ListenerArn: 'arn:listener-arn4',
LoadBalancerArn: 'arn:load-balancer1',
Protocol: 'TCP',
Port: 999,
},
],
});
}
else if (params.LoadBalancerArn === 'arn:load-balancer2') {
cb(null, {
Listeners: [
{
// Wrong port, wrong protocol => no match
ListenerArn: 'arn:listener-arn5',
LoadBalancerArn: 'arn:load-balancer2',
Protocol: 'HTTP',
Port: 80,
},
{
// Right port, right protocol => match
ListenerArn: 'arn:listener-arn6',
LoadBalancerArn: 'arn:load-balancer2',
Port: 443,
Protocol: 'TCP',
},
],
});
}
else {
cb(new Error(`Unexpected request: ${JSON.stringify(params)}'`), {});
}
});
// WHEN
const listener = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.APPLICATION,
loadBalancerTags: [{ key: 'some', value: 'tag' }],
listenerProtocol: cxschema.LoadBalancerListenerProtocol.TCP,
listenerPort: 443,
});
// THEN
expect(listener.listenerArn).toEqual('arn:listener-arn6');
expect(listener.listenerPort).toEqual(443);
expect(listener.securityGroupIds).toEqual(['sg-2345']);
});
test('filters by associated load balancer type', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
describeLoadBalancersExpected: { LoadBalancerArns: undefined },
loadBalancers: [
{
// This one has wrong type => no match
LoadBalancerArn: 'arn:load-balancer-arn1',
SecurityGroups: [],
Type: 'application',
},
{
// Right type => match
LoadBalancerArn: 'arn:load-balancer-arn2',
SecurityGroups: [],
Type: 'network',
},
],
tagDescriptions: [
{
ResourceArn: 'arn:load-balancer-arn1',
Tags: [{ Key: 'some', Value: 'tag' }],
},
{
ResourceArn: 'arn:load-balancer-arn2',
Tags: [{ Key: 'some', Value: 'tag' }],
},
],
describeListenersExpected: { LoadBalancerArn: 'arn:load-balancer-arn2' },
listeners: [
{
ListenerArn: 'arn:listener-arn2',
LoadBalancerArn: 'arn:load-balancer-arn2',
Port: 443,
},
],
});
// WHEN
const listener = await provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.NETWORK,
loadBalancerTags: [{ key: 'some', value: 'tag' }],
listenerPort: 443,
});
// THEN
expect(listener.listenerArn).toEqual('arn:listener-arn2');
expect(listener.listenerPort).toEqual(443);
});
test('errors when associated load balancer is wrong type', async () => {
// GIVEN
const provider = new load_balancers_1.LoadBalancerListenerContextProviderPlugin(mockSDK);
mockALBLookup({
describeListenersExpected: { ListenerArns: ['arn:listener-arn1'] },
listeners: [
{
ListenerArn: 'arn:listener-arn1',
LoadBalancerArn: 'arn:load-balancer-arn1',
Port: 443,
},
],
describeLoadBalancersExpected: { LoadBalancerArns: ['arn:load-balancer-arn1'] },
loadBalancers: [
{
// This one has wrong type => no match
LoadBalancerArn: 'arn:load-balancer-arn1',
SecurityGroups: [],
Type: 'application',
},
],
});
// WHEN
await expect(provider.getValue({
account: '1234',
region: 'us-east-1',
loadBalancerType: cxschema.LoadBalancerType.NETWORK,
listenerArn: 'arn:listener-arn1',
})).rejects.toThrow(/no associated load balancer found/i);
});
});
function mockALBLookup(options) {
AWS.mock('ELBv2', 'describeLoadBalancers', (_params, cb) => {
if (options.describeLoadBalancersExpected !== undefined) {
expect(_params).toEqual(options.describeLoadBalancersExpected);
}
cb(null, { LoadBalancers: options.loadBalancers });
});
AWS.mock('ELBv2', 'describeTags', (_params, cb) => {
if (options.describeTagsExpected !== undefined) {
expect(_params).toEqual(options.describeTagsExpected);
}
cb(null, { TagDescriptions: options.tagDescriptions });
});
AWS.mock('ELBv2', 'describeListeners', (_params, cb) => {
if (options.describeListenersExpected !== undefined) {
expect(_params).toEqual(options.describeListenersExpected);
}
cb(null, { Listeners: options.listeners });
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZC1iYWxhbmNlcnMudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxvYWQtYmFsYW5jZXJzLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxpQ0FBaUM7QUFDakMsMkRBQTJEO0FBRTNELG9DQUFvQztBQUNwQywrRUFBOE47QUFDOU4sK0NBQW1EO0FBRW5ELEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO0FBRXZDLE1BQU0sT0FBTyxHQUFHLElBQUksMEJBQWUsRUFBRSxDQUFDO0FBSXRDLFNBQVMsQ0FBQyxJQUFJLENBQUMsRUFBRTtJQUNmLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNkLElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsV0FBVyxFQUFFLEdBQUcsRUFBRTtJQUN6QixJQUFJLENBQUMsbUNBQW1DLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDbkQsTUFBTSxZQUFZLEdBQTZDLEVBQUUsQ0FBQztRQUNsRSxLQUFLLE1BQU0sV0FBVyxJQUFJLENBQUMsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQyxFQUFFLENBQUMsRUFBRTtZQUNqRixZQUFZLENBQUMsV0FBVyxDQUFDLEdBQUc7Z0JBQzFCLFdBQVcsRUFBRSxXQUFXO2dCQUN4QixJQUFJLEVBQUU7b0JBQ0osRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUU7aUJBQ3BDO2FBQ0YsQ0FBQztTQUNIO1FBQUEsQ0FBQztRQUVGLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDLE9BQW9DLEVBQUUsRUFBNkMsRUFBRSxFQUFFO1lBQ3hILE1BQU0sQ0FBQyxPQUFPLENBQUMs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