返回 AiToEarn
kwai.service.spec.ts
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
106 lines TYPESCRIPT