eletrotupi / tcc/ api/tests/database-auth.test.tsc7215bd8104bddff6ccb49e8e753cf0132aee0fe
7.0 KB | 260 lines Raw
1
import test from "ava";
2
import { PrismaClient } from '@prisma/client';
3
import { PrismaPg } from '@prisma/adapter-pg';
4
import bcrypt from 'bcryptjs';
5
import 'dotenv/config';
6

7
let prisma: PrismaClient;
8

9
test.before(() => {
10
  process.env.NODE_ENV = 'test';
11

12
  const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
13
  const adapter = new PrismaPg({ connectionString });
14
  prisma = new PrismaClient({ adapter });
15
});
16

17
test.beforeEach(async () => {
18
  // Clean database before each test
19
  try {
20
    await prisma.user.deleteMany({});
21
  } catch (err) {
22
    console.error('Error cleaning test DB:', err);
23
  }
24
});
25

26
test.after.always(async () => {
27
  const deleteUsers = prisma.user.deleteMany();
28

29
  await prisma.$transaction([
30
    deleteUsers
31
  ])
32

33
  await prisma?.$disconnect();
34
});
35

36
// Basic database connectivity test
37
test("database connection works", async (t) => {
38
  const result = await prisma.$queryRaw`SELECT 1 as test`;
39
  t.truthy(result);
40
});
41

42
// User creation and authentication tests
43
test("can create user with encrypted password", async (t) => {
44
  const encryptedPassword = bcrypt.hashSync("testPassword123", 10);
45

46
  const user = await prisma.user.create({
47
    data: {
48
      email: "test@example.com",
49
      firstName: "Test",
50
      lastName: "User",
51
      encryptedPassword: encryptedPassword
52
    }
53
  });
54

55
  t.is(user.email, "test@example.com");
56
  t.is(user.firstName, "Test");
57
  t.truthy(user.id);
58

59
  // Verify password can be validated
60
  t.true(bcrypt.compareSync("testPassword123", user.encryptedPassword));
61
  t.false(bcrypt.compareSync("wrongPassword", user.encryptedPassword));
62
});
63

64
test("can find user by email for authentication", async (t) => {
65
  // Create user
66
  await prisma.user.create({
67
    data: {
68
      email: "login@example.com",
69
      firstName: "Login",
70
      lastName: "User",
71
      encryptedPassword: bcrypt.hashSync("password123", 10)
72
    }
73
  });
74

75
  // Simulate login lookup
76
  const foundUser = await prisma.user.findUnique({
77
    where: { email: "login@example.com" }
78
  });
79

80
  t.truthy(foundUser);
81
  t.is(foundUser!.email, "login@example.com");
82
  t.true(bcrypt.compareSync("password123", foundUser!.encryptedPassword));
83
  t.false(bcrypt.compareSync("wrongPassword", foundUser!.encryptedPassword));
84
});
85

86
test("email uniqueness constraint prevents duplicate registrations", async (t) => {
87
  const email = "duplicate@example.com";
88

89
  // Create first user
90
  await prisma.user.create({
91
    data: {
92
      email,
93
      firstName: "First",
94
      lastName: "User",
95
      encryptedPassword: bcrypt.hashSync("password", 10)
96
    }
97
  });
98

99
  // Try to create second user with same email - should fail
100
  await t.throwsAsync(
101
    async () => {
102
      await prisma.user.create({
103
        data: {
104
          email,
105
          firstName: "Second",
106
          lastName: "User",
107
          encryptedPassword: bcrypt.hashSync("password", 10)
108
        }
109
      });
110
    }
111
  );
112
});
113

114
test("password reset flow works with database", async (t) => {
115
  const resetToken = "reset-token-123";
116
  const resetExpires = new Date(Date.now() + 3600000); // 1 hour from now
117

118
  // Create user
119
  const user = await prisma.user.create({
120
    data: {
121
      email: "reset@example.com",
122
      firstName: "Reset",
123
      lastName: "User",
124
      encryptedPassword: bcrypt.hashSync("oldPassword", 10)
125
    }
126
  });
127

128
  // Step 1: Set reset token (forgot password)
129
  await prisma.user.update({
130
    where: { id: user.id },
131
    data: {
132
      passwordResetToken: resetToken,
133
      passwordResetExpires: resetExpires
134
    }
135
  });
136

137
  // Step 2: Find user by reset token
138
  const userWithToken = await prisma.user.findFirst({
139
    where: {
140
      passwordResetToken: resetToken,
141
      passwordResetExpires: { gte: new Date() } // Token not expired
142
    }
143
  });
144

145
  t.truthy(userWithToken);
146
  t.is(userWithToken!.email, "reset@example.com");
147

148
  // Step 3: Reset password
149
  const newPassword = bcrypt.hashSync("newPassword123", 10);
150
  const updatedUser = await prisma.user.update({
151
    where: { id: user.id },
152
    data: {
153
      encryptedPassword: newPassword,
154
      passwordResetToken: null,
155
      passwordResetExpires: null
156
    }
157
  });
158

159
  // Verify new password works and reset fields are cleared
160
  t.true(bcrypt.compareSync("newPassword123", updatedUser.encryptedPassword));
161
  t.false(bcrypt.compareSync("oldPassword", updatedUser.encryptedPassword));
162
  t.is(updatedUser.passwordResetToken, null);
163
  t.is(updatedUser.passwordResetExpires, null);
164
});
165

166
test("expired reset tokens are not found", async (t) => {
167
  const expiredResetToken = "expired-token";
168
  const pastDate = new Date(Date.now() - 3600000); // 1 hour ago
169

170
  // Create user with expired token
171
  const user = await prisma.user.create({
172
    data: {
173
      email: "expired@example.com",
174
      firstName: "Expired",
175
      lastName: "User",
176
      encryptedPassword: bcrypt.hashSync("password", 10),
177
      passwordResetToken: expiredResetToken,
178
      passwordResetExpires: pastDate
179
    }
180
  });
181

182
  // Try to find user by expired token
183
  const foundUser = await prisma.user.findFirst({
184
    where: {
185
      passwordResetToken: expiredResetToken,
186
      passwordResetExpires: { gte: new Date() } // Token not expired
187
    }
188
  });
189

190
  // Should not find the user because token is expired
191
  t.is(foundUser, null);
192

193
  // But user still exists
194
  const stillExists = await prisma.user.findUnique({ where: { id: user.id } });
195
  t.truthy(stillExists);
196
});
197

198
test("user profile update works", async (t) => {
199
  // Create user
200
  const user = await prisma.user.create({
201
    data: {
202
      email: "profile@example.com",
203
      firstName: "Original",
204
      lastName: "Name",
205
      encryptedPassword: bcrypt.hashSync("password", 10)
206
    }
207
  });
208

209
  // Update profile
210
  const updatedUser = await prisma.user.update({
211
    where: { id: user.id },
212
    data: {
213
      firstName: "Updated",
214
      lastName: "Profile"
215
    }
216
  });
217

218
  t.is(updatedUser.firstName, "Updated");
219
  t.is(updatedUser.lastName, "Profile");
220
  t.is(updatedUser.email, "profile@example.com"); // Should remain unchanged
221
  t.true(updatedUser.updatedAt > user.updatedAt); // updatedAt should be newer
222
});
223

224
test("timestamps are automatically managed", async (t) => {
225
  const beforeCreate = new Date();
226

227
  const user = await prisma.user.create({
228
    data: {
229
      email: "timestamps@example.com",
230
      firstName: "Time",
231
      lastName: "Stamps",
232
      encryptedPassword: bcrypt.hashSync("password", 10)
233
    }
234
  });
235

236
  const afterCreate = new Date();
237

238
  // Check creation timestamps
239
  t.true(user.createdAt >= beforeCreate);
240
  t.true(user.createdAt <= afterCreate);
241
  t.true(user.updatedAt >= beforeCreate);
242
  t.true(user.updatedAt <= afterCreate);
243

244
  // Wait and update to test updatedAt behavior
245
  await new Promise(resolve => setTimeout(resolve, 100));
246
  const beforeUpdate = new Date();
247

248
  const updatedUser = await prisma.user.update({
249
    where: { id: user.id },
250
    data: { firstName: "Updated" }
251
  });
252

253
  const afterUpdate = new Date();
254

255
  // createdAt should remain the same, updatedAt should be newer
256
  t.deepEqual(updatedUser.createdAt, user.createdAt);
257
  t.true(updatedUser.updatedAt >= beforeUpdate);
258
  t.true(updatedUser.updatedAt <= afterUpdate);
259
  t.true(updatedUser.updatedAt > user.updatedAt);
260
});