Skip to content

Add E2E test coverage for users, courses, quizzes, and credentials modules #2

@DeFiVC

Description

@DeFiVC

Description

The API currently has E2E tests for only 2 of 6 modules:

  • tests/e2e/auth.test.ts — 4 tests
  • tests/e2e/rewards.test.ts — 4 tests

The remaining 4 modules have zero test coverage:

  • users — profile CRUD, progress aggregation
  • courses — listing, filtering, enrollment, detail
  • quizzes — generation, submission, scoring
  • credentials — listing, minting

Test pattern to follow

Use the existing structure from tests/e2e/auth.test.ts:

import { describe, it, expect, beforeAll, afterAll } from "vitest";
import type { FastifyInstance } from "fastify";
import { buildApp } from "../../src/server.js";

describe("ModuleName API", () => {
  let app: FastifyInstance;

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

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

  // Use app.inject() for HTTP requests
  // Use app.jwt.sign() to create test tokens
});

Files to create

tests/e2e/users.test.ts

Tests for GET /api/users/me, PUT /api/users/me, GET /api/users/me/progress:

describe("Users API")
  describe("GET /api/users/me")
    - should reject unauthenticated requests (401)
    - should return user profile when authenticated (200)
    - should return correct stellarAddress in profile

  describe("PUT /api/users/me")
    - should reject unauthenticated requests (401)
    - should update displayName
    - should update background and learningGoal
    - should reject invalid background values (400)

  describe("GET /api/users/me/progress")
    - should reject unauthenticated requests (401)
    - should return progress aggregate for enrolled user

Auth pattern for authenticated requests:

const token = app.jwt.sign({
  sub: "00000000-0000-0000-0000-000000000001",
  stellarAddress: "GALICE0000000000000000000000000000000000000000000000000000000",
});

const response = await app.inject({
  method: "GET",
  url: "/api/users/me",
  headers: { authorization: `Bearer ${token}` },
});

tests/e2e/courses.test.ts

Tests for GET /api/courses, GET /api/courses/:id, POST /api/courses/:id/enroll:

describe("Courses API")
  describe("GET /api/courses")
    - should return paginated course list (200)
    - should filter by difficulty query param
    - should return enrolledCount for each course
    - should include isEnrolled when authenticated

  describe("GET /api/courses/:id")
    - should return course detail with modules (200)
    - should return 404 for non-existent course
    - should include enrollment status when authenticated

  describe("POST /api/courses/:id/enroll")
    - should reject unauthenticated requests (401)
    - should enroll user in course (201)
    - should reject duplicate enrollment (409)
    - should return 404 for non-existent course

tests/e2e/quizzes.test.ts

Tests for POST /api/quizzes/generate, POST /api/quizzes/:id/submit:

describe("Quizzes API")
  describe("POST /api/quizzes/generate")
    - should reject unauthenticated requests (401)
    - should reject if not enrolled in course (403)
    - should return generated quiz questions (200)
    - should return same quiz on repeat generation (idempotent)

  describe("POST /api/quizzes/:id/submit")
    - should reject unauthenticated requests (401)
    - should submit answers and return score (200)
    - should include feedback in response
    - should set rewardAvailable when score >= 70%
    - should reject duplicate submission (409)

tests/e2e/credentials.test.ts

Tests for GET /api/credentials, POST /api/credentials/mint:

describe("Credentials API")
  describe("GET /api/credentials")
    - should reject unauthenticated requests (401)
    - should return user's credentials list (200)

  describe("POST /api/credentials/mint")
    - should reject unauthenticated requests (401)
    - should mint credential for completed course (201)
    - should reject if course not completed
    - should reject duplicate credential (409)

Important notes

  • Some tests may fail without a running PostgreSQL + Redis. Use beforeAll/afterAll for cleanup, or consider mocking the database layer.
  • The app.inject() method does NOT require a running server — it uses Fastify's test injection.
  • Use realistic UUIDs for user IDs and Stellar addresses that match the DB schema constraints.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official Campaigngood first issueGood for newcomerstestsTest additions or improvementstypescriptTypeScript language

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions