-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: configure jest for TypeScript and test database setup * feat: expand e2e tests for auth.controller and add new migration file * feat: update password change functionality and schema * feat: initialize test context and services * feat: create unit test setup function * feat: initialize test context and services for e2e tests * feat: create e2e test setup function * refactor: improve user and credential factories, add userWithCredentialFactory * feat: update dependencies and add JWT_REFRESH_SECRET * refactor: refactor user factory and add hashPassword function * fix: update password handling in user controller e2e tests * feat: refactor userFactory and authService.register * chore: update assertions in auth and users service tests
- Loading branch information
1 parent
26173dc
commit 03fcabb
Showing
32 changed files
with
1,599 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
DATABASE_URL="postgres://postgres:guidebook@localhost:5432/guidebook" | ||
JWT_SECRET= | ||
REFRESH_SECRET= | ||
JWT_REFRESH_SECRET= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { Config } from "jest"; | ||
|
||
const config: Config = { | ||
moduleFileExtensions: ["js", "json", "ts"], | ||
rootDir: ".", | ||
testRegex: ".*\\.spec\\.ts$", | ||
transform: { | ||
"^.+\\.(t|j)s$": "ts-jest", | ||
}, | ||
collectCoverageFrom: ["**/*.(t|j)s"], | ||
coverageDirectory: "./coverage", | ||
testEnvironment: "node", | ||
setupFilesAfterEnv: ["<rootDir>/test/jest-setup.ts"], | ||
moduleNameMapper: { | ||
"^src/(.*)$": "<rootDir>/src/$1", | ||
}, | ||
modulePaths: ["."], | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
examples/common_nestjs_remix/apps/api/src/auth/__tests__/auth.controller.e2e-spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
import { DatabasePg } from "../../common/index"; | ||
import { INestApplication } from "@nestjs/common"; | ||
import { isArray } from "lodash"; | ||
import request from "supertest"; | ||
import { createUserFactory } from "../../../test/factory/user.factory"; | ||
import { createE2ETest } from "../../../test/create-e2e-test"; | ||
import { AuthService } from "../auth.service"; | ||
import * as cookie from "cookie"; | ||
|
||
describe("AuthController (e2e)", () => { | ||
let app: INestApplication; | ||
let authService: AuthService; | ||
let db: DatabasePg; | ||
let userFactory: ReturnType<typeof createUserFactory>; | ||
|
||
beforeAll(async () => { | ||
const { app: testApp } = await createE2ETest(); | ||
app = testApp; | ||
authService = app.get(AuthService); | ||
db = app.get("DB"); | ||
userFactory = createUserFactory(db); | ||
}); | ||
|
||
describe("POST /auth/register", () => { | ||
it("should register a new user", async () => { | ||
const user = await userFactory | ||
.withCredentials({ password: "password123" }) | ||
.build(); | ||
|
||
const response = await request(app.getHttpServer()) | ||
.post("/auth/register") | ||
.set("Accept", "application/json") | ||
.set("Content-Type", "application/json") | ||
.send({ | ||
email: user.email, | ||
password: user.credentials?.password, | ||
}); | ||
|
||
expect(response.status).toEqual(201); | ||
expect(response.body.data).toHaveProperty("id"); | ||
expect(response.body.data.email).toBe(user.email); | ||
}); | ||
|
||
it("should return 409 if user already exists", async () => { | ||
const existingUser = { | ||
email: "[email protected]", | ||
password: "password123", | ||
}; | ||
|
||
await authService.register(existingUser.email, existingUser.password); | ||
|
||
await request(app.getHttpServer()) | ||
.post("/auth/register") | ||
.send(existingUser) | ||
.expect(409); | ||
}); | ||
}); | ||
|
||
describe("POST /auth/login", () => { | ||
it("should login and return user data with cookies", async () => { | ||
const user = await userFactory | ||
.withCredentials({ | ||
password: "password123", | ||
}) | ||
.create({ | ||
email: "[email protected]", | ||
}); | ||
|
||
const response = await request(app.getHttpServer()) | ||
.post("/auth/login") | ||
.send({ | ||
email: user.email, | ||
password: user.credentials?.password, | ||
}); | ||
|
||
expect(response.status).toEqual(201); | ||
expect(response.body.data).toHaveProperty("id"); | ||
expect(response.body.data.email).toBe(user.email); | ||
expect(response.headers["set-cookie"]).toBeDefined(); | ||
expect(response.headers["set-cookie"].length).toBe(2); | ||
}); | ||
|
||
it("should return 401 for invalid credentials", async () => { | ||
await request(app.getHttpServer()) | ||
.post("/auth/login") | ||
.send({ | ||
email: "[email protected]", | ||
password: "wrongpassword", | ||
}) | ||
.expect(401); | ||
}); | ||
}); | ||
|
||
describe("POST /auth/logout", () => { | ||
it("should clear token cookies for a logged-in user", async () => { | ||
let accessToken = ""; | ||
|
||
const user = userFactory.build(); | ||
const password = "password123"; | ||
await authService.register(user.email, password); | ||
|
||
const loginResponse = await request(app.getHttpServer()) | ||
.post("/auth/login") | ||
.send({ | ||
email: user.email, | ||
password: password, | ||
}); | ||
|
||
const cookies = loginResponse.headers["set-cookie"]; | ||
|
||
if (Array.isArray(cookies)) { | ||
cookies.forEach((cookieString) => { | ||
const parsedCookie = cookie.parse(cookieString); | ||
if ("access_token" in parsedCookie) { | ||
accessToken = parsedCookie.access_token; | ||
} | ||
}); | ||
} | ||
|
||
const logoutResponse = await request(app.getHttpServer()) | ||
.post("/auth/logout") | ||
.set("Cookie", `access_token=${accessToken};`); | ||
|
||
const logoutCookies = logoutResponse.headers["set-cookie"]; | ||
|
||
expect(loginResponse.status).toBe(201); | ||
expect(logoutResponse.status).toBe(201); | ||
expect(logoutResponse.headers["set-cookie"]).toBeDefined(); | ||
expect(logoutCookies.length).toBe(2); | ||
expect(logoutCookies[0]).toContain("access_token=;"); | ||
expect(logoutCookies[1]).toContain("refresh_token=;"); | ||
}); | ||
}); | ||
|
||
describe("POST /auth/refresh", () => { | ||
it("should refresh tokens", async () => { | ||
const user = await userFactory.build(); | ||
const password = "password123"; | ||
await authService.register(user.email, password); | ||
|
||
const loginResponse = await request(app.getHttpServer()) | ||
.post("/auth/login") | ||
.send({ | ||
email: user.email, | ||
password: password, | ||
}) | ||
.expect(201); | ||
|
||
const cookies = loginResponse.headers["set-cookie"]; | ||
|
||
let refreshToken = ""; | ||
|
||
if (isArray(cookies)) { | ||
cookies.forEach((cookie) => { | ||
if (cookie.startsWith("refresh_token=")) { | ||
refreshToken = cookie; | ||
} | ||
}); | ||
} | ||
|
||
const response = await request(app.getHttpServer()) | ||
.post("/auth/refresh") | ||
.set("Cookie", [refreshToken]) | ||
.expect(201); | ||
|
||
expect(response.headers["set-cookie"]).toBeDefined(); | ||
expect(response.headers["set-cookie"].length).toBe(2); | ||
}); | ||
|
||
it("should return 401 for invalid refresh token", async () => { | ||
await request(app.getHttpServer()) | ||
.post("/auth/refresh") | ||
.set("Cookie", ["refreshToken=invalid_token"]) | ||
.expect(401); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.