Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

41 update get post and put endpoints for donateditem with full program and donor details #49

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
5 changes: 2 additions & 3 deletions client-app/src/Components/DonorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,8 @@ const DonorForm: React.FC = () => {
)}
{renderFormField('State', 'state')}
{renderFormField('City', 'city')}
{renderFormField('Zip Code', 'zipcode')}*/ //
{/* Email Opt-In Field */}
/*
{renderFormField('Zip Code', 'zipcode')}

<div className="form-field">
<label
htmlFor="emailOptIn"
Expand Down
14 changes: 14 additions & 0 deletions server/src/__mocks__/mockDonatedItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const updateItem = {
itemType: 'Updated Book',
currentStatus: 'Received',
programId: 1,
donorId: 1,
dateDonated: new Date().toISOString()
};
export const newItem = {
itemType: 'Book',
currentStatus: 'Received',
programId: 1,
donorId: 1,
dateDonated: new Date().toISOString()
};
12 changes: 12 additions & 0 deletions server/src/__mocks__/mockDonor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const newDonor = {
id: 1,
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
contact: '1234567890',
addressLine1: '123 Main St',
state: 'Missouri',
city: 'St. Louis',
zipcode: '63108',
emailOptIn: false
};
13 changes: 13 additions & 0 deletions server/src/__mocks__/mockPrismaClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@ const mockPrismaClient = {
donor: {
create: jest.fn(),
findMany: jest.fn(),
findUnique: jest.fn(),
},
program: {
findUnique: jest.fn(),
},
donatedItem: {
create: jest.fn(),
update: jest.fn(),
findUnique: jest.fn(),
},
donatedItemStatus: {
create: jest.fn(),
},
};


export default mockPrismaClient;
7 changes: 7 additions & 0 deletions server/src/__mocks__/mockProgram.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const newProgram = {
id: 1,
name: 'Valid Program',
description: 'Valid',
startDate: '2024-10-04',
aimAndCause: 'recycle'
};
71 changes: 71 additions & 0 deletions server/src/__tests__/donatedItem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import request from 'supertest';
import express from 'express';
import donatedItemRoutes from '../routes/donatedItemRoutes';
import mockPrismaClient from '../__mocks__/mockPrismaClient';
import { newProgram } from '../__mocks__/mockProgram';
import { newDonor } from '../__mocks__/mockDonor';
import { updateItem,newItem } from '../__mocks__/mockDonatedItem';

const app = express();
app.use(express.json());
app.use('/donatedItem', donatedItemRoutes);

describe('DonatedItem API Tests', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests look really good and pretty extensive, good job!

beforeEach(() => {
jest.clearAllMocks();
// Setup mock responses
mockPrismaClient.program.findUnique.mockResolvedValue(newProgram);
mockPrismaClient.donor.findUnique.mockResolvedValue(newDonor);
mockPrismaClient.donatedItem.create.mockResolvedValue({ id: 1, ...newItem });
mockPrismaClient.donatedItem.update.mockResolvedValue({ id: 1, ...updateItem });
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Keeping the required mock resolved values for prisma at one place seems more clear


// Test POST /donatedItem
it('validates Program name and Donor email before creating a donated item', async () => {
const response = await request(app).post('/donatedItem').send(newItem);
expect(response.status).toBe(201);
expect(mockPrismaClient.donatedItem.create).toHaveBeenCalled();

});

it('handles errors when the provided Program or Donor does not exist', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long term, will this unit test be able to work going into the future? Especially when we get to a point where we definitely have more than 30 users? I personally would consider making it a certain amount higher than the amount of users/programs we have.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@truffer11 It wouldn't check in the real or actual database; it will use mock prisma client and you can see at this line that it resolves to value null and it always resolves to null values irrespective of the size of database

const programId = 99;
const donorId = 29;
mockPrismaClient.program.findUnique.mockResolvedValue(null);
mockPrismaClient.donor.findUnique.mockResolvedValue(null);

const response = await request(app).post('/donatedItem').send({
...newItem,
programId,
donorId,
dateDonated: new Date().toISOString()
});
expect(response.status).toBe(400);
expect(response.body.error).toContain(`Donor with ID: ${donorId} does not exist.`);

});

// Test PUT /donatedItem/details/{id}
it('updates donated item details correctly', async () => {
const response = await request(app).put('/donatedItem/details/1').send(updateItem);
expect(response.status).toBe(200);
expect(mockPrismaClient.donatedItem.update).toHaveBeenCalled();
expect(response.body.itemType).toBe(updateItem.itemType);
});

it('returns error responses for invalid Program or Donor values', async () => {
mockPrismaClient.program.findUnique.mockResolvedValue(null);
mockPrismaClient.donor.findUnique.mockResolvedValue(null);

const donorId = 19;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to what I mentioned above, you may want to consider modifying your testing to work beyond just 20 total donors

const programId = 99;

const response = await request(app).put('/donatedItem/details/1').send({
...updateItem,
donorId,
programId
});
expect(response.status).toBe(400);
expect(response.body.error).toContain(`Donor with ID: ${donorId} does not exist.`);
});
});
41 changes: 38 additions & 3 deletions server/src/routes/donatedItemRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { Router, Request, Response } from 'express';
import prisma from '../prismaClient'; // Import Prisma client
import { donatedItemValidator } from '../validators/donatedItemValidator'; // Import the validator
import { validateDonor } from '../services/donorService';
import { validateProgram } from '../services/programService';
import { date } from 'joi';

const router = Router();

// POST /donatedItem - Create a new DonatedItem
router.post('/', donatedItemValidator, async (req: Request, res: Response) => {
try {

const { dateDonated, ...rest } = req.body;


try {
await validateDonor(req.body.donorId);
await validateProgram(req.body.programId);
} catch (error) {
if (error instanceof Error) {
return res.status(400).json({ error: error.message });
}
}
const dateDonatedDateTime = new Date(dateDonated);
dateDonatedDateTime.setUTCHours(0, 0, 0, 0); // Set time to 00:00:00 UTC

Expand All @@ -21,8 +32,20 @@ router.post('/', donatedItemValidator, async (req: Request, res: Response) => {
// dateDonated: new Date(dateDonated).setUTCHours(0,0,0,0), // Set time to 00:00:00 UTC
},
});
console.log('New donated item created:', newItem);
res.status(201).json(newItem);
const newStatus = await prisma.donatedItemStatus.create({
data: {
statusType: 'Received',
dateModified: dateDonatedDateTime, // Use the same date as dateDonated
donatedItemId: newItem.id,
},
});

res.status(201).json({
donatedItem: newItem,
donatedItemStatus: newStatus,
});


} catch (error) {
console.error('Error creating donated item:', error);
res.status(500).json({ message: 'Error creating donated item' });
Expand All @@ -35,6 +58,8 @@ router.get('/', async (req: Request, res: Response) => {
const items = await prisma.donatedItem.findMany({
include: {
statuses: true, // Include related status updates
program:true,
donor:true,
},
});
res.json(items);
Expand All @@ -50,6 +75,15 @@ router.put(
donatedItemValidator,
async (req: Request, res: Response) => {
try {
try {
await validateDonor(req.body.donorId);
await validateProgram(req.body.programId);
} catch (error) {
if (error instanceof Error) {
return res.status(400).json({ error: error.message });
}
}

const updatedItem = await prisma.donatedItem.update({
where: { id: Number(req.params.id) },
data: { ...req.body, lastUpdated: new Date() },
Expand All @@ -60,6 +94,7 @@ router.put(
console.error('Error updating donated item details:', error);
res.status(500).json({
message: 'Error updating donated item details',

});
}
},
Expand Down
1 change: 1 addition & 0 deletions server/src/routes/donorRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ router.get('/', async (req: Request, res: Response) => {
const donors = await prisma.donor.findMany();
res.json(donors);
} catch (error) {
console.error('Error fetching donor:', error);
res.status(500).json({ message: 'Error fetching donors' });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unable to test what both of these do, but do both of these do different things? At a glance, they seem to have the same functionality

}
});
Expand Down
4 changes: 2 additions & 2 deletions server/src/schemas/donatedItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export const donatedItemSchema = Joi.object({
currentStatus: Joi.string()
.valid('Received', 'Pending', 'Processed', 'Delivered')
.required(),
donorEmail: Joi.string().email().required(),
program: Joi.string().required(),
donorId: Joi.number().integer().required(),
programId: Joi.number().integer().required(),
dateDonated: Joi.date().required(), // Validates as a proper date
});

Expand Down
11 changes: 11 additions & 0 deletions server/src/services/donorService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import prisma from '../prismaClient'; // Adjust the import path as necessary

export async function validateDonor(donorId: number) {
const donor = await prisma.donor.findUnique({
where: { id: donorId }
});
if (!donor) {
throw new Error(`Donor with ID: ${donorId} does not exist.`);
}
return donor;
}
11 changes: 11 additions & 0 deletions server/src/services/programService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import prisma from '../prismaClient'; // Adjust the import path as necessary

export async function validateProgram(programId: number) {
const program = await prisma.program.findUnique({
where: { id: programId }
});
if (!program) {
throw new Error(`Program ID ${programId} is not valid or does not exist.`);
}
return program;
}
Loading