Skip to content

Commit fc024c3

Browse files
committed
test: add load tests
1 parent d555fda commit fc024c3

File tree

5 files changed

+243
-82
lines changed

5 files changed

+243
-82
lines changed

packages/bot-framework/package-lock.json

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/bot-framework/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"gts": "^4.0.0",
5858
"lru-cache": "^7.14.0",
5959
"mocha": "^10.0.0",
60+
"mocked-env": "^1.3.5",
6061
"nock": "^13.2.9",
6162
"sinon": "^15.0.0",
6263
"smee-client": "^1.2.3",

packages/bot-framework/src/bootstrapper.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ interface BootstrapperBaseOptions {
7272
installationHandler?: InstallationHandler;
7373
}
7474

75-
interface BootstrapperLoadOptions extends BootstrapperBaseOptions {
75+
export interface BootstrapperLoadOptions extends BootstrapperBaseOptions {
7676
projectId?: string;
7777
botName?: string;
7878
secretLoader?: SecretLoader;
@@ -102,6 +102,10 @@ interface EnqueueTaskParams {
102102
delayInSeconds?: number;
103103
}
104104

105+
export interface BootstrapperFactory {
106+
build(options: BootstrapperLoadOptions): Promise<Bootstrapper>;
107+
}
108+
105109
export class Bootstrapper {
106110
private taskEnqueuer: TaskEnqueuer;
107111
private projectId: string;

packages/bot-framework/test/bootstrapper.ts

Lines changed: 68 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,8 @@ import sinon from 'sinon';
1717
import nock from 'nock';
1818

1919
import {Webhooks} from '@octokit/webhooks';
20-
import * as express from 'express';
21-
import fs from 'fs';
2220
import {Bootstrapper, HandlerFunction} from '../src/bootstrapper';
2321
import {NoopTaskEnqueuer} from '../src/background/task-enqueuer';
24-
import {
25-
InstallationHandler,
26-
AppInstallation,
27-
InstalledRepository,
28-
} from '../src/installations';
2922
import {GCFLogger} from '../src';
3023
// eslint-disable-next-line node/no-extraneous-import
3124
import {RequestError} from '@octokit/request-error';
@@ -34,89 +27,83 @@ import {GraphqlResponseError} from '@octokit/graphql';
3427
import * as errorLoggingModule from '../src/logging/error-logging';
3528
import AggregateError from 'aggregate-error';
3629
import {ServiceUnavailable} from '../src/errors';
30+
import {
31+
mockRequest,
32+
mockResponse,
33+
mockRequestFromFixture,
34+
MockInstallationHandler,
35+
MockSecretLoader,
36+
} from './helpers';
37+
import {RestoreFn} from 'mocked-env';
38+
import mockedEnv from 'mocked-env';
39+
import assert from 'assert';
40+
import { GoogleSecretLoader } from '../src/secrets/google-secret-loader';
3741

3842
nock.disableNetConnect();
39-
4043
const sandbox = sinon.createSandbox();
4144

42-
function mockRequest(body: object, headers: Record<string, any>) {
43-
const request = Object.create(
44-
Object.getPrototypeOf(express.request),
45-
Object.getOwnPropertyDescriptors(express.request)
46-
);
47-
request.rawBody = Buffer.from(JSON.stringify(body));
48-
request.body = body;
49-
request.headers = headers;
50-
return request;
51-
}
52-
function mockRequestFromFixture(fixture: string, headers: Record<string, any>) {
53-
const request = Object.create(
54-
Object.getPrototypeOf(express.request),
55-
Object.getOwnPropertyDescriptors(express.request)
56-
);
57-
const rawBody = fs.readFileSync(fixture);
58-
request.rawBody = rawBody;
59-
request.body = JSON.parse(rawBody.toString('utf-8'));
60-
request.headers = headers;
61-
return request;
62-
}
63-
64-
function mockResponse() {
65-
const response = {} as any;
66-
response.status = sandbox.stub().returns(response);
67-
response.json = sandbox.stub().returns(response);
68-
response.send = sandbox.stub().returns(response);
69-
return response;
70-
}
71-
72-
class MockInstallationHandler implements InstallationHandler {
73-
private installations: AppInstallation[] = [];
74-
private installedRepositoriesByInstallation: Map<
75-
number,
76-
InstalledRepository[]
77-
> = new Map();
78-
79-
reset() {
80-
this.installations = [];
81-
this.installedRepositoriesByInstallation = new Map();
82-
}
83-
84-
setInstallations(installations: AppInstallation[]) {
85-
this.installations = installations;
86-
}
87-
88-
setInstalledRepositories(
89-
installationId: number,
90-
InstalledRepositories: InstalledRepository[]
91-
) {
92-
this.installedRepositoriesByInstallation.set(
93-
installationId,
94-
InstalledRepositories
95-
);
96-
}
97-
98-
async *eachInstallation(): AsyncGenerator<AppInstallation, void, void> {
99-
for (const installation of this.installations) {
100-
yield installation;
101-
}
102-
}
103-
async *eachInstalledRepository(
104-
installationId: number
105-
): AsyncGenerator<InstalledRepository, void, void> {
106-
const installedRepositories =
107-
this.installedRepositoriesByInstallation.get(installationId) || [];
108-
for (const repo of installedRepositories) {
109-
yield repo;
110-
}
111-
}
112-
}
113-
11445
describe('Bootstrapper', () => {
46+
let restoreEnv: RestoreFn | null;
11547
afterEach(() => {
11648
sandbox.restore();
49+
if (restoreEnv) {
50+
restoreEnv();
51+
restoreEnv = null;
52+
}
11753
});
11854

119-
describe('load', () => {});
55+
describe('load', () => {
56+
it('requires a project id', async () => {
57+
await assert.rejects(async () => {
58+
await Bootstrapper.load({});
59+
}, e => {
60+
return (e as Error).message.includes('PROJECT_ID');
61+
});
62+
});
63+
it('requires a bot name', async () => {
64+
await assert.rejects(async () => {
65+
await Bootstrapper.load({
66+
projectId: 'my-project',
67+
});
68+
}, e => {
69+
return (e as Error).message.includes('GCF_SHORT_FUNCTION_NAME');
70+
});
71+
});
72+
it('requires a location', async () => {
73+
await assert.rejects(async () => {
74+
await Bootstrapper.load({
75+
projectId: 'my-project',
76+
botName: 'my-bot-name',
77+
});
78+
}, e => {
79+
return (e as Error).message.includes('GCF_LOCATION');
80+
});
81+
});
82+
it('detects from env var', async () => {
83+
restoreEnv = mockedEnv({
84+
GCF_SHORT_FUNCTION_NAME: 'my-bot-name',
85+
GCF_LOCATION: 'my-location',
86+
PROJECT_ID: 'my-project',
87+
})
88+
const bootstrapper = await Bootstrapper.load({
89+
secretLoader: new MockSecretLoader(),
90+
});
91+
assert.ok(bootstrapper);
92+
});
93+
it('loads secrets from Secret Manager', async () => {
94+
sandbox.stub(GoogleSecretLoader.prototype, 'load').resolves({
95+
privateKey: 'my-private-key',
96+
webhookSecret: 'my-webhook-secret',
97+
appId: '123456',
98+
})
99+
const bootstrapper = await Bootstrapper.load({
100+
projectId: 'my-project',
101+
botName: 'my-bot-name',
102+
location: 'my-location',
103+
});
104+
assert.ok(bootstrapper);
105+
});
106+
});
120107

121108
describe('handler', () => {
122109
describe('webhooks', async () => {

0 commit comments

Comments
 (0)