@@ -17,15 +17,8 @@ import sinon from 'sinon';
17
17
import nock from 'nock' ;
18
18
19
19
import { Webhooks } from '@octokit/webhooks' ;
20
- import * as express from 'express' ;
21
- import fs from 'fs' ;
22
20
import { Bootstrapper , HandlerFunction } from '../src/bootstrapper' ;
23
21
import { NoopTaskEnqueuer } from '../src/background/task-enqueuer' ;
24
- import {
25
- InstallationHandler ,
26
- AppInstallation ,
27
- InstalledRepository ,
28
- } from '../src/installations' ;
29
22
import { GCFLogger } from '../src' ;
30
23
// eslint-disable-next-line node/no-extraneous-import
31
24
import { RequestError } from '@octokit/request-error' ;
@@ -34,89 +27,83 @@ import {GraphqlResponseError} from '@octokit/graphql';
34
27
import * as errorLoggingModule from '../src/logging/error-logging' ;
35
28
import AggregateError from 'aggregate-error' ;
36
29
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' ;
37
41
38
42
nock . disableNetConnect ( ) ;
39
-
40
43
const sandbox = sinon . createSandbox ( ) ;
41
44
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
-
114
45
describe ( 'Bootstrapper' , ( ) => {
46
+ let restoreEnv : RestoreFn | null ;
115
47
afterEach ( ( ) => {
116
48
sandbox . restore ( ) ;
49
+ if ( restoreEnv ) {
50
+ restoreEnv ( ) ;
51
+ restoreEnv = null ;
52
+ }
117
53
} ) ;
118
54
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
+ } ) ;
120
107
121
108
describe ( 'handler' , ( ) => {
122
109
describe ( 'webhooks' , async ( ) => {
0 commit comments