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
66 changes: 66 additions & 0 deletions tests/e2e/course-prerequisites-check.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import type { FastifyInstance } from "fastify";
import { buildApp } from "../../src/server.js";

/**
* E2E tests for course prerequisites endpoint (#644).
*
* Covers:
* - GET /api/v1/courses/:id/prerequisites (prerequisites listing and completion status)
*/
describe("Course Prerequisites Check API (#644)", () => {
let app: FastifyInstance;

beforeAll(async () => {
app = await buildApp();
await app.ready();
});

afterAll(async () => {
await app.close();
});

const tokenFor = (sub: string) =>
app.jwt.sign({
sub,
stellarAddress:
"GALICE0000000000000000000000000000000000000000000000000000000",
});

it("rejects invalid course UUID parameter", async () => {
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/not-a-valid-uuid/prerequisites",
});

expect(response.statusCode).toBe(400);
const body = JSON.parse(response.payload);
expect(body.error).toBe("VALIDATION_ERROR");
});

it("handles unauthenticated request for prerequisites", async () => {
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/c1111111-1111-4111-8111-111111111111/prerequisites",
});

expect([200, 404, 500]).toContain(response.statusCode);
});

it("handles authenticated request returning prerequisites with completion status", async () => {
const token = tokenFor("a1111111-1111-4111-8111-111111111111");
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/c1111111-1111-4111-8111-111111111111/prerequisites",
headers: {
authorization: `Bearer ${token}`,
},
});

expect([200, 404, 500]).toContain(response.statusCode);
if (response.statusCode === 200) {
const body = JSON.parse(response.payload);
expect(body).toHaveProperty("success");
}
});
});
70 changes: 70 additions & 0 deletions tests/e2e/course-recommended.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import type { FastifyInstance } from "fastify";
import { buildApp } from "../../src/server.js";

/**
* E2E tests for course recommended endpoint (#642).
*
* Covers:
* - GET /api/v1/courses/recommended (personalized recommended courses, query validation)
*/
describe("Course Recommended API (#642)", () => {
let app: FastifyInstance;

beforeAll(async () => {
app = await buildApp();
await app.ready();
});

afterAll(async () => {
await app.close();
});

const tokenFor = (sub: string) =>
app.jwt.sign({
sub,
stellarAddress:
"GALICE0000000000000000000000000000000000000000000000000000000",
});

it("rejects unauthenticated requests", async () => {
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/recommended",
});

expect(response.statusCode).toBe(401);
});

it("handles authenticated recommendations request", async () => {
const token = tokenFor("a1111111-1111-4111-8111-111111111111");
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/recommended",
headers: {
authorization: `Bearer ${token}`,
},
});

expect([200, 404, 500]).toContain(response.statusCode);
if (response.statusCode === 200) {
const body = JSON.parse(response.payload);
expect(body).toHaveProperty("success");
}
});

it("rejects invalid limit query parameter", async () => {
const token = tokenFor("a1111111-1111-4111-8111-111111111111");
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/recommended?limit=0",
headers: {
authorization: `Bearer ${token}`,
},
});

expect(response.statusCode).toBe(400);
const body = JSON.parse(response.payload);
expect(body.error).toBe("VALIDATION_ERROR");
});
});
50 changes: 50 additions & 0 deletions tests/e2e/course-stats.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import type { FastifyInstance } from "fastify";
import { buildApp } from "../../src/server.js";

/**
* E2E tests for course stats endpoint (#645).
*
* Covers:
* - GET /api/v1/courses/stats (course statistics overview and caching behavior)
*/
describe("Course Stats API (#645)", () => {
let app: FastifyInstance;

beforeAll(async () => {
app = await buildApp();
await app.ready();
});

afterAll(async () => {
await app.close();
});

it("returns course statistics without requiring auth", async () => {
const response = await app.inject({
method: "GET",
url: "/api/v1/courses/stats",
});

expect([200, 500]).toContain(response.statusCode);
if (response.statusCode === 200) {
const body = JSON.parse(response.payload);
expect(body).toHaveProperty("success");
expect(body).toHaveProperty("data");
}
});

it("serves consistent responses across multiple calls (cached)", async () => {
const res1 = await app.inject({
method: "GET",
url: "/api/v1/courses/stats",
});

const res2 = await app.inject({
method: "GET",
url: "/api/v1/courses/stats",
});

expect(res1.statusCode).toBe(res2.statusCode);
});
});