turns-00077.parquet:12115
165bb83aff40e2a82fb55784
turn 93/124gpt-4.1-mini-2025-04-14EnglishKenya990 words
degenerate_repetitionAbsentFinal dense release
USER
here is the file
/**
* Integration tests for Proposal API routes of UjamaaDAO backend.
*
* These tests verify creation, retrieval, listing, and updating of proposals
* using Prisma for real database operations.
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import request from 'supertest';
import express from 'express';
// Import prisma client WITHOUT .js extension in tests
import prisma, { ProposalType, ProposalStatus } from '@prisma/client';
// Import routes WITH .js extension in tests as they import source files compiled to JS
import proposalRoutes from '../src/routes/proposal';
const app = express();
app.use(express.json());
app.use('/api/proposals', proposalRoutes);
beforeAll(async () => {
// Clean database before tests
await prisma.proposal.deleteMany();
await prisma.groupMember.deleteMany();
await prisma.group.deleteMany();
await prisma.user.deleteMany();
});
afterAll(async () => {
await prisma.$disconnect();
});
beforeEach(async () => {
await prisma.proposal.deleteMany();
await prisma.groupMember.deleteMany();
await prisma.group.deleteMany();
await prisma.user.deleteMany();
});
describe('Proposal API', () => {
it('should return 400 when required fields are missing on create', async () => {
const res = await request(app).post('/api/proposals/create').send({});
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(res.body.error).toContain('creatorUserId or creatorGroupId');
});
it('should create a new funded proposal successfully', async () => {
// Create group first for FK
const group = await prisma.group.create({
data: {
name: 'Test Group',
walletAddress: '0x123abc',
constituency: 'Nairobi West',
county: 'Nairobi',
industryFocus: 'Tech',
productsServices: ['Software'],
},
});
const newProposal = {
creatorGroupId: group.id,
proposalType: ProposalType.BUSINESS,
funded: true,
title: 'New Business Proposal',
description: 'Description of proposal',
budget: 10000,
timeline: '6 months',
locationScope: 'Local',
constituency: 'Nairobi West',
county: 'Nairobi',
purposeDetails: {
businessModel: 'Sell X services',
profitProjection: 'Expected 20% profit margin',
communityBenefit: 'Job creation',
},
};
const res = await request(app).post('/api/proposals/create').send(newProposal);
expect(res.status).toBe(201);
expect(res.body.success).toBe(true);
expect(typeof res.body.proposalId).toBe('string');
});
it('should get proposal details by id', async () => {
// Prepare group and proposal
const group = await prisma.group.create({
data: {
name: 'Detail Group',
walletAddress: '0x456def',
constituency: 'Nairobi West',
county: 'Nairobi',
industryFocus: 'Tech',
productsServices: [],
},
});
const created = await prisma.proposal.create({
data: {
creatorGroupId: group.id,
proposalType: ProposalType.NON_PROFIT,
funded: false,
title: 'Community Cleanup',
description: 'Cleaning the park',
locationScope: 'LOCAL',
constituency: 'Nairobi West',
county: 'Nairobi',
status: ProposalStatus.DRAFT,
},
});
const res = await request(app).get(`/api/proposals/${created.id}`);
expect(res.status).toBe(200);
expect(res.body.id).toBe(created.id);
expect(res.body.title).toBe('Community Cleanup');
});
it('should return 404 for non-existent proposal', async () => {
const res = await request(app).get('/api/proposals/non-existent-id');
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
});
it('should list proposals with optional filtering', async () => {
// Create groups
const group1 = await prisma.group.create({
data: {
name: 'G1',
walletAddress: '0xabc1',
constituency: 'Nairobi West',
county: 'Nairobi',
industryFocus: 'Business',
productsServices: [],
},
});
const group2 = await prisma.group.create({
data: {
name: 'G2',
walletAddress: '0xabc2',
constituency: 'Kisumu East',
county: 'Kisumu',
industryFocus: 'Non-Profit',
productsServices: [],
},
});
await prisma.proposal.createMany({
data: [
{
creatorGroupId: group1.id,
proposalType: ProposalType.BUSINESS,
funded: true,
title: 'Proposal 1',
description: '',
locationScope: 'LOCAL',
constituency: 'Nairobi West',
county: 'Nairobi',
status: ProposalStatus.VOTING,
},
{
creatorGroupId: group2.id,
proposalType: ProposalType.NON_PROFIT,
funded: false,
title: 'Proposal 2',
description: '',
locationScope: 'COUNTY',
constituency: null,
county: 'Kisumu',
status: ProposalStatus.APPROVED,
},
],
});
const res = await request(app).get('/api/proposals').query({ locationScope: 'Local' });
expect(res.status).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBeGreaterThan(0);
expect(res.body[0].locationScope).toBe('Local');
});
it('should update proposal status and other fields', async () => {
const group = await prisma.group.create({
data: {
name: 'Update Group',
walletAddress: '0xxyz123',
constituency: 'Nairobi West',
county: 'Nairobi',
industryFocus: 'Tech',
productsServices: [],
},
});
const proposal = await prisma.proposal.create({
data: {
creatorGroupId: group.id,
proposalType: ProposalType.BUSINESS,
funded: true,
title: 'Update Me',
description: 'Initial',
locationScope: 'LOCAL',
constituency: 'Nairobi West',
county: 'Nairobi',
status: ProposalStatus.DRAFT,
},
});
const updateData = {
status: ProposalStatus.VOTING,
title: 'Updated Title',
};
const res = await request(app).patch(`/api/proposals/${proposal.id}`).send(updateData);
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
expect(res.body.updatedStatus).toBe(ProposalStatus.VOTING);
const updated = await prisma.proposal.findUnique({ where: { id: proposal.id } });
expect(updated?.title).toBe('Updated Title');
expect(updated?.status).toBe(ProposalStatus.VOTING);
});
it('should return 400 on invalid status update', async () => {
const group = await prisma.group.create({
data: {
name: 'Invalid Group',
walletAddress: '0xabc999',
constituency: 'Nairobi West',
county: 'Nairobi',
industryFocus: 'Tech',
productsServices: [],
},
});
const proposal = await prisma.proposal.create({
data: {
creatorGroupId: group.id,
proposalType: ProposalType.BUSINESS,
funded: true,
title: 'Invalid Status Update',
description: '',
locationScope: 'LOCAL',
constituency: 'Nairobi West',
county: 'Nairobi',
status: ProposalStatus.DRAFT,
},
});
const res = await request(app).patch(`/api/proposals/${proposal.id}`).send({ status: 'INVALID_STATUS' });
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
});
it('should return 404 when updating non-existent proposal', async () => {
const res = await request(app).patch('/api/proposals/non-existent-id').send({ status: ProposalStatus.VOTING });
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
});
});ASSISTANT
Your test file looks very well structured and is mostly correct! But based on past issues you had, here are a few clarifications and fixes to make sure this runs smoothly:
---
### Key Fixes / Best Practices for this file:
1. **Correct import of Prisma client and enums:**
You should import the Prisma client instance **from your own client file**, and enums from the package, like this:
```ts
import prisma from '../src/prismaClient.js'; // Your PrismaClient instance (.js extension for ESM runtime)
import { ProposalType, ProposalStatus } from '@prisma/client'; // Enums from package
```
2. **Delete test records before tests:**
You may want to delete all dependent records (proposals, groups, groupMembers, users) before each test for proper isolation:
```ts
beforeEach(async () => {
await prisma.proposal.deleteMany();
await prisma.groupMember.deleteMany();
await prisma.group.deleteMany();
await prisma.user.deleteMany();
});
```
3. **Ensure enums are properly used in test data:**
Use `ProposalType.BUSINESS`, not `"Business"` strings (which you already do).
Similarly for status: `ProposalStatus.DRAFT`, not string `"DRAFT"`.
4. **Verify that group creation uses unique walletAddress per test run:**
If multiple tests run fast, creating groups with the same walletAddress will cause conflicts. Use dynamic/random strings or `Date.now()` to avoid that.
---
### Here are some minor example tweaks:
Change
```ts
const group = await prisma.group.create({
data: {
name: 'Test Group',
walletAddress: '0x123abc',
// ...
},
});
```
to
```ts
const group = await prisma.group.create({
data: {
name: `Test Group ${Date.now()}`,
walletAddress: `0x123abc${Date.now()}`,
// ...
},
});
```
to prevent duplicates during rapid test runs.
---
### Overall this test file is solid.
If you're still encountering any test failures or errors, please share the exact message and line number, so I can help you debug specifically!
---
### Do you want me to review or provide a minimal working example repository for your entire test setup?
Or assist with setting up a test environment script to clean and seed data automatically before tests run?
Let me know!