From 16609f33101ef4f6254d7e2fbd7dbd5c1f911db9 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Wed, 9 Apr 2025 23:46:19 +0000 Subject: [PATCH 1/9] chore(cloudrun): initialize package.json sith project metadata and dependencies --- run/service-auth/package.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 run/service-auth/package.json diff --git a/run/service-auth/package.json b/run/service-auth/package.json new file mode 100644 index 0000000000..95e3991844 --- /dev/null +++ b/run/service-auth/package.json @@ -0,0 +1,27 @@ +{ + "name": "service-auth", + "description": "Node.js samples for authenticated service-to-service communication", + "version": "0.0.1", + "private": true, + "license": "Apache-2.0", + "author": "Google LLC", + "engines": { + "node": "20.x" + }, + "scripts": { + "start": "node index.js", + "deploy": "gcloud run deploy service-auth --source .", + "unit-test": "c8 mocha -p -j 2 test/ --timeout=10000 --exit", + "test": "npm run unit-test" + }, + "dependencies": { + "express": "^4.17.1", + "google-auth-library": "^9.0.0" + }, + "devDependencies": { + "c8": "^10.0.0", + "chai": "^4.5.0", + "mocha": "^10.0.0", + "sinon": "^18.0.0" + } +} \ No newline at end of file From 7270cd08c41df9b965ef6b46e3ac2f4b36d193b4 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Thu, 10 Apr 2025 15:00:33 +0000 Subject: [PATCH 2/9] feat(cloudrun): add sample to receive and validate ID token from HTTP request --- run/service-auth/receive.js | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 run/service-auth/receive.js diff --git a/run/service-auth/receive.js b/run/service-auth/receive.js new file mode 100644 index 0000000000..5049a41b9f --- /dev/null +++ b/run/service-auth/receive.js @@ -0,0 +1,60 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(req) { + // [START auth_validate_and_decode_bearer_token_on_express] + // [START cloudrun_service_to_service_receive] + const {OAuth2Client} = require('google-auth-library'); + + const client = new OAuth2Client(); + + // Inner function that parses and verifies the token. + async function receiveRequestAndParseAuthHeader(request) { + const authHeader = request.headers.authorization; + if (authHeader) { + // Split the auth type and token value from the Authorization header. + const [type, token] = authHeader.split(' '); + + if (type.toLowerCase() === 'bearer') { + // More info on verifyIdToken: + // https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/verifyIdToken-iap.js + try { + const ticket = await client.verifyIdToken({ + idToken: token, + audience: process.env.CLIENT_ID, + }); + const payload = ticket.getPayload(); + console.log(`Hello, ${payload.email}!\n`); + return; + } catch (err) { + console.log(`Invalid token: ${err.message}\n`); + return; + } + } else { + console.log(`Unhandled header format(${type}).\n`); + return; + } + } + + console.log('Hello, anonymous user.\n'); + } + + await receiveRequestAndParseAuthHeader(req); +} +// [END cloudrun_service_to_service_receive] +// [END auth_validate_and_decode_bearer_token_on_express] + +module.exports = {main}; From 70985f8194bd6edaf36c2acb7753417d72ff9bf5 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Thu, 10 Apr 2025 15:04:34 +0000 Subject: [PATCH 3/9] feat(cloudrun): add Express server to invoke sample logic over HTTP --- run/service-auth/index.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 run/service-auth/index.js diff --git a/run/service-auth/index.js b/run/service-auth/index.js new file mode 100644 index 0000000000..801327430d --- /dev/null +++ b/run/service-auth/index.js @@ -0,0 +1,38 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const express = require('express'); +const {receiveRequestAndParseAuthHeader} = require('./receive'); + +const app = express(); + +app.get('/', async (req, res) => { + try { + const response = await receiveRequestAndParseAuthHeader(req); + + const status = response.includes('Hello') ? 200 : 401; + res.status(status).send(response); + } catch (e) { + res.status(401).send(`Error verifying ID token: ${e.message}`); + } +}); + +const PORT = process.env.PORT || 8080; +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); + +module.exports = app; From 6e4451d8fd9d4431b4c2f12a82c6ecc61059cfb8 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Thu, 10 Apr 2025 15:07:33 +0000 Subject: [PATCH 4/9] test(cloudrun): add unit test for token parsing and validation sample --- run/service-auth/test/receive.test.js | 82 +++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 run/service-auth/test/receive.test.js diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js new file mode 100644 index 0000000000..c6870d54f2 --- /dev/null +++ b/run/service-auth/test/receive.test.js @@ -0,0 +1,82 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {expect} = require('chai'); +const sinon = require('sinon'); +const {OAuth2Client} = require('google-auth-library'); +const {main} = require('../receive'); + +describe('receiveRequestAndParseAuthHeader sample', () => { + let verifyStub, consoleStub; + + beforeEach(() => { + verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); + consoleStub = sinon.stub(console, 'log'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should log greeting if token is valid', async () => { + const mockReq = { + headers: { + authorization: 'Bearer valid-token', + }, + }; + + verifyStub.resolves({ + getPayload: () => ({email: 'test@example.com'}), + }); + + await main(mockReq); + expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; + }); + + it('should log error message if token is invalid', async () => { + const mockReq = { + headers: { + authorization: 'Bearer invalid-token', + }, + }; + + verifyStub.rejects(new Error('invalid')); + + await main(mockReq); + expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; + }); + + it('should log anonymous message if no auth header', async () => { + const mockReq = { + headers: {}, + }; + + await main(mockReq); + expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; + }); + + it('should log unhandled format message if auth is not bearer', async () => { + const mockReq = { + headers: { + authorization: 'Basic something', + }, + }; + + await main(mockReq); + expect(consoleStub.calledWith('Unhandled header format (Basic).\n')).to.be + .true; + }); +}); From d7df2f26156dae460b54d78a972854781ce1e3b5 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Thu, 10 Apr 2025 15:54:27 +0000 Subject: [PATCH 5/9] test(cloudrun): correct unit tests --- run/service-auth/test/receive.test.js | 93 ++++++++++++--------------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js index c6870d54f2..bf56959141 100644 --- a/run/service-auth/test/receive.test.js +++ b/run/service-auth/test/receive.test.js @@ -14,69 +14,60 @@ 'use strict'; -const {expect} = require('chai'); +const { expect } = require('chai'); const sinon = require('sinon'); -const {OAuth2Client} = require('google-auth-library'); -const {main} = require('../receive'); +const { OAuth2Client } = require('google-auth-library'); +const { main } = require('../receive'); -describe('receiveRequestAndParseAuthHeader sample', () => { - let verifyStub, consoleStub; - - beforeEach(() => { - verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); - consoleStub = sinon.stub(console, 'log'); - }); +const TEST_VALID_TOKEN = 'test-valid-token'; +const TEST_INVALID_TOKEN = 'test-invalid-token'; - afterEach(() => { - sinon.restore(); - }); +describe('receiveRequestAndParseAuthHeader sample', () => { + let verifyStub, consoleStub; - it('should log greeting if token is valid', async () => { - const mockReq = { - headers: { - authorization: 'Bearer valid-token', - }, - }; + beforeEach(() => { + verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); + consoleStub = sinon.stub(console, 'log'); + }); - verifyStub.resolves({ - getPayload: () => ({email: 'test@example.com'}), + afterEach(() => { + sinon.restore(); }); - await main(mockReq); - expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; - }); + it('should log greeting if token is valid', async () => { + const mockReq = { + headers: { + authorization: `Bearer ${TEST_VALID_TOKEN}`, + }, + }; - it('should log error message if token is invalid', async () => { - const mockReq = { - headers: { - authorization: 'Bearer invalid-token', - }, - }; + verifyStub.resolves({ + getPayload: () => ({ email: 'test@example.com' }), + }); - verifyStub.rejects(new Error('invalid')); + await main(mockReq); + expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; + }); - await main(mockReq); - expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; - }); + it('should log error message if token is invalid', async () => { + const mockReq = { + headers: { + authorization: `Bearer ${TEST_INVALID_TOKEN}`, + }, + }; - it('should log anonymous message if no auth header', async () => { - const mockReq = { - headers: {}, - }; + verifyStub.rejects(new Error('invalid')); - await main(mockReq); - expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; - }); + await main(mockReq); + expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; + }); - it('should log unhandled format message if auth is not bearer', async () => { - const mockReq = { - headers: { - authorization: 'Basic something', - }, - }; + it('should log anonymous message if no auth header', async () => { + const mockReq = { + headers: {}, + }; - await main(mockReq); - expect(consoleStub.calledWith('Unhandled header format (Basic).\n')).to.be - .true; - }); + await main(mockReq); + expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; + }); }); From 214df19890659759cf83a8ebada7751de53210ea Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Thu, 10 Apr 2025 15:56:47 +0000 Subject: [PATCH 6/9] fix(cloudrun): correct linting issues in unit tests --- run/service-auth/test/receive.test.js | 80 +++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js index bf56959141..0a8e3bc97d 100644 --- a/run/service-auth/test/receive.test.js +++ b/run/service-auth/test/receive.test.js @@ -14,60 +14,60 @@ 'use strict'; -const { expect } = require('chai'); +const {expect} = require('chai'); const sinon = require('sinon'); -const { OAuth2Client } = require('google-auth-library'); -const { main } = require('../receive'); +const {OAuth2Client} = require('google-auth-library'); +const {main} = require('../receive'); const TEST_VALID_TOKEN = 'test-valid-token'; const TEST_INVALID_TOKEN = 'test-invalid-token'; describe('receiveRequestAndParseAuthHeader sample', () => { - let verifyStub, consoleStub; + let verifyStub, consoleStub; - beforeEach(() => { - verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); - consoleStub = sinon.stub(console, 'log'); - }); - - afterEach(() => { - sinon.restore(); - }); + beforeEach(() => { + verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); + consoleStub = sinon.stub(console, 'log'); + }); - it('should log greeting if token is valid', async () => { - const mockReq = { - headers: { - authorization: `Bearer ${TEST_VALID_TOKEN}`, - }, - }; + afterEach(() => { + sinon.restore(); + }); - verifyStub.resolves({ - getPayload: () => ({ email: 'test@example.com' }), - }); + it('should log greeting if token is valid', async () => { + const mockReq = { + headers: { + authorization: `Bearer ${TEST_VALID_TOKEN}`, + }, + }; - await main(mockReq); - expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; + verifyStub.resolves({ + getPayload: () => ({email: 'test@example.com'}), }); - it('should log error message if token is invalid', async () => { - const mockReq = { - headers: { - authorization: `Bearer ${TEST_INVALID_TOKEN}`, - }, - }; + await main(mockReq); + expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; + }); - verifyStub.rejects(new Error('invalid')); + it('should log error message if token is invalid', async () => { + const mockReq = { + headers: { + authorization: `Bearer ${TEST_INVALID_TOKEN}`, + }, + }; - await main(mockReq); - expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; - }); + verifyStub.rejects(new Error('invalid')); - it('should log anonymous message if no auth header', async () => { - const mockReq = { - headers: {}, - }; + await main(mockReq); + expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; + }); - await main(mockReq); - expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; - }); + it('should log anonymous message if no auth header', async () => { + const mockReq = { + headers: {}, + }; + + await main(mockReq); + expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; + }); }); From a6ac296326726aed4a1dcf621de2e150c871f868 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Mon, 14 Apr 2025 16:42:01 +0000 Subject: [PATCH 7/9] test(integration): add end-to-end Cloud Run integration tests for service-to-service auth Includes the following: - Deploys the sample to Cloud Run before running tests - Retrieves deployed service URL dynamically - Generates a valid ID token using gcloud CLI - Sends real authenticated requests to the service - Asserts behavior for valid, invalid, and missing tokens - Cleans up Cloud Run service after tests complete - Replaces previous unit tests with full integration coverage Also updates package.json to include axios and uuid as devDependencies. --- run/service-auth/package.json | 6 +- run/service-auth/test/receive.test.js | 100 ++++++++++++++++---------- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/run/service-auth/package.json b/run/service-auth/package.json index 95e3991844..07bb8f7973 100644 --- a/run/service-auth/package.json +++ b/run/service-auth/package.json @@ -19,9 +19,11 @@ "google-auth-library": "^9.0.0" }, "devDependencies": { + "axios": "^1.8.4", "c8": "^10.0.0", "chai": "^4.5.0", "mocha": "^10.0.0", - "sinon": "^18.0.0" + "sinon": "^18.0.0", + "uuid": "^11.1.0" } -} \ No newline at end of file +} diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js index 0a8e3bc97d..0f4126c5d2 100644 --- a/run/service-auth/test/receive.test.js +++ b/run/service-auth/test/receive.test.js @@ -15,59 +15,81 @@ 'use strict'; const {expect} = require('chai'); -const sinon = require('sinon'); -const {OAuth2Client} = require('google-auth-library'); -const {main} = require('../receive'); +const axios = require('axios'); +const {execSync} = require('child_process'); +const {v4: uuidv4} = require('uuid'); -const TEST_VALID_TOKEN = 'test-valid-token'; -const TEST_INVALID_TOKEN = 'test-invalid-token'; +const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT; +const REGION = 'us-central1'; -describe('receiveRequestAndParseAuthHeader sample', () => { - let verifyStub, consoleStub; +describe('receiveRequestAndParseAuthHeader sample (Cloud Run integration)', function () { + this.timeout(5 * 60 * 1000); // 5 minutes for deploy + test + const serviceName = `receive-${uuidv4().slice(0, 8)}`; + let serviceUrl; - beforeEach(() => { - verifyStub = sinon.stub(OAuth2Client.prototype, 'verifyIdToken'); - consoleStub = sinon.stub(console, 'log'); + before(() => { + console.log(`Deploying Cloud Run service: ${serviceName}...`); + execSync( + `gcloud run deploy ${serviceName} \ + --project=${PROJECT_ID} \ + --region=${REGION} \ + --source=. \ + --allow-unauthenticated \ + --quiet`, + {stdio: 'inherit'} + ); + + console.log('Fetching service URL...'); + serviceUrl = execSync(`gcloud run services describe ${serviceName} \ + --project=${PROJECT_ID} \ + --region=${REGION} \ + --format='value(status.url)'`) + .toString() + .trim(); + + console.log(`Service URL: ${serviceUrl}`); }); - afterEach(() => { - sinon.restore(); + after(() => { + console.log(`Deleting service: ${serviceName}...`); + execSync(`gcloud run services delete ${serviceName} \ + --project=${PROJECT_ID} \ + --region=${REGION} \ + --quiet`); }); - it('should log greeting if token is valid', async () => { - const mockReq = { + function getIdentityToken() { + return execSync('gcloud auth print-identity-token').toString().trim(); + } + + it('should respond with greeting if token is valid', async () => { + const token = getIdentityToken(); + const res = await axios.get(serviceUrl, { headers: { - authorization: `Bearer ${TEST_VALID_TOKEN}`, + Authorization: `Bearer ${token}`, }, - }; - - verifyStub.resolves({ - getPayload: () => ({email: 'test@example.com'}), }); - await main(mockReq); - expect(consoleStub.calledWith('Hello, test@example.com!\n')).to.be.true; + expect(res.status).to.equal(200); + expect(res.data).to.match(/^Hello, .@.\.\w+!\n$/); }); - it('should log error message if token is invalid', async () => { - const mockReq = { - headers: { - authorization: `Bearer ${TEST_INVALID_TOKEN}`, - }, - }; - - verifyStub.rejects(new Error('invalid')); - - await main(mockReq); - expect(consoleStub.calledWithMatch(/Invalid token: invalid/)).to.be.true; + it('should respond with anonymous if no token is provided', async () => { + const res = await axios.get(serviceUrl); + expect(res.status).to.equal(200); + expect(res.data).to.equal('Hello, anonymous user.\n'); }); - it('should log anonymous message if no auth header', async () => { - const mockReq = { - headers: {}, - }; - - await main(mockReq); - expect(consoleStub.calledWith('Hello, anonymous user.\n')).to.be.true; + it('should respond with 401 if token is invalid', async () => { + try { + await axios.get(serviceUrl, { + headers: { + Authorization: 'Bearer invalid-token', + }, + }); + } catch (err) { + expect(err.response.status).to.equal(401); + expect(err.response.data).to.include('Invalid token'); + } }); }); From a3cfac31efce37d07604dc8a3617d39a018900a8 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Mon, 14 Apr 2025 16:52:00 +0000 Subject: [PATCH 8/9] fix(cloudrun): correct unit tests --- run/service-auth/test/receive.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js index 0f4126c5d2..fda8c2df00 100644 --- a/run/service-auth/test/receive.test.js +++ b/run/service-auth/test/receive.test.js @@ -19,6 +19,7 @@ const axios = require('axios'); const {execSync} = require('child_process'); const {v4: uuidv4} = require('uuid'); +const INVALID_TOKEN = 'invalid-token'; const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT; const REGION = 'us-central1'; @@ -84,7 +85,7 @@ describe('receiveRequestAndParseAuthHeader sample (Cloud Run integration)', func try { await axios.get(serviceUrl, { headers: { - Authorization: 'Bearer invalid-token', + Authorization: `Bearer ${INVALID_TOKEN}`, }, }); } catch (err) { From 21cf2c3ccfe50b47f561e020710d4c4d28778887 Mon Sep 17 00:00:00 2001 From: Ivan Hernandez Date: Mon, 14 Apr 2025 17:09:38 +0000 Subject: [PATCH 9/9] refactor(tests): replace numeric HTTP status codes with descriptive constants Improves readability and maintainability by using `StatusCodes` from `http-status-codes` package instead of hardcoded numeric values (e.g., 200, 401) in integration tests. --- package.json | 1 + run/service-auth/test/receive.test.js | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b239c7ad54..8c8ba1ccc6 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "eslint-plugin-n": "^14.0.0", "eslint-plugin-prettier": "^5.0.0-alpha.1", "gts": "5.3.0", + "http-status-codes": "^2.3.0", "mocha": "^10.2.0", "nunjucks": "^3.2.4", "prettier": "^3.0.3", diff --git a/run/service-auth/test/receive.test.js b/run/service-auth/test/receive.test.js index fda8c2df00..9c4d36812d 100644 --- a/run/service-auth/test/receive.test.js +++ b/run/service-auth/test/receive.test.js @@ -18,8 +18,9 @@ const {expect} = require('chai'); const axios = require('axios'); const {execSync} = require('child_process'); const {v4: uuidv4} = require('uuid'); +const {StatusCodes} = require('http-status-codes'); -const INVALID_TOKEN = 'invalid-token'; +const TEST_INVALID_TOKEN = 'test-invalid-token'; const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT; const REGION = 'us-central1'; @@ -71,25 +72,25 @@ describe('receiveRequestAndParseAuthHeader sample (Cloud Run integration)', func }, }); - expect(res.status).to.equal(200); + expect(res.status).to.equal(StatusCodes.OK); expect(res.data).to.match(/^Hello, .@.\.\w+!\n$/); }); it('should respond with anonymous if no token is provided', async () => { const res = await axios.get(serviceUrl); - expect(res.status).to.equal(200); + expect(res.status).to.equal(StatusCodes.OK); expect(res.data).to.equal('Hello, anonymous user.\n'); }); - it('should respond with 401 if token is invalid', async () => { + it('should respond with unoauthorized if token is invalid', async () => { try { await axios.get(serviceUrl, { headers: { - Authorization: `Bearer ${INVALID_TOKEN}`, + Authorization: `Bearer ${TEST_INVALID_TOKEN}`, }, }); } catch (err) { - expect(err.response.status).to.equal(401); + expect(err.response.status).to.equal(StatusCodes.UNAUTHORIZED); expect(err.response.data).to.include('Invalid token'); } });