Skip to content

Commit 7d464bd

Browse files
Extract auth status service
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f700b48 commit 7d464bd

4 files changed

Lines changed: 138 additions & 105 deletions

File tree

Lines changed: 7 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,28 @@
11
import Status from './status.js'
2-
import {getAuthStatus} from '@shopify/cli-kit/node/session'
3-
import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output'
2+
import {authStatusService} from '../../services/commands/auth-status.js'
43

5-
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'
4+
import {beforeEach, describe, expect, test, vi} from 'vitest'
65

7-
vi.mock('@shopify/cli-kit/node/session')
6+
vi.mock('../../services/commands/auth-status.js')
87

98
describe('auth status command', () => {
109
beforeEach(() => {
11-
mockAndCaptureOutput().clear()
1210
vi.clearAllMocks()
13-
process.exitCode = undefined
1411
})
1512

16-
afterEach(() => {
17-
mockAndCaptureOutput().clear()
18-
process.exitCode = undefined
19-
})
20-
21-
test('prints authenticated status as text', async () => {
22-
// Given
23-
const outputMock = mockAndCaptureOutput()
24-
vi.mocked(getAuthStatus).mockResolvedValue({
25-
status: 'authenticated',
26-
authenticated: true,
27-
account: {userId: 'user-id', alias: 'user@example.com'},
28-
identityFqdn: 'accounts.shopify.com',
29-
expiresAt: '2030-01-01T00:00:00.000Z',
30-
agentGuidance: {
31-
instruction: 'A Shopify CLI session is available. Continue with the requested Shopify CLI command.',
32-
},
33-
})
34-
13+
test('checks auth status as text by default', async () => {
3514
// When
3615
await Status.run([])
3716

3817
// Then
39-
expect(outputMock.info()).toBe('Logged in as user@example.com.')
40-
expect(process.exitCode).toBeUndefined()
18+
expect(authStatusService).toHaveBeenCalledWith(false)
4119
})
4220

43-
test('prints status as JSON', async () => {
44-
// Given
45-
const outputMock = mockAndCaptureOutput()
46-
const status = {
47-
status: 'not_authenticated' as const,
48-
authenticated: false,
49-
identityFqdn: 'accounts.shopify.com',
50-
agentGuidance: {
51-
instruction:
52-
'No usable Shopify CLI session is available. Run `shopify auth login`, show the verification URL and user code to the user, and keep the command running until authentication completes.',
53-
nextCommand: 'shopify auth login',
54-
},
55-
}
56-
vi.mocked(getAuthStatus).mockResolvedValue(status)
57-
21+
test('checks auth status as JSON', async () => {
5822
// When
5923
await Status.run(['--json'])
6024

6125
// Then
62-
expect(JSON.parse(outputMock.output())).toEqual(status)
63-
expect(process.exitCode).toBe(1)
64-
})
65-
66-
test('sets a failing exit code when not authenticated', async () => {
67-
// Given
68-
const outputMock = mockAndCaptureOutput()
69-
vi.mocked(getAuthStatus).mockResolvedValue({
70-
status: 'not_authenticated',
71-
authenticated: false,
72-
identityFqdn: 'accounts.shopify.com',
73-
agentGuidance: {
74-
instruction:
75-
'No usable Shopify CLI session is available. Run `shopify auth login`, show the verification URL and user code to the user, and keep the command running until authentication completes.',
76-
nextCommand: 'shopify auth login',
77-
},
78-
})
79-
80-
// When
81-
await Status.run([])
82-
83-
// Then
84-
expect(outputMock.info()).toBe('Not logged in. Run `shopify auth login`.')
85-
expect(process.exitCode).toBe(1)
26+
expect(authStatusService).toHaveBeenCalledWith(true)
8627
})
8728
})
Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,6 @@
1+
import {authStatusService} from '../../services/commands/auth-status.js'
12
import Command from '@shopify/cli-kit/node/base-command'
23
import {jsonFlag} from '@shopify/cli-kit/node/cli'
3-
import {outputInfo, outputResult} from '@shopify/cli-kit/node/output'
4-
import {AuthStatus, getAuthStatus} from '@shopify/cli-kit/node/session'
5-
6-
function serializeAuthStatus(status: AuthStatus): string {
7-
return JSON.stringify(status, null, 2)
8-
}
9-
10-
function displayAuthStatus(status: AuthStatus): void {
11-
switch (status.status) {
12-
case 'authenticated': {
13-
const account = status.account?.alias ?? status.account?.userId
14-
outputInfo(`Logged in as ${account}.`)
15-
return
16-
}
17-
case 'needs_refresh': {
18-
const account = status.account?.alias ?? status.account?.userId
19-
outputInfo(`Logged in as ${account}, but the session may refresh before use.`)
20-
return
21-
}
22-
case 'not_authenticated': {
23-
outputInfo('Not logged in. Run `shopify auth login`.')
24-
return
25-
}
26-
case 'invalid': {
27-
outputInfo('The saved Shopify CLI session is invalid. Run `shopify auth login`.')
28-
}
29-
}
30-
}
314

325
export default class Status extends Command {
336
static summary = 'Show Shopify account authentication status.'
@@ -46,16 +19,6 @@ Use \`--json\` for stable machine-readable output. Agents should check this comm
4619

4720
async run(): Promise<void> {
4821
const {flags} = await this.parse(Status)
49-
const status = await getAuthStatus()
50-
51-
if (flags.json) {
52-
outputResult(serializeAuthStatus(status))
53-
} else {
54-
displayAuthStatus(status)
55-
}
56-
57-
if (!status.authenticated) {
58-
process.exitCode = 1
59-
}
22+
await authStatusService(flags.json)
6023
}
6124
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {authStatusService} from './auth-status.js'
2+
import {getAuthStatus} from '@shopify/cli-kit/node/session'
3+
import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output'
4+
5+
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'
6+
7+
vi.mock('@shopify/cli-kit/node/session')
8+
9+
describe('authStatusService', () => {
10+
beforeEach(() => {
11+
mockAndCaptureOutput().clear()
12+
vi.clearAllMocks()
13+
process.exitCode = undefined
14+
})
15+
16+
afterEach(() => {
17+
mockAndCaptureOutput().clear()
18+
process.exitCode = undefined
19+
})
20+
21+
test('prints authenticated status as text', async () => {
22+
// Given
23+
const outputMock = mockAndCaptureOutput()
24+
vi.mocked(getAuthStatus).mockResolvedValue({
25+
status: 'authenticated',
26+
authenticated: true,
27+
account: {userId: 'user-id', alias: 'user@example.com'},
28+
identityFqdn: 'accounts.shopify.com',
29+
expiresAt: '2030-01-01T00:00:00.000Z',
30+
agentGuidance: {
31+
instruction: 'A Shopify CLI session is available. Continue with the requested Shopify CLI command.',
32+
},
33+
})
34+
35+
// When
36+
await authStatusService(false)
37+
38+
// Then
39+
expect(outputMock.info()).toBe('Logged in as user@example.com.')
40+
expect(process.exitCode).toBeUndefined()
41+
})
42+
43+
test('prints status as JSON', async () => {
44+
// Given
45+
const outputMock = mockAndCaptureOutput()
46+
const status = {
47+
status: 'not_authenticated' as const,
48+
authenticated: false,
49+
identityFqdn: 'accounts.shopify.com',
50+
agentGuidance: {
51+
instruction:
52+
'No usable Shopify CLI session is available. Run `shopify auth login`, show the verification URL and user code to the user, and keep the command running until authentication completes.',
53+
nextCommand: 'shopify auth login',
54+
},
55+
}
56+
vi.mocked(getAuthStatus).mockResolvedValue(status)
57+
58+
// When
59+
await authStatusService(true)
60+
61+
// Then
62+
expect(JSON.parse(outputMock.output())).toEqual(status)
63+
expect(process.exitCode).toBe(1)
64+
})
65+
66+
test('sets a failing exit code when not authenticated', async () => {
67+
// Given
68+
const outputMock = mockAndCaptureOutput()
69+
vi.mocked(getAuthStatus).mockResolvedValue({
70+
status: 'not_authenticated',
71+
authenticated: false,
72+
identityFqdn: 'accounts.shopify.com',
73+
agentGuidance: {
74+
instruction:
75+
'No usable Shopify CLI session is available. Run `shopify auth login`, show the verification URL and user code to the user, and keep the command running until authentication completes.',
76+
nextCommand: 'shopify auth login',
77+
},
78+
})
79+
80+
// When
81+
await authStatusService(false)
82+
83+
// Then
84+
expect(outputMock.info()).toBe('Not logged in. Run `shopify auth login`.')
85+
expect(process.exitCode).toBe(1)
86+
})
87+
})
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {outputInfo, outputResult} from '@shopify/cli-kit/node/output'
2+
import {AuthStatus, getAuthStatus} from '@shopify/cli-kit/node/session'
3+
4+
function serializeAuthStatus(status: AuthStatus): string {
5+
return JSON.stringify(status, null, 2)
6+
}
7+
8+
function displayAuthStatus(status: AuthStatus): void {
9+
switch (status.status) {
10+
case 'authenticated': {
11+
const account = status.account?.alias ?? status.account?.userId
12+
outputInfo(`Logged in as ${account}.`)
13+
return
14+
}
15+
case 'needs_refresh': {
16+
const account = status.account?.alias ?? status.account?.userId
17+
outputInfo(`Logged in as ${account}, but the session may refresh before use.`)
18+
return
19+
}
20+
case 'not_authenticated': {
21+
outputInfo('Not logged in. Run `shopify auth login`.')
22+
return
23+
}
24+
case 'invalid': {
25+
outputInfo('The saved Shopify CLI session is invalid. Run `shopify auth login`.')
26+
}
27+
}
28+
}
29+
30+
export async function authStatusService(json: boolean): Promise<void> {
31+
const status = await getAuthStatus()
32+
33+
if (json) {
34+
outputResult(serializeAuthStatus(status))
35+
} else {
36+
displayAuthStatus(status)
37+
}
38+
39+
if (!status.authenticated) {
40+
process.exitCode = 1
41+
}
42+
}

0 commit comments

Comments
 (0)