Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Tests run against **live local services** — there is no mocking. Failures are
| `admin` | the above **plus** the Next.js admin frontend on `ADMIN_BASE_URL` | Every admin test fails in the `setup` project before its own body runs |
| both | `tests/.env` populated from `tests/.env.example` | `Missing required env var "X" — check tests/.env` (thrown by `roles.data.ts`) |

The simplest way to satisfy both rows is the full application stack from wcc-backend, driven from here: `npm run env:up` (= `${WCC_BACKEND_DIR:-../wcc-backend}/scripts/app-stack.sh up`). It starts the backend and the admin portal on `http://localhost:3000`, then runs the backend's seed script, which creates the MENTORSHIP_ADMIN, MENTOR (`mentor@wcc.dev`, long-term) and LEADER accounts the role fixtures log in as — plus `mentor-adhoc@wcc.dev` (ad-hoc mentor) and `member@wcc.dev` (plain member), not yet wired to fixtures — the MENTORS page and an open mentorship cycle (the backend itself bootstraps only `admin@wcc.dev`). Without the seed, only the admin account exists. `npm run env:purge` resets the database; `npm run env:cycle -- <long-term|ad-hoc>` switches which cycle is open. `tests/.env.example` already holds the stack's values — copy it to `tests/.env` as-is.
The simplest way to satisfy both rows is the full application stack from wcc-backend, driven from here: `npm run env:up` (= `${WCC_BACKEND_DIR:-../wcc-backend}/scripts/app-stack.sh up`). It starts the backend and the admin portal on `http://localhost:3000`, then runs the backend's seed script, which creates the MENTORSHIP_ADMIN, MENTOR (`mentor@wcc.dev`, long-term), LEADER, ad-hoc MENTOR (`mentor-adhoc@wcc.dev`), VIEWER (`member@wcc.dev`) and MENTEE (`mentee@wcc.dev`) accounts the role fixtures log in as — the MENTORS page and an open mentorship cycle (the backend itself bootstraps only `admin@wcc.dev`). Without the seed, only the admin account exists. `npm run env:purge` resets the database; `npm run env:cycle -- <long-term|ad-hoc>` switches which cycle is open. `tests/.env.example` already holds the stack's values — copy it to `tests/.env` as-is.

`admin` depends on the `setup` project, which drives a real browser login per role and writes `tests/admin/.auth/*.json`. A wall of admin failures with no assertion errors means setup could not reach the frontend.

Expand Down
1 change: 1 addition & 0 deletions helpers/datafactory/constants/admin-nav.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const NAV_LINKS: Record<Role, readonly string[]> = {
[Role.MENTORSHIP_ADMIN]: ["Dashboard", "Mentors", "Members", "Mentees", "Mentorship", "Cycles"],
[Role.LEADER]: ["Dashboard", "Mentors", "Members", "Users"],
[Role.MENTOR]: ["Dashboard", "Mentor Dashboard", "My Profile"],
[Role.MENTEE]: ["Dashboard"],
[Role.MENTOR_AD_HOC]: ["Dashboard", "Mentor Dashboard", "My Profile"],
[Role.MEMBER]: ["Dashboard"],
};
6 changes: 6 additions & 0 deletions helpers/datafactory/constants/roles.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum Role {
ADMIN = "admin",
LEADER = "leader",
MENTOR = "mentor",
MENTEE = "mentee",
MENTORSHIP_ADMIN = "mentorshipAdmin",
MEMBER = "member",
MENTOR_AD_HOC = "mentorAdHoc",
Expand Down Expand Up @@ -39,6 +40,11 @@ export const USERS: Record<Role, USER> = {
password: getEnvVariable("MENTOR_PASSWORD"),
storageState: "tests/admin/.auth/mentor.json",
},
[Role.MENTEE]: {
email: getEnvVariable("MENTEE_EMAIL"),
password: getEnvVariable("MENTEE_PASSWORD"),
storageState: "tests/admin/.auth/mentee.json",
},
[Role.LEADER]: {
email: getEnvVariable("LEADER_EMAIL"),
password: getEnvVariable("LEADER_PASSWORD"),
Expand Down
4 changes: 3 additions & 1 deletion helpers/fixtures/common.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { INVALID_TOKEN } from "helpers/datafactory/constants/auth.data";
* authApi X-API-KEY only public / registration endpoints
* noApiKeyApi no headers at all asserting the API-key filter
* invalidTokenApi X-API-KEY + unknown token asserting rejected sessions
* <role>Api X-API-KEY + role's token adminApi, leaderApi, mentorApi, mentorshipAdminApi, memberApi, mentorAdHocApi
* <role>Api X-API-KEY + role's token adminApi, leaderApi, mentorApi, menteeApi, mentorshipAdminApi, memberApi, mentorAdHocApi
* apiForRole(role) same, for role-matrix tests
* mailhog no headers, MAILHOG_HOST email delivery assertions (not an APIService: different host)
*
Expand All @@ -27,6 +27,7 @@ type TestFixtures = {
adminApi: APIService;
leaderApi: APIService;
mentorApi: APIService;
menteeApi: APIService;
mentorshipAdminApi: APIService;
memberApi: APIService;
mentorAdHocApi: APIService;
Expand Down Expand Up @@ -115,6 +116,7 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
adminApi: async ({ apiForRole }, use) => use(await apiForRole(Role.ADMIN)),
leaderApi: async ({ apiForRole }, use) => use(await apiForRole(Role.LEADER)),
mentorApi: async ({ apiForRole }, use) => use(await apiForRole(Role.MENTOR)),
menteeApi: async ({ apiForRole }, use) => use(await apiForRole(Role.MENTEE)),
mentorshipAdminApi: async ({ apiForRole }, use) => use(await apiForRole(Role.MENTORSHIP_ADMIN)),
memberApi: async ({ apiForRole }, use) => use(await apiForRole(Role.MEMBER)),
mentorAdHocApi: async ({ apiForRole }, use) => use(await apiForRole(Role.MENTOR_AD_HOC)),
Expand Down
3 changes: 3 additions & 0 deletions tests/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ MEMBER_PASSWORD=wcc-admin
MENTOR_AD_HOC_EMAIL=mentor-adhoc@wcc.dev
MENTOR_AD_HOC_PASSWORD=wcc-admin

MENTEE_EMAIL=mentee@wcc.dev
MENTEE_PASSWORD=wcc-admin

# Only for the @ad-hoc phase of `npm test`: where wcc-backend is checked out, relative to this repo.
# WCC_BACKEND_DIR=../wcc-backend

Expand Down
23 changes: 11 additions & 12 deletions tests/api/TEST_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@

**File:** `tests/api/tests/auth/auth.flow.spec.ts`

| ID | Title | Endpoint | Expected Result |
| ------- | --------------------------------------------------- | ---------------------- | ------------------------------------------------------ |
| AUTH-01 | Login with valid credentials returns token | `POST /api/auth/login` | 200, body contains `token`, `expiresAt`, `roles` |
| AUTH-02 | Login with missing email and password returns 400 | `POST /api/auth/login` | 400 (`@NotNull` validation; blank strings return 401) |
| AUTH-03 | Login with wrong password returns 401 | `POST /api/auth/login` | 401 |
| AUTH-04 | Get current user with valid token returns user info | `GET /api/auth/me` | 200, `roles` contains the role, `member.email` matches |
| AUTH-05 | Get current user without token returns 401 | `GET /api/auth/me` | 401 |
| AUTH-06 | Get current user with invalid token returns 401 | `GET /api/auth/me` | 401 |
| AUTH-07 | Get users with admin token returns user list | `GET /api/auth/users` | 200, non-empty list |
| AUTH-08 | Get current user without API key returns 401 | `GET /api/auth/me` | 401 |

---
| ID | Title | Endpoint | Expected Result |
| ------- | -------------------------------------------------------------------------- | ---------------------- | --------------------------------------------------------- |
| AUTH-01 | Login with valid credentials returns token | `POST /api/auth/login` | 200, body contains `token`, `expiresAt`, `roles` |
| AUTH-02 | Login with missing email and password returns 400 | `POST /api/auth/login` | 400 (`@NotNull` validation; blank strings return 401) |
| AUTH-03 | Login with wrong password returns 401 | `POST /api/auth/login` | 401 |
| AUTH-04 | Get current user with valid token returns user info | `GET /api/auth/me` | 200, `roles` contains the role, `member.email` matches |
| AUTH-05 | Get current user without token returns 401 | `GET /api/auth/me` | 401 |
| AUTH-06 | Get current user with invalid token returns 401 | `GET /api/auth/me` | 401 |
| AUTH-07 | Get users with admin token returns user list | `GET /api/auth/users` | 200, non-empty list |
| AUTH-08 | Get current user without API key returns 401 | `GET /api/auth/me` | 401 |
| AUTH-09 | Get current user with each seeded role's token returns that role's account | `GET /api/auth/me` | 200, `member.email` matches the role's account in `USERS` |

## Flow 2: Mentor — Register and Accept

Expand Down
12 changes: 12 additions & 0 deletions tests/api/tests/auth/auth.flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,15 @@ test.describe("AUTH-08: Request without API key", () => {
expect(response.status()).toBe(401);
});
});

test.describe("AUTH-09: Every seeded role can log in", () => {
for (const role of Object.values(Role)) {
test(`Get current user with ${role} token returns role's account`, { tag: "@smoke" }, async ({ apiForRole }) => {
const api = await apiForRole(role);
const response = await api.authentication.me(true);

const { member } = meResponseSchema.parse(await response.json());
expect(member.email).toBe(USERS[role].email);
});
}
});
Loading