mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-08 15:05:22 +02:00
Merge pull request #135 from tmyracle/fix-integration-tests
Fix the failing integration tests
This commit is contained in:
commit
0c8c889fcb
11 changed files with 53 additions and 64 deletions
|
@ -85,7 +85,6 @@ export const authOptions = {
|
|||
strategy: 'jwt' as SessionStrategy,
|
||||
maxAge: 1 * 24 * 60 * 60, // 1 Day
|
||||
},
|
||||
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
name: 'Credentials',
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
} from '@maybe-finance/server/features'
|
||||
import { InMemoryQueueFactory, PgService, type IQueueFactory } from '@maybe-finance/server/shared'
|
||||
import { createLogger, transports } from 'winston'
|
||||
import isCI from 'is-ci'
|
||||
import nock from 'nock'
|
||||
import Decimal from 'decimal.js'
|
||||
import { startServer, stopServer } from './utils/server'
|
||||
|
@ -27,7 +26,7 @@ const prisma = new PrismaClient()
|
|||
// For TypeScript support
|
||||
const plaid = jest.mocked(_plaid) // eslint-disable-line
|
||||
|
||||
const auth0Id = isCI ? 'auth0|61afd38f678a0c006895f046' : 'auth0|61afd340678a0c006895f000'
|
||||
const authId = '__TEST_USER_ID__'
|
||||
let axios: AxiosInstance
|
||||
let user: User
|
||||
|
||||
|
@ -38,7 +37,7 @@ if (process.env.IS_VSCODE_DEBUG === 'true') {
|
|||
|
||||
beforeEach(async () => {
|
||||
// Clears old user and data, creates new user
|
||||
user = await resetUser(auth0Id)
|
||||
user = await resetUser(authId)
|
||||
})
|
||||
|
||||
describe('/v1/accounts API', () => {
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { AxiosInstance } from 'axios'
|
|||
import type { SharedType } from '@maybe-finance/shared'
|
||||
import type { Prisma, AccountConnection, AccountSyncStatus, User } from '@prisma/client'
|
||||
import type { ItemRemoveResponse } from 'plaid'
|
||||
import isCI from 'is-ci'
|
||||
import { startServer, stopServer } from './utils/server'
|
||||
import { getAxiosClient } from './utils/axios'
|
||||
import prisma from '../lib/prisma'
|
||||
|
@ -18,7 +17,7 @@ jest.mock('plaid')
|
|||
// For TypeScript support
|
||||
const plaid = jest.mocked(_plaid)
|
||||
|
||||
const auth0Id = isCI ? 'auth0|61afd38f678a0c006895f046' : 'auth0|61afd340678a0c006895f000'
|
||||
const authId = '__TEST_USER_ID__'
|
||||
let axios: AxiosInstance
|
||||
let user: User | null
|
||||
let connection: AccountConnection
|
||||
|
@ -45,7 +44,7 @@ afterAll(async () => {
|
|||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await resetUser(auth0Id)
|
||||
user = await resetUser(authId)
|
||||
|
||||
connectionData = {
|
||||
data: {
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
import type { AxiosResponse } from 'axios'
|
||||
import type { SharedType } from '@maybe-finance/shared'
|
||||
import { superjson } from '@maybe-finance/shared'
|
||||
import env from '../../../env'
|
||||
import isCI from 'is-ci'
|
||||
import Axios from 'axios'
|
||||
import { encode } from 'next-auth/jwt'
|
||||
|
||||
// Fetches Auth0 access token (JWT) and prepares Axios client to use it on each request
|
||||
export async function getAxiosClient() {
|
||||
const tenantUrl = isCI
|
||||
? 'REPLACE_THIS-staging.us.auth0.com'
|
||||
: 'REPLACE_THIS-development.us.auth0.com'
|
||||
|
||||
const {
|
||||
data: { access_token: token },
|
||||
} = await Axios.request({
|
||||
method: 'POST',
|
||||
url: `https://${tenantUrl}/oauth/token`,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
data: {
|
||||
grant_type: 'password',
|
||||
username: 'REPLACE_THIS',
|
||||
password: 'REPLACE_THIS',
|
||||
audience: 'https://maybe-finance-api/v1',
|
||||
scope: '',
|
||||
client_id: isCI ? 'REPLACE_THIS' : 'REPLACE_THIS',
|
||||
const baseUrl = 'http://127.0.0.1:53333/v1'
|
||||
const jwt = await encode({
|
||||
maxAge: 1 * 24 * 60 * 60,
|
||||
secret: process.env.NEXTAUTH_SECRET || 'CHANGE_ME',
|
||||
token: {
|
||||
sub: '__TEST_USER_ID__',
|
||||
user: '__TEST_USER_ID__',
|
||||
'https://maybe.co/email': 'REPLACE_THIS',
|
||||
firstName: 'REPLACE_THIS',
|
||||
lastName: 'REPLACE_THIS',
|
||||
name: 'REPLACE_THIS',
|
||||
},
|
||||
})
|
||||
|
||||
const defaultHeaders = {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Credentials': true,
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
}
|
||||
const axiosOptions = {
|
||||
baseURL: baseUrl,
|
||||
headers: defaultHeaders,
|
||||
}
|
||||
|
||||
const axios = Axios.create({
|
||||
baseURL: 'http://127.0.0.1:53333/v1',
|
||||
...axiosOptions,
|
||||
validateStatus: () => true, // Tests should determine whether status is correct, not Axios
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
axios.interceptors.response.use((response: AxiosResponse<SharedType.BaseResponse>) => {
|
||||
|
|
|
@ -2,17 +2,20 @@ import cookieParser from 'cookie-parser'
|
|||
import { decode } from 'next-auth/jwt'
|
||||
|
||||
const SECRET = process.env.NEXTAUTH_SECRET ?? 'REPLACE_THIS'
|
||||
|
||||
export const validateAuthJwt = async (req, res, next) => {
|
||||
cookieParser(SECRET)(req, res, async (err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ message: 'Internal Server Error' })
|
||||
}
|
||||
|
||||
if (req.cookies && 'next-auth.session-token' in req.cookies) {
|
||||
const cookieName = req.secure
|
||||
? '__Secure-next-auth.session-token'
|
||||
: 'next-auth.session-token'
|
||||
|
||||
if (req.cookies && cookieName in req.cookies) {
|
||||
try {
|
||||
const token = await decode({
|
||||
token: req.cookies['next-auth.session-token'],
|
||||
token: req.cookies[cookieName],
|
||||
secret: SECRET,
|
||||
})
|
||||
|
||||
|
@ -26,6 +29,18 @@ export const validateAuthJwt = async (req, res, next) => {
|
|||
console.error('Error in token validation', error)
|
||||
return res.status(500).json({ message: 'Internal Server Error' })
|
||||
}
|
||||
} else if (req.headers.authorization) {
|
||||
const token = req.headers.authorization.split(' ')[1]
|
||||
const decoded = await decode({
|
||||
token,
|
||||
secret: SECRET,
|
||||
})
|
||||
if (decoded) {
|
||||
req.user = decoded
|
||||
return next()
|
||||
} else {
|
||||
return res.status(401).json({ message: 'Unauthorized' })
|
||||
}
|
||||
} else {
|
||||
return res.status(401).json({ message: 'Unauthorized' })
|
||||
}
|
||||
|
|
|
@ -48,8 +48,6 @@ const envSchema = z.object({
|
|||
NX_SENTRY_DSN: z.string().optional(),
|
||||
NX_SENTRY_ENV: z.string().optional(),
|
||||
|
||||
NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'),
|
||||
|
||||
NX_POLYGON_API_KEY: z.string().default(''),
|
||||
|
||||
NX_PORT: z.string().default('3333'),
|
||||
|
|
|
@ -68,8 +68,8 @@ describe('Finicity', () => {
|
|||
userId: user.id,
|
||||
name: 'TEST_FINICITY',
|
||||
type: 'finicity',
|
||||
finicityInstitutionId: 'REPLACE_THIS',
|
||||
finicityInstitutionLoginId: 'REPLACE_THIS',
|
||||
finicityInstitutionId: '101732',
|
||||
finicityInstitutionLoginId: '6000483842',
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -22,8 +22,6 @@ const envSchema = z.object({
|
|||
NX_SENTRY_DSN: z.string().optional(),
|
||||
NX_SENTRY_ENV: z.string().optional(),
|
||||
|
||||
NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'),
|
||||
|
||||
NX_REDIS_URL: z.string().default('redis://localhost:6379'),
|
||||
|
||||
NX_POLYGON_API_KEY: z.string().default(''),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import crypto from 'crypto'
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
export interface ICryptoService {
|
||||
encrypt(plainText: string): string
|
||||
|
@ -6,32 +6,13 @@ export interface ICryptoService {
|
|||
}
|
||||
|
||||
export class CryptoService implements ICryptoService {
|
||||
private key: Buffer
|
||||
private ivLength = 16 // Initialization vector length. For AES, this is always 16
|
||||
|
||||
constructor(private readonly secret: string) {
|
||||
// Ensure the key length is suitable for AES-256
|
||||
this.key = crypto.createHash('sha256').update(String(this.secret)).digest()
|
||||
}
|
||||
constructor(private readonly secret: string) {}
|
||||
|
||||
encrypt(plainText: string) {
|
||||
const iv = crypto.randomBytes(this.ivLength)
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv)
|
||||
let encrypted = cipher.update(plainText, 'utf8', 'hex')
|
||||
encrypted += cipher.final('hex')
|
||||
|
||||
// Include the IV at the start of the encrypted result
|
||||
return iv.toString('hex') + ':' + encrypted
|
||||
return CryptoJS.AES.encrypt(plainText, this.secret).toString()
|
||||
}
|
||||
|
||||
decrypt(encrypted: string) {
|
||||
const textParts = encrypted.split(':')
|
||||
const iv = Buffer.from(textParts.shift()!, 'hex')
|
||||
const encryptedText = textParts.join(':')
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv)
|
||||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
|
||||
decrypted += decipher.final('utf8')
|
||||
|
||||
return decrypted
|
||||
return CryptoJS.AES.decrypt(encrypted, this.secret).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"bcrypt": "^5.1.1",
|
||||
"bull": "^4.10.2",
|
||||
"classnames": "^2.3.1",
|
||||
"cookie": "^0.6.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"core-js": "^3.6.5",
|
||||
"cors": "^2.8.5",
|
||||
|
|
|
@ -7988,7 +7988,7 @@ cookie@0.5.0, cookie@^0.5.0:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookie@0.6.0:
|
||||
cookie@0.6.0, cookie@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
|
||||
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue