Add unit tests for models and services

- Implemented unit tests for the Package model, covering methods such as generateId, findById, findByName, and version management.
- Created unit tests for the Repository model, including repository creation, name validation, and retrieval methods.
- Added tests for the Session model, focusing on session creation, validation, and invalidation.
- Developed unit tests for the User model, ensuring user creation, password hashing, and retrieval methods function correctly.
- Implemented AuthService tests, validating login, token refresh, and session management.
- Added TokenService tests, covering token creation, validation, and revocation processes.
This commit is contained in:
2025-11-28 15:27:04 +00:00
parent 61324ba195
commit 44e92d48f2
50 changed files with 4403 additions and 108 deletions

View File

@@ -0,0 +1,169 @@
/**
* Authentication integration tests
* Tests the full authentication flow through the API
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import {
setupTestDb,
teardownTestDb,
cleanupTestDb,
createTestUser,
post,
get,
assertStatus,
createAuthHeader,
} from '../helpers/index.ts';
describe('Auth API Integration', () => {
beforeAll(async () => {
await setupTestDb();
});
afterAll(async () => {
await teardownTestDb();
});
beforeEach(async () => {
await cleanupTestDb();
});
describe('POST /api/v1/auth/login', () => {
it('should login with valid credentials', async () => {
const { user, password } = await createTestUser({
email: 'api-login@example.com',
status: 'active',
});
const response = await post('/api/v1/auth/login', {
email: user.email,
password: password,
});
assertStatus(response, 200);
const body = response.body as Record<string, unknown>;
assertExists(body.accessToken);
assertExists(body.refreshToken);
assertExists(body.user);
});
it('should return 401 for invalid credentials', async () => {
const response = await post('/api/v1/auth/login', {
email: 'nonexistent@example.com',
password: 'wrongpassword',
});
assertStatus(response, 401);
const body = response.body as Record<string, unknown>;
assertEquals(body.error, 'INVALID_CREDENTIALS');
});
it('should return 401 for inactive user', async () => {
const { user, password } = await createTestUser({
email: 'suspended@example.com',
status: 'suspended',
});
const response = await post('/api/v1/auth/login', {
email: user.email,
password: password,
});
assertStatus(response, 401);
const body = response.body as Record<string, unknown>;
assertEquals(body.error, 'ACCOUNT_INACTIVE');
});
});
describe('POST /api/v1/auth/refresh', () => {
it('should refresh access token', async () => {
const { user, password } = await createTestUser({
email: 'refresh@example.com',
status: 'active',
});
// Login first
const loginResponse = await post('/api/v1/auth/login', {
email: user.email,
password: password,
});
const loginBody = loginResponse.body as Record<string, unknown>;
// Refresh
const refreshResponse = await post('/api/v1/auth/refresh', {
refreshToken: loginBody.refreshToken,
});
assertStatus(refreshResponse, 200);
const refreshBody = refreshResponse.body as Record<string, unknown>;
assertExists(refreshBody.accessToken);
});
it('should return 401 for invalid refresh token', async () => {
const response = await post('/api/v1/auth/refresh', {
refreshToken: 'invalid-token',
});
assertStatus(response, 401);
});
});
describe('GET /api/v1/auth/me', () => {
it('should return current user info', async () => {
const { user, password } = await createTestUser({
email: 'me@example.com',
status: 'active',
});
// Login
const loginResponse = await post('/api/v1/auth/login', {
email: user.email,
password: password,
});
const loginBody = loginResponse.body as Record<string, unknown>;
// Get current user
const meResponse = await get(
'/api/v1/auth/me',
createAuthHeader(loginBody.accessToken as string)
);
assertStatus(meResponse, 200);
const meBody = meResponse.body as Record<string, unknown>;
assertEquals(meBody.email, user.email);
});
it('should return 401 without token', async () => {
const response = await get('/api/v1/auth/me');
assertStatus(response, 401);
});
});
describe('POST /api/v1/auth/logout', () => {
it('should invalidate session', async () => {
const { user, password } = await createTestUser({
email: 'logout@example.com',
status: 'active',
});
// Login
const loginResponse = await post('/api/v1/auth/login', {
email: user.email,
password: password,
});
const loginBody = loginResponse.body as Record<string, unknown>;
const token = loginBody.accessToken as string;
// Logout
const logoutResponse = await post('/api/v1/auth/logout', {}, createAuthHeader(token));
assertStatus(logoutResponse, 200);
// Token should no longer work
const meResponse = await get('/api/v1/auth/me', createAuthHeader(token));
assertStatus(meResponse, 401);
});
});
});

View File

@@ -0,0 +1,228 @@
/**
* Organization integration tests
* Tests organization CRUD and member management through the API
*/
import { assertEquals, assertExists } from 'jsr:@std/assert';
import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd';
import {
setupTestDb,
teardownTestDb,
cleanupTestDb,
createTestUser,
loginUser,
post,
get,
put,
del,
assertStatus,
createAuthHeader,
} from '../helpers/index.ts';
describe('Organization API Integration', () => {
let accessToken: string;
let testUserId: string;
beforeAll(async () => {
await setupTestDb();
});
afterAll(async () => {
await teardownTestDb();
});
beforeEach(async () => {
await cleanupTestDb();
const { user, password } = await createTestUser({ status: 'active' });
testUserId = user.id;
const tokens = await loginUser(user.email, password);
accessToken = tokens.accessToken;
});
describe('POST /api/v1/organizations', () => {
it('should create organization', async () => {
const response = await post(
'/api/v1/organizations',
{
name: 'my-org',
displayName: 'My Organization',
description: 'A test organization',
},
createAuthHeader(accessToken)
);
assertStatus(response, 201);
const body = response.body as Record<string, unknown>;
assertEquals(body.name, 'my-org');
assertEquals(body.displayName, 'My Organization');
});
it('should create organization with dots in name', async () => {
const response = await post(
'/api/v1/organizations',
{
name: 'push.rocks',
displayName: 'Push Rocks',
},
createAuthHeader(accessToken)
);
assertStatus(response, 201);
const body = response.body as Record<string, unknown>;
assertEquals(body.name, 'push.rocks');
});
it('should reject duplicate org name', async () => {
await post(
'/api/v1/organizations',
{ name: 'duplicate', displayName: 'First' },
createAuthHeader(accessToken)
);
const response = await post(
'/api/v1/organizations',
{ name: 'duplicate', displayName: 'Second' },
createAuthHeader(accessToken)
);
assertStatus(response, 409);
});
it('should reject invalid org name', async () => {
const response = await post(
'/api/v1/organizations',
{ name: '.invalid', displayName: 'Invalid' },
createAuthHeader(accessToken)
);
assertStatus(response, 400);
});
});
describe('GET /api/v1/organizations', () => {
it('should list user organizations', async () => {
// Create some organizations
await post(
'/api/v1/organizations',
{ name: 'org1', displayName: 'Org 1' },
createAuthHeader(accessToken)
);
await post(
'/api/v1/organizations',
{ name: 'org2', displayName: 'Org 2' },
createAuthHeader(accessToken)
);
const response = await get('/api/v1/organizations', createAuthHeader(accessToken));
assertStatus(response, 200);
const body = response.body as Record<string, unknown>[];
assertEquals(body.length >= 2, true);
});
});
describe('GET /api/v1/organizations/:orgName', () => {
it('should get organization by name', async () => {
await post(
'/api/v1/organizations',
{ name: 'get-me', displayName: 'Get Me' },
createAuthHeader(accessToken)
);
const response = await get('/api/v1/organizations/get-me', createAuthHeader(accessToken));
assertStatus(response, 200);
const body = response.body as Record<string, unknown>;
assertEquals(body.name, 'get-me');
});
it('should return 404 for non-existent org', async () => {
const response = await get(
'/api/v1/organizations/non-existent',
createAuthHeader(accessToken)
);
assertStatus(response, 404);
});
});
describe('PUT /api/v1/organizations/:orgName', () => {
it('should update organization', async () => {
await post(
'/api/v1/organizations',
{ name: 'update-me', displayName: 'Original' },
createAuthHeader(accessToken)
);
const response = await put(
'/api/v1/organizations/update-me',
{ displayName: 'Updated', description: 'New description' },
createAuthHeader(accessToken)
);
assertStatus(response, 200);
const body = response.body as Record<string, unknown>;
assertEquals(body.displayName, 'Updated');
assertEquals(body.description, 'New description');
});
});
describe('DELETE /api/v1/organizations/:orgName', () => {
it('should delete organization', async () => {
await post(
'/api/v1/organizations',
{ name: 'delete-me', displayName: 'Delete Me' },
createAuthHeader(accessToken)
);
const response = await del('/api/v1/organizations/delete-me', createAuthHeader(accessToken));
assertStatus(response, 200);
// Verify deleted
const getResponse = await get(
'/api/v1/organizations/delete-me',
createAuthHeader(accessToken)
);
assertStatus(getResponse, 404);
});
});
describe('Organization Members', () => {
it('should list organization members', async () => {
await post(
'/api/v1/organizations',
{ name: 'members-org', displayName: 'Members Org' },
createAuthHeader(accessToken)
);
const response = await get(
'/api/v1/organizations/members-org/members',
createAuthHeader(accessToken)
);
assertStatus(response, 200);
const body = response.body as Record<string, unknown>[];
assertEquals(body.length >= 1, true); // At least the creator
});
it('should add member to organization', async () => {
// Create another user
const { user: newUser } = await createTestUser({ email: 'newmember@example.com' });
await post(
'/api/v1/organizations',
{ name: 'add-member-org', displayName: 'Add Member Org' },
createAuthHeader(accessToken)
);
const response = await post(
'/api/v1/organizations/add-member-org/members',
{ userId: newUser.id, role: 'member' },
createAuthHeader(accessToken)
);
assertStatus(response, 201);
});
});
});