api/tests: fix ava loading paths & add/rework user specs
Pedro Lucas Porcellis porcellis@eletrotupi.com · 6 months ago
1d108862effda2b34ab10ccce2ba8799782afec0 Browse filesParents
ad58511Changed files
7
Additions
+365
Deletions
-17
api/ava.config.mjs
1 1
export default {
2 -
"typescript": {
3 -
"extensions": [
4 -
"ts",
5 -
"tsx"
6 -
],
7 -
"rewritePaths": {
8 -
"src/": "dist/"
9 -
},
10 -
compile: false
2 +
extensions: {
3 +
ts: 'commonjs'
11 4
},
12 -
// extensions: {
13 -
// ts: 'module'
14 -
// },
15 -
// nodeArguments: [
16 -
// '--import=tsx'
17 -
// ],
5 +
nodeArguments: [
6 +
'--require=tsx/cjs',
7 +
'--require=tsconfig-paths/register'
8 +
],
18 9
files: [
19 10
'tests/**/*.test.ts'
20 11
],
21 12
environmentVariables: {
22 -
NODE_ENV: 'test'
13 +
NODE_ENV: 'test',
14 +
TS_NODE_PROJECT: './tsconfig.json'
23 15
},
24 16
timeout: '30s',
25 17
concurrency: 1, // Run tests serially to avoid database conflicts
26 18
verbose: true
27 -
};
19 +
};api/tests/database-auth.test.ts
1 1
import test from "ava";
2 2
import { PrismaClient } from '@prisma/client';
3 3
import { PrismaPg } from '@prisma/adapter-pg';
4 4
import bcrypt from 'bcryptjs';
5 5
import 'dotenv/config';
22 22
console.error('Error cleaning test DB:', err);
23 23
}
24 24
});
25 25
26 26
test.after.always(async () => {
27 +
const deleteUsers = prisma.user.deleteMany();
28 +
29 +
await prisma.$transaction([
30 +
deleteUsers
31 +
])
32 +
27 33
await prisma?.$disconnect();
28 34
});
29 35
30 36
// Basic database connectivity test
31 37
test("database connection works", async (t) => {
250 256
t.deepEqual(updatedUser.createdAt, user.createdAt);
251 257
t.true(updatedUser.updatedAt >= beforeUpdate);
252 258
t.true(updatedUser.updatedAt <= afterUpdate);
253 259
t.true(updatedUser.updatedAt > user.updatedAt);
254 260
});
/dev/null → api/tests/users/create.test.ts
1 +
import test from "ava";
2 +
import { PrismaClient } from '@prisma/client';
3 +
import { PrismaPg } from '@prisma/adapter-pg';
4 +
import request from 'supertest';
5 +
import jwt from 'jsonwebtoken';
6 +
import 'dotenv/config';
7 +
import { Express } from 'express';
8 +
import { createApp } from '../../src/createApp';
9 +
10 +
let prisma: PrismaClient;
11 +
let app: Express;
12 +
13 +
test.before(() => {
14 +
process.env.NODE_ENV = 'test';
15 +
process.env.JWT_SECRET = 'test-secret-key';
16 +
17 +
const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
18 +
const adapter = new PrismaPg({ connectionString });
19 +
prisma = new PrismaClient({ adapter });
20 +
21 +
// Create Express app with test Prisma client
22 +
app = createApp(prisma);
23 +
});
24 +
25 +
test.beforeEach(async () => {
26 +
// Clean database before each test
27 +
try {
28 +
await prisma.user.deleteMany({});
29 +
} catch (err) {
30 +
console.error('Error cleaning test DB:', err);
31 +
}
32 +
});
33 +
34 +
test.after.always(async () => {
35 +
await prisma?.$disconnect();
36 +
});
37 +
38 +
test("POST /users creates a new user and returns JWT token", async (t) => {
39 +
const userData = {
40 +
email: "falamansa@example.com",
41 +
firstName: "Fala",
42 +
lastName: "Mansa",
43 +
password: "IscreviSeuNomeNaAreia"
44 +
};
45 +
46 +
const response = await request(app)
47 +
.post('/users')
48 +
.send(userData)
49 +
.expect('Content-Type', /json/)
50 +
.expect(201);
51 +
52 +
// Should return a JWT token
53 +
t.truthy(response.body.token);
54 +
55 +
// Verify the token is valid
56 +
const decoded = jwt.verify(response.body.token, process.env.JWT_SECRET!) as any;
57 +
t.is(decoded.email, userData.email);
58 +
t.truthy(decoded.userId);
59 +
60 +
// Verify user was created in database
61 +
const dbUser = await prisma.user.findUnique({
62 +
where: { email: userData.email }
63 +
});
64 +
t.truthy(dbUser);
65 +
t.is(dbUser!.email, userData.email);
66 +
t.is(dbUser!.firstName, userData.firstName);
67 +
t.is(dbUser!.lastName, userData.lastName);
68 +
});
69 +
70 +
test("POST /users returns 400 for missing required fields", async (t) => {
71 +
const response = await request(app)
72 +
.post('/users')
73 +
.send({
74 +
email: "invalid@example.com"
75 +
// Missing firstName and password
76 +
})
77 +
.expect('Content-Type', /json/)
78 +
.expect(400);
79 +
80 +
t.truthy(response.body.error);
81 +
t.is(response.body.error, "Campos faltantes"); // TODO: i18n?
82 +
t.truthy(response.body.missing);
83 +
t.true(response.body.missing.includes('password'));
84 +
t.true(response.body.missing.includes('firstName'));
85 +
});
86 +
87 +
test("POST /users returns 409 for duplicate email", async (t) => {
88 +
const email = "duplicate@example.com";
89 +
90 +
// Create first user
91 +
await prisma.user.create({
92 +
data: {
93 +
email,
94 +
firstName: "First",
95 +
lastName: "User",
96 +
encryptedPassword: "$2a$10$test"
97 +
}
98 +
});
99 +
100 +
// Try to create second user with same email
101 +
const response = await request(app)
102 +
.post('/users')
103 +
.send({
104 +
email,
105 +
firstName: "Second",
106 +
lastName: "User",
107 +
password: "hunter2"
108 +
})
109 +
.expect('Content-Type', /json/)
110 +
.expect(409);
111 +
112 +
t.truthy(response.body.error);
113 +
});
114 +
115 +
test("createApp successfully integrates with Prisma and Express", async (t) => {
116 +
// Test that the app factory works correctly
117 +
const testApp = createApp(prisma);
118 +
119 +
t.truthy(testApp);
120 +
t.is(typeof testApp.use, 'function');
121 +
t.is(typeof testApp.listen, 'function');
122 +
123 +
// Verify the app has the prisma client in locals
124 +
t.is(testApp.locals.prisma, prisma);
125 +
});
/dev/null → api/tests/users/delete.test.ts
1 +
import test from "ava";
2 +
import { PrismaClient } from '@prisma/client';
3 +
import { PrismaPg } from '@prisma/adapter-pg';
4 +
import request from 'supertest';
5 +
import bcrypt from 'bcryptjs';
6 +
import 'dotenv/config';
7 +
import { Express } from 'express';
8 +
import { createApp } from '../../src/createApp';
9 +
10 +
let prisma: PrismaClient;
11 +
let app: Express;
12 +
13 +
test.before(() => {
14 +
process.env.NODE_ENV = 'test';
15 +
16 +
const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
17 +
const adapter = new PrismaPg({ connectionString });
18 +
prisma = new PrismaClient({ adapter });
19 +
20 +
// Create Express app with test Prisma client
21 +
app = createApp(prisma);
22 +
});
23 +
24 +
test.beforeEach(async () => {
25 +
// Clean database before each test
26 +
try {
27 +
await prisma.user.deleteMany({});
28 +
} catch (err) {
29 +
console.error('Error cleaning test DB:', err);
30 +
}
31 +
});
32 +
33 +
test.after.always(async () => {
34 +
await prisma?.$disconnect();
35 +
});
36 +
37 +
//test("DELETE /users/:id deletes a user", async (t) => {
38 +
// t.pass()
39 +
// // Create a user first
40 +
// const user = await prisma.user.create({
41 +
// data: {
42 +
// email: "delete@example.com",
43 +
// firstName: "Delete",
44 +
// lastName: "Me",
45 +
// encryptedPassword: bcrypt.hashSync("password", 10)
46 +
// }
47 +
// });
48 +
//
49 +
// await request(app)
50 +
// .delete(`/users/${user.id}`)
51 +
// .expect(204);
52 +
//
53 +
// // Verify user was deleted
54 +
// const dbUser = await prisma.user.findUnique({
55 +
// where: { id: user.id }
56 +
// });
57 +
// t.is(dbUser, null);
58 +
//});
/dev/null → api/tests/users/fetch.test.ts
1 +
import test from "ava";
2 +
import { PrismaClient } from '@prisma/client';
3 +
import { PrismaPg } from '@prisma/adapter-pg';
4 +
import request from 'supertest';
5 +
import bcrypt from 'bcryptjs';
6 +
import 'dotenv/config';
7 +
import { Express } from 'express';
8 +
import { createApp } from '../../src/createApp';
9 +
10 +
let prisma: PrismaClient;
11 +
let app: Express;
12 +
13 +
test.before(() => {
14 +
process.env.NODE_ENV = 'test';
15 +
16 +
const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
17 +
const adapter = new PrismaPg({ connectionString });
18 +
prisma = new PrismaClient({ adapter });
19 +
20 +
// Create Express app with test Prisma client
21 +
app = createApp(prisma);
22 +
});
23 +
24 +
test.beforeEach(async () => {
25 +
// Clean database before each test
26 +
try {
27 +
await prisma.user.deleteMany({});
28 +
} catch (err) {
29 +
console.error('Error cleaning test DB:', err);
30 +
}
31 +
});
32 +
33 +
test.after.always(async () => {
34 +
await prisma?.$disconnect();
35 +
});
36 +
37 +
//test("GET /users/:id retrieves a user", async (t) => {
38 +
// t.pass()
39 +
// // Create a user first
40 +
// const user = await prisma.user.create({
41 +
// data: {
42 +
// email: "get@example.com",
43 +
// firstName: "Get",
44 +
// lastName: "User",
45 +
// encryptedPassword: bcrypt.hashSync("password", 10)
46 +
// }
47 +
// });
48 +
//
49 +
// const response = await request(app)
50 +
// .get(`/users/${user.id}`)
51 +
// .expect('Content-Type', /json/)
52 +
// .expect(200);
53 +
//
54 +
// t.is(response.body.id, user.id);
55 +
// t.is(response.body.email, user.email);
56 +
// t.is(response.body.firstName, user.firstName);
57 +
// t.is(response.body.lastName, user.lastName);
58 +
// t.falsy(response.body.encryptedPassword); // Should not expose password
59 +
//});
60 +
//
61 +
//test("GET /users/:id returns 404 for non-existent user", async (t) => {
62 +
// const response = await request(app)
63 +
// .get('/users/999999')
64 +
// .expect('Content-Type', /json/)
65 +
// .expect(404);
66 +
//
67 +
// t.truthy(response.body.error);
68 +
//});
/dev/null → api/tests/users/update.test.ts
1 +
import test from "ava";
2 +
import { PrismaClient } from '@prisma/client';
3 +
import { PrismaPg } from '@prisma/adapter-pg';
4 +
import request from 'supertest';
5 +
import bcrypt from 'bcryptjs';
6 +
import 'dotenv/config';
7 +
import { Express } from 'express';
8 +
import { createApp } from '../../src/createApp';
9 +
10 +
let prisma: PrismaClient;
11 +
let app: Express;
12 +
13 +
test.before(() => {
14 +
process.env.NODE_ENV = 'test';
15 +
16 +
const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
17 +
const adapter = new PrismaPg({ connectionString });
18 +
prisma = new PrismaClient({ adapter });
19 +
20 +
// Create Express app with test Prisma client
21 +
app = createApp(prisma);
22 +
});
23 +
24 +
test.beforeEach(async () => {
25 +
// Clean database before each test
26 +
try {
27 +
await prisma.user.deleteMany({});
28 +
} catch (err) {
29 +
console.error('Error cleaning test DB:', err);
30 +
}
31 +
});
32 +
33 +
test.after.always(async () => {
34 +
await prisma?.$disconnect();
35 +
});
36 +
37 +
test("PUT /users/:id updates user profile", async (t) => {
38 +
// Create a user first
39 +
const user = await prisma.user.create({
40 +
data: {
41 +
email: "update@example.com",
42 +
firstName: "Original",
43 +
lastName: "Name",
44 +
encryptedPassword: bcrypt.hashSync("password", 10)
45 +
}
46 +
});
47 +
48 +
const updateData = {
49 +
firstName: "Updated",
50 +
lastName: "Profile"
51 +
};
52 +
53 +
// TODO: Gotta check why its failing. Most likely due to the
54 +
// cors/credentials: true line
55 +
const response = await request(app)
56 +
.put(`/users/${user.id}`)
57 +
.send(updateData)
58 +
.expect('Content-Type', /json/)
59 +
.expect(200);
60 +
61 +
t.is(response.body.user.firstName, updateData.firstName);
62 +
t.is(response.body.user.lastName, updateData.lastName);
63 +
t.is(response.body.user.email, user.email); // Should remain unchanged
64 +
65 +
// Verify in database
66 +
const dbUser = await prisma.user.findUnique({
67 +
where: { id: user.id }
68 +
});
69 +
70 +
t.is(dbUser!.firstName, updateData.firstName);
71 +
t.is(dbUser!.lastName, updateData.lastName);
72 +
});
73 +
74 +
test("PUT /users/:id returns 404 for non-existent user", async (t) => {
75 +
const response = await request(app)
76 +
.put('/users/999999')
77 +
.send({
78 +
firstName: "Updated",
79 +
lastName: "Name"
80 +
})
81 +
.expect('Content-Type', /json/)
82 +
.expect(404);
83 +
84 +
t.truthy(response.body.error);
85 +
});
/dev/null → api/tsconfig.test.json
1 +
{
2 +
"extends": "./tsconfig.json",
3 +
"compilerOptions": {
4 +
"module": "ESNext",
5 +
"moduleResolution": "node",
6 +
"allowImportingTsExtensions": true,
7 +
"resolveJsonModule": true,
8 +
"noEmit": true
9 +
},
10 +
"ts-node": {
11 +
"esm": true,
12 +
"experimentalSpecifierResolution": "node",
13 +
"transpileOnly": true,
14 +
"require": ["tsconfig-paths/register"]
15 +
}
16 +
}