UNPKG

@aaronshaf/ger

Version:

Gerrit CLI and SDK - A modern CLI tool and TypeScript SDK for Gerrit Code Review, built with Effect-TS

257 lines (217 loc) 8.32 kB
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test' import { Effect, Layer } from 'effect' import { HttpResponse, http } from 'msw' import { setupServer } from 'msw/node' import { GerritApiServiceLive } from '@/api/gerrit' import { groupsMembersCommand } from '@/cli/commands/groups-members' import { ConfigService } from '@/services/config' import { createMockConfigService } from './helpers/config-mock' const mockMembers = [ { _account_id: 1000, name: 'John Doe', email: 'john@example.com', username: 'jdoe', }, { _account_id: 1001, name: 'Jane Smith', email: 'jane@example.com', username: 'jsmith', }, { _account_id: 1002, name: 'Bob Johnson', email: 'bob@example.com', }, ] const server = setupServer( http.get('*/a/accounts/self', ({ request }) => { const auth = request.headers.get('Authorization') if (!auth || !auth.startsWith('Basic ')) { return HttpResponse.text('Unauthorized', { status: 401 }) } return HttpResponse.json({ _account_id: 1000, name: 'Test User', email: 'test@example.com', }) }), ) describe('groups-members command', () => { let mockConsoleLog: ReturnType<typeof mock> let mockConsoleError: ReturnType<typeof mock> beforeAll(() => { server.listen({ onUnhandledRequest: 'bypass' }) }) afterAll(() => { server.close() }) beforeEach(() => { mockConsoleLog = mock(() => {}) mockConsoleError = mock(() => {}) console.log = mockConsoleLog console.error = mockConsoleError }) afterEach(() => { server.resetHandlers() }) it('should list all members of a group', async () => { server.use( http.get('*/a/groups/administrators/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify(mockMembers)}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('administrators', {}).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('Members of "administrators" (3)') expect(output).toContain('John Doe') expect(output).toContain('Email: john@example.com') expect(output).toContain('Username: jdoe') expect(output).toContain('Account ID: 1000') expect(output).toContain('Jane Smith') expect(output).toContain('Bob Johnson') }) it('should handle group with no members', async () => { server.use( http.get('*/a/groups/empty-group/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify([])}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('empty-group', {}).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('Group "empty-group" has no members') }) it('should output XML format when --xml flag is used', async () => { server.use( http.get('*/a/groups/administrators/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify(mockMembers)}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('administrators', { xml: true }).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('<?xml version="1.0" encoding="UTF-8"?>') expect(output).toContain('<group_members_result>') expect(output).toContain('<status>success</status>') expect(output).toContain('<group_id><![CDATA[administrators]]></group_id>') expect(output).toContain('<count>3</count>') expect(output).toContain('<members>') expect(output).toContain('<member>') expect(output).toContain('<account_id>1000</account_id>') expect(output).toContain('<name><![CDATA[John Doe]]></name>') expect(output).toContain('<email><![CDATA[john@example.com]]></email>') expect(output).toContain('<username><![CDATA[jdoe]]></username>') expect(output).toContain('</member>') expect(output).toContain('</members>') expect(output).toContain('</group_members_result>') }) it('should handle empty members with XML format', async () => { server.use( http.get('*/a/groups/empty-group/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify([])}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('empty-group', { xml: true }).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('<group_members_result>') expect(output).toContain('<status>success</status>') expect(output).toContain('<count>0</count>') expect(output).toContain('<members />') }) it('should handle group not found (404)', async () => { server.use( http.get('*/a/groups/nonexistent/members/', () => { return HttpResponse.text('Not Found', { status: 404 }) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('nonexistent', {}).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await expect(Effect.runPromise(program)).rejects.toThrow() }) it('should handle network errors gracefully', async () => { server.use( http.get('*/a/groups/administrators/members/', () => { return HttpResponse.error() }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('administrators', {}).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await expect(Effect.runPromise(program)).rejects.toThrow() }) it('should handle members without optional fields (email, name)', async () => { const minimalMembers = [ { _account_id: 1000, }, { _account_id: 1001, username: 'jsmith', }, ] server.use( http.get('*/a/groups/minimal-group/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify(minimalMembers)}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('minimal-group', {}).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('Account 1000') expect(output).toContain('Account ID: 1000') expect(output).toContain('jsmith') expect(output).toContain('Account ID: 1001') }) it('should handle members without optional fields in XML', async () => { const minimalMembers = [ { _account_id: 1000, }, ] server.use( http.get('*/a/groups/minimal-group/members/', () => { return HttpResponse.text(`)]}'\n${JSON.stringify(minimalMembers)}`) }), ) const mockConfigLayer = Layer.succeed(ConfigService, createMockConfigService()) const program = groupsMembersCommand('minimal-group', { xml: true }).pipe( Effect.provide(GerritApiServiceLive), Effect.provide(mockConfigLayer), ) await Effect.runPromise(program) const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n') expect(output).toContain('<account_id>1000</account_id>') expect(output).not.toContain('<name>') expect(output).not.toContain('<email>') expect(output).not.toContain('<username>') }) })