| 1 | import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios' |
| 2 | import type { KwaiPlatformResponseBody } from './kwai.exception' |
| 3 | import { AxiosError } from 'axios' |
| 4 | import { PlatformErrorCategory, PlatformErrorCauseType } from '../platforms.exception' |
| 5 | import { KwaiPlatformException } from './kwai.exception' |
| 6 | import { KwaiService } from './kwai.service' |
| 7 | |
| 8 | function createService() { |
| 9 | return new KwaiService({ |
| 10 | clientId: 'client-id', |
| 11 | clientSecret: 'client-secret', |
| 12 | redirectUri: 'https://api.example.test/api/v2/channels/accounts/auth/KWAI/callback', |
| 13 | logoUrl: 'https://assets.aitoearn.ai/platforms/kwai.svg', |
| 14 | scopes: ['user_info', 'user_video_publish'], |
| 15 | } as never) |
| 16 | } |
| 17 | |
| 18 | describe('kwai service auth url', () => { |
| 19 | it('adds pc ua parameter for desktop auth', () => { |
| 20 | const service = createService() |
| 21 | |
| 22 | const authUrl = new URL(service.generateAuthUrl(['user_info'], 'state-1', 'desktop')) |
| 23 | |
| 24 | expect(authUrl.searchParams.get('ua')).toBe('pc') |
| 25 | }) |
| 26 | |
| 27 | it.each(['mobile', 'tablet', 'unknown'] as const)('does not add pc ua parameter for %s auth', (deviceType) => { |
| 28 | const service = createService() |
| 29 | |
| 30 | const authUrl = new URL(service.generateAuthUrl(['user_info'], 'state-1', deviceType)) |
| 31 | |
| 32 | expect(authUrl.searchParams.has('ua')).toBe(false) |
| 33 | }) |
| 34 | }) |
| 35 | |
| 36 | describe('kwai service axios interceptors', () => { |
| 37 | it('converts successful HTTP platform body errors in the response interceptor', async () => { |
| 38 | const service = createService() |
| 39 | const serviceWithHttp = service as unknown as { http: { defaults: { adapter: unknown } } } |
| 40 | serviceWithHttp.http.defaults.adapter = async (config: unknown) => ({ |
| 41 | data: { |
| 42 | result: 1001, |
| 43 | error_msg: 'invalid authorization code', |
| 44 | }, |
| 45 | status: 200, |
| 46 | statusText: 'OK', |
| 47 | headers: {}, |
| 48 | config, |
| 49 | }) |
| 50 | |
| 51 | await expect(service.exchangeCode('bad-code')).rejects.toMatchObject({ |
| 52 | category: PlatformErrorCategory.Auth, |
| 53 | platformCause: { |
| 54 | platformCode: 1001, |
| 55 | platformMessage: 'invalid authorization code', |
| 56 | }, |
| 57 | }) |
| 58 | }) |
| 59 | }) |
| 60 | |
| 61 | describe('kwai platform exception', () => { |
| 62 | it('classifies axios network errors as retryable network failures', () => { |
| 63 | const config = { |
| 64 | method: 'get', |
| 65 | url: 'https://open.kuaishou.com/openapi/photo/info', |
| 66 | } as InternalAxiosRequestConfig |
| 67 | const error = new AxiosError('socket hang up', 'ECONNRESET', config) |
| 68 | |
| 69 | const exception = KwaiPlatformException.fromAxiosError(error) |
| 70 | |
| 71 | expect(exception.context?.endpoint).toBe('GET /openapi/photo/info') |
| 72 | expect(exception.category).toBe(PlatformErrorCategory.Network) |
| 73 | expect(exception.retryable).toBe(true) |
| 74 | expect(exception.platformCause).toMatchObject({ |
| 75 | type: PlatformErrorCauseType.Network, |
| 76 | platformMessage: 'socket hang up', |
| 77 | }) |
| 78 | }) |
| 79 | |
| 80 | it('marks post-publish VIDEO_NOT_EXIST photo info responses as retryable', () => { |
| 81 | const response: AxiosResponse<KwaiPlatformResponseBody> = { |
| 82 | data: { |
| 83 | result: 100120001, |
| 84 | error: 'video_not_exist', |
| 85 | error_msg: 'VIDEO_NOT_EXIST', |
| 86 | }, |
| 87 | status: 200, |
| 88 | statusText: 'OK', |
| 89 | headers: {}, |
| 90 | config: { |
| 91 | method: 'get', |
| 92 | url: 'https://open.kuaishou.com/openapi/photo/info', |
| 93 | } as InternalAxiosRequestConfig, |
| 94 | } |
| 95 | |
| 96 | expect(KwaiPlatformException.fromPlatformResponse(response)).toMatchObject({ |
| 97 | category: PlatformErrorCategory.MediaProcessingFailed, |
| 98 | retryable: true, |
| 99 | platformCause: { |
| 100 | platformCode: 100120001, |
| 101 | platformMessage: 'VIDEO_NOT_EXIST', |
| 102 | }, |
| 103 | }) |
| 104 | }) |
| 105 | }) |
| 106 |