Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: Callofduty2497
POSTGRES_DB: valetapptest

options: >-
--health-cmd pg_isready
--health-interval 10s
Expand Down Expand Up @@ -49,4 +50,5 @@ jobs:
env:
NODE_ENV: test
DATABASE_URL: postgres://postgres:Callofduty2497@localhost:5433/valetapptest
JWT_SECRET: ${{ secrets.JWT_SECRET }}
run: npm test
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ RUN npm install

COPY . .


COPY .env .env
RUN npx prisma generate
EXPOSE 3000
CMD ["npm", "run", "dev"]
104 changes: 104 additions & 0 deletions __tests__/auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import request from 'supertest'
import app from '../src/app'
import prisma from '../src/services/prisma'
import jwt from 'jsonwebtoken'
import authenticate from '../src/middlewares/authenicated'

import { Request, Response, NextFunction } from 'express'

beforeEach(async () => {
await prisma.entrance.deleteMany()
await prisma.employee.deleteMany()
await prisma.location.deleteMany()
})

test('Login with valid credentials', async () => {
// First, create a test location and employee in the database
const location = await prisma.location.create({ data: { name: 'Test Location' } })
const employee = await prisma.employee.create({
data: { name: 'Test Employee', pin: '1234', locationId: location.id },
})

const token = jwt.sign({ locationId: location.id }, process.env.JWT_SECRET!)

const res = await request(app)
.post(`/v1/location/${location.id}/auth/login`)
.set('Authorization', `Bearer ${token}`)
.send({ pin: '1234' })

expect(res.status).toBe(200)
expect(res.body).toHaveProperty('message', 'Login successful')
expect(res.body).toHaveProperty('employee')
expect(res.body.employee).toHaveProperty('id', employee.id)
expect(res.body.employee).toHaveProperty('name', 'Test Employee')
expect(res.headers['set-cookie']).toBeDefined()

// Clean up
// await prisma.employee.delete({ where: { id: employee.id } })
// await prisma.location.delete({ where: { id: location.id } })
})

test('Login with invalid PIN', async () => {
// First, create a test location and employee in the database
const location = await prisma.location.create({ data: { name: 'Test Location' } })
await prisma.employee.create({
data: { name: 'Test Employee', pin: '1234', locationId: location.id },
})
const token = jwt.sign({ locationId: location.id }, process.env.JWT_SECRET!)
const res = await request(app)
.post(`/v1/location/${location.id}/auth/login`)
.set('Authorization', `Bearer ${token}`)
.send({ pin: 'wrongpin' })

expect(res.status).toBe(401)
expect(res.body).toHaveProperty('error', 'Invalid PIN or location')

// Clean up
await prisma.employee.deleteMany({ where: { locationId: location.id } })
await prisma.location.delete({ where: { id: location.id } })
})

describe('Auth Middleware', () => {
it('calls next() when token is valid', () => {
const token = jwt.sign({ employeeId: 1 }, process.env.JWT_SECRET)
const req = {
headers: { authorization: `Bearer ${token}` },
} as Partial<Request>
const res: Partial<Response> = {
status: jest.fn().mockReturnThis(),
json: jest.fn(),
}
const next = jest.fn() as NextFunction

authenticate(req as Request, res as Response, next)
expect(next).toHaveBeenCalled()
})

it('returns 401 when token is missing', () => {
const req = { cookies: {} } as Partial<Request>
const res = {
status: jest.fn().mockReturnThis(),
json: jest.fn(),
} as Partial<Response>
const next = jest.fn() as NextFunction

authenticate(req as Request, res as Response, next)
expect(res.status).toHaveBeenCalledWith(401)
expect(res.json).toHaveBeenCalledWith({ error: 'Invalid or expired token' })
expect(next).not.toHaveBeenCalled()
})

it('returns 401 when token is invalid', () => {
const req = { cookies: { token: 'invalidtoken' } } as Partial<Request>
const res = {
status: jest.fn().mockReturnThis(),
json: jest.fn(),
} as Partial<Response>
const next = jest.fn() as NextFunction

authenticate(req as Request, res as Response, next)
expect(res.status).toHaveBeenCalledWith(401)
expect(res.json).toHaveBeenCalledWith({ error: 'Invalid or expired token' })
expect(next).not.toHaveBeenCalled()
})
})
150 changes: 149 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@
"dependencies": {
"@prisma/client": "^6.16.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"pg": "^8.16.3",
"pino": "^8.19.0"
},
Expand All @@ -41,6 +43,7 @@
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
"@types/jest": "^30.0.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^20.11.30",
"@types/supertest": "^6.0.3",
"@typescript-eslint/eslint-plugin": "^7.3.1",
Expand Down
2 changes: 2 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
generator client {
provider = "prisma-client-js"
// output = "../src/generated/prisma"
binaryTargets = ["native", "linux-musl-arm64-openssl-3.0.x"]

}

datasource db {
Expand Down
Loading