Skip to content

Commit 5379dcf

Browse files
committed
Fix CLI login typechecking
1 parent 992fcdd commit 5379dcf

File tree

6 files changed

+62
-41
lines changed

6 files changed

+62
-41
lines changed

cli/src/__tests__/e2e/first-time-login.test.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ import {
88

99
import type { Logger } from '@codebuff/common/types/contracts/logger'
1010

11-
const createLogger = (): Logger & Record<string, ReturnType<typeof mock>> => ({
12-
info: mock(() => {}),
13-
error: mock(() => {}),
14-
warn: mock(() => {}),
15-
debug: mock(() => {}),
11+
type MockLogger = {
12+
[K in keyof Logger]: ReturnType<typeof mock> & Logger[K]
13+
}
14+
15+
const createLogger = (): MockLogger => ({
16+
info: mock(() => {}) as ReturnType<typeof mock> & Logger['info'],
17+
error: mock(() => {}) as ReturnType<typeof mock> & Logger['error'],
18+
warn: mock(() => {}) as ReturnType<typeof mock> & Logger['warn'],
19+
debug: mock(() => {}) as ReturnType<typeof mock> & Logger['debug'],
1620
})
1721

1822
describe('First-Time Login Flow (helpers)', () => {
@@ -87,11 +91,12 @@ describe('First-Time Login Flow (helpers)', () => {
8791
)
8892

8993
expect(result.status).toBe('success')
94+
if (result.status !== 'success') {
95+
throw new Error(`Expected polling success but received ${result.status}`)
96+
}
9097
expect(result.attempts).toBe(3)
91-
expect(result).toHaveProperty('user')
92-
expect(
93-
(result as { user: { id: string } }).user.id,
94-
).toBe('new-user-123')
98+
const user = result.user as { id?: unknown }
99+
expect(user?.id).toBe('new-user-123')
95100
expect(fetchMock.mock.calls.length).toBe(3)
96101
})
97102

cli/src/__tests__/e2e/logout-relogin-flow.test.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import {
1010
type User,
1111
} from '../../utils/auth'
1212

13-
import type { Logger } from '@codebuff/common/types/contracts/logger'
14-
1513
const ORIGINAL_USER: User = {
1614
id: 'user-001',
1715
name: 'CLI Tester',
@@ -28,13 +26,6 @@ const RELOGIN_USER: User = {
2826
fingerprintHash: 'fingerprint-hash-new',
2927
}
3028

31-
const createLogger = (): Logger & Record<string, ReturnType<typeof mock>> => ({
32-
info: mock(() => {}),
33-
error: mock(() => {}),
34-
warn: mock(() => {}),
35-
debug: mock(() => {}),
36-
})
37-
3829
describe('Logout and Re-login helpers', () => {
3930
let tempConfigDir: string
4031

@@ -64,7 +55,7 @@ describe('Logout and Re-login helpers', () => {
6455
const credentialsPath = path.join(tempConfigDir, 'credentials.json')
6556
expect(fs.existsSync(credentialsPath)).toBe(true)
6657

67-
const result = await logoutUser(createLogger())
58+
const result = await logoutUser()
6859
expect(result).toBe(true)
6960
expect(fs.existsSync(credentialsPath)).toBe(false)
7061
})
@@ -76,7 +67,7 @@ describe('Logout and Re-login helpers', () => {
7667
const firstLoaded = getUserCredentials()
7768
expect(firstLoaded?.authToken).toBe('token-original')
7869

79-
await logoutUser(createLogger())
70+
await logoutUser()
8071
expect(getUserCredentials()).toBeNull()
8172

8273
saveUserCredentials(RELOGIN_USER)
@@ -88,10 +79,10 @@ describe('Logout and Re-login helpers', () => {
8879
test('logoutUser is idempotent when credentials are already missing', async () => {
8980
mockConfigPaths()
9081

91-
const resultFirst = await logoutUser(createLogger())
82+
const resultFirst = await logoutUser()
9283
expect(resultFirst).toBe(true)
9384

94-
const resultSecond = await logoutUser(createLogger())
85+
const resultSecond = await logoutUser()
9586
expect(resultSecond).toBe(true)
9687
})
9788
})

cli/src/__tests__/e2e/returning-user-auth.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,9 @@ describe('Returning User Authentication helpers', () => {
9898
logger,
9999
})
100100

101-
expect(result).toEqual({
102-
id: RETURNING_USER.id,
103-
email: RETURNING_USER.email,
104-
})
101+
expect(RETURNING_USER.id).toBeDefined()
102+
expect(result.id).toBe(RETURNING_USER.id!)
103+
expect(result.email).toBe(RETURNING_USER.email)
105104
expect(mockGetUserInfoFromApiKey).toHaveBeenCalledTimes(1)
106105
})
107106
})

cli/src/__tests__/integration/login-polling-working.test.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import { generateLoginUrl, pollLoginStatus } from '../../login/login-flow'
55
import type { Logger } from '@codebuff/common/types/contracts/logger'
66
import type { LoginUrlResponse } from '../../login/login-flow'
77

8-
const createLogger = (): Logger & Record<string, ReturnType<typeof mock>> => ({
9-
info: mock(() => {}),
10-
error: mock(() => {}),
11-
warn: mock(() => {}),
12-
debug: mock(() => {}),
8+
type MockLogger = {
9+
[K in keyof Logger]: ReturnType<typeof mock> & Logger[K]
10+
}
11+
12+
const createLogger = (): MockLogger => ({
13+
info: mock(() => {}) as ReturnType<typeof mock> & Logger['info'],
14+
error: mock(() => {}) as ReturnType<typeof mock> & Logger['error'],
15+
warn: mock(() => {}) as ReturnType<typeof mock> & Logger['warn'],
16+
debug: mock(() => {}) as ReturnType<typeof mock> & Logger['debug'],
1317
})
1418

1519
const createClock = () => {
@@ -63,6 +67,9 @@ describe('Login Polling (Working)', () => {
6367
)
6468

6569
expect(result.status).toBe('success')
70+
if (result.status !== 'success') {
71+
throw new Error(`Expected polling success but received ${result.status}`)
72+
}
6673
expect(result.attempts).toBe(2)
6774
expect(fetchMock.mock.calls.length).toBe(2)
6875
})
@@ -131,6 +138,9 @@ describe('Login Polling (Working)', () => {
131138
)
132139

133140
expect(result.status).toBe('success')
141+
if (result.status !== 'success') {
142+
throw new Error(`Expected polling success but received ${result.status}`)
143+
}
134144
expect(fetchMock.mock.calls.length).toBe(1)
135145
})
136146

@@ -200,12 +210,20 @@ describe('Login Polling (Working)', () => {
200210
)
201211

202212
expect(result.status).toBe('success')
213+
if (result.status !== 'success') {
214+
throw new Error(`Expected polling success but received ${result.status}`)
215+
}
203216
expect(fetchMock.mock.calls.length).toBeGreaterThan(1)
204-
expect(
205-
logger.error.mock.calls.some(([payload]) =>
206-
JSON.stringify(payload).includes('network failed'),
207-
),
208-
).toBe(true)
217+
const errorCalls = logger.error.mock.calls as Array<
218+
Parameters<Logger['error']>
219+
>
220+
const sawNetworkFailure = errorCalls.some(([payload]) => {
221+
if (!payload || typeof payload !== 'object') {
222+
return false
223+
}
224+
return JSON.stringify(payload as any).includes('network failed')
225+
})
226+
expect(sawNetworkFailure).toBe(true)
209227
})
210228

211229
test('P0: fetchLoginUrl wrapper - should hit backend and return payload', async () => {

cli/src/hooks/use-auth-query.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ interface ValidateAuthParams {
2626
logger?: Logger
2727
}
2828

29+
type ValidatedUserInfo = {
30+
id: string
31+
email: string
32+
}
33+
2934
/**
3035
* Validates an API key by calling the backend
3136
*
@@ -36,18 +41,20 @@ export async function validateApiKey({
3641
apiKey,
3742
getUserInfoFromApiKey = defaultGetUserInfoFromApiKey,
3843
logger = defaultLogger,
39-
}: ValidateAuthParams) {
44+
}: ValidateAuthParams): Promise<ValidatedUserInfo> {
45+
const requestedFields = ['id', 'email'] as const
46+
4047
logger.info(
4148
{
4249
apiKeyPrefix: apiKey.substring(0, 10) + '...',
43-
fields: ['id', 'email'],
50+
fields: requestedFields,
4451
},
4552
'🔐 Validating API key via getUserInfoFromApiKey',
4653
)
4754

4855
const authResult = await getUserInfoFromApiKey({
4956
apiKey,
50-
fields: ['id', 'email'],
57+
fields: requestedFields,
5158
logger,
5259
})
5360

cli/src/login/login-flow.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,9 @@ export async function pollLoginStatus(
205205
continue
206206
}
207207

208-
const user = (data as Record<string, unknown> | null)?.user
209-
if (user && typeof user === 'object') {
208+
const rawUser = (data as { user?: unknown } | null)?.user
209+
if (rawUser && typeof rawUser === 'object') {
210+
const user = rawUser as Record<string, unknown>
210211
logger.info(
211212
{
212213
attempts,

0 commit comments

Comments
 (0)