api/tests: fix ava loading paths & add/rework user specs
Parents:
ad585117 file(s) changed
- api/ava.config.mjs +8 -17
- api/tests/database-auth.test.ts +6 -0
- api/tests/users/create.test.ts +125 -0
- api/tests/users/delete.test.ts +58 -0
- api/tests/users/fetch.test.ts +68 -0
- api/tests/users/update.test.ts +85 -0
- api/tsconfig.test.json +15 -0
api/ava.config.mjs
@@ -1,27 +1,19 @@
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
@@ -24,6 +24,12 @@ }
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
api/tests/users/create.test.ts
@@ -0,0 +1,125 @@
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 + });
api/tests/users/delete.test.ts
@@ -0,0 +1,58 @@
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 + //});
api/tests/users/fetch.test.ts
@@ -0,0 +1,68 @@
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 + //});
api/tests/users/update.test.ts
@@ -0,0 +1,85 @@
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 + });
api/tsconfig.test.json
@@ -0,0 +1,16 @@
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 + }