/** * 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; 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; 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[]; 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; 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; 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[]; 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); }); }); });