Skip to content

Latest commit

 

History

History
188 lines (133 loc) · 3.95 KB

File metadata and controls

188 lines (133 loc) · 3.95 KB

Registration and login tests

tests/
├── application/
│   └── use-cases/
│       ├── register-organization.test.ts
│       └── login.test.ts
├── infra/
│   └── auth/
│       ├── argon2-password-hasher.test.ts
│       └── jose-access-token-service.test.ts
├── delivery/
│   └── graphql/
│       ├── auth.test.ts
│       └── context.test.ts
└── database/
    ├── prisma-organization-registration-store.test.ts
    └── prisma-user-repo.test.ts

flow to test

register mutation
    ↓
argon2.hash(password)
    ↓
store passwordHash on User
login mutation
    ↓
argon2.verify(user.passwordHash, password)
    ↓
new SignJWT({ sub: user.id})
    ↓
return access token
every GraphQL request
    ↓
read Authorization: Bearer <token>
    ↓
jose.jwtVerify(...)
    ↓
put authenticated user/claims in GraphQL context
    ↓
resolvers/use-cases check context.actor

suggested order

register

tests/application/use-cases/register-organization.test.ts

describe("RegisterOrganization", () => {
  it("registers an organization with its initial user");

  it("normalizes the organization and user email addresses");

  it("hashes the raw password before persisting the user");

  it("does not call the registration store when password hashing fails");

  it("returns the created organization and user IDs");
});

tests/database/prisma-organization-registration-store.test.ts

describe("PrismaOrganizationRegistrationStore", () => {
  it("persists the organization and its linked initial user");

  it("rolls back the organization when initial user creation fails");

  it("translates a duplicate email constraint into a controlled error");
});

tests/delivery/graphql/auth.test.ts

describe("registerOrganization", () => {
  it("registers without an actor and returns the organization and user IDs");

  it("returns a controlled error for a duplicate email");

  it("rejects invalid registration input");
});

login

tests/application/use-cases/login.test.ts

describe("Login", () => {
  it("returns an access token for valid credentials");

  it("normalizes the email before looking up the user");

  it("rejects an unknown email with invalid credentials");

  it("rejects a wrong password with invalid credentials");
});

tests/infra/auth/argon2-password-hasher.test.ts

describe("Argon2PasswordHasher", () => {
  it("hashes a password without retaining the plaintext");

  it("verifies the correct password");

  it("rejects an incorrect password");
});

tests/infra/auth/jose-access-token-service.test.ts

describe("JoseAccessTokenService", () => {
  it("creates and verifies a token containing the user ID");

  it("rejects an expired token");

  it("rejects a token signed with another secret");

  it("rejects a token with an invalid issuer");

  it("rejects a token with an invalid audience");
});

tests/delivery/graphql/auth.test.ts

describe("login", () => {
  it("returns an access token for valid credentials");

  it("returns a controlled error for invalid credentials");

  it("rejects invalid login input");
});

user

tests/database/prisma-user-repo.test.ts

describe("PrismaUserRepository", () => {
  it("finds and maps a user by email");

  it("returns undefined when no user has the email");

  it("finds and maps a user by ID");

  it("returns undefined when no user has the ID");
});

tests/delivery/graphql/context.test.ts

describe("GraphQL context", () => {
  it("creates a context without an actor when no token is provided");

  it("rejects a malformed authorization header");

  it("rejects an invalid token");

  it("rejects a valid token when the user no longer exists");

  it("creates an authenticated actor from a valid token and existing user");

  it("returns a controlled GraphQL error for an invalid token");
});