Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completed Tasks 4 & 5 with unit tests. #8

Merged
merged 17 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/deno.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ jobs:
run: cd backend; deno lint

- name: Run tests
run: cd backend; deno test -A
run: cd backend; deno test --allow-read --allow-env --trace-leaks

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

# temporary files
*.tmp

# Environment variables
.env
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ deno run dev

(in `/backend`)
```sh
deno run test
deno test --allow-read --allow-env --trace-leaks
```
1 change: 1 addition & 0 deletions backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
JWT_SECRET_KEY=team6-resume-analyser
110 changes: 110 additions & 0 deletions backend/api/register_component/user_registration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { assertEquals } from "@std/assert";
import { createMockContext } from "@oak/oak/testing";
import { Router } from "@oak/oak";
import { userRegistration } from "./user_registration.ts";
import { createBody } from "../../util/util.test.ts";

Deno.test("POST /api/register - Successful Registration", async () => {
const ctx = createMockContext({
method: "POST",
path: "/api/register",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
email: "[email protected]",
password: "securePassword",
username: "user123",
}),
),
});

const router = new Router();
userRegistration(router);

// Mock `next` function
const next = async () => {};

// Call the registration handler
await router.routes()(ctx, next);

assertEquals(ctx.response.status, 201);
assertEquals(
ctx.response.body,
JSON.stringify({
isError: false,
message: "Registered successfully",
}),
);
});

Deno.test("POST /api/register - Missing Fields", async () => {
const ctx = createMockContext({
method: "POST",
path: "/api/register",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
email: "[email protected]",
username: "user123", // Missing password
}),
),
});

const router = new Router();
userRegistration(router);

const next = async () => {};

await router.routes()(ctx, next);

assertEquals(ctx.response.status, 400);
assertEquals(
ctx.response.body,
JSON.stringify({
isError: true,
message: "All fields are required",
}),
);
});

Deno.test("POST /api/register - Duplicate Email", async () => {
const mockBody = {
email: "[email protected]",
password: "securePassword",
username: "user123",
};

const router = new Router();
userRegistration(router);

// First registration
const ctx1 = createMockContext({
method: "POST",
path: "/api/register",
headers: [["Content-Type", "application/json"]],
body: createBody(JSON.stringify(mockBody)),
});

const next = async () => {};

await router.routes()(ctx1, next);

// Duplicate registration
const ctx2 = createMockContext({
method: "POST",
path: "/api/register",
headers: [["Content-Type", "application/json"]],
body: createBody(JSON.stringify(mockBody)),
});

await router.routes()(ctx2, next);

assertEquals(ctx2.response.status, 400);
assertEquals(
ctx2.response.body,
JSON.stringify({
isError: true,
message: "Email already registered",
}),
);
});
48 changes: 48 additions & 0 deletions backend/api/register_component/user_registration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Router } from "@oak/oak";
import { generateHash } from "../../services/generate_hash.ts";

export const users: Record<
string,
{ email: string; username: string; password: string }
> = {}; // In-memory store for simplicity

export function userRegistration(router: Router) {
router.post("/api/register", async (context) => {
// Get the body object
const body = await context.request.body.json();

// Parse the JSON body
const { email, password, username } = body;

// Validate inputs
if (!email || !password || !username) {
context.response.status = 400;
context.response.body = JSON.stringify({
isError: true,
message: "All fields are required",
});
return;
}

// Check for email uniqueness
if (users[email]) {
context.response.status = 400;
context.response.body = JSON.stringify({
isError: true,
message: "Email already registered",
});
return;
}

const hashedPassword = await generateHash(email, password);
// // Save the user to the store
users[email] = { email, username, password: hashedPassword };

// Respond with success
context.response.status = 201;
context.response.body = JSON.stringify({
isError: false,
message: "Registered successfully",
});
});
}
190 changes: 190 additions & 0 deletions backend/api/user_login_component/user_login.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { assertEquals } from "@std/assert";
import { createMockContext } from "@oak/oak/testing";
import { Router } from "@oak/oak";
import { userLogin } from "./user_login.ts";
import { users } from "../register_component/user_registration.ts";
import { createBody } from "../../util/util.test.ts";
import { generateHash } from "../../services/generate_hash.ts";

Deno.test("POST /api/login - Successful Login", async () => {
// Generate the hashed password
const hashedPassword = await generateHash(
"[email protected]",
"securePassword",
);

// Pre-populate the imported users object with a registered user
users["[email protected]"] = {
email: "[email protected]",
username: "user123",
password: hashedPassword, // SHA-256 hash for 'securePassword'
};
const ctx = createMockContext({
method: "POST",
path: "/api/login",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
email: "[email protected]",
password: "securePassword",
}),
),
});

const router = new Router();
userLogin(router);

// Mock `next` function
const next = async () => {};

// Call the login handler
await router.routes()(ctx, next);

// Assertions
assertEquals(ctx.response.status, 200);

// Cleanup: Remove the user from the users object after the test
delete users["[email protected]"];
});

Deno.test("POST /api/login - Missing User Email", async () => {
// Generate the hashed password
const hashedPassword = await generateHash(
"[email protected]",
"securePassword",
);

// Pre-populate the imported users object with a registered user
users["[email protected]"] = {
email: "[email protected]",
username: "user123",
password: hashedPassword, // SHA-256 hash for 'securePassword'
};
const ctx = createMockContext({
method: "POST",
path: "/api/login",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
password: "securePassword",
}),
),
});

const router = new Router();
userLogin(router);

// Mock `next` function
const next = async () => {};

// Call the login handler
await router.routes()(ctx, next);

// Assertions
assertEquals(ctx.response.status, 400);
assertEquals(
ctx.response.body,
JSON.stringify({
isError: true,
message: "Email and password are required",
}),
);

// Cleanup: Remove the user from the users object after the test
delete users["[email protected]"];
});

Deno.test("POST /api/login - Missing User Password", async () => {
// Generate the hashed password
const hashedPassword = await generateHash(
"[email protected]",
"securePassword",
);

// Pre-populate the imported users object with a registered user
users["[email protected]"] = {
email: "[email protected]",
username: "user123",
password: hashedPassword, // SHA-256 hash for 'securePassword'
};
const ctx = createMockContext({
method: "POST",
path: "/api/login",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
email: "[email protected]",
}),
),
});

const router = new Router();
userLogin(router);

// Mock `next` function
const next = async () => {};

// Call the login handler
await router.routes()(ctx, next);

// Assertions
assertEquals(ctx.response.status, 400);
assertEquals(
ctx.response.body,
JSON.stringify({
isError: true,
message: "Email and password are required",
}),
);

// Cleanup: Remove the user from the users object after the test
delete users["[email protected]"];
});

Deno.test("POST /api/login - Invalid password", async () => {
// Generate the hashed password
const hashedPassword = await generateHash(
"[email protected]",
"securePassword",
);

// Pre-populate the imported users object with a registered user
users["[email protected]"] = {
email: "[email protected]",
username: "user123",
password: hashedPassword, // SHA-256 hash for 'securePassword'
};
const ctx = createMockContext({
method: "POST",
path: "/api/login",
headers: [["Content-Type", "application/json"]],
body: createBody(
JSON.stringify({
email: "[email protected]",
password: "securePassword11",
}),
),
});

const router = new Router();
userLogin(router);

// Mock `next` function
const next = async () => {};

// Call the login handler
await router.routes()(ctx, next);

// Assertions
assertEquals(ctx.response.status, 401);
assertEquals(
ctx.response.body,
JSON.stringify({
isError: true,
message: "Invalid email or password",
}),
);

// Cleanup: Remove the user from the users object after the test
delete users["[email protected]"];
});
Loading