|
1 | | -import { describe, expect, it, vi } from 'vitest' |
| 1 | +import { beforeEach, describe, expect, it, vi } from 'vitest' |
2 | 2 |
|
3 | 3 | import { fetchSecurityPolicy } from './fetch-security-policy.mts' |
4 | 4 |
|
5 | 5 | // Mock the dependencies. |
6 | | - |
7 | | -vi.mock('../../utils/sdk.mts', async importOriginal => { |
8 | | - const actual = await importOriginal() |
9 | | - return { |
10 | | - ...actual, |
11 | | - setupSdk: vi.fn(), |
12 | | - } |
13 | | -}) |
14 | | - |
15 | | -vi.mock('../../utils/api.mts', () => ({ |
16 | | - handleApiCall: vi.fn(), |
| 6 | +vi.mock('../../utils/sdk.mts', () => ({ |
| 7 | + withSdk: vi.fn(), |
17 | 8 | })) |
18 | 9 |
|
19 | 10 | describe('fetchSecurityPolicy', () => { |
| 11 | + beforeEach(() => { |
| 12 | + vi.clearAllMocks() |
| 13 | + }) |
| 14 | + |
20 | 15 | it('fetches security policy successfully', async () => { |
21 | | - const { handleApiCall } = await import('../../utils/api.mts') |
22 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
23 | | - const mockHandleApi = vi.mocked(handleApiCall) |
24 | | - const mockSetupSdk = vi.mocked(setupSdk) |
25 | | - |
26 | | - const mockSdk = { |
27 | | - getOrgSecurityPolicy: vi.fn().mockResolvedValue({ |
28 | | - success: true, |
29 | | - data: { |
30 | | - policy: { |
31 | | - block_high_severity: true, |
32 | | - block_critical_severity: true, |
33 | | - block_medium_severity: false, |
34 | | - block_low_severity: false, |
35 | | - auto_scan: true, |
36 | | - scan_on_push: true, |
37 | | - require_approval: true, |
38 | | - }, |
39 | | - updated_at: '2025-01-15T10:00:00Z', |
40 | | - }, |
41 | | - }), |
42 | | - } |
| 16 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 17 | + const mockWithSdk = vi.mocked(withSdk) |
43 | 18 |
|
44 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
45 | | - mockHandleApi.mockResolvedValue({ |
46 | | - ok: true, |
| 19 | + const successResult = { |
| 20 | + ok: true as const, |
47 | 21 | data: { |
48 | | - policy: expect.any(Object), |
| 22 | + policy: { |
| 23 | + block_high_severity: true, |
| 24 | + block_critical_severity: true, |
| 25 | + block_medium_severity: false, |
| 26 | + block_low_severity: false, |
| 27 | + auto_scan: true, |
| 28 | + scan_on_push: true, |
| 29 | + require_approval: true, |
| 30 | + }, |
49 | 31 | updated_at: '2025-01-15T10:00:00Z', |
50 | 32 | }, |
51 | | - }) |
| 33 | + } |
| 34 | + |
| 35 | + mockWithSdk.mockResolvedValueOnce(successResult) |
52 | 36 |
|
53 | 37 | const result = await fetchSecurityPolicy('test-org') |
54 | 38 |
|
55 | | - expect(mockSdk.getOrgSecurityPolicy).toHaveBeenCalledWith('test-org') |
56 | | - expect(mockHandleApi).toHaveBeenCalledWith(expect.any(Promise), { |
57 | | - description: 'organization security policy', |
58 | | - }) |
59 | | - expect(result.ok).toBe(true) |
| 39 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 40 | + expect.any(Function), |
| 41 | + 'organization security policy', |
| 42 | + undefined, |
| 43 | + ) |
| 44 | + expect(result).toEqual(successResult) |
60 | 45 | }) |
61 | 46 |
|
62 | 47 | it('handles SDK setup failure', async () => { |
63 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
64 | | - const mockSetupSdk = vi.mocked(setupSdk) |
| 48 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 49 | + const mockWithSdk = vi.mocked(withSdk) |
65 | 50 |
|
66 | 51 | const error = { |
67 | | - ok: false, |
| 52 | + ok: false as const, |
68 | 53 | code: 1, |
69 | 54 | message: 'Failed to setup SDK', |
70 | | - cause: 'Authentication failed', |
| 55 | + cause: 'Invalid API token', |
71 | 56 | } |
72 | | - mockSetupSdk.mockResolvedValue(error) |
| 57 | + mockWithSdk.mockResolvedValueOnce(error) |
73 | 58 |
|
74 | 59 | const result = await fetchSecurityPolicy('my-org') |
75 | 60 |
|
| 61 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 62 | + expect.any(Function), |
| 63 | + 'organization security policy', |
| 64 | + undefined, |
| 65 | + ) |
76 | 66 | expect(result).toEqual(error) |
77 | 67 | }) |
78 | 68 |
|
79 | 69 | it('handles API call failure', async () => { |
80 | | - const { handleApiCall } = await import('../../utils/api.mts') |
81 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
82 | | - const mockHandleApi = vi.mocked(handleApiCall) |
83 | | - const mockSetupSdk = vi.mocked(setupSdk) |
| 70 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 71 | + const mockWithSdk = vi.mocked(withSdk) |
84 | 72 |
|
85 | | - const mockSdk = { |
86 | | - getOrgSecurityPolicy: vi.fn().mockRejectedValue(new Error('Forbidden')), |
| 73 | + const error = { |
| 74 | + ok: false as const, |
| 75 | + error: 'Security policy not found', |
| 76 | + code: 404, |
| 77 | + message: 'Security policy not found', |
87 | 78 | } |
| 79 | + mockWithSdk.mockResolvedValueOnce(error) |
88 | 80 |
|
89 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
90 | | - mockHandleApi.mockResolvedValue({ |
91 | | - ok: false, |
92 | | - error: 'Access forbidden', |
93 | | - code: 403, |
94 | | - }) |
95 | | - |
96 | | - const result = await fetchSecurityPolicy('restricted-org') |
| 81 | + const result = await fetchSecurityPolicy('nonexistent-org') |
97 | 82 |
|
| 83 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 84 | + expect.any(Function), |
| 85 | + 'organization security policy', |
| 86 | + undefined, |
| 87 | + ) |
98 | 88 | expect(result.ok).toBe(false) |
99 | | - expect(result.code).toBe(403) |
| 89 | + expect(result.code).toBe(404) |
100 | 90 | }) |
101 | 91 |
|
102 | 92 | it('passes custom SDK options', async () => { |
103 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
104 | | - const { handleApiCall } = await import('../../utils/api.mts') |
105 | | - const mockSetupSdk = vi.mocked(setupSdk) |
106 | | - const mockHandleApi = vi.mocked(handleApiCall) |
| 93 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 94 | + const mockWithSdk = vi.mocked(withSdk) |
107 | 95 |
|
108 | | - const mockSdk = { |
109 | | - getOrgSecurityPolicy: vi.fn().mockResolvedValue({}), |
| 96 | + const successResult = { |
| 97 | + ok: true as const, |
| 98 | + data: {}, |
110 | 99 | } |
111 | | - |
112 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
113 | | - mockHandleApi.mockResolvedValue({ ok: true, data: {} }) |
| 100 | + mockWithSdk.mockResolvedValueOnce(successResult) |
114 | 101 |
|
115 | 102 | const sdkOpts = { |
116 | | - apiToken: 'security-token', |
| 103 | + apiToken: 'security-policy-token', |
117 | 104 | baseUrl: 'https://security.api.com', |
118 | 105 | } |
119 | 106 |
|
120 | | - await fetchSecurityPolicy('my-org', { sdkOpts }) |
| 107 | + await fetchSecurityPolicy('custom-org', { sdkOpts }) |
121 | 108 |
|
122 | | - expect(mockSetupSdk).toHaveBeenCalledWith(sdkOpts) |
| 109 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 110 | + expect.any(Function), |
| 111 | + 'organization security policy', |
| 112 | + { sdkOpts }, |
| 113 | + ) |
123 | 114 | }) |
124 | 115 |
|
125 | 116 | it('handles default security policy', async () => { |
126 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
127 | | - const { handleApiCall } = await import('../../utils/api.mts') |
128 | | - const mockSetupSdk = vi.mocked(setupSdk) |
129 | | - const mockHandleApi = vi.mocked(handleApiCall) |
| 117 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 118 | + const mockWithSdk = vi.mocked(withSdk) |
130 | 119 |
|
131 | | - const mockSdk = { |
132 | | - getOrgSecurityPolicy: vi.fn().mockResolvedValue({ |
133 | | - policy: { |
134 | | - block_high_severity: false, |
135 | | - block_critical_severity: false, |
136 | | - auto_scan: false, |
137 | | - }, |
138 | | - }), |
139 | | - } |
140 | | - |
141 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
142 | | - mockHandleApi.mockResolvedValue({ |
143 | | - ok: true, |
| 120 | + const successResult = { |
| 121 | + ok: true as const, |
144 | 122 | data: { |
145 | 123 | policy: { |
146 | 124 | block_high_severity: false, |
147 | 125 | block_critical_severity: false, |
| 126 | + block_medium_severity: false, |
| 127 | + block_low_severity: false, |
148 | 128 | auto_scan: false, |
| 129 | + scan_on_push: false, |
| 130 | + require_approval: false, |
149 | 131 | }, |
| 132 | + updated_at: null, |
150 | 133 | }, |
151 | | - }) |
| 134 | + } |
| 135 | + mockWithSdk.mockResolvedValueOnce(successResult) |
152 | 136 |
|
153 | | - const result = await fetchSecurityPolicy('new-org') |
| 137 | + const result = await fetchSecurityPolicy('default-policy-org') |
154 | 138 |
|
| 139 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 140 | + expect.any(Function), |
| 141 | + 'organization security policy', |
| 142 | + undefined, |
| 143 | + ) |
155 | 144 | expect(result.ok).toBe(true) |
156 | | - expect(result.data.policy.auto_scan).toBe(false) |
157 | 145 | }) |
158 | 146 |
|
159 | 147 | it('handles various org slugs', async () => { |
160 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
161 | | - const { handleApiCall } = await import('../../utils/api.mts') |
162 | | - const mockSetupSdk = vi.mocked(setupSdk) |
163 | | - const mockHandleApi = vi.mocked(handleApiCall) |
| 148 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 149 | + const mockWithSdk = vi.mocked(withSdk) |
164 | 150 |
|
165 | | - const mockSdk = { |
166 | | - getOrgSecurityPolicy: vi.fn().mockResolvedValue({}), |
| 151 | + const successResult = { |
| 152 | + ok: true as const, |
| 153 | + data: {}, |
167 | 154 | } |
168 | 155 |
|
169 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
170 | | - mockHandleApi.mockResolvedValue({ ok: true, data: {} }) |
171 | | - |
172 | | - const orgSlugs = [ |
173 | | - 'simple-org', |
174 | | - 'org_with_underscore', |
175 | | - 'org-123-numbers', |
176 | | - 'MyOrganization', |
177 | | - ] |
| 156 | + const orgSlugs = ['org-1', 'org-2', 'my-company', 'test-org'] |
178 | 157 |
|
179 | 158 | for (const orgSlug of orgSlugs) { |
| 159 | + mockWithSdk.mockResolvedValueOnce(successResult) |
180 | 160 | // eslint-disable-next-line no-await-in-loop |
181 | 161 | await fetchSecurityPolicy(orgSlug) |
182 | | - expect(mockSdk.getOrgSecurityPolicy).toHaveBeenCalledWith(orgSlug) |
183 | 162 | } |
| 163 | + |
| 164 | + expect(mockWithSdk).toHaveBeenCalledTimes(orgSlugs.length) |
| 165 | + expect(mockWithSdk).toHaveBeenCalledWith( |
| 166 | + expect.any(Function), |
| 167 | + 'organization security policy', |
| 168 | + undefined, |
| 169 | + ) |
184 | 170 | }) |
185 | 171 |
|
186 | 172 | it('uses null prototype for options', async () => { |
187 | | - const { setupSdk } = await import('../../utils/sdk.mts') |
188 | | - const { handleApiCall } = await import('../../utils/api.mts') |
189 | | - const mockSetupSdk = vi.mocked(setupSdk) |
190 | | - const mockHandleApi = vi.mocked(handleApiCall) |
| 173 | + const { withSdk } = await import('../../utils/sdk.mts') |
| 174 | + const mockWithSdk = vi.mocked(withSdk) |
191 | 175 |
|
192 | | - const mockSdk = { |
193 | | - getOrgSecurityPolicy: vi.fn().mockResolvedValue({}), |
| 176 | + const successResult = { |
| 177 | + ok: true as const, |
| 178 | + data: {}, |
194 | 179 | } |
195 | | - |
196 | | - mockSetupSdk.mockResolvedValue({ ok: true, data: mockSdk }) |
197 | | - mockHandleApi.mockResolvedValue({ ok: true, data: {} }) |
| 180 | + mockWithSdk.mockResolvedValueOnce(successResult) |
198 | 181 |
|
199 | 182 | // This tests that the function properly uses __proto__: null. |
200 | 183 | await fetchSecurityPolicy('test-org') |
201 | 184 |
|
202 | 185 | // The function should work without prototype pollution issues. |
203 | | - expect(mockSdk.getOrgSecurityPolicy).toHaveBeenCalled() |
| 186 | + expect(mockWithSdk).toHaveBeenCalled() |
204 | 187 | }) |
205 | 188 | }) |
0 commit comments