|
| 1 | +/** |
| 2 | + * Unit tests for Coana spawn utilities. |
| 3 | + * |
| 4 | + * Purpose: |
| 5 | + * Tests the Coana CLI spawning functionality. |
| 6 | + * |
| 7 | + * Test Coverage: |
| 8 | + * - spawnCoana function export |
| 9 | + * - Environment variable handling |
| 10 | + * - Error handling |
| 11 | + * |
| 12 | + * Related Files: |
| 13 | + * - utils/coana/spawn.mts (implementation) |
| 14 | + */ |
| 15 | + |
| 16 | +import { beforeEach, describe, expect, it, vi } from 'vitest' |
| 17 | + |
| 18 | +// Mock dependencies. |
| 19 | +const mockSpawnNode = vi.hoisted(() => vi.fn()) |
| 20 | +const mockDlxPackage = vi.hoisted(() => vi.fn()) |
| 21 | +const mockGetDefaultOrgSlug = vi.hoisted(() => vi.fn()) |
| 22 | +const mockGetCliVersion = vi.hoisted(() => vi.fn()) |
| 23 | +const mockGetCoanaVersion = vi.hoisted(() => vi.fn()) |
| 24 | +const mockGetDefaultApiToken = vi.hoisted(() => vi.fn()) |
| 25 | +const mockGetDefaultProxyUrl = vi.hoisted(() => vi.fn()) |
| 26 | +const mockGetErrorCause = vi.hoisted(() => vi.fn()) |
| 27 | + |
| 28 | +vi.mock('@socketsecurity/lib/dlx/package', () => ({ |
| 29 | + dlxPackage: mockDlxPackage, |
| 30 | +})) |
| 31 | + |
| 32 | +vi.mock('../../../../src/commands/ci/fetch-default-org-slug.mts', () => ({ |
| 33 | + getDefaultOrgSlug: mockGetDefaultOrgSlug, |
| 34 | +})) |
| 35 | + |
| 36 | +vi.mock('../../../../src/env/cli-version.mts', () => ({ |
| 37 | + getCliVersion: mockGetCliVersion, |
| 38 | +})) |
| 39 | + |
| 40 | +vi.mock('../../../../src/env/coana-version.mts', () => ({ |
| 41 | + getCoanaVersion: mockGetCoanaVersion, |
| 42 | +})) |
| 43 | + |
| 44 | +vi.mock('../../../../src/env/socket-cli-coana-local-path.mts', () => ({ |
| 45 | + SOCKET_CLI_COANA_LOCAL_PATH: null, |
| 46 | +})) |
| 47 | + |
| 48 | +vi.mock('../../../../src/utils/socket/sdk.mts', () => ({ |
| 49 | + getDefaultApiToken: mockGetDefaultApiToken, |
| 50 | + getDefaultProxyUrl: mockGetDefaultProxyUrl, |
| 51 | +})) |
| 52 | + |
| 53 | +vi.mock('../../../../src/utils/error/errors.mts', () => ({ |
| 54 | + getErrorCause: mockGetErrorCause, |
| 55 | +})) |
| 56 | + |
| 57 | +vi.mock('../../../../src/utils/spawn/spawn-node.mjs', () => ({ |
| 58 | + spawnNode: mockSpawnNode, |
| 59 | +})) |
| 60 | + |
| 61 | +import { spawnCoana } from '../../../../src/utils/coana/spawn.mts' |
| 62 | + |
| 63 | +describe('coana/spawn', () => { |
| 64 | + beforeEach(() => { |
| 65 | + vi.clearAllMocks() |
| 66 | + |
| 67 | + // Default mock implementations. |
| 68 | + mockGetCliVersion.mockReturnValue('1.0.0') |
| 69 | + mockGetCoanaVersion.mockReturnValue('1.0.0') |
| 70 | + mockGetDefaultApiToken.mockReturnValue(null) |
| 71 | + mockGetDefaultProxyUrl.mockReturnValue(null) |
| 72 | + mockGetDefaultOrgSlug.mockResolvedValue({ ok: false }) |
| 73 | + mockGetErrorCause.mockReturnValue('Unknown error') |
| 74 | + |
| 75 | + mockDlxPackage.mockResolvedValue({ |
| 76 | + spawnPromise: Promise.resolve({ stdout: 'success', code: 0 }), |
| 77 | + }) |
| 78 | + }) |
| 79 | + |
| 80 | + describe('spawnCoana', () => { |
| 81 | + it('exports spawnCoana function', () => { |
| 82 | + expect(typeof spawnCoana).toBe('function') |
| 83 | + }) |
| 84 | + |
| 85 | + it('spawns coana with dlxPackage when no local path set', async () => { |
| 86 | + const result = await spawnCoana(['--version']) |
| 87 | + |
| 88 | + expect(result.ok).toBe(true) |
| 89 | + expect(mockDlxPackage).toHaveBeenCalled() |
| 90 | + }) |
| 91 | + |
| 92 | + it('passes environment variables to coana', async () => { |
| 93 | + mockGetDefaultApiToken.mockReturnValue('test-token') |
| 94 | + mockGetDefaultProxyUrl.mockReturnValue('http://proxy.example.com') |
| 95 | + |
| 96 | + await spawnCoana(['--version']) |
| 97 | + |
| 98 | + const call = mockDlxPackage.mock.calls[0] |
| 99 | + const spawnOptions = call[1].spawnOptions |
| 100 | + expect(spawnOptions.env.SOCKET_CLI_API_TOKEN).toBe('test-token') |
| 101 | + expect(spawnOptions.env.SOCKET_CLI_API_PROXY).toBe( |
| 102 | + 'http://proxy.example.com', |
| 103 | + ) |
| 104 | + }) |
| 105 | + |
| 106 | + it('includes org slug in environment when provided', async () => { |
| 107 | + await spawnCoana(['--version'], 'my-org') |
| 108 | + |
| 109 | + const call = mockDlxPackage.mock.calls[0] |
| 110 | + const spawnOptions = call[1].spawnOptions |
| 111 | + expect(spawnOptions.env.SOCKET_ORG_SLUG).toBe('my-org') |
| 112 | + }) |
| 113 | + |
| 114 | + it('fetches default org slug when not provided', async () => { |
| 115 | + mockGetDefaultOrgSlug.mockResolvedValue({ ok: true, data: 'default-org' }) |
| 116 | + |
| 117 | + await spawnCoana(['--version']) |
| 118 | + |
| 119 | + expect(mockGetDefaultOrgSlug).toHaveBeenCalled() |
| 120 | + const call = mockDlxPackage.mock.calls[0] |
| 121 | + const spawnOptions = call[1].spawnOptions |
| 122 | + expect(spawnOptions.env.SOCKET_ORG_SLUG).toBe('default-org') |
| 123 | + }) |
| 124 | + |
| 125 | + it('handles errors gracefully', async () => { |
| 126 | + mockDlxPackage.mockRejectedValue(new Error('Spawn failed')) |
| 127 | + mockGetErrorCause.mockReturnValue('Spawn failed') |
| 128 | + |
| 129 | + const result = await spawnCoana(['--version']) |
| 130 | + |
| 131 | + expect(result.ok).toBe(false) |
| 132 | + expect(result.message).toBe('Spawn failed') |
| 133 | + }) |
| 134 | + |
| 135 | + it('uses force:true for dlxPackage', async () => { |
| 136 | + await spawnCoana(['--version']) |
| 137 | + |
| 138 | + const call = mockDlxPackage.mock.calls[0] |
| 139 | + expect(call[1].force).toBe(true) |
| 140 | + }) |
| 141 | + |
| 142 | + it('passes CLI version to environment', async () => { |
| 143 | + mockGetCliVersion.mockReturnValue('2.0.0') |
| 144 | + |
| 145 | + await spawnCoana(['--version']) |
| 146 | + |
| 147 | + const call = mockDlxPackage.mock.calls[0] |
| 148 | + const spawnOptions = call[1].spawnOptions |
| 149 | + expect(spawnOptions.env.SOCKET_CLI_VERSION).toBe('2.0.0') |
| 150 | + }) |
| 151 | + }) |
| 152 | +}) |
0 commit comments