neonctl 2.22.2 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/analytics.js +5 -2
- package/commands/branches.js +9 -1
- package/commands/connection_string.js +9 -1
- package/commands/functions.js +277 -0
- package/commands/index.js +4 -0
- package/commands/neon_auth.js +1013 -0
- package/commands/projects.js +9 -1
- package/commands/psql.js +6 -1
- package/functions_api.js +44 -0
- package/package.json +15 -5
- package/psql/cli.js +51 -0
- package/psql/command/cmd_cond.js +437 -0
- package/psql/command/cmd_connect.js +815 -0
- package/psql/command/cmd_copy.js +1025 -0
- package/psql/command/cmd_describe.js +1810 -0
- package/psql/command/cmd_format.js +909 -0
- package/psql/command/cmd_io.js +2187 -0
- package/psql/command/cmd_lo.js +385 -0
- package/psql/command/cmd_meta.js +970 -0
- package/psql/command/cmd_misc.js +187 -0
- package/psql/command/cmd_pipeline.js +1141 -0
- package/psql/command/cmd_restrict.js +171 -0
- package/psql/command/cmd_show.js +751 -0
- package/psql/command/dispatch.js +343 -0
- package/psql/command/inputQueue.js +42 -0
- package/psql/command/shared.js +71 -0
- package/psql/complete/filenames.js +139 -0
- package/psql/complete/index.js +104 -0
- package/psql/complete/matcher.js +314 -0
- package/psql/complete/psqlVars.js +247 -0
- package/psql/complete/queries.js +491 -0
- package/psql/complete/rules.js +2387 -0
- package/psql/core/common.js +1250 -0
- package/psql/core/help.js +576 -0
- package/psql/core/mainloop.js +1353 -0
- package/psql/core/prompt.js +437 -0
- package/psql/core/settings.js +684 -0
- package/psql/core/sqlHelp.js +1066 -0
- package/psql/core/startup.js +840 -0
- package/psql/core/syncVars.js +116 -0
- package/psql/core/variables.js +287 -0
- package/psql/describe/formatters.js +1277 -0
- package/psql/describe/processNamePattern.js +270 -0
- package/psql/describe/queries.js +2373 -0
- package/psql/describe/versionGate.js +43 -0
- package/psql/index.js +2005 -0
- package/psql/io/history.js +299 -0
- package/psql/io/input.js +120 -0
- package/psql/io/lineEditor/buffer.js +323 -0
- package/psql/io/lineEditor/complete.js +227 -0
- package/psql/io/lineEditor/filename.js +159 -0
- package/psql/io/lineEditor/index.js +891 -0
- package/psql/io/lineEditor/keymap.js +738 -0
- package/psql/io/lineEditor/vt100.js +363 -0
- package/psql/io/pgpass.js +202 -0
- package/psql/io/pgservice.js +194 -0
- package/psql/io/psqlrc.js +422 -0
- package/psql/print/aligned.js +1756 -0
- package/psql/print/asciidoc.js +248 -0
- package/psql/print/crosstab.js +460 -0
- package/psql/print/csv.js +92 -0
- package/psql/print/html.js +258 -0
- package/psql/print/json.js +96 -0
- package/psql/print/latex.js +396 -0
- package/psql/print/pager.js +265 -0
- package/psql/print/troff.js +258 -0
- package/psql/print/unaligned.js +118 -0
- package/psql/print/units.js +135 -0
- package/psql/scanner/slash.js +513 -0
- package/psql/scanner/sql.js +910 -0
- package/psql/scanner/stringutils.js +390 -0
- package/psql/types/backslash.js +1 -0
- package/psql/types/connection.js +1 -0
- package/psql/types/index.js +7 -0
- package/psql/types/printer.js +1 -0
- package/psql/types/repl.js +1 -0
- package/psql/types/scanner.js +24 -0
- package/psql/types/settings.js +1 -0
- package/psql/types/variables.js +1 -0
- package/psql/wire/connection.js +2844 -0
- package/psql/wire/copy.js +108 -0
- package/psql/wire/notify.js +59 -0
- package/psql/wire/pipeline.js +519 -0
- package/psql/wire/protocol.js +466 -0
- package/psql/wire/sasl.js +296 -0
- package/psql/wire/tls.js +596 -0
- package/test_utils/fixtures.js +1 -0
- package/utils/esbuild.js +147 -0
- package/utils/psql.js +107 -11
- package/utils/zip.js +4 -0
- package/writer.js +1 -1
- package/commands/auth.test.js +0 -211
- package/commands/branches.test.js +0 -460
- package/commands/checkout.test.js +0 -170
- package/commands/connection_string.test.js +0 -196
- package/commands/data_api.test.js +0 -169
- package/commands/databases.test.js +0 -39
- package/commands/help.test.js +0 -9
- package/commands/init.test.js +0 -56
- package/commands/ip_allow.test.js +0 -59
- package/commands/link.test.js +0 -381
- package/commands/operations.test.js +0 -7
- package/commands/orgs.test.js +0 -7
- package/commands/projects.test.js +0 -144
- package/commands/psql.test.js +0 -49
- package/commands/roles.test.js +0 -37
- package/commands/set_context.test.js +0 -159
- package/commands/vpc_endpoints.test.js +0 -69
- package/context.test.js +0 -119
- package/env.test.js +0 -55
- package/utils/formats.test.js +0 -32
- package/writer.test.js +0 -104
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import { describe } from 'vitest';
|
|
2
|
-
import { test } from '../test_utils/fixtures';
|
|
3
|
-
describe('branches', () => {
|
|
4
|
-
/* list */
|
|
5
|
-
test('list/yaml', async ({ testCliCommand }) => {
|
|
6
|
-
await testCliCommand(['branches', 'list', '--project-id', 'test']);
|
|
7
|
-
});
|
|
8
|
-
test('list/table output', async ({ testCliCommand }) => {
|
|
9
|
-
await testCliCommand(['branches', 'list', '--project-id', 'test'], {
|
|
10
|
-
outputTable: true,
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
/* create */
|
|
14
|
-
test('create by default with r/w endpoint', async ({ testCliCommand }) => {
|
|
15
|
-
await testCliCommand([
|
|
16
|
-
'branches',
|
|
17
|
-
'create',
|
|
18
|
-
'--project-id',
|
|
19
|
-
'test',
|
|
20
|
-
'--name',
|
|
21
|
-
'test_branch',
|
|
22
|
-
]);
|
|
23
|
-
});
|
|
24
|
-
test('create branch and connect with psql', async ({ testCliCommand }) => {
|
|
25
|
-
await testCliCommand([
|
|
26
|
-
'branches',
|
|
27
|
-
'create',
|
|
28
|
-
'--project-id',
|
|
29
|
-
'test',
|
|
30
|
-
'--name',
|
|
31
|
-
'test_branch',
|
|
32
|
-
'--psql',
|
|
33
|
-
]);
|
|
34
|
-
});
|
|
35
|
-
test('create branch and connect with psql and psql args', async ({ testCliCommand, }) => {
|
|
36
|
-
await testCliCommand([
|
|
37
|
-
'branches',
|
|
38
|
-
'create',
|
|
39
|
-
'--project-id',
|
|
40
|
-
'test',
|
|
41
|
-
'--name',
|
|
42
|
-
'test_branch',
|
|
43
|
-
'--psql',
|
|
44
|
-
'--',
|
|
45
|
-
'-c',
|
|
46
|
-
'SELECT 1',
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
test('create with readonly endpoint', async ({ testCliCommand }) => {
|
|
50
|
-
await testCliCommand([
|
|
51
|
-
'branches',
|
|
52
|
-
'create',
|
|
53
|
-
'--project-id',
|
|
54
|
-
'test',
|
|
55
|
-
'--name',
|
|
56
|
-
'test_branch',
|
|
57
|
-
'--type',
|
|
58
|
-
'read_only',
|
|
59
|
-
]);
|
|
60
|
-
});
|
|
61
|
-
test('create without endpoint', async ({ testCliCommand }) => {
|
|
62
|
-
await testCliCommand([
|
|
63
|
-
'branches',
|
|
64
|
-
'create',
|
|
65
|
-
'--project-id',
|
|
66
|
-
'test',
|
|
67
|
-
'--name',
|
|
68
|
-
'test_branch',
|
|
69
|
-
'--no-compute',
|
|
70
|
-
]);
|
|
71
|
-
});
|
|
72
|
-
test('create with parent by name', async ({ testCliCommand }) => {
|
|
73
|
-
await testCliCommand([
|
|
74
|
-
'branches',
|
|
75
|
-
'create',
|
|
76
|
-
'--project-id',
|
|
77
|
-
'test',
|
|
78
|
-
'--name',
|
|
79
|
-
'test_branch_with_parent_name',
|
|
80
|
-
'--parent',
|
|
81
|
-
'main',
|
|
82
|
-
]);
|
|
83
|
-
});
|
|
84
|
-
test('create with parent by lsn', async ({ testCliCommand }) => {
|
|
85
|
-
await testCliCommand([
|
|
86
|
-
'branches',
|
|
87
|
-
'create',
|
|
88
|
-
'--project-id',
|
|
89
|
-
'test',
|
|
90
|
-
'--name',
|
|
91
|
-
'test_branch_with_parent_lsn',
|
|
92
|
-
'--parent',
|
|
93
|
-
'0/123ABC',
|
|
94
|
-
]);
|
|
95
|
-
});
|
|
96
|
-
test('create with parent by timestamp', async ({ testCliCommand }) => {
|
|
97
|
-
await testCliCommand([
|
|
98
|
-
'branches',
|
|
99
|
-
'create',
|
|
100
|
-
'--project-id',
|
|
101
|
-
'test',
|
|
102
|
-
'--name',
|
|
103
|
-
'test_branch_with_parent_timestamp',
|
|
104
|
-
'--parent',
|
|
105
|
-
'2021-01-01T00:00:00.000Z',
|
|
106
|
-
]);
|
|
107
|
-
});
|
|
108
|
-
test('create with suspend timeout', async ({ testCliCommand }) => {
|
|
109
|
-
await testCliCommand([
|
|
110
|
-
'branches',
|
|
111
|
-
'create',
|
|
112
|
-
'--project-id',
|
|
113
|
-
'test',
|
|
114
|
-
'--name',
|
|
115
|
-
'test_branch_with_suspend_timeout',
|
|
116
|
-
'--suspend-timeout',
|
|
117
|
-
'60',
|
|
118
|
-
]);
|
|
119
|
-
});
|
|
120
|
-
test('create with fixed size CU', async ({ testCliCommand }) => {
|
|
121
|
-
await testCliCommand([
|
|
122
|
-
'branches',
|
|
123
|
-
'create',
|
|
124
|
-
'--project-id',
|
|
125
|
-
'test',
|
|
126
|
-
'--name',
|
|
127
|
-
'test_branch_with_fixed_cu',
|
|
128
|
-
'--cu',
|
|
129
|
-
'2',
|
|
130
|
-
]);
|
|
131
|
-
});
|
|
132
|
-
test('create with autoscaled CU', async ({ testCliCommand }) => {
|
|
133
|
-
await testCliCommand([
|
|
134
|
-
'branches',
|
|
135
|
-
'create',
|
|
136
|
-
'--project-id',
|
|
137
|
-
'test',
|
|
138
|
-
'--name',
|
|
139
|
-
'test_branch_with_autoscaling',
|
|
140
|
-
'--cu',
|
|
141
|
-
'0.5-2',
|
|
142
|
-
]);
|
|
143
|
-
});
|
|
144
|
-
test('create schema-only branch', async ({ testCliCommand }) => {
|
|
145
|
-
await testCliCommand([
|
|
146
|
-
'branches',
|
|
147
|
-
'create',
|
|
148
|
-
'--project-id',
|
|
149
|
-
'test',
|
|
150
|
-
'--name',
|
|
151
|
-
'test_branch',
|
|
152
|
-
'--schema-only',
|
|
153
|
-
]);
|
|
154
|
-
});
|
|
155
|
-
test('create schema-only branch fails without compute', async ({ testCliCommand, }) => {
|
|
156
|
-
await testCliCommand([
|
|
157
|
-
'branches',
|
|
158
|
-
'create',
|
|
159
|
-
'--project-id',
|
|
160
|
-
'test',
|
|
161
|
-
'--name',
|
|
162
|
-
'test_branch',
|
|
163
|
-
'--schema-only',
|
|
164
|
-
'--no-compute',
|
|
165
|
-
], {
|
|
166
|
-
mockDir: 'main',
|
|
167
|
-
code: 1,
|
|
168
|
-
stderr: 'ERROR: Schema-only branches require a compute endpoint',
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
test('create schema-only branch fails with read-only compute', async ({ testCliCommand, }) => {
|
|
172
|
-
await testCliCommand([
|
|
173
|
-
'branches',
|
|
174
|
-
'create',
|
|
175
|
-
'--project-id',
|
|
176
|
-
'test',
|
|
177
|
-
'--name',
|
|
178
|
-
'test_branch',
|
|
179
|
-
'--schema-only',
|
|
180
|
-
'--type',
|
|
181
|
-
'read_only',
|
|
182
|
-
], {
|
|
183
|
-
mockDir: 'main',
|
|
184
|
-
code: 1,
|
|
185
|
-
stderr: 'ERROR: Schema-only branches require a read-write compute endpoint',
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
test('create with expiration date', async ({ testCliCommand }) => {
|
|
189
|
-
await testCliCommand([
|
|
190
|
-
'branches',
|
|
191
|
-
'create',
|
|
192
|
-
'--project-id',
|
|
193
|
-
'test',
|
|
194
|
-
'--name',
|
|
195
|
-
'test_branch',
|
|
196
|
-
'--expiration-date',
|
|
197
|
-
'2021-01-01T00:00:00.000Z',
|
|
198
|
-
]);
|
|
199
|
-
});
|
|
200
|
-
/* delete */
|
|
201
|
-
test('delete by id', async ({ testCliCommand }) => {
|
|
202
|
-
await testCliCommand([
|
|
203
|
-
'branches',
|
|
204
|
-
'delete',
|
|
205
|
-
'br-sunny-branch-123456',
|
|
206
|
-
'--project-id',
|
|
207
|
-
'test',
|
|
208
|
-
]);
|
|
209
|
-
});
|
|
210
|
-
/* rename */
|
|
211
|
-
test('rename', async ({ testCliCommand }) => {
|
|
212
|
-
await testCliCommand([
|
|
213
|
-
'branches',
|
|
214
|
-
'rename',
|
|
215
|
-
'test_branch',
|
|
216
|
-
'new_test_branch',
|
|
217
|
-
'--project-id',
|
|
218
|
-
'test',
|
|
219
|
-
]);
|
|
220
|
-
});
|
|
221
|
-
/* set default */
|
|
222
|
-
test('set default by id', async ({ testCliCommand }) => {
|
|
223
|
-
await testCliCommand([
|
|
224
|
-
'branches',
|
|
225
|
-
'set-default',
|
|
226
|
-
'br-sunny-branch-123456',
|
|
227
|
-
'--project-id',
|
|
228
|
-
'test',
|
|
229
|
-
]);
|
|
230
|
-
});
|
|
231
|
-
/* get */
|
|
232
|
-
test('get by id', async ({ testCliCommand }) => {
|
|
233
|
-
await testCliCommand([
|
|
234
|
-
'branches',
|
|
235
|
-
'get',
|
|
236
|
-
'br-sunny-branch-123456',
|
|
237
|
-
'--project-id',
|
|
238
|
-
'test',
|
|
239
|
-
]);
|
|
240
|
-
});
|
|
241
|
-
test('get by id', async ({ testCliCommand }) => {
|
|
242
|
-
await testCliCommand([
|
|
243
|
-
'branches',
|
|
244
|
-
'get',
|
|
245
|
-
'br-cloudy-branch-12345678',
|
|
246
|
-
'--project-id',
|
|
247
|
-
'test',
|
|
248
|
-
]);
|
|
249
|
-
});
|
|
250
|
-
test('get by name', async ({ testCliCommand }) => {
|
|
251
|
-
await testCliCommand([
|
|
252
|
-
'branches',
|
|
253
|
-
'get',
|
|
254
|
-
'test_branch',
|
|
255
|
-
'--project-id',
|
|
256
|
-
'test',
|
|
257
|
-
]);
|
|
258
|
-
});
|
|
259
|
-
test('get by name with numeric name', async ({ testCliCommand }) => {
|
|
260
|
-
await testCliCommand(['branches', 'get', '123', '--project-id', 'test']);
|
|
261
|
-
});
|
|
262
|
-
/* add compute */
|
|
263
|
-
test('add compute', async ({ testCliCommand }) => {
|
|
264
|
-
await testCliCommand([
|
|
265
|
-
'branches',
|
|
266
|
-
'add-compute',
|
|
267
|
-
'test_branch',
|
|
268
|
-
'--project-id',
|
|
269
|
-
'test',
|
|
270
|
-
]);
|
|
271
|
-
});
|
|
272
|
-
test('add compute with fixed size CU', async ({ testCliCommand }) => {
|
|
273
|
-
await testCliCommand([
|
|
274
|
-
'branches',
|
|
275
|
-
'add-compute',
|
|
276
|
-
'test_branch_with_fixed_cu',
|
|
277
|
-
'--project-id',
|
|
278
|
-
'test',
|
|
279
|
-
'--cu',
|
|
280
|
-
'2',
|
|
281
|
-
]);
|
|
282
|
-
});
|
|
283
|
-
test('add compute with autoscaled CU', async ({ testCliCommand }) => {
|
|
284
|
-
await testCliCommand([
|
|
285
|
-
'branches',
|
|
286
|
-
'add-compute',
|
|
287
|
-
'test_branch_with_autoscaling',
|
|
288
|
-
'--project-id',
|
|
289
|
-
'test',
|
|
290
|
-
'--cu',
|
|
291
|
-
'0.5-2',
|
|
292
|
-
]);
|
|
293
|
-
});
|
|
294
|
-
test('add compute with a name', async ({ testCliCommand }) => {
|
|
295
|
-
await testCliCommand([
|
|
296
|
-
'branches',
|
|
297
|
-
'add-compute',
|
|
298
|
-
'test_branch_with_autoscaling',
|
|
299
|
-
'--project-id',
|
|
300
|
-
'test',
|
|
301
|
-
'--cu',
|
|
302
|
-
'0.5-2',
|
|
303
|
-
'--name',
|
|
304
|
-
'My fancy new compute',
|
|
305
|
-
]);
|
|
306
|
-
});
|
|
307
|
-
/* reset */
|
|
308
|
-
test('reset branch to parent', async ({ testCliCommand }) => {
|
|
309
|
-
await testCliCommand([
|
|
310
|
-
'branches',
|
|
311
|
-
'reset',
|
|
312
|
-
'test_branch',
|
|
313
|
-
'--project-id',
|
|
314
|
-
'test',
|
|
315
|
-
'--parent',
|
|
316
|
-
]);
|
|
317
|
-
});
|
|
318
|
-
/* restore */
|
|
319
|
-
test('restore branch to lsn', async ({ testCliCommand }) => {
|
|
320
|
-
await testCliCommand([
|
|
321
|
-
'branches',
|
|
322
|
-
'restore',
|
|
323
|
-
'br-self-tolsn-123456',
|
|
324
|
-
'^self@0/123ABC',
|
|
325
|
-
'--project-id',
|
|
326
|
-
'test',
|
|
327
|
-
'--preserve-under-name',
|
|
328
|
-
'backup',
|
|
329
|
-
], {
|
|
330
|
-
mockDir: 'restore',
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
test('restore to parent branch timestamp by name', async ({ testCliCommand, }) => {
|
|
334
|
-
await testCliCommand([
|
|
335
|
-
'branches',
|
|
336
|
-
'restore',
|
|
337
|
-
'parent-tots',
|
|
338
|
-
'^parent@2021-01-01T00:00:00.000Z',
|
|
339
|
-
'--project-id',
|
|
340
|
-
'test',
|
|
341
|
-
], {
|
|
342
|
-
mockDir: 'restore',
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
test('restore to another branch head', async ({ testCliCommand }) => {
|
|
346
|
-
await testCliCommand([
|
|
347
|
-
'branches',
|
|
348
|
-
'restore',
|
|
349
|
-
'br-another-branch-123456',
|
|
350
|
-
'br-any-branch-123456',
|
|
351
|
-
'--project-id',
|
|
352
|
-
'test',
|
|
353
|
-
], {
|
|
354
|
-
mockDir: 'restore',
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
test('restore with unexisted branch outputs error', async ({ testCliCommand, }) => {
|
|
358
|
-
await testCliCommand([
|
|
359
|
-
'branches',
|
|
360
|
-
'restore',
|
|
361
|
-
'unexisting-branch',
|
|
362
|
-
'^parent',
|
|
363
|
-
'--project-id',
|
|
364
|
-
'test',
|
|
365
|
-
], {
|
|
366
|
-
mockDir: 'restore',
|
|
367
|
-
code: 1,
|
|
368
|
-
stderr: `ERROR: Branch unexisting-branch not found.
|
|
369
|
-
Available branches: self-tolsn-123456, any-branch, parent-tots, another-branch`,
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
/* set expiration */
|
|
373
|
-
test('set expiration date', async ({ testCliCommand }) => {
|
|
374
|
-
await testCliCommand([
|
|
375
|
-
'branches',
|
|
376
|
-
'set-expiration',
|
|
377
|
-
'br-sunny-branch-123456',
|
|
378
|
-
'--project-id',
|
|
379
|
-
'test',
|
|
380
|
-
'--expires-at',
|
|
381
|
-
'2024-12-31T23:59:59Z',
|
|
382
|
-
]);
|
|
383
|
-
});
|
|
384
|
-
test('remove expiration date', async ({ testCliCommand }) => {
|
|
385
|
-
await testCliCommand([
|
|
386
|
-
'branches',
|
|
387
|
-
'set-expiration',
|
|
388
|
-
'br-sunny-branch-123456',
|
|
389
|
-
'--project-id',
|
|
390
|
-
'test',
|
|
391
|
-
]);
|
|
392
|
-
});
|
|
393
|
-
test('set expiration by branch name', async ({ testCliCommand }) => {
|
|
394
|
-
await testCliCommand([
|
|
395
|
-
'branches',
|
|
396
|
-
'set-expiration',
|
|
397
|
-
'test_branch',
|
|
398
|
-
'--project-id',
|
|
399
|
-
'test',
|
|
400
|
-
'--expires-at',
|
|
401
|
-
'2024-12-31T23:59:59Z',
|
|
402
|
-
]);
|
|
403
|
-
});
|
|
404
|
-
test('set expiration fails on default branch', async ({ testCliCommand }) => {
|
|
405
|
-
await testCliCommand([
|
|
406
|
-
'branches',
|
|
407
|
-
'set-expiration',
|
|
408
|
-
'br-main-branch-123456',
|
|
409
|
-
'--project-id',
|
|
410
|
-
'test',
|
|
411
|
-
'--expires-at',
|
|
412
|
-
'2024-12-31T23:59:59Z',
|
|
413
|
-
], {
|
|
414
|
-
code: 1,
|
|
415
|
-
stderr: 'ERROR: Default branch cannot have an expiration date',
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
test('set expiration fails on default branch by name', async ({ testCliCommand, }) => {
|
|
419
|
-
await testCliCommand([
|
|
420
|
-
'branches',
|
|
421
|
-
'set-expiration',
|
|
422
|
-
'main',
|
|
423
|
-
'--project-id',
|
|
424
|
-
'test',
|
|
425
|
-
'--expires-at',
|
|
426
|
-
'2024-12-31T23:59:59Z',
|
|
427
|
-
], {
|
|
428
|
-
code: 1,
|
|
429
|
-
stderr: 'ERROR: Default branch cannot have an expiration date',
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
test('set expiration fails on protected branch', async ({ testCliCommand, }) => {
|
|
433
|
-
await testCliCommand([
|
|
434
|
-
'branches',
|
|
435
|
-
'set-expiration',
|
|
436
|
-
'br-protected-branch-123456',
|
|
437
|
-
'--project-id',
|
|
438
|
-
'test',
|
|
439
|
-
'--expires-at',
|
|
440
|
-
'2024-12-31T23:59:59Z',
|
|
441
|
-
], {
|
|
442
|
-
code: 1,
|
|
443
|
-
stderr: 'ERROR: Protected branch cannot have an expiration date',
|
|
444
|
-
});
|
|
445
|
-
});
|
|
446
|
-
test('set expiration fails on protected branch by name', async ({ testCliCommand, }) => {
|
|
447
|
-
await testCliCommand([
|
|
448
|
-
'branches',
|
|
449
|
-
'set-expiration',
|
|
450
|
-
'protected_branch',
|
|
451
|
-
'--project-id',
|
|
452
|
-
'test',
|
|
453
|
-
'--expires-at',
|
|
454
|
-
'2024-12-31T23:59:59Z',
|
|
455
|
-
], {
|
|
456
|
-
code: 1,
|
|
457
|
-
stderr: 'ERROR: Protected branch cannot have an expiration date',
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { describe, expect } from 'vitest';
|
|
5
|
-
import { test as originalTest } from '../test_utils/fixtures';
|
|
6
|
-
// All tests in this file share a single temporary directory whose path is
|
|
7
|
-
// normalized in snapshots to `<TMP>` so absolute paths in command output stay
|
|
8
|
-
// stable across runs and machines.
|
|
9
|
-
const TEST_TMP = mkdtempSync(join(tmpdir(), 'neonctl-checkout-'));
|
|
10
|
-
const test = originalTest.extend({
|
|
11
|
-
// eslint-disable-next-line no-empty-pattern
|
|
12
|
-
readFile: async ({}, use) => {
|
|
13
|
-
await use((name) => readFileSync(name, 'utf-8'));
|
|
14
|
-
},
|
|
15
|
-
// eslint-disable-next-line no-empty-pattern
|
|
16
|
-
removeFile: async ({}, use) => {
|
|
17
|
-
await use((name) => {
|
|
18
|
-
try {
|
|
19
|
-
rmSync(name);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// ignore
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
},
|
|
26
|
-
// eslint-disable-next-line no-empty-pattern
|
|
27
|
-
tmpContext: async ({}, use) => {
|
|
28
|
-
await use((label, seed) => {
|
|
29
|
-
const dir = join(TEST_TMP, label);
|
|
30
|
-
mkdirSync(dir, { recursive: true });
|
|
31
|
-
const ctx = join(dir, '.neon');
|
|
32
|
-
if (seed) {
|
|
33
|
-
writeFileSync(ctx, JSON.stringify(seed, null, 2));
|
|
34
|
-
}
|
|
35
|
-
return ctx;
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
const parseContext = (raw) => JSON.parse(raw);
|
|
40
|
-
describe('checkout', () => {
|
|
41
|
-
test('resolves a branch by name and writes branchId to a fresh .neon', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
42
|
-
const ctx = tmpContext('by_name_fresh');
|
|
43
|
-
await testCliCommand([
|
|
44
|
-
'checkout',
|
|
45
|
-
'main',
|
|
46
|
-
'--project-id',
|
|
47
|
-
'test',
|
|
48
|
-
'--context-file',
|
|
49
|
-
ctx,
|
|
50
|
-
]);
|
|
51
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
52
|
-
projectId: 'test',
|
|
53
|
-
branchId: 'br-main-branch-123456',
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
test('resolves a branch by id and writes branchId to a fresh .neon', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
57
|
-
const ctx = tmpContext('by_id_fresh');
|
|
58
|
-
await testCliCommand([
|
|
59
|
-
'checkout',
|
|
60
|
-
'br-sunny-branch-123456',
|
|
61
|
-
'--project-id',
|
|
62
|
-
'test',
|
|
63
|
-
'--context-file',
|
|
64
|
-
ctx,
|
|
65
|
-
]);
|
|
66
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
67
|
-
projectId: 'test',
|
|
68
|
-
branchId: 'br-sunny-branch-123456',
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
test('preserves orgId/projectId already present in the .neon file', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
72
|
-
const ctx = tmpContext('preserve_org', {
|
|
73
|
-
orgId: 'org-keep',
|
|
74
|
-
projectId: 'test',
|
|
75
|
-
});
|
|
76
|
-
await testCliCommand(['checkout', 'test_branch', '--context-file', ctx]);
|
|
77
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
78
|
-
orgId: 'org-keep',
|
|
79
|
-
projectId: 'test',
|
|
80
|
-
branchId: 'br-sunny-branch-123456',
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
test('heals a missing orgId by resolving it from the project', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
84
|
-
// The .neon only has projectId; checkout should look up the project's
|
|
85
|
-
// org_id and write all three fields so the context file ends up complete.
|
|
86
|
-
const ctx = tmpContext('heal_org', { projectId: 'test' });
|
|
87
|
-
await testCliCommand(['checkout', 'main', '--context-file', ctx], {
|
|
88
|
-
mockDir: 'checkout_heal_org',
|
|
89
|
-
});
|
|
90
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
91
|
-
orgId: 'org-healed-123',
|
|
92
|
-
projectId: 'test',
|
|
93
|
-
branchId: 'br-main-branch-123456',
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
test('resolves projectId from the .neon file when no flag is passed', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
97
|
-
const ctx = tmpContext('project_from_file', { projectId: 'test' });
|
|
98
|
-
await testCliCommand(['checkout', 'main', '--context-file', ctx]);
|
|
99
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
100
|
-
projectId: 'test',
|
|
101
|
-
branchId: 'br-main-branch-123456',
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
test('auto-detects the project when the API key maps to a single project', async ({ testCliCommand, readFile, tmpContext, }) => {
|
|
105
|
-
// No --project-id and a fresh .neon: checkout should fall
|
|
106
|
-
// back to single-project auto-detection (same behaviour as branches / cs).
|
|
107
|
-
const ctx = tmpContext('autodetect_single');
|
|
108
|
-
await testCliCommand(['checkout', 'main', '--context-file', ctx], {
|
|
109
|
-
mockDir: 'single_project',
|
|
110
|
-
});
|
|
111
|
-
expect(parseContext(readFile(ctx))).toEqual({
|
|
112
|
-
projectId: 'test-project-123456',
|
|
113
|
-
branchId: 'br-main-branch-123456',
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
test('fails with a telling error when no project can be resolved (non-interactive)', async ({ testCliCommand, removeFile, tmpContext, }) => {
|
|
117
|
-
// Fresh .neon, no --project-id, and the mock account has no projects so
|
|
118
|
-
// single-project auto-detection can't pick one. The forked CLI has no TTY,
|
|
119
|
-
// so we expect the telling error instead of a prompt.
|
|
120
|
-
const ctx = tmpContext('no_project');
|
|
121
|
-
await testCliCommand(['checkout', 'main', '--context-file', ctx], {
|
|
122
|
-
mockDir: 'checkout_no_project',
|
|
123
|
-
code: 1,
|
|
124
|
-
stderr: 'ERROR: Could not determine which Neon project to check out a branch from. Provide one via the --project-id flag or a .neon file (created by `neonctl link` / `neonctl set-context`).',
|
|
125
|
-
});
|
|
126
|
-
removeFile(ctx);
|
|
127
|
-
});
|
|
128
|
-
test('fails with a helpful error when the branch is not found', async ({ testCliCommand, removeFile, tmpContext, }) => {
|
|
129
|
-
const ctx = tmpContext('not_found');
|
|
130
|
-
await testCliCommand([
|
|
131
|
-
'checkout',
|
|
132
|
-
'does-not-exist',
|
|
133
|
-
'--project-id',
|
|
134
|
-
'test',
|
|
135
|
-
'--context-file',
|
|
136
|
-
ctx,
|
|
137
|
-
], {
|
|
138
|
-
code: 1,
|
|
139
|
-
stderr: 'ERROR: Branch does-not-exist not found. Available branches: main, test_branch, 123, test_branch_with_fixed_cu, test_branch_with_autoscaling, protected_branch',
|
|
140
|
-
});
|
|
141
|
-
removeFile(ctx);
|
|
142
|
-
});
|
|
143
|
-
test('errors when a branch id is not found (ids are never auto-created)', async ({ testCliCommand, removeFile, tmpContext, }) => {
|
|
144
|
-
// A `br-…` value is treated as an id and matched strictly; a non-existent
|
|
145
|
-
// id is a hard not-found error (no create offer, even interactively).
|
|
146
|
-
const ctx = tmpContext('id_not_found');
|
|
147
|
-
await testCliCommand([
|
|
148
|
-
'checkout',
|
|
149
|
-
'br-does-not-exist-123456',
|
|
150
|
-
'--project-id',
|
|
151
|
-
'test',
|
|
152
|
-
'--context-file',
|
|
153
|
-
ctx,
|
|
154
|
-
], {
|
|
155
|
-
code: 1,
|
|
156
|
-
stderr: 'ERROR: Branch br-does-not-exist-123456 not found. Available branches: main, test_branch, 123, test_branch_with_fixed_cu, test_branch_with_autoscaling, protected_branch',
|
|
157
|
-
});
|
|
158
|
-
removeFile(ctx);
|
|
159
|
-
});
|
|
160
|
-
test('errors when no branch is given in a non-interactive context', async ({ testCliCommand, removeFile, tmpContext, }) => {
|
|
161
|
-
// Project resolves fine (from --project-id), but no branch was passed and
|
|
162
|
-
// the forked CLI has no TTY, so the interactive picker is not available.
|
|
163
|
-
const ctx = tmpContext('no_branch');
|
|
164
|
-
await testCliCommand(['checkout', '--project-id', 'test', '--context-file', ctx], {
|
|
165
|
-
code: 1,
|
|
166
|
-
stderr: 'ERROR: No branch specified. Pass a branch name or id (e.g. `neonctl checkout main`), or run interactively to pick one from a list.',
|
|
167
|
-
});
|
|
168
|
-
removeFile(ctx);
|
|
169
|
-
});
|
|
170
|
-
});
|