Skip to content

Commit c5a3283

Browse files
committed
Add some tests
Signed-off-by: Phil Ewels <[email protected]>
1 parent 64bc9cb commit c5a3283

File tree

2 files changed

+566
-0
lines changed

2 files changed

+566
-0
lines changed
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/*
2+
* Copyright 2013-2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.cli
18+
19+
import nextflow.exception.AbortOperationException
20+
import org.junit.Rule
21+
import spock.lang.Specification
22+
import spock.lang.TempDir
23+
import test.OutputCapture
24+
25+
import java.nio.file.Files
26+
import java.nio.file.Path
27+
28+
/**
29+
* Test CmdAuth functionality
30+
*
31+
* @author Phil Ewels <[email protected]>
32+
*/
33+
class CmdAuthTest extends Specification {
34+
35+
@Rule
36+
OutputCapture capture = new OutputCapture()
37+
38+
@TempDir
39+
Path tempDir
40+
41+
def 'should have correct name'() {
42+
given:
43+
def cmd = new CmdAuth()
44+
45+
expect:
46+
cmd.getName() == 'auth'
47+
}
48+
49+
def 'should define correct constants'() {
50+
expect:
51+
CmdAuth.SEQERA_ENDPOINTS.prod == 'https://api.cloud.seqera.io'
52+
CmdAuth.SEQERA_ENDPOINTS.stage == 'https://api.cloud.stage-seqera.io'
53+
CmdAuth.SEQERA_ENDPOINTS.dev == 'https://api.cloud.dev-seqera.io'
54+
CmdAuth.API_TIMEOUT_MS == 10000
55+
CmdAuth.AUTH_POLL_TIMEOUT_RETRIES == 60
56+
CmdAuth.AUTH_POLL_INTERVAL_SECONDS == 5
57+
CmdAuth.WORKSPACE_SELECTION_THRESHOLD == 8
58+
}
59+
60+
def 'should show usage when no args provided'() {
61+
given:
62+
def cmd = new CmdAuth()
63+
64+
when:
65+
cmd.run()
66+
67+
then:
68+
def output = capture.toString()
69+
output.contains('Manage Seqera Platform authentication')
70+
output.contains('Usage: nextflow auth <sub-command> [options]')
71+
output.contains('Commands:')
72+
output.contains('login')
73+
output.contains('logout')
74+
output.contains('config')
75+
output.contains('status')
76+
}
77+
78+
def 'should show specific command usage'() {
79+
given:
80+
def cmd = new CmdAuth()
81+
cmd.args = ['login']
82+
83+
when:
84+
cmd.usage()
85+
86+
then:
87+
def output = capture.toString()
88+
output.contains('Authenticate with Seqera Platform')
89+
output.contains('Usage: nextflow auth login')
90+
output.contains('-u, -url <endpoint>')
91+
}
92+
93+
def 'should throw error for unknown command'() {
94+
given:
95+
def cmd = new CmdAuth()
96+
cmd.args = ['unknown']
97+
98+
when:
99+
cmd.run()
100+
101+
then:
102+
def ex = thrown(AbortOperationException)
103+
ex.message.contains('Unknown auth sub-command: unknown')
104+
}
105+
106+
def 'should suggest closest command for typos'() {
107+
given:
108+
def cmd = new CmdAuth()
109+
110+
when:
111+
cmd.getCmd(['loginn'])
112+
113+
then:
114+
def ex = thrown(AbortOperationException)
115+
ex.message.contains('Unknown auth sub-command: loginn')
116+
ex.message.contains('Did you mean one of these?')
117+
ex.message.contains('login')
118+
}
119+
120+
def 'should identify cloud endpoints correctly'() {
121+
given:
122+
def cmd = new CmdAuth()
123+
124+
expect:
125+
cmd.getCloudEndpointInfo('https://api.cloud.seqera.io').isCloud == true
126+
cmd.getCloudEndpointInfo('https://api.cloud.seqera.io').environment == 'prod'
127+
cmd.getCloudEndpointInfo('https://api.cloud.stage-seqera.io').isCloud == true
128+
cmd.getCloudEndpointInfo('https://api.cloud.stage-seqera.io').environment == 'stage'
129+
cmd.getCloudEndpointInfo('https://api.cloud.dev-seqera.io').isCloud == true
130+
cmd.getCloudEndpointInfo('https://api.cloud.dev-seqera.io').environment == 'dev'
131+
cmd.getCloudEndpointInfo('https://cloud.seqera.io/api').isCloud == true
132+
cmd.getCloudEndpointInfo('https://cloud.seqera.io/api').environment == 'prod'
133+
cmd.getCloudEndpointInfo('https://enterprise.example.com').isCloud == false
134+
cmd.getCloudEndpointInfo('https://enterprise.example.com').environment == null
135+
}
136+
137+
def 'should identify cloud endpoint from URL'() {
138+
given:
139+
def cmd = new CmdAuth()
140+
141+
expect:
142+
cmd.isCloudEndpoint('https://api.cloud.seqera.io') == true
143+
cmd.isCloudEndpoint('https://api.cloud.stage-seqera.io') == true
144+
cmd.isCloudEndpoint('https://enterprise.example.com') == false
145+
}
146+
147+
def 'should validate argument count correctly'() {
148+
given:
149+
def cmd = new CmdAuth()
150+
151+
when:
152+
cmd.validateArgumentCount(['extra'], 'test')
153+
154+
then:
155+
def ex = thrown(AbortOperationException)
156+
ex.message == 'Too many arguments for test command'
157+
158+
when:
159+
cmd.validateArgumentCount([], 'test')
160+
161+
then:
162+
noExceptionThrown()
163+
}
164+
165+
def 'should read config correctly'() {
166+
given:
167+
def cmd = new CmdAuth()
168+
169+
expect:
170+
// readConfig method should return a Map
171+
cmd.readConfig() instanceof Map
172+
}
173+
174+
def 'should clean tower config from existing content'() {
175+
given:
176+
def cmd = new CmdAuth()
177+
def content = '''
178+
// Some other config
179+
process {
180+
executor = 'local'
181+
}
182+
183+
// Seqera Platform configuration
184+
tower {
185+
accessToken = 'old-token'
186+
enabled = true
187+
}
188+
189+
tower.endpoint = 'old-endpoint'
190+
191+
// More config
192+
params.test = true
193+
'''
194+
195+
when:
196+
def cleaned = cmd.cleanTowerConfig(content)
197+
198+
then:
199+
!cleaned.contains('tower {')
200+
!cleaned.contains('accessToken = \'old-token\'')
201+
!cleaned.contains('tower.endpoint')
202+
!cleaned.contains('Seqera Platform configuration')
203+
cleaned.contains('process {')
204+
cleaned.contains('params.test = true')
205+
}
206+
207+
def 'should handle config writing'() {
208+
given:
209+
def cmd = new CmdAuth()
210+
def config = [
211+
'tower.accessToken': 'test-token',
212+
'tower.enabled': true
213+
]
214+
215+
when:
216+
cmd.writeConfig(config, null)
217+
218+
then:
219+
noExceptionThrown()
220+
}
221+
222+
def 'login command should validate too many arguments'() {
223+
given:
224+
def cmd = new CmdAuth()
225+
cmd.args = ['login', 'extra']
226+
227+
when:
228+
cmd.run()
229+
230+
then:
231+
def ex = thrown(AbortOperationException)
232+
ex.message.contains('Too many arguments for login command')
233+
}
234+
235+
def 'logout command should validate too many arguments'() {
236+
given:
237+
def cmd = new CmdAuth()
238+
cmd.args = ['logout', 'extra']
239+
240+
when:
241+
cmd.run()
242+
243+
then:
244+
def ex = thrown(AbortOperationException)
245+
ex.message.contains('Too many arguments for logout command')
246+
}
247+
248+
def 'config command should validate too many arguments'() {
249+
given:
250+
def cmd = new CmdAuth()
251+
cmd.args = ['config', 'extra']
252+
253+
when:
254+
cmd.run()
255+
256+
then:
257+
def ex = thrown(AbortOperationException)
258+
ex.message.contains('Too many arguments for config command')
259+
}
260+
261+
def 'status command should validate too many arguments'() {
262+
given:
263+
def cmd = new CmdAuth()
264+
cmd.args = ['status', 'extra']
265+
266+
when:
267+
cmd.run()
268+
269+
then:
270+
def ex = thrown(AbortOperationException)
271+
ex.message.contains('Too many arguments for status command')
272+
}
273+
274+
def 'login command should use provided API URL'() {
275+
given:
276+
def cmd = new CmdAuth()
277+
cmd.args = ['login']
278+
cmd.apiUrl = 'https://api.example.com'
279+
280+
when:
281+
def loginCmd = cmd.getCmd(cmd.args)
282+
283+
then:
284+
loginCmd instanceof CmdAuth.LoginCmd
285+
286+
when:
287+
cmd.run()
288+
289+
then:
290+
loginCmd.apiUrl == 'https://api.example.com'
291+
}
292+
293+
def 'should have all required subcommands'() {
294+
given:
295+
def cmd = new CmdAuth()
296+
297+
expect:
298+
cmd.commands.size() == 4
299+
cmd.commands.find { it.name == 'login' } != null
300+
cmd.commands.find { it.name == 'logout' } != null
301+
cmd.commands.find { it.name == 'config' } != null
302+
cmd.commands.find { it.name == 'status' } != null
303+
}
304+
305+
def 'should create HTTP connection with correct properties'() {
306+
given:
307+
def cmd = new CmdAuth()
308+
309+
when:
310+
def connection = cmd.createHttpConnection('https://example.com', 'GET', 'test-token')
311+
312+
then:
313+
connection.requestMethod == 'GET'
314+
connection.connectTimeout == CmdAuth.API_TIMEOUT_MS
315+
connection.readTimeout == CmdAuth.API_TIMEOUT_MS
316+
317+
when:
318+
def connectionNoAuth = cmd.createHttpConnection('https://example.com', 'POST')
319+
320+
then:
321+
connectionNoAuth.requestMethod == 'POST'
322+
connectionNoAuth.connectTimeout == CmdAuth.API_TIMEOUT_MS
323+
connectionNoAuth.readTimeout == CmdAuth.API_TIMEOUT_MS
324+
}
325+
}

0 commit comments

Comments
 (0)