├── .eslintrc ├── .gcloudignore ├── .gitignore ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── serverless.yml └── tests ├── eventHelloWorld.test.js └── httpHelloWorld.test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 8 8 | }, 9 | "rules": { 10 | "no-await-in-loop": 2, 11 | "no-compare-neg-zero": 2, 12 | "no-cond-assign": 2, 13 | "no-console": 2, 14 | "no-constant-condition": 2, 15 | "no-control-regex": 2, 16 | "no-debugger": 2, 17 | "no-dupe-args": 2, 18 | "no-dupe-keys": 2, 19 | "no-duplicate-case": 2, 20 | "no-empty": 2, 21 | "no-empty-character-class": 2, 22 | "no-ex-assign": 2, 23 | "no-extra-boolean-cast": 2, 24 | "no-extra-parens": 2, 25 | "no-extra-semi" :2, 26 | "no-func-assign": 2, 27 | "no-inner-declarations": 2, 28 | "no-invalid-regexp": 2, 29 | "no-irregular-whitespace": 2, 30 | "no-obj-calls": 2, 31 | "no-prototype-builtins": 2, 32 | "no-regex-spaces": 2, 33 | "no-sparse-arrays": 2, 34 | "no-template-curly-in-string": 2, 35 | "no-unexpected-multiline": 2, 36 | "no-unreachable": 2, 37 | "no-unsafe-finally": 2, 38 | "no-unsafe-negation": 2, 39 | "use-isnan": 2, 40 | "valid-jsdoc": 2, 41 | "valid-typeof": 2, 42 | "accessor-pairs": 2, 43 | "array-callback-return": 2, 44 | "block-scoped-var": 2, 45 | "class-methods-use-this": 2, 46 | "complexity": [0, 10], 47 | "consistent-return": 2, 48 | "curly": 2, 49 | "default-case": 2, 50 | "dot-location": 2, 51 | "dot-notation": [2, {"allowPattern": "^(finally|catch)$"}], 52 | "eqeqeq": 2, 53 | "guard-for-in": 2, 54 | "no-alert": 2, 55 | "no-caller": 2, 56 | "no-case-declarations": 2, 57 | "no-div-regex": 2, 58 | "no-else-return": 2, 59 | "no-empty-function": 2, 60 | "no-empty-pattern": 2, 61 | "no-eq-null": 2, 62 | "no-eval": 2, 63 | "no-extend-native": 2, 64 | "no-extra-bind": 2, 65 | "no-extra-label": 2, 66 | "no-fallthrough": 2, 67 | "no-floating-decimal": 2, 68 | "no-global-assign": 2, 69 | "no-implicit-coercion": 2, 70 | "no-implicit-globals": 2, 71 | "no-implied-eval": 2, 72 | "no-invalid-this": 2, 73 | "no-iterator": 2, 74 | "no-labels": 2, 75 | "no-lone-blocks": 2, 76 | "no-loop-func": 2, 77 | "no-magic-numbers": 0, 78 | "no-multi-spaces": 2, 79 | "no-multi-str": 2, 80 | "no-new": 2, 81 | "no-new-func": 2, 82 | "no-new-wrappers": 2, 83 | "no-octal": 2, 84 | "no-octal-escape": 2, 85 | "no-param-reassign": 2, 86 | "no-proto": 2, 87 | "no-redeclare": 2, 88 | "no-restricted-properties": 2, 89 | "no-return-assign": 2, 90 | "no-return-await": 2, 91 | "no-script-url": 2, 92 | "no-self-assign": 2, 93 | "no-self-compare": 2, 94 | "no-sequences": 2, 95 | "no-throw-literal": 2, 96 | "no-unmodified-loop-condition": 2, 97 | "no-unused-expressions": 2, 98 | "no-unused-labels": 2, 99 | "no-useless-call": 2, 100 | "no-useless-concat": 2, 101 | "no-useless-escape": 2, 102 | "no-useless-return": 2, 103 | "no-void": 2, 104 | "no-warning-comments": 2, 105 | "no-with": 2, 106 | "prefer-promise-reject-errors": 2, 107 | "radix": 2, 108 | "require-await": 0, 109 | "vars-on-top": 2, 110 | "wrap-iife": 2, 111 | "yoda": 2, 112 | "strict": [2, "global"], 113 | "init-declarations": 0, 114 | "no-catch-shadow": 2, 115 | "no-delete-var": 2, 116 | "no-label-var": 2, 117 | "no-restricted-globals": 2, 118 | "no-shadow": 2, 119 | "no-shadow-restricted-names": 2, 120 | "no-undef": 2, 121 | "no-undef-init": 2, 122 | "no-undefined": 2, 123 | "no-unused-vars": 2, 124 | "no-use-before-define": 2, 125 | "callback-return": 2, 126 | "global-require": 2, 127 | "handle-callback-err": 2, 128 | "no-mixed-requires": [2, false], 129 | "no-new-require": 2, 130 | "no-path-concat": 2, 131 | "no-process-env": 2, 132 | "no-process-exit": 2, 133 | "no-restricted-modules": 2, 134 | "no-sync": 2, 135 | "array-bracket-spacing": 2, 136 | "block-spacing": 2, 137 | "brace-style": 2, 138 | "camelcase": 0, 139 | "capitalized-comments": 0, 140 | "comma-dangle": 2, 141 | "comma-spacing": 2, 142 | "comma-style": 2, 143 | "computed-property-spacing": 2, 144 | "consistent-this": 2, 145 | "eol-last": 2, 146 | "func-call-spacing": 2, 147 | "func-name-matching": 2, 148 | "func-names": 2, 149 | "func-style": [0, "expression"], 150 | "id-blacklist": 2, 151 | "id-length": 0, 152 | "id-match": 2, 153 | "indent": ["error", 2, { "SwitchCase": 1 }], 154 | "jsx-quotes": 2, 155 | "key-spacing": 2, 156 | "keyword-spacing": 2, 157 | "line-comment-position": 2, 158 | "linebreak-style": 2, 159 | "lines-around-comment": 2, 160 | "lines-around-directive": 2, 161 | "max-depth": 2, 162 | "max-len": 0, 163 | "max-lines": [2, {"max": 550}], 164 | "max-nested-callbacks": [2, { "max": 11 }], 165 | "max-params": 0, 166 | "max-statements": [0, 12, { "ignoreTopLevelFunctions": true }], 167 | "max-statements-per-line": 2, 168 | "multiline-ternary": 0, 169 | "new-cap": 0, 170 | "new-parens": 2, 171 | "newline-after-var": 2, 172 | "newline-before-return": 2, 173 | "newline-per-chained-call": 0, 174 | "no-array-constructor": 2, 175 | "no-bitwise": 2, 176 | "no-continue": 0, 177 | "no-inline-comments": 0, 178 | "no-lonely-if": 2, 179 | "no-mixed-spaces-and-tabs": 2, 180 | "no-multi-assign": 2, 181 | "no-multiple-empty-lines": 2, 182 | "no-negated-condition": 2, 183 | "no-nested-ternary": 2, 184 | "no-new-object": 2, 185 | "no-plusplus": 0, 186 | "no-restricted-syntax": 2, 187 | "no-tabs": 2, 188 | "no-ternary": 0, 189 | "no-trailing-spaces": 2, 190 | "no-underscore-dangle": 0, 191 | "no-unneeded-ternary": 2, 192 | "no-whitespace-before-property": 2, 193 | "nonblock-statement-body-position": 2, 194 | "object-curly-newline": 0, 195 | "object-curly-spacing": [2, "always"], 196 | "object-property-newline": 0, 197 | "one-var": 0, 198 | "one-var-declaration-per-line": 2, 199 | "operator-assignment": 2, 200 | "operator-linebreak": 2, 201 | "padded-blocks": 0, 202 | "quote-props": [2, "as-needed", { "keywords": true, "unnecessary": true }], 203 | "quotes": [2, "single"], 204 | "require-jsdoc": 0, 205 | "semi": [2, "always"], 206 | "semi-spacing": 2, 207 | "sort-keys": 0, 208 | "sort-vars": 2, 209 | "space-before-blocks": 2, 210 | "space-before-function-paren": 2, 211 | "space-in-parens": 2, 212 | "space-infix-ops": 2, 213 | "space-unary-ops": 2, 214 | "spaced-comment": 2, 215 | "template-tag-spacing": 2, 216 | "unicode-bom": 2, 217 | "wrap-regex": 2, 218 | "arrow-body-style": 0, 219 | "arrow-parens": 2, 220 | "arrow-spacing": 2, 221 | "constructor-super": 2, 222 | "generator-star-spacing": 2, 223 | "no-class-assign": 2, 224 | "no-confusing-arrow": 2, 225 | "no-const-assign": 2, 226 | "no-dupe-class-members": 2, 227 | "no-duplicate-imports": 2, 228 | "no-new-symbol": 2, 229 | "no-restricted-imports": 2, 230 | "no-this-before-super": 2, 231 | "no-useless-computed-key": 2, 232 | "no-useless-constructor": 2, 233 | "no-useless-rename": 2, 234 | "no-var": 2, 235 | "object-shorthand": 2, 236 | "prefer-arrow-callback": 2, 237 | "prefer-const": 2, 238 | "prefer-destructuring": 0, 239 | "prefer-numeric-literals": 2, 240 | "prefer-rest-params": 2, 241 | "prefer-spread": 2, 242 | "prefer-template": 2, 243 | "require-yield": 2, 244 | "rest-spread-spacing": 2, 245 | "sort-import": 0, 246 | "symbol-description": 2, 247 | "template-curly-spacing": 2, 248 | "yield-star-spacing": 2 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | tests/ 14 | .git 15 | .gitignore 16 | .eslintrc 17 | package-lock.json 18 | README.md 19 | 20 | node_modules 21 | #!include:.gitignore 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.tmp 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Dependency directories 19 | node_modules 20 | jspm_packages 21 | 22 | # Compiled directory 23 | dist 24 | 25 | # Optional npm cache directory 26 | .npm 27 | .npmrc 28 | 29 | # Optional REPL history 30 | .node_repl_history 31 | 32 | # OS X junk 33 | .DS_Store 34 | 35 | # IntelliJ junk 36 | .idea 37 | 38 | # VSCode junk 39 | .vscode 40 | 41 | # VIM 42 | *.swp 43 | *.swo 44 | 45 | # Espresso 46 | *.esproj 47 | 48 | coverage.html 49 | coverage 50 | .serverless 51 | 52 | chassis/submodules/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-function-template 2 | 3 | ## How to setup, write, test and deploy your own serverless functions 4 | 5 | ### Prepare your local machine 6 | 7 | #### Step 1 8 | 9 | ##### Google projects 10 | We have many GCP projects. Here is the list of them. Developers should use playground for all of their testing. You should not create new projects. Prod, QA and Dev have deployment pipelines to deploy cloud functions. 11 | 12 | Playground (strong-keyword-184513) 13 | 14 | experimental environment for all service proof of concepts 15 | 16 | Platform-Dev (platform-dev-788014) 17 | 18 | development environment for automated deployments and initial testing 19 | 20 | Platform-QA (platform-qa-953425) 21 | 22 | Qa environment for integration tests 23 | 24 | Platform-Prod (platform-prod-399563) 25 | 26 | Production environment 27 | 28 | #### Step 2 29 | 30 | ##### Install gcloud cli 31 | 32 | https://cloud.google.com/sdk/downloads 33 | 34 | Once the tar.gz is installed initialize your gcloud... 35 | 36 | ```bash 37 | gcloud init 38 | ``` 39 | 40 | Choose account to perform operations for this configuration 41 | 42 | Select your O’Reilly domain name 43 | 44 | Pick cloud project to use 45 | 46 | Select project for playground (strong-keyword-184513) 47 | 48 | Do you want to configure a default Compute Region and Zone? 49 | 50 | Answer Yes 51 | 52 | Set region zone to (us-central1-a) 53 | 54 | ##### Update components and enable beta 55 | 56 | ```bash 57 | gcloud components update && gcloud components install beta 58 | ``` 59 | 60 | Init again to edit these settings or add other configurations 61 | 62 | Also you can override your project and zone with the --project and --zone options 63 | 64 | Example to list functions from non-default project and zone 65 | gcloud beta functions list --project platform-dev-788014 --zone us-west1-b 66 | 67 | #### Step 3 68 | 69 | ##### Setup node.js 70 | 71 | Install nvm 72 | 73 | https://github.com/creationix/nvm#installation 74 | 75 | Google Cloud Functions only support node.js v6.11.5 as of 3/12/2018 76 | 77 | Install node.js v6.11.5 78 | 79 | ```bash 80 | nvm install 6.11.5 81 | ``` 82 | 83 | ```bash 84 | nvm use 6.11.5 85 | ``` 86 | 87 | #### Step 4 88 | 89 | ##### Install serverless framework 90 | 91 | https://serverless.com/ 92 | 93 | Install with npm 94 | 95 | ```bash 96 | npm install serverless -g 97 | ``` 98 | 99 | This will install serverless into the nvm env, if you wish to have it available in your standard path 100 | 101 | ```bash 102 | ln -fs /Users/{user_name}/.nvm/versions/node/v{node_version}/bin/serverless /usr/local/bin 103 | ``` 104 | 105 | Run the tool 106 | 107 | serverless --version 108 | 109 | Serverless authentication 110 | 111 | A keyfile.json is necessary to deploy serverless functions in our playground project 112 | 113 | Go to the API Credentials page in GCP 114 | 115 | https://console.cloud.google.com/apis/credentials?project=strong-keyword-184513&organizationId=887495730325 116 | 117 | Select Create Credentials 118 | 119 | Select Service Account Key 120 | 121 | Under Service Account select the “serverless” service account 122 | 123 | Ensure JSON is selected 124 | 125 | Press Create 126 | 127 | A json file will download, move it to ~/.gcloud/keyfile.json 128 | 129 | #### Step 5 130 | 131 | ##### Install cloud-function-emulator 132 | 133 | https://cloud.google.com/functions/docs/emulator 134 | 135 | Install with npm 136 | 137 | ```bash 138 | npm install -g @google-cloud/functions-emulator 139 | ``` 140 | 141 | Run the tool (some shells will have a conflict with the default functions, use functions-emulator) 142 | 143 | ```bash 144 | functions-emulator --help 145 | ``` 146 | 147 | ### Creating a serverless project 148 | 149 | When starting a new serverless project first consider what feature or service your functions will serve. Check to see if a serverless project already exists for that feature or service. If not create a new project using the ORM project template. 150 | 151 | Create a function from the ORM template 152 | Pull down the ORM cloud function template and give it a unique named directory 153 | serverless install --url https://github.com/oreillymedia/cloud-function-template --name {name_of_your_serverless_project} 154 | 155 | You now have the ORM default template for google cloud functions installed in your directory. This includes a .gitignore, .eslintrc, .gcloudignore, README.md, index.js, package.json, serverless.yml, as well as a tests and functions directories. 156 | 157 | Update package.json including name, version, description, tests, and dependencies 158 | 159 | Update serverless.yml including service name, function name(s) and provider project fields. 160 | 161 | Update README.md to describe the service your functions will serve. 162 | 163 | Once set git init and create a repo in git oreillymedia organization and push your initial commit. 164 | 165 | ### Developing a function 166 | 167 | During the development lifecycle we are using node 6.11.5 and es6 as standards for writing. ESlint and jest are standards for linting and testing. 168 | 169 | Notice in the project template there is a a single index.js module, this stores the set of functions that serve a similar feature or service. By default there are 2 sample functions httpHelloWorld and eventHelloWorld. http* is triggered by a web request while event is triggered by pubsub events. 170 | 171 | Cloud functions are limited to deploying functions from one module so when writing if multiple function deployments will reside in one project they must be exported from the main index.js. AWS Lambda and Kubeless do not have this limitation and multiple js modules can be deployed simultaneously. This is a current limitation with google cloud functions only 172 | 173 | We can use google-cloud-emulator to deploy and test a function locally to ensure it is working as expected. Serverless offers this with their invoke local method but it does not support google cloud functions… yet. 174 | 175 | Start the emulator 176 | 177 | ```bash 178 | functions-emulator start 179 | ``` 180 | 181 | Deploy the exported function name with an http trigger (as opposed to pub/sub event trigger) 182 | 183 | ```bash 184 | functions-emulator deploy {my_function} --trigger-http 185 | ``` 186 | 187 | Call the deployed function 188 | 189 | ```bash 190 | functions-emulator call {my_function} 191 | ``` 192 | 193 | Inspect the function from a browser 194 | 195 | ```bash 196 | functions-emulator inspect {my_function} 197 | ``` 198 | 199 | View logs to get the Debugger url 200 | 201 | ```bash 202 | functions_emulator logs read 203 | ``` 204 | 205 | You will see output like... 206 | Debugger listening on ws://127.0.0.1:9229/fb9906cd-a063-42ba-bcf2-af04fc2e2655 207 | 208 | Copy and paste the url to your browser and call the function. You can now see vars, step through code and inspect your function to the fullest. 209 | 210 | Turn off inspection 211 | 212 | ```bash 213 | functions_emulator reset {my_function} 214 | ``` 215 | 216 | Stop the emulator 217 | 218 | ```bash 219 | functions-emulator stop 220 | ``` 221 | 222 | ### Deploying a function 223 | 224 | Deploy your function 225 | As you prepare to deploy functions be sure you are only deploying to playground from your local computer. Dev, QA and Prod will have deployments based on git hooks. 226 | 227 | Serverless framework can deploy functions to gcp, aws, kubeless, and many more. With a simple change of the serverless.yml this function can be running in multiple clouds. 228 | 229 | First package your functions to ensure they compile and package properly, find the results in the .serverless directory 230 | 231 | ```bash 232 | serverless package 233 | ``` 234 | 235 | Once your function is tested with functions-emulator and receive the proper output you can push it to the serverless provider set in your serverless.yml 236 | 237 | ```bash 238 | serverless deploy 239 | ``` 240 | 241 | Once deployed you can now get back logs from your function 242 | 243 | ```bash 244 | serverless logs -f my_function 245 | ``` 246 | 247 | Serverless Plugins: 248 | Serverless framework allows us to use, or create, plugins to facilitate certain tasks. 249 | 250 | By default our cloud-function-template uses serverless-google-cloudfunctions, but there are others that may be beneficial. To use a plugin they must be installed in the package.json AND referenced in the serverless.yml file plugins. 251 | 252 | List available plugins 253 | 254 | ```bash 255 | serverless plugins list 256 | ``` 257 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * @function httpHelloWorld 5 | * @param {object} request object received from the caller 6 | * @param {object} response object created in response to the request 7 | */ 8 | exports.httpHelloWorld = (request, response) => { 9 | response.status(200).send('Hello World!'); 10 | }; 11 | 12 | /* 13 | * 14 | * @function eventHelloWorld 15 | * @param { Object } event read event from configured pubsub topic 16 | * @param { Function } callback function 17 | */ 18 | exports.eventHelloWorld = (event, callback) => { 19 | callback(`Hello ${event.data.name || 'World'}!`); 20 | }; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-nodejs-chassis", 3 | "version": "0.0.1", 4 | "description": "serverless nodejs chassis", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rm -rf node_modules coverage", 8 | "lint": "eslint ./index.js", 9 | "lintFix": "eslint ./index.js --fix", 10 | "serverless": "npm install -g @google-cloud/functions-emulator serverless firebase-tools", 11 | "start": "node index.js", 12 | "test": "NODE_ENV=test jest --coverage .", 13 | "update": "./node_modules/npm-check-updates/bin/ncu -a" 14 | }, 15 | "repository": { 16 | "url": "git+https://github.com/oreillymedia/cloud-function-template.git", 17 | "type": "git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/oreillymedia/cloud-function-template/issues" 23 | }, 24 | "homepage": "https://github.com/oreillymedia/cloud-function-template#readme", 25 | "dependencies": { 26 | "serverless-google-cloudfunctions": "^1.1.1" 27 | }, 28 | "devDependencies": { 29 | "eslint": "^4.19.0", 30 | "jest": "^22.4.2", 31 | "npm-check-updates": "^2.12.1" 32 | } 33 | } -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: gcf-nodejs # NOTE: Don't put the word "google" in here 2 | 3 | provider: 4 | name: google 5 | runtime: nodejs 6 | project: strong-keyword-184513 7 | # the path to the credentials file needs to be absolute 8 | credentials: ~/.gcloud/keyfile.json 9 | 10 | plugins: 11 | - serverless-google-cloudfunctions 12 | 13 | # needs more granular excluding in production as only the serverless provider npm 14 | # package should be excluded (and not the whole node_modules directory) 15 | package: 16 | exclude: 17 | - node_modules/** 18 | - .gitignore 19 | - .git/** 20 | - .eslintrc 21 | - .gcloudignore 22 | - .serverless 23 | - .vscode 24 | - coverage 25 | - Jenkinsfile 26 | - README.md 27 | - "**/tests" 28 | 29 | functions: 30 | http: 31 | handler: httpHelloWorld 32 | events: 33 | - http: path 34 | event: 35 | handler: eventHelloWorld 36 | events: 37 | - event: 38 | eventType: providers/cloud.pubsub/eventTypes/topic.publish 39 | resource: projects/strong-keyword-184513/topics/cloud-function-template 40 | 41 | # NOTE: the following uses an "event" event (pubSub event in this case). 42 | # Please create the corresponding resources in the Google Cloud 43 | # before deploying this service through Serverless 44 | 45 | #second: 46 | # handler: pubSub 47 | # events: 48 | # - event: 49 | # eventType: providers/cloud.pubsub/eventTypes/topic.publish 50 | # resource: projects/*/topics/my-topic 51 | 52 | # you can define resources, templates etc. the same way you would in a 53 | # Google Cloud deployment configuration 54 | #resources: 55 | # resources: 56 | # - type: storage.v1.bucket 57 | # name: my-serverless-service-bucket 58 | # imports: 59 | # - path: my_template.jinja 60 | -------------------------------------------------------------------------------- /tests/eventHelloWorld.test.js: -------------------------------------------------------------------------------- 1 | const cloudFunction = require('../'); 2 | 3 | describe('#eventHelloWorld.handler web request no data', () => { 4 | test('should return Hello World! when event.data.name is null', (done) => { 5 | const mockEvent = { 6 | data: {} 7 | }; 8 | 9 | function callback(data) { 10 | expect(data).toBe('Hello World!'); 11 | done(); 12 | }; 13 | 14 | cloudFunction.eventHelloWorld(mockEvent, callback); 15 | }) 16 | }) 17 | 18 | describe('#eventHelloWorld.handler pubsub request with data', () => { 19 | test('should return Hello Nurse! when event.data.name is Nurse', (done) => { 20 | const mockEvent = { 21 | data: { 22 | name: 'Nurse' 23 | } 24 | }; 25 | 26 | function callback(data) { 27 | expect(data).toBe('Hello Nurse!'); 28 | done(); 29 | }; 30 | 31 | cloudFunction.eventHelloWorld(mockEvent, callback); 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/httpHelloWorld.test.js: -------------------------------------------------------------------------------- 1 | const cloudFunction = require('../'); 2 | 3 | describe('#httpHelloWorld.handler web request', () => { 4 | test('should return 200 status and send Hello World!', (done) => { 5 | const mockRequest = { 6 | method: 'GET' 7 | }; 8 | 9 | const mockResponse = { 10 | status: (code) => { 11 | expect(code).toEqual(200); 12 | return { 13 | send: jest.fn((label) => { 14 | expect(label).toBe('Hello World!'); 15 | done(); 16 | }) 17 | } 18 | } 19 | }; 20 | 21 | cloudFunction.httpHelloWorld(mockRequest, mockResponse); 22 | }) 23 | }) 24 | --------------------------------------------------------------------------------