@open-kingdom/shared-backend-integration-test-doubles 0.0.2-17 → 0.0.2-19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -5
- package/dist/gcs/gcs-error-mode.d.ts +161 -0
- package/dist/gcs/gcs-error-mode.d.ts.map +1 -0
- package/dist/gcs/gcs-error-mode.js +256 -0
- package/dist/gcs/gcs-error-mode.js.map +1 -0
- package/dist/gcs/gcs-error-simulation.interceptor.d.ts +21 -0
- package/dist/gcs/gcs-error-simulation.interceptor.d.ts.map +1 -0
- package/dist/gcs/gcs-error-simulation.interceptor.js +40 -0
- package/dist/gcs/gcs-error-simulation.interceptor.js.map +1 -0
- package/dist/gcs/gcs-twin.d.ts +1 -0
- package/dist/gcs/gcs-twin.d.ts.map +1 -1
- package/dist/gcs/gcs-twin.js +19 -1
- package/dist/gcs/gcs-twin.js.map +1 -1
- package/dist/gcs/index.d.ts +3 -0
- package/dist/gcs/index.d.ts.map +1 -1
- package/dist/gcs/index.js +2 -0
- package/dist/gcs/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptor/http-helper.d.ts +37 -0
- package/dist/interceptor/http-helper.d.ts.map +1 -0
- package/dist/interceptor/http-helper.js +109 -0
- package/dist/interceptor/http-helper.js.map +1 -0
- package/dist/interceptor/index.d.ts +4 -0
- package/dist/interceptor/index.d.ts.map +1 -0
- package/dist/interceptor/index.js +5 -0
- package/dist/interceptor/index.js.map +1 -0
- package/dist/interceptor/node-interceptor.d.ts +40 -0
- package/dist/interceptor/node-interceptor.d.ts.map +1 -0
- package/dist/interceptor/node-interceptor.js +188 -0
- package/dist/interceptor/node-interceptor.js.map +1 -0
- package/dist/interceptor/routing-table.config.d.ts +3 -0
- package/dist/interceptor/routing-table.config.d.ts.map +1 -0
- package/dist/interceptor/routing-table.config.js +25 -0
- package/dist/interceptor/routing-table.config.js.map +1 -0
- package/dist/interceptor/routing-table.d.ts +15 -0
- package/dist/interceptor/routing-table.d.ts.map +1 -0
- package/dist/interceptor/routing-table.js +44 -0
- package/dist/interceptor/routing-table.js.map +1 -0
- package/dist/shared/constants.d.ts +4 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +3 -1
- package/dist/shared/constants.js.map +1 -1
- package/dist/youtube/index.d.ts +15 -0
- package/dist/youtube/index.d.ts.map +1 -0
- package/dist/youtube/index.js +11 -0
- package/dist/youtube/index.js.map +1 -0
- package/dist/youtube/msw-handlers.d.ts +40 -0
- package/dist/youtube/msw-handlers.d.ts.map +1 -0
- package/dist/youtube/msw-handlers.js +45 -0
- package/dist/youtube/msw-handlers.js.map +1 -0
- package/dist/youtube/player-shim.d.ts +3 -0
- package/dist/youtube/player-shim.d.ts.map +1 -0
- package/dist/youtube/player-shim.js +296 -0
- package/dist/youtube/player-shim.js.map +1 -0
- package/dist/youtube/search-fixtures.d.ts +29 -0
- package/dist/youtube/search-fixtures.d.ts.map +1 -0
- package/dist/youtube/search-fixtures.js +392 -0
- package/dist/youtube/search-fixtures.js.map +1 -0
- package/dist/youtube/search-response.d.ts +51 -0
- package/dist/youtube/search-response.d.ts.map +1 -0
- package/dist/youtube/search-response.js +53 -0
- package/dist/youtube/search-response.js.map +1 -0
- package/dist/youtube/youtube-error-mode.d.ts +36 -0
- package/dist/youtube/youtube-error-mode.d.ts.map +1 -0
- package/dist/youtube/youtube-error-mode.js +87 -0
- package/dist/youtube/youtube-error-mode.js.map +1 -0
- package/dist/youtube/youtube-twin.config.d.ts +13 -0
- package/dist/youtube/youtube-twin.config.d.ts.map +1 -0
- package/dist/youtube/youtube-twin.config.js +32 -0
- package/dist/youtube/youtube-twin.config.js.map +1 -0
- package/dist/youtube/youtube-twin.d.ts +14 -0
- package/dist/youtube/youtube-twin.d.ts.map +1 -0
- package/dist/youtube/youtube-twin.js +162 -0
- package/dist/youtube/youtube-twin.js.map +1 -0
- package/package.json +15 -2
- package/test-assets/youtube/sample.mp4 +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-01.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-02.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-03.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-04.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-05.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-06.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-07.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-08.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-09.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-10.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-11.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-12.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-13.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-14.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-15.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-16.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-17.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-18.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-19.jpg +0 -0
- package/test-assets/youtube/thumbnails/thumbnail-20.jpg +0 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canned YouTube video search fixtures.
|
|
3
|
+
*
|
|
4
|
+
* Each fixture represents a single video result with realistic metadata.
|
|
5
|
+
* The fixture set spans diverse content categories to support query-aware
|
|
6
|
+
* filtering in tests.
|
|
7
|
+
*/ export const videoFixtures = [
|
|
8
|
+
{
|
|
9
|
+
videoId: 'test-vid-001',
|
|
10
|
+
title: 'Morning Yoga Flow - 20 Minutes',
|
|
11
|
+
channelTitle: 'Wellness Daily',
|
|
12
|
+
channelId: 'UC-wellness-daily-001',
|
|
13
|
+
description: 'Start your day with this gentle 20-minute yoga flow for all levels.',
|
|
14
|
+
publishedAt: '2024-03-15T10:00:00Z',
|
|
15
|
+
thumbnailIndex: 1,
|
|
16
|
+
duration: 'PT20M15S',
|
|
17
|
+
viewCount: '1250000',
|
|
18
|
+
tags: [
|
|
19
|
+
'yoga',
|
|
20
|
+
'morning',
|
|
21
|
+
'wellness',
|
|
22
|
+
'fitness',
|
|
23
|
+
'stretching'
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
videoId: 'test-vid-002',
|
|
28
|
+
title: 'How to Make Classic Italian Pasta carbonara',
|
|
29
|
+
channelTitle: 'Chef Roberto',
|
|
30
|
+
channelId: 'UC-chef-roberto-002',
|
|
31
|
+
description: 'Learn the secrets to authentic, creamy Roman carbonara without cream!',
|
|
32
|
+
publishedAt: '2024-03-14T17:30:00Z',
|
|
33
|
+
thumbnailIndex: 2,
|
|
34
|
+
duration: 'PT12M45S',
|
|
35
|
+
viewCount: '450000',
|
|
36
|
+
tags: [
|
|
37
|
+
'cooking',
|
|
38
|
+
'pasta',
|
|
39
|
+
'carbonara',
|
|
40
|
+
'italian',
|
|
41
|
+
'recipe'
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
videoId: 'test-vid-003',
|
|
46
|
+
title: 'TypeScript in 100 Seconds',
|
|
47
|
+
channelTitle: 'Code Academy',
|
|
48
|
+
channelId: 'UC-code-academy-003',
|
|
49
|
+
description: 'A quick overview of TypeScript features and type safety benefits.',
|
|
50
|
+
publishedAt: '2024-03-13T12:00:00Z',
|
|
51
|
+
thumbnailIndex: 3,
|
|
52
|
+
duration: 'PT1M40S',
|
|
53
|
+
viewCount: '890000',
|
|
54
|
+
tags: [
|
|
55
|
+
'programming',
|
|
56
|
+
'typescript',
|
|
57
|
+
'javascript',
|
|
58
|
+
'coding',
|
|
59
|
+
'tutorial'
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
videoId: 'test-vid-004',
|
|
64
|
+
title: 'Solo Travel Guide: 5 Days in Tokyo',
|
|
65
|
+
channelTitle: 'Wanderlust Journal',
|
|
66
|
+
channelId: 'UC-wanderlust-journal-004',
|
|
67
|
+
description: 'Discover the best sights, food, and neighborhoods in Tokyo as a solo traveler.',
|
|
68
|
+
publishedAt: '2024-03-12T08:15:00Z',
|
|
69
|
+
thumbnailIndex: 4,
|
|
70
|
+
duration: 'PT25M10S',
|
|
71
|
+
viewCount: '620000',
|
|
72
|
+
tags: [
|
|
73
|
+
'travel',
|
|
74
|
+
'tokyo',
|
|
75
|
+
'japan',
|
|
76
|
+
'guide',
|
|
77
|
+
'solo'
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
videoId: 'test-vid-005',
|
|
82
|
+
title: 'Acoustic Guitar Cover of Popular Hits 2024',
|
|
83
|
+
channelTitle: 'Acoustic Sessions',
|
|
84
|
+
channelId: 'UC-acoustic-sessions-005',
|
|
85
|
+
description: 'Relaxing instrumental acoustic guitar covers for studying or working.',
|
|
86
|
+
publishedAt: '2024-03-11T20:00:00Z',
|
|
87
|
+
thumbnailIndex: 5,
|
|
88
|
+
duration: 'PT45M00S',
|
|
89
|
+
viewCount: '340000',
|
|
90
|
+
tags: [
|
|
91
|
+
'music',
|
|
92
|
+
'guitar',
|
|
93
|
+
'acoustic',
|
|
94
|
+
'covers',
|
|
95
|
+
'relax'
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
videoId: 'test-vid-006',
|
|
100
|
+
title: 'What Happens Inside a Black Hole?',
|
|
101
|
+
channelTitle: 'Curiosity Lab',
|
|
102
|
+
channelId: 'UC-curiosity-lab-006',
|
|
103
|
+
description: 'Exploring the science of event horizons and singularity.',
|
|
104
|
+
publishedAt: '2024-03-10T14:00:00Z',
|
|
105
|
+
thumbnailIndex: 6,
|
|
106
|
+
duration: 'PT15M30S',
|
|
107
|
+
viewCount: '2100000',
|
|
108
|
+
tags: [
|
|
109
|
+
'science',
|
|
110
|
+
'space',
|
|
111
|
+
'blackhole',
|
|
112
|
+
'physics',
|
|
113
|
+
'astronomy'
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
videoId: 'test-vid-007',
|
|
118
|
+
title: 'Elden Ring Speedrun World Record Attempt',
|
|
119
|
+
channelTitle: 'Pixel Adventures',
|
|
120
|
+
channelId: 'UC-pixel-adventures-007',
|
|
121
|
+
description: 'Running through the Lands Between to beat the clock.',
|
|
122
|
+
publishedAt: '2024-03-09T19:45:00Z',
|
|
123
|
+
thumbnailIndex: 7,
|
|
124
|
+
duration: 'PT58M20S',
|
|
125
|
+
viewCount: '150000',
|
|
126
|
+
tags: [
|
|
127
|
+
'gaming',
|
|
128
|
+
'elden-ring',
|
|
129
|
+
'speedrun',
|
|
130
|
+
'streamer',
|
|
131
|
+
'rpg'
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
videoId: 'test-vid-008',
|
|
136
|
+
title: 'Linear Algebra: Matrices Made Intuitive',
|
|
137
|
+
channelTitle: 'Learn Math Easily',
|
|
138
|
+
channelId: 'UC-learn-math-easily-008',
|
|
139
|
+
description: 'Visualizing matrix transformations, eigenvectors, and eigenvalues.',
|
|
140
|
+
publishedAt: '2024-03-08T11:00:00Z',
|
|
141
|
+
thumbnailIndex: 8,
|
|
142
|
+
duration: 'PT30M15S',
|
|
143
|
+
viewCount: '980000',
|
|
144
|
+
tags: [
|
|
145
|
+
'education',
|
|
146
|
+
'math',
|
|
147
|
+
'algebra',
|
|
148
|
+
'matrices',
|
|
149
|
+
'visual'
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
videoId: 'test-vid-009',
|
|
154
|
+
title: 'The Great Migration: Serengeti Wild Life',
|
|
155
|
+
channelTitle: 'Earth Documentaries',
|
|
156
|
+
channelId: 'UC-earth-docs-009',
|
|
157
|
+
description: 'Witnessing millions of wildebeests cross the Mara River in Africa.',
|
|
158
|
+
publishedAt: '2024-03-07T06:00:00Z',
|
|
159
|
+
thumbnailIndex: 9,
|
|
160
|
+
duration: 'PT48M50S',
|
|
161
|
+
viewCount: '3200000',
|
|
162
|
+
tags: [
|
|
163
|
+
'nature',
|
|
164
|
+
'wildlife',
|
|
165
|
+
'serengeti',
|
|
166
|
+
'documentary',
|
|
167
|
+
'africa'
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
videoId: 'test-vid-010',
|
|
172
|
+
title: 'Landscape Photography: Mastering the Golden Hour',
|
|
173
|
+
channelTitle: 'Shutter Mastery',
|
|
174
|
+
channelId: 'UC-shutter-mastery-010',
|
|
175
|
+
description: 'Tips and tricks for capturing perfect lighting in outdoor landscape shots.',
|
|
176
|
+
publishedAt: '2024-03-06T15:40:00Z',
|
|
177
|
+
thumbnailIndex: 10,
|
|
178
|
+
duration: 'PT18M12S',
|
|
179
|
+
viewCount: '280000',
|
|
180
|
+
tags: [
|
|
181
|
+
'photography',
|
|
182
|
+
'landscape',
|
|
183
|
+
'camera',
|
|
184
|
+
'golden-hour',
|
|
185
|
+
'tutorial'
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
videoId: 'test-vid-011',
|
|
190
|
+
title: 'Design System Architecture: Figma to Code',
|
|
191
|
+
channelTitle: 'Creative UI/UX',
|
|
192
|
+
channelId: 'UC-creative-uiux-011',
|
|
193
|
+
description: 'How to structure design tokens and components for seamless engineering hands.',
|
|
194
|
+
publishedAt: '2024-03-05T09:30:00Z',
|
|
195
|
+
thumbnailIndex: 11,
|
|
196
|
+
duration: 'PT22M05S',
|
|
197
|
+
viewCount: '410000',
|
|
198
|
+
tags: [
|
|
199
|
+
'design',
|
|
200
|
+
'ui',
|
|
201
|
+
'ux',
|
|
202
|
+
'figma',
|
|
203
|
+
'architecture'
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
videoId: 'test-vid-012',
|
|
208
|
+
title: 'How We Built a $10M SaaS: Startup Lessons',
|
|
209
|
+
channelTitle: 'Startup Grind',
|
|
210
|
+
channelId: 'UC-startup-grind-012',
|
|
211
|
+
description: 'An honest breakdown of product-market fit, pricing, and scaling strategies.',
|
|
212
|
+
publishedAt: '2024-03-04T13:10:00Z',
|
|
213
|
+
thumbnailIndex: 12,
|
|
214
|
+
duration: 'PT16M55S',
|
|
215
|
+
viewCount: '540000',
|
|
216
|
+
tags: [
|
|
217
|
+
'business',
|
|
218
|
+
'saas',
|
|
219
|
+
'startup',
|
|
220
|
+
'scaling',
|
|
221
|
+
'lessons'
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
videoId: 'test-vid-013',
|
|
226
|
+
title: 'Learn French: 100 Common Phrases for Beginners',
|
|
227
|
+
channelTitle: 'Speak French Fluently',
|
|
228
|
+
channelId: 'UC-speak-french-013',
|
|
229
|
+
description: 'Essential French phrases for travel, greeting, and simple daily conversation.',
|
|
230
|
+
publishedAt: '2024-03-03T07:00:00Z',
|
|
231
|
+
thumbnailIndex: 13,
|
|
232
|
+
duration: 'PT14M20S',
|
|
233
|
+
viewCount: '730000',
|
|
234
|
+
tags: [
|
|
235
|
+
'language-learning',
|
|
236
|
+
'french',
|
|
237
|
+
'phrases',
|
|
238
|
+
'beginners',
|
|
239
|
+
'vocabulary'
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
videoId: 'test-vid-014',
|
|
244
|
+
title: 'DIY Oak Coffee Table: Woodworking Guide',
|
|
245
|
+
channelTitle: 'Home Woodworking',
|
|
246
|
+
channelId: 'UC-home-woodworking-014',
|
|
247
|
+
description: 'Building a modern coffee table from scratch using white oak and mortise joinery.',
|
|
248
|
+
publishedAt: '2024-03-02T16:20:00Z',
|
|
249
|
+
thumbnailIndex: 14,
|
|
250
|
+
duration: 'PT28M45S',
|
|
251
|
+
viewCount: '190000',
|
|
252
|
+
tags: [
|
|
253
|
+
'diy',
|
|
254
|
+
'woodworking',
|
|
255
|
+
'table',
|
|
256
|
+
'furniture',
|
|
257
|
+
'maker'
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
videoId: 'test-vid-015',
|
|
262
|
+
title: '15-Minute Mindfulness Meditation for Anxiety',
|
|
263
|
+
channelTitle: 'Calm Minds',
|
|
264
|
+
channelId: 'UC-calm-minds-015',
|
|
265
|
+
description: 'A guided meditation to reduce stress, calm the nervous system, and return to center.',
|
|
266
|
+
publishedAt: '2024-03-01T21:00:00Z',
|
|
267
|
+
thumbnailIndex: 15,
|
|
268
|
+
duration: 'PT15M00S',
|
|
269
|
+
viewCount: '1500000',
|
|
270
|
+
tags: [
|
|
271
|
+
'meditation',
|
|
272
|
+
'mindfulness',
|
|
273
|
+
'calm',
|
|
274
|
+
'anxiety',
|
|
275
|
+
'mentalhealth'
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
videoId: 'test-vid-016',
|
|
280
|
+
title: 'HIIT Workout: Full Body Fat Burner',
|
|
281
|
+
channelTitle: 'Active Training',
|
|
282
|
+
channelId: 'UC-active-training-016',
|
|
283
|
+
description: 'High intensity interval training routine. No equipment needed.',
|
|
284
|
+
publishedAt: '2024-02-28T09:00:00Z',
|
|
285
|
+
thumbnailIndex: 16,
|
|
286
|
+
duration: 'PT30M00S',
|
|
287
|
+
viewCount: '2500000',
|
|
288
|
+
tags: [
|
|
289
|
+
'sports',
|
|
290
|
+
'hiit',
|
|
291
|
+
'workout',
|
|
292
|
+
'cardio',
|
|
293
|
+
'fitness'
|
|
294
|
+
]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
videoId: 'test-vid-017',
|
|
298
|
+
title: 'iPhone 16 Pro Max: Honest Review After 6 Months',
|
|
299
|
+
channelTitle: 'Gadget Sphere',
|
|
300
|
+
channelId: 'UC-gadget-sphere-017',
|
|
301
|
+
description: 'Is it still worth buying? Camera, battery life, and durability analysis.',
|
|
302
|
+
publishedAt: '2024-02-27T14:30:00Z',
|
|
303
|
+
thumbnailIndex: 17,
|
|
304
|
+
duration: 'PT13M40S',
|
|
305
|
+
viewCount: '870000',
|
|
306
|
+
tags: [
|
|
307
|
+
'tech-reviews',
|
|
308
|
+
'iphone',
|
|
309
|
+
'apple',
|
|
310
|
+
'smartphone',
|
|
311
|
+
'gadgets'
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
videoId: 'test-vid-018',
|
|
316
|
+
title: 'The Rise and Fall of the Roman Empire',
|
|
317
|
+
channelTitle: 'Chronicles of Old',
|
|
318
|
+
channelId: 'UC-chronicles-old-018',
|
|
319
|
+
description: 'Understanding the political and social dynamics that shaped European history.',
|
|
320
|
+
publishedAt: '2024-02-26T10:00:00Z',
|
|
321
|
+
thumbnailIndex: 18,
|
|
322
|
+
duration: 'PT52M15S',
|
|
323
|
+
viewCount: '1850000',
|
|
324
|
+
tags: [
|
|
325
|
+
'history',
|
|
326
|
+
'rome',
|
|
327
|
+
'roman-empire',
|
|
328
|
+
'documentary',
|
|
329
|
+
'history-lessons'
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
videoId: 'test-vid-019',
|
|
334
|
+
title: 'When Programmers Try to Socialize (Sketch)',
|
|
335
|
+
channelTitle: 'Laughter Zone',
|
|
336
|
+
channelId: 'UC-laughter-zone-019',
|
|
337
|
+
description: 'A funny comedy sketch about introverted developers attending a house party.',
|
|
338
|
+
publishedAt: '2024-02-25T18:00:00Z',
|
|
339
|
+
thumbnailIndex: 19,
|
|
340
|
+
duration: 'PT4M15S',
|
|
341
|
+
viewCount: '950000',
|
|
342
|
+
tags: [
|
|
343
|
+
'comedy',
|
|
344
|
+
'programming',
|
|
345
|
+
'developer-humor',
|
|
346
|
+
'skit',
|
|
347
|
+
'funny'
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
videoId: 'test-vid-020',
|
|
352
|
+
title: 'Behind the Scenes: Hand-Drawn Animation Loop',
|
|
353
|
+
channelTitle: 'Cartoon Studio',
|
|
354
|
+
channelId: 'UC-cartoon-studio-020',
|
|
355
|
+
description: 'Timelapse of a 24-frame classical animation walk cycle on light box.',
|
|
356
|
+
publishedAt: '2024-02-24T12:00:00Z',
|
|
357
|
+
thumbnailIndex: 20,
|
|
358
|
+
duration: 'PT8M50S',
|
|
359
|
+
viewCount: '320000',
|
|
360
|
+
tags: [
|
|
361
|
+
'animation',
|
|
362
|
+
'cartoon',
|
|
363
|
+
'art',
|
|
364
|
+
'timelapse',
|
|
365
|
+
'drawing'
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
];
|
|
369
|
+
/**
|
|
370
|
+
* Search fixtures using query-aware filtering.
|
|
371
|
+
*
|
|
372
|
+
* Matches `q` against title, description, and tags (case-insensitive).
|
|
373
|
+
* If no matches found, returns a general fallback subset.
|
|
374
|
+
* Never returns empty unless the fixture set itself is empty.
|
|
375
|
+
*/ export function searchFixtures(query, maxResults = 10, fixtures = videoFixtures) {
|
|
376
|
+
const clampedMax = Math.min(Math.max(maxResults, 1), 50);
|
|
377
|
+
if (fixtures.length === 0) {
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
if (!query || query.trim() === '') {
|
|
381
|
+
return fixtures.slice(0, clampedMax);
|
|
382
|
+
}
|
|
383
|
+
const q = query.toLowerCase().trim();
|
|
384
|
+
const matches = fixtures.filter((v)=>v.title.toLowerCase().includes(q) || v.tags.some((t)=>t.toLowerCase().includes(q)) || v.description.toLowerCase().includes(q));
|
|
385
|
+
if (matches.length === 0) {
|
|
386
|
+
// Fallback: return general subset so tests don't get unexpected empty results
|
|
387
|
+
return fixtures.slice(0, clampedMax);
|
|
388
|
+
}
|
|
389
|
+
return matches.slice(0, clampedMax);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
//# sourceMappingURL=search-fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/youtube/search-fixtures.ts"],"sourcesContent":["/**\n * Canned YouTube video search fixtures.\n *\n * Each fixture represents a single video result with realistic metadata.\n * The fixture set spans diverse content categories to support query-aware\n * filtering in tests.\n */\n\nexport interface VideoFixture {\n videoId: string;\n title: string;\n channelTitle: string;\n channelId: string;\n description: string;\n publishedAt: string; // ISO 8601\n thumbnailIndex: number; // Maps to thumbnail-XX.jpg\n duration: string; // ISO 8601 duration (e.g., \"PT20M15S\")\n viewCount: string;\n tags: string[];\n}\n\nexport const videoFixtures: VideoFixture[] = [\n {\n videoId: 'test-vid-001',\n title: 'Morning Yoga Flow - 20 Minutes',\n channelTitle: 'Wellness Daily',\n channelId: 'UC-wellness-daily-001',\n description:\n 'Start your day with this gentle 20-minute yoga flow for all levels.',\n publishedAt: '2024-03-15T10:00:00Z',\n thumbnailIndex: 1,\n duration: 'PT20M15S',\n viewCount: '1250000',\n tags: ['yoga', 'morning', 'wellness', 'fitness', 'stretching'],\n },\n {\n videoId: 'test-vid-002',\n title: 'How to Make Classic Italian Pasta carbonara',\n channelTitle: 'Chef Roberto',\n channelId: 'UC-chef-roberto-002',\n description:\n 'Learn the secrets to authentic, creamy Roman carbonara without cream!',\n publishedAt: '2024-03-14T17:30:00Z',\n thumbnailIndex: 2,\n duration: 'PT12M45S',\n viewCount: '450000',\n tags: ['cooking', 'pasta', 'carbonara', 'italian', 'recipe'],\n },\n {\n videoId: 'test-vid-003',\n title: 'TypeScript in 100 Seconds',\n channelTitle: 'Code Academy',\n channelId: 'UC-code-academy-003',\n description:\n 'A quick overview of TypeScript features and type safety benefits.',\n publishedAt: '2024-03-13T12:00:00Z',\n thumbnailIndex: 3,\n duration: 'PT1M40S',\n viewCount: '890000',\n tags: ['programming', 'typescript', 'javascript', 'coding', 'tutorial'],\n },\n {\n videoId: 'test-vid-004',\n title: 'Solo Travel Guide: 5 Days in Tokyo',\n channelTitle: 'Wanderlust Journal',\n channelId: 'UC-wanderlust-journal-004',\n description:\n 'Discover the best sights, food, and neighborhoods in Tokyo as a solo traveler.',\n publishedAt: '2024-03-12T08:15:00Z',\n thumbnailIndex: 4,\n duration: 'PT25M10S',\n viewCount: '620000',\n tags: ['travel', 'tokyo', 'japan', 'guide', 'solo'],\n },\n {\n videoId: 'test-vid-005',\n title: 'Acoustic Guitar Cover of Popular Hits 2024',\n channelTitle: 'Acoustic Sessions',\n channelId: 'UC-acoustic-sessions-005',\n description:\n 'Relaxing instrumental acoustic guitar covers for studying or working.',\n publishedAt: '2024-03-11T20:00:00Z',\n thumbnailIndex: 5,\n duration: 'PT45M00S',\n viewCount: '340000',\n tags: ['music', 'guitar', 'acoustic', 'covers', 'relax'],\n },\n {\n videoId: 'test-vid-006',\n title: 'What Happens Inside a Black Hole?',\n channelTitle: 'Curiosity Lab',\n channelId: 'UC-curiosity-lab-006',\n description: 'Exploring the science of event horizons and singularity.',\n publishedAt: '2024-03-10T14:00:00Z',\n thumbnailIndex: 6,\n duration: 'PT15M30S',\n viewCount: '2100000',\n tags: ['science', 'space', 'blackhole', 'physics', 'astronomy'],\n },\n {\n videoId: 'test-vid-007',\n title: 'Elden Ring Speedrun World Record Attempt',\n channelTitle: 'Pixel Adventures',\n channelId: 'UC-pixel-adventures-007',\n description: 'Running through the Lands Between to beat the clock.',\n publishedAt: '2024-03-09T19:45:00Z',\n thumbnailIndex: 7,\n duration: 'PT58M20S',\n viewCount: '150000',\n tags: ['gaming', 'elden-ring', 'speedrun', 'streamer', 'rpg'],\n },\n {\n videoId: 'test-vid-008',\n title: 'Linear Algebra: Matrices Made Intuitive',\n channelTitle: 'Learn Math Easily',\n channelId: 'UC-learn-math-easily-008',\n description:\n 'Visualizing matrix transformations, eigenvectors, and eigenvalues.',\n publishedAt: '2024-03-08T11:00:00Z',\n thumbnailIndex: 8,\n duration: 'PT30M15S',\n viewCount: '980000',\n tags: ['education', 'math', 'algebra', 'matrices', 'visual'],\n },\n {\n videoId: 'test-vid-009',\n title: 'The Great Migration: Serengeti Wild Life',\n channelTitle: 'Earth Documentaries',\n channelId: 'UC-earth-docs-009',\n description:\n 'Witnessing millions of wildebeests cross the Mara River in Africa.',\n publishedAt: '2024-03-07T06:00:00Z',\n thumbnailIndex: 9,\n duration: 'PT48M50S',\n viewCount: '3200000',\n tags: ['nature', 'wildlife', 'serengeti', 'documentary', 'africa'],\n },\n {\n videoId: 'test-vid-010',\n title: 'Landscape Photography: Mastering the Golden Hour',\n channelTitle: 'Shutter Mastery',\n channelId: 'UC-shutter-mastery-010',\n description:\n 'Tips and tricks for capturing perfect lighting in outdoor landscape shots.',\n publishedAt: '2024-03-06T15:40:00Z',\n thumbnailIndex: 10,\n duration: 'PT18M12S',\n viewCount: '280000',\n tags: ['photography', 'landscape', 'camera', 'golden-hour', 'tutorial'],\n },\n {\n videoId: 'test-vid-011',\n title: 'Design System Architecture: Figma to Code',\n channelTitle: 'Creative UI/UX',\n channelId: 'UC-creative-uiux-011',\n description:\n 'How to structure design tokens and components for seamless engineering hands.',\n publishedAt: '2024-03-05T09:30:00Z',\n thumbnailIndex: 11,\n duration: 'PT22M05S',\n viewCount: '410000',\n tags: ['design', 'ui', 'ux', 'figma', 'architecture'],\n },\n {\n videoId: 'test-vid-012',\n title: 'How We Built a $10M SaaS: Startup Lessons',\n channelTitle: 'Startup Grind',\n channelId: 'UC-startup-grind-012',\n description:\n 'An honest breakdown of product-market fit, pricing, and scaling strategies.',\n publishedAt: '2024-03-04T13:10:00Z',\n thumbnailIndex: 12,\n duration: 'PT16M55S',\n viewCount: '540000',\n tags: ['business', 'saas', 'startup', 'scaling', 'lessons'],\n },\n {\n videoId: 'test-vid-013',\n title: 'Learn French: 100 Common Phrases for Beginners',\n channelTitle: 'Speak French Fluently',\n channelId: 'UC-speak-french-013',\n description:\n 'Essential French phrases for travel, greeting, and simple daily conversation.',\n publishedAt: '2024-03-03T07:00:00Z',\n thumbnailIndex: 13,\n duration: 'PT14M20S',\n viewCount: '730000',\n tags: ['language-learning', 'french', 'phrases', 'beginners', 'vocabulary'],\n },\n {\n videoId: 'test-vid-014',\n title: 'DIY Oak Coffee Table: Woodworking Guide',\n channelTitle: 'Home Woodworking',\n channelId: 'UC-home-woodworking-014',\n description:\n 'Building a modern coffee table from scratch using white oak and mortise joinery.',\n publishedAt: '2024-03-02T16:20:00Z',\n thumbnailIndex: 14,\n duration: 'PT28M45S',\n viewCount: '190000',\n tags: ['diy', 'woodworking', 'table', 'furniture', 'maker'],\n },\n {\n videoId: 'test-vid-015',\n title: '15-Minute Mindfulness Meditation for Anxiety',\n channelTitle: 'Calm Minds',\n channelId: 'UC-calm-minds-015',\n description:\n 'A guided meditation to reduce stress, calm the nervous system, and return to center.',\n publishedAt: '2024-03-01T21:00:00Z',\n thumbnailIndex: 15,\n duration: 'PT15M00S',\n viewCount: '1500000',\n tags: ['meditation', 'mindfulness', 'calm', 'anxiety', 'mentalhealth'],\n },\n {\n videoId: 'test-vid-016',\n title: 'HIIT Workout: Full Body Fat Burner',\n channelTitle: 'Active Training',\n channelId: 'UC-active-training-016',\n description:\n 'High intensity interval training routine. No equipment needed.',\n publishedAt: '2024-02-28T09:00:00Z',\n thumbnailIndex: 16,\n duration: 'PT30M00S',\n viewCount: '2500000',\n tags: ['sports', 'hiit', 'workout', 'cardio', 'fitness'],\n },\n {\n videoId: 'test-vid-017',\n title: 'iPhone 16 Pro Max: Honest Review After 6 Months',\n channelTitle: 'Gadget Sphere',\n channelId: 'UC-gadget-sphere-017',\n description:\n 'Is it still worth buying? Camera, battery life, and durability analysis.',\n publishedAt: '2024-02-27T14:30:00Z',\n thumbnailIndex: 17,\n duration: 'PT13M40S',\n viewCount: '870000',\n tags: ['tech-reviews', 'iphone', 'apple', 'smartphone', 'gadgets'],\n },\n {\n videoId: 'test-vid-018',\n title: 'The Rise and Fall of the Roman Empire',\n channelTitle: 'Chronicles of Old',\n channelId: 'UC-chronicles-old-018',\n description:\n 'Understanding the political and social dynamics that shaped European history.',\n publishedAt: '2024-02-26T10:00:00Z',\n thumbnailIndex: 18,\n duration: 'PT52M15S',\n viewCount: '1850000',\n tags: ['history', 'rome', 'roman-empire', 'documentary', 'history-lessons'],\n },\n {\n videoId: 'test-vid-019',\n title: 'When Programmers Try to Socialize (Sketch)',\n channelTitle: 'Laughter Zone',\n channelId: 'UC-laughter-zone-019',\n description:\n 'A funny comedy sketch about introverted developers attending a house party.',\n publishedAt: '2024-02-25T18:00:00Z',\n thumbnailIndex: 19,\n duration: 'PT4M15S',\n viewCount: '950000',\n tags: ['comedy', 'programming', 'developer-humor', 'skit', 'funny'],\n },\n {\n videoId: 'test-vid-020',\n title: 'Behind the Scenes: Hand-Drawn Animation Loop',\n channelTitle: 'Cartoon Studio',\n channelId: 'UC-cartoon-studio-020',\n description:\n 'Timelapse of a 24-frame classical animation walk cycle on light box.',\n publishedAt: '2024-02-24T12:00:00Z',\n thumbnailIndex: 20,\n duration: 'PT8M50S',\n viewCount: '320000',\n tags: ['animation', 'cartoon', 'art', 'timelapse', 'drawing'],\n },\n];\n\n/**\n * Search fixtures using query-aware filtering.\n *\n * Matches `q` against title, description, and tags (case-insensitive).\n * If no matches found, returns a general fallback subset.\n * Never returns empty unless the fixture set itself is empty.\n */\nexport function searchFixtures(\n query: string,\n maxResults = 10,\n fixtures: VideoFixture[] = videoFixtures\n): VideoFixture[] {\n const clampedMax = Math.min(Math.max(maxResults, 1), 50);\n\n if (fixtures.length === 0) {\n return [];\n }\n\n if (!query || query.trim() === '') {\n return fixtures.slice(0, clampedMax);\n }\n\n const q = query.toLowerCase().trim();\n const matches = fixtures.filter(\n (v) =>\n v.title.toLowerCase().includes(q) ||\n v.tags.some((t) => t.toLowerCase().includes(q)) ||\n v.description.toLowerCase().includes(q)\n );\n\n if (matches.length === 0) {\n // Fallback: return general subset so tests don't get unexpected empty results\n return fixtures.slice(0, clampedMax);\n }\n\n return matches.slice(0, clampedMax);\n}\n"],"names":["videoFixtures","videoId","title","channelTitle","channelId","description","publishedAt","thumbnailIndex","duration","viewCount","tags","searchFixtures","query","maxResults","fixtures","clampedMax","Math","min","max","length","trim","slice","q","toLowerCase","matches","filter","v","includes","some","t"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { VideoFixture } from './search-fixtures.js';
|
|
2
|
+
/**
|
|
3
|
+
* Format an array of VideoFixture objects into a youtube#searchListResponse.
|
|
4
|
+
*
|
|
5
|
+
* The response shape exactly matches the Google YouTube Data API v3
|
|
6
|
+
* search.list endpoint documented at:
|
|
7
|
+
* https://developers.google.com/youtube/v3/docs/search/list
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatSearchResponse(fixtures: VideoFixture[], baseUrl: string, totalResults: number): {
|
|
10
|
+
kind: string;
|
|
11
|
+
etag: string;
|
|
12
|
+
regionCode: string;
|
|
13
|
+
pageInfo: {
|
|
14
|
+
totalResults: number;
|
|
15
|
+
resultsPerPage: number;
|
|
16
|
+
};
|
|
17
|
+
items: {
|
|
18
|
+
kind: string;
|
|
19
|
+
etag: string;
|
|
20
|
+
id: {
|
|
21
|
+
kind: string;
|
|
22
|
+
videoId: string;
|
|
23
|
+
};
|
|
24
|
+
snippet: {
|
|
25
|
+
publishedAt: string;
|
|
26
|
+
channelId: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
thumbnails: {
|
|
30
|
+
default: {
|
|
31
|
+
url: string;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
};
|
|
35
|
+
medium: {
|
|
36
|
+
url: string;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
};
|
|
40
|
+
high: {
|
|
41
|
+
url: string;
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
channelTitle: string;
|
|
47
|
+
liveBroadcastContent: string;
|
|
48
|
+
};
|
|
49
|
+
}[];
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=search-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-response.d.ts","sourceRoot":"","sources":["../../src/youtube/search-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDrB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an array of VideoFixture objects into a youtube#searchListResponse.
|
|
3
|
+
*
|
|
4
|
+
* The response shape exactly matches the Google YouTube Data API v3
|
|
5
|
+
* search.list endpoint documented at:
|
|
6
|
+
* https://developers.google.com/youtube/v3/docs/search/list
|
|
7
|
+
*/ export function formatSearchResponse(fixtures, baseUrl, totalResults) {
|
|
8
|
+
const etagSeed = fixtures.map((f)=>f.videoId).join('-');
|
|
9
|
+
return {
|
|
10
|
+
kind: 'youtube#searchListResponse',
|
|
11
|
+
etag: `test-etag-${etagSeed || 'empty'}`,
|
|
12
|
+
regionCode: 'US',
|
|
13
|
+
pageInfo: {
|
|
14
|
+
totalResults,
|
|
15
|
+
resultsPerPage: fixtures.length
|
|
16
|
+
},
|
|
17
|
+
items: fixtures.map((fixture)=>({
|
|
18
|
+
kind: 'youtube#searchResult',
|
|
19
|
+
etag: `item-etag-${fixture.videoId}`,
|
|
20
|
+
id: {
|
|
21
|
+
kind: 'youtube#video',
|
|
22
|
+
videoId: fixture.videoId
|
|
23
|
+
},
|
|
24
|
+
snippet: {
|
|
25
|
+
publishedAt: fixture.publishedAt,
|
|
26
|
+
channelId: fixture.channelId,
|
|
27
|
+
title: fixture.title,
|
|
28
|
+
description: fixture.description,
|
|
29
|
+
thumbnails: {
|
|
30
|
+
default: {
|
|
31
|
+
url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(fixture.thumbnailIndex).padStart(2, '0')}.jpg`,
|
|
32
|
+
width: 120,
|
|
33
|
+
height: 90
|
|
34
|
+
},
|
|
35
|
+
medium: {
|
|
36
|
+
url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(fixture.thumbnailIndex).padStart(2, '0')}.jpg`,
|
|
37
|
+
width: 320,
|
|
38
|
+
height: 180
|
|
39
|
+
},
|
|
40
|
+
high: {
|
|
41
|
+
url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(fixture.thumbnailIndex).padStart(2, '0')}.jpg`,
|
|
42
|
+
width: 480,
|
|
43
|
+
height: 360
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
channelTitle: fixture.channelTitle,
|
|
47
|
+
liveBroadcastContent: 'none'
|
|
48
|
+
}
|
|
49
|
+
}))
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=search-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/youtube/search-response.ts"],"sourcesContent":["import { VideoFixture } from './search-fixtures.js';\n\n/**\n * Format an array of VideoFixture objects into a youtube#searchListResponse.\n *\n * The response shape exactly matches the Google YouTube Data API v3\n * search.list endpoint documented at:\n * https://developers.google.com/youtube/v3/docs/search/list\n */\nexport function formatSearchResponse(\n fixtures: VideoFixture[],\n baseUrl: string,\n totalResults: number\n) {\n const etagSeed = fixtures.map((f) => f.videoId).join('-');\n return {\n kind: 'youtube#searchListResponse',\n etag: `test-etag-${etagSeed || 'empty'}`,\n regionCode: 'US',\n pageInfo: {\n totalResults,\n resultsPerPage: fixtures.length,\n },\n items: fixtures.map((fixture) => ({\n kind: 'youtube#searchResult',\n etag: `item-etag-${fixture.videoId}`,\n id: {\n kind: 'youtube#video',\n videoId: fixture.videoId,\n },\n snippet: {\n publishedAt: fixture.publishedAt,\n channelId: fixture.channelId,\n title: fixture.title,\n description: fixture.description,\n thumbnails: {\n default: {\n url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(\n fixture.thumbnailIndex\n ).padStart(2, '0')}.jpg`,\n width: 120,\n height: 90,\n },\n medium: {\n url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(\n fixture.thumbnailIndex\n ).padStart(2, '0')}.jpg`,\n width: 320,\n height: 180,\n },\n high: {\n url: `${baseUrl}/test-assets/thumbnails/thumbnail-${String(\n fixture.thumbnailIndex\n ).padStart(2, '0')}.jpg`,\n width: 480,\n height: 360,\n },\n },\n channelTitle: fixture.channelTitle,\n liveBroadcastContent: 'none',\n },\n })),\n };\n}\n"],"names":["formatSearchResponse","fixtures","baseUrl","totalResults","etagSeed","map","f","videoId","join","kind","etag","regionCode","pageInfo","resultsPerPage","length","items","fixture","id","snippet","publishedAt","channelId","title","description","thumbnails","default","url","String","thumbnailIndex","padStart","width","height","medium","high","channelTitle","liveBroadcastContent"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAEA;;;;;;CAMC,GACD,OAAO,SAASA,qBACdC,QAAwB,EACxBC,OAAe,EACfC,YAAoB;IAEpB,MAAMC,WAAWH,SAASI,GAAG,CAAC,CAACC,IAAMA,EAAEC,OAAO,EAAEC,IAAI,CAAC;IACrD,OAAO;QACLC,MAAM;QACNC,MAAM,CAAC,UAAU,EAAEN,YAAY,QAAQ,CAAC;QACxCO,YAAY;QACZC,UAAU;YACRT;YACAU,gBAAgBZ,SAASa,MAAM;QACjC;QACAC,OAAOd,SAASI,GAAG,CAAC,CAACW,UAAa,CAAA;gBAChCP,MAAM;gBACNC,MAAM,CAAC,UAAU,EAAEM,QAAQT,OAAO,CAAC,CAAC;gBACpCU,IAAI;oBACFR,MAAM;oBACNF,SAASS,QAAQT,OAAO;gBAC1B;gBACAW,SAAS;oBACPC,aAAaH,QAAQG,WAAW;oBAChCC,WAAWJ,QAAQI,SAAS;oBAC5BC,OAAOL,QAAQK,KAAK;oBACpBC,aAAaN,QAAQM,WAAW;oBAChCC,YAAY;wBACVC,SAAS;4BACPC,KAAK,CAAC,EAAEvB,QAAQ,kCAAkC,EAAEwB,OAClDV,QAAQW,cAAc,EACtBC,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC;4BACxBC,OAAO;4BACPC,QAAQ;wBACV;wBACAC,QAAQ;4BACNN,KAAK,CAAC,EAAEvB,QAAQ,kCAAkC,EAAEwB,OAClDV,QAAQW,cAAc,EACtBC,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC;4BACxBC,OAAO;4BACPC,QAAQ;wBACV;wBACAE,MAAM;4BACJP,KAAK,CAAC,EAAEvB,QAAQ,kCAAkC,EAAEwB,OAClDV,QAAQW,cAAc,EACtBC,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC;4BACxBC,OAAO;4BACPC,QAAQ;wBACV;oBACF;oBACAG,cAAcjB,QAAQiB,YAAY;oBAClCC,sBAAsB;gBACxB;YACF,CAAA;IACF;AACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YouTube error mode simulation — pure state management.
|
|
3
|
+
*
|
|
4
|
+
* Design follows GcsErrorModeManager conventions:
|
|
5
|
+
* - Single-mode constraint: only one mode active at a time.
|
|
6
|
+
* - setMode() replaces any existing mode.
|
|
7
|
+
* - reset() and clearMode() clear state to prevent leakage between tests.
|
|
8
|
+
*/
|
|
9
|
+
export type YoutubeApiErrorModeType = 'daily-limit-exceeded' | 'invalid-api-key' | 'empty-results';
|
|
10
|
+
export type YoutubePlayerErrorCode = 2 | 5 | 100 | 101 | 150;
|
|
11
|
+
export type YoutubeErrorModeType = YoutubeApiErrorModeType | `player-error-${YoutubePlayerErrorCode}`;
|
|
12
|
+
export interface YoutubeErrorModeConfig {
|
|
13
|
+
type: YoutubeErrorModeType;
|
|
14
|
+
}
|
|
15
|
+
export interface YoutubeErrorSimulationResult {
|
|
16
|
+
status: number;
|
|
17
|
+
body: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export declare class YoutubeErrorModeManager {
|
|
20
|
+
private activeMode;
|
|
21
|
+
setMode(config: YoutubeErrorModeConfig): void;
|
|
22
|
+
clearMode(): void;
|
|
23
|
+
getMode(): YoutubeErrorModeConfig | null;
|
|
24
|
+
/**
|
|
25
|
+
* Check if the current mode is a player error mode.
|
|
26
|
+
* Returns the numeric error code, or null if not a player error.
|
|
27
|
+
*/
|
|
28
|
+
getPlayerErrorCode(): number | null;
|
|
29
|
+
/**
|
|
30
|
+
* Match a search request against the active API error mode.
|
|
31
|
+
* Returns null for pass-through (no error, or player-only mode).
|
|
32
|
+
*/
|
|
33
|
+
matchSearchRequest(): YoutubeErrorSimulationResult | null;
|
|
34
|
+
reset(): void;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=youtube-error-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtube-error-mode.d.ts","sourceRoot":"","sources":["../../src/youtube/youtube-error-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,uBAAuB,GAC/B,sBAAsB,GACtB,iBAAiB,GACjB,eAAe,CAAC;AAEpB,MAAM,MAAM,sBAAsB,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE7D,MAAM,MAAM,oBAAoB,GAC5B,uBAAuB,GACvB,gBAAgB,sBAAsB,EAAE,CAAC;AAE7C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,oBAAoB,CAAC;CAC5B;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAqCD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,UAAU,CAAuC;IAEzD,OAAO,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAI7C,SAAS,IAAI,IAAI;IAIjB,OAAO,IAAI,sBAAsB,GAAG,IAAI;IAIxC;;;OAGG;IACH,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAMnC;;;OAGG;IACH,kBAAkB,IAAI,4BAA4B,GAAG,IAAI;IAoBzD,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YouTube error mode simulation — pure state management.
|
|
3
|
+
*
|
|
4
|
+
* Design follows GcsErrorModeManager conventions:
|
|
5
|
+
* - Single-mode constraint: only one mode active at a time.
|
|
6
|
+
* - setMode() replaces any existing mode.
|
|
7
|
+
* - reset() and clearMode() clear state to prevent leakage between tests.
|
|
8
|
+
*/ // ─── Type Definitions ────────────────────────────────────────────────
|
|
9
|
+
// ─── Error Response Factories ─────────────────────────────────────────
|
|
10
|
+
function youtubeApiError(status, message, reason, domain = 'usageLimits') {
|
|
11
|
+
return {
|
|
12
|
+
status,
|
|
13
|
+
body: {
|
|
14
|
+
error: {
|
|
15
|
+
code: status,
|
|
16
|
+
message,
|
|
17
|
+
errors: [
|
|
18
|
+
{
|
|
19
|
+
domain,
|
|
20
|
+
reason,
|
|
21
|
+
message
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function emptySearchResponse() {
|
|
29
|
+
return {
|
|
30
|
+
status: 200,
|
|
31
|
+
body: {
|
|
32
|
+
kind: 'youtube#searchListResponse',
|
|
33
|
+
etag: 'empty-etag',
|
|
34
|
+
regionCode: 'US',
|
|
35
|
+
pageInfo: {
|
|
36
|
+
totalResults: 0,
|
|
37
|
+
resultsPerPage: 0
|
|
38
|
+
},
|
|
39
|
+
items: []
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// ─── Manager ──────────────────────────────────────────────────────────
|
|
44
|
+
export class YoutubeErrorModeManager {
|
|
45
|
+
setMode(config) {
|
|
46
|
+
this.activeMode = config;
|
|
47
|
+
}
|
|
48
|
+
clearMode() {
|
|
49
|
+
this.activeMode = null;
|
|
50
|
+
}
|
|
51
|
+
getMode() {
|
|
52
|
+
return this.activeMode;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if the current mode is a player error mode.
|
|
56
|
+
* Returns the numeric error code, or null if not a player error.
|
|
57
|
+
*/ getPlayerErrorCode() {
|
|
58
|
+
if (!this.activeMode) return null;
|
|
59
|
+
const match = this.activeMode.type.match(/^player-error-(\d+)$/);
|
|
60
|
+
return match ? parseInt(match[1], 10) : null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Match a search request against the active API error mode.
|
|
64
|
+
* Returns null for pass-through (no error, or player-only mode).
|
|
65
|
+
*/ matchSearchRequest() {
|
|
66
|
+
if (!this.activeMode) return null;
|
|
67
|
+
switch(this.activeMode.type){
|
|
68
|
+
case 'daily-limit-exceeded':
|
|
69
|
+
return youtubeApiError(403, 'Daily Limit Exceeded', 'dailyLimitExceeded');
|
|
70
|
+
case 'invalid-api-key':
|
|
71
|
+
return youtubeApiError(400, 'API key not valid', 'keyInvalid');
|
|
72
|
+
case 'empty-results':
|
|
73
|
+
return emptySearchResponse();
|
|
74
|
+
default:
|
|
75
|
+
// Player error modes don't affect the search endpoint
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
reset() {
|
|
80
|
+
this.activeMode = null;
|
|
81
|
+
}
|
|
82
|
+
constructor(){
|
|
83
|
+
this.activeMode = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=youtube-error-mode.js.map
|