eletrotupi / tcc/ api/tests/database-auth.test.tsbdc1f3aa45bfff5472ae567a15ca20cc76ec3370
6.9 KB | 254 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
  await prisma?.$disconnect();
28
});
29

30
// Basic database connectivity test
31
test("database connection works", async (t) => {
32
  const result = await prisma.$queryRaw`SELECT 1 as test`;
33
  t.truthy(result);
34
});
35

36
// User creation and authentication tests
37
test("can create user with encrypted password", async (t) => {
38
  const encryptedPassword = bcrypt.hashSync("testPassword123", 10);
39

40
  const user = await prisma.user.create({
41
    data: {
42
      email: "test@example.com",
43
      firstName: "Test",
44
      lastName: "User",
45
      encryptedPassword: encryptedPassword
46
    }
47
  });
48

49
  t.is(user.email, "test@example.com");
50
  t.is(user.firstName, "Test");
51
  t.truthy(user.id);
52

53
  // Verify password can be validated
54
  t.true(bcrypt.compareSync("testPassword123", user.encryptedPassword));
55
  t.false(bcrypt.compareSync("wrongPassword", user.encryptedPassword));
56
});
57

58
test("can find user by email for authentication", async (t) => {
59
  // Create user
60
  await prisma.user.create({
61
    data: {
62
      email: "login@example.com",
63
      firstName: "Login",
64
      lastName: "User",
65
      encryptedPassword: bcrypt.hashSync("password123", 10)
66
    }
67
  });
68

69
  // Simulate login lookup
70
  const foundUser = await prisma.user.findUnique({
71
    where: { email: "login@example.com" }
72
  });
73

74
  t.truthy(foundUser);
75
  t.is(foundUser!.email, "login@example.com");
76
  t.true(bcrypt.compareSync("password123", foundUser!.encryptedPassword));
77
  t.false(bcrypt.compareSync("wrongPassword", foundUser!.encryptedPassword));
78
});
79

80
test("email uniqueness constraint prevents duplicate registrations", async (t) => {
81
  const email = "duplicate@example.com";
82

83
  // Create first user
84
  await prisma.user.create({
85
    data: {
86
      email,
87
      firstName: "First",
88
      lastName: "User",
89
      encryptedPassword: bcrypt.hashSync("password", 10)
90
    }
91
  });
92

93
  // Try to create second user with same email - should fail
94
  await t.throwsAsync(
95
    async () => {
96
      await prisma.user.create({
97
        data: {
98
          email,
99
          firstName: "Second",
100
          lastName: "User",
101
          encryptedPassword: bcrypt.hashSync("password", 10)
102
        }
103
      });
104
    }
105
  );
106
});
107

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

112
  // Create user
113
  const user = await prisma.user.create({
114
    data: {
115
      email: "reset@example.com",
116
      firstName: "Reset",
117
      lastName: "User",
118
      encryptedPassword: bcrypt.hashSync("oldPassword", 10)
119
    }
120
  });
121

122
  // Step 1: Set reset token (forgot password)
123
  await prisma.user.update({
124
    where: { id: user.id },
125
    data: {
126
      passwordResetToken: resetToken,
127
      passwordResetExpires: resetExpires
128
    }
129
  });
130

131
  // Step 2: Find user by reset token
132
  const userWithToken = await prisma.user.findFirst({
133
    where: {
134
      passwordResetToken: resetToken,
135
      passwordResetExpires: { gte: new Date() } // Token not expired
136
    }
137
  });
138

139
  t.truthy(userWithToken);
140
  t.is(userWithToken!.email, "reset@example.com");
141

142
  // Step 3: Reset password
143
  const newPassword = bcrypt.hashSync("newPassword123", 10);
144
  const updatedUser = await prisma.user.update({
145
    where: { id: user.id },
146
    data: {
147
      encryptedPassword: newPassword,
148
      passwordResetToken: null,
149
      passwordResetExpires: null
150
    }
151
  });
152

153
  // Verify new password works and reset fields are cleared
154
  t.true(bcrypt.compareSync("newPassword123", updatedUser.encryptedPassword));
155
  t.false(bcrypt.compareSync("oldPassword", updatedUser.encryptedPassword));
156
  t.is(updatedUser.passwordResetToken, null);
157
  t.is(updatedUser.passwordResetExpires, null);
158
});
159

160
test("expired reset tokens are not found", async (t) => {
161
  const expiredResetToken = "expired-token";
162
  const pastDate = new Date(Date.now() - 3600000); // 1 hour ago
163

164
  // Create user with expired token
165
  const user = await prisma.user.create({
166
    data: {
167
      email: "expired@example.com",
168
      firstName: "Expired",
169
      lastName: "User",
170
      encryptedPassword: bcrypt.hashSync("password", 10),
171
      passwordResetToken: expiredResetToken,
172
      passwordResetExpires: pastDate
173
    }
174
  });
175

176
  // Try to find user by expired token
177
  const foundUser = await prisma.user.findFirst({
178
    where: {
179
      passwordResetToken: expiredResetToken,
180
      passwordResetExpires: { gte: new Date() } // Token not expired
181
    }
182
  });
183

184
  // Should not find the user because token is expired
185
  t.is(foundUser, null);
186

187
  // But user still exists
188
  const stillExists = await prisma.user.findUnique({ where: { id: user.id } });
189
  t.truthy(stillExists);
190
});
191

192
test("user profile update works", async (t) => {
193
  // Create user
194
  const user = await prisma.user.create({
195
    data: {
196
      email: "profile@example.com",
197
      firstName: "Original",
198
      lastName: "Name",
199
      encryptedPassword: bcrypt.hashSync("password", 10)
200
    }
201
  });
202

203
  // Update profile
204
  const updatedUser = await prisma.user.update({
205
    where: { id: user.id },
206
    data: {
207
      firstName: "Updated",
208
      lastName: "Profile"
209
    }
210
  });
211

212
  t.is(updatedUser.firstName, "Updated");
213
  t.is(updatedUser.lastName, "Profile");
214
  t.is(updatedUser.email, "profile@example.com"); // Should remain unchanged
215
  t.true(updatedUser.updatedAt > user.updatedAt); // updatedAt should be newer
216
});
217

218
test("timestamps are automatically managed", async (t) => {
219
  const beforeCreate = new Date();
220

221
  const user = await prisma.user.create({
222
    data: {
223
      email: "timestamps@example.com",
224
      firstName: "Time",
225
      lastName: "Stamps",
226
      encryptedPassword: bcrypt.hashSync("password", 10)
227
    }
228
  });
229

230
  const afterCreate = new Date();
231

232
  // Check creation timestamps
233
  t.true(user.createdAt >= beforeCreate);
234
  t.true(user.createdAt <= afterCreate);
235
  t.true(user.updatedAt >= beforeCreate);
236
  t.true(user.updatedAt <= afterCreate);
237

238
  // Wait and update to test updatedAt behavior
239
  await new Promise(resolve => setTimeout(resolve, 100));
240
  const beforeUpdate = new Date();
241

242
  const updatedUser = await prisma.user.update({
243
    where: { id: user.id },
244
    data: { firstName: "Updated" }
245
  });
246

247
  const afterUpdate = new Date();
248

249
  // createdAt should remain the same, updatedAt should be newer
250
  t.deepEqual(updatedUser.createdAt, user.createdAt);
251
  t.true(updatedUser.updatedAt >= beforeUpdate);
252
  t.true(updatedUser.updatedAt <= afterUpdate);
253
  t.true(updatedUser.updatedAt > user.updatedAt);
254
});