Skip to content

Commit bcab5fb

Browse files
committed
vibe unit tests
1 parent 93e9d23 commit bcab5fb

File tree

1 file changed

+319
-0
lines changed

1 file changed

+319
-0
lines changed

tests/unit/tools-utils-test.ts

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { ACTOR_ENUM_MAX_LENGTH, ACTOR_MAX_DESCRIPTION_LENGTH } from '../../src/const.js';
4+
import { buildNestedProperties, markInputPropertiesAsRequired, shortenProperties } from '../../src/tools/utils.js';
5+
import type { IActorInputSchema, ISchemaProperties } from '../../src/types.js';
6+
7+
describe('buildNestedProperties', () => {
8+
it('should add useApifyProxy property to proxy objects', () => {
9+
const properties: Record<string, ISchemaProperties> = {
10+
proxy: {
11+
type: 'object',
12+
editor: 'proxy',
13+
title: 'Proxy configuration',
14+
description: 'Proxy settings',
15+
properties: {},
16+
},
17+
otherProp: {
18+
type: 'string',
19+
title: 'Other property',
20+
description: 'Some other property',
21+
},
22+
};
23+
24+
const result = buildNestedProperties(properties);
25+
26+
// Check that proxy object has useApifyProxy property
27+
expect(result.proxy.properties).toBeDefined();
28+
expect(result.proxy.properties?.useApifyProxy).toBeDefined();
29+
expect(result.proxy.properties?.useApifyProxy.type).toBe('boolean');
30+
expect(result.proxy.properties?.useApifyProxy.default).toBe(true);
31+
expect(result.proxy.required).toContain('useApifyProxy');
32+
33+
// Check that other properties remain unchanged
34+
expect(result.otherProp).toEqual(properties.otherProp);
35+
});
36+
37+
it('should add URL structure to requestListSources array items', () => {
38+
const properties: Record<string, ISchemaProperties> = {
39+
sources: {
40+
type: 'array',
41+
editor: 'requestListSources',
42+
title: 'Request list sources',
43+
description: 'Sources to scrape',
44+
},
45+
otherProp: {
46+
type: 'string',
47+
title: 'Other property',
48+
description: 'Some other property',
49+
},
50+
};
51+
52+
const result = buildNestedProperties(properties);
53+
54+
// Check that requestListSources array has proper item structure
55+
expect(result.sources.items).toBeDefined();
56+
expect(result.sources.items?.type).toBe('object');
57+
expect(result.sources.items?.properties?.url).toBeDefined();
58+
expect(result.sources.items?.properties?.url.type).toBe('string');
59+
60+
// Check that other properties remain unchanged
61+
expect(result.otherProp).toEqual(properties.otherProp);
62+
});
63+
64+
it('should not modify properties that don\'t match special cases', () => {
65+
const properties: Record<string, ISchemaProperties> = {
66+
regularObject: {
67+
type: 'object',
68+
title: 'Regular object',
69+
description: 'A regular object without special editor',
70+
properties: {
71+
subProp: {
72+
type: 'string',
73+
title: 'Sub property',
74+
description: 'Sub property description',
75+
},
76+
},
77+
},
78+
regularArray: {
79+
type: 'array',
80+
title: 'Regular array',
81+
description: 'A regular array without special editor',
82+
items: {
83+
type: 'string',
84+
title: 'Item',
85+
description: 'Item description',
86+
},
87+
},
88+
};
89+
90+
const result = buildNestedProperties(properties);
91+
92+
// Check that regular properties remain unchanged
93+
expect(result).toEqual(properties);
94+
});
95+
96+
it('should handle empty properties object', () => {
97+
const properties: Record<string, ISchemaProperties> = {};
98+
const result = buildNestedProperties(properties);
99+
expect(result).toEqual({});
100+
});
101+
});
102+
103+
describe('markInputPropertiesAsRequired', () => {
104+
it('should add REQUIRED prefix to required properties', () => {
105+
const input: IActorInputSchema = {
106+
title: 'Test Schema',
107+
type: 'object',
108+
required: ['requiredProp1', 'requiredProp2'],
109+
properties: {
110+
requiredProp1: {
111+
type: 'string',
112+
title: 'Required Property 1',
113+
description: 'This is required',
114+
},
115+
requiredProp2: {
116+
type: 'number',
117+
title: 'Required Property 2',
118+
description: 'This is also required',
119+
},
120+
optionalProp: {
121+
type: 'boolean',
122+
title: 'Optional Property',
123+
description: 'This is optional',
124+
},
125+
},
126+
};
127+
128+
const result = markInputPropertiesAsRequired(input);
129+
130+
// Check that required properties have REQUIRED prefix
131+
expect(result.requiredProp1.description).toContain('**REQUIRED**');
132+
expect(result.requiredProp2.description).toContain('**REQUIRED**');
133+
134+
// Check that optional properties remain unchanged
135+
expect(result.optionalProp.description).toBe('This is optional');
136+
});
137+
138+
it('should handle input without required fields', () => {
139+
const input: IActorInputSchema = {
140+
title: 'Test Schema',
141+
type: 'object',
142+
properties: {
143+
prop1: {
144+
type: 'string',
145+
title: 'Property 1',
146+
description: 'Description 1',
147+
},
148+
prop2: {
149+
type: 'number',
150+
title: 'Property 2',
151+
description: 'Description 2',
152+
},
153+
},
154+
};
155+
156+
const result = markInputPropertiesAsRequired(input);
157+
158+
// Check that no properties were modified
159+
expect(result).toEqual(input.properties);
160+
});
161+
162+
it('should handle empty required array', () => {
163+
const input: IActorInputSchema = {
164+
title: 'Test Schema',
165+
type: 'object',
166+
required: [],
167+
properties: {
168+
prop1: {
169+
type: 'string',
170+
title: 'Property 1',
171+
description: 'Description 1',
172+
},
173+
},
174+
};
175+
176+
const result = markInputPropertiesAsRequired(input);
177+
178+
// Check that no properties were modified
179+
expect(result).toEqual(input.properties);
180+
});
181+
});
182+
183+
describe('shortenProperties', () => {
184+
it('should truncate long descriptions', () => {
185+
const longDescription = 'a'.repeat(ACTOR_MAX_DESCRIPTION_LENGTH + 100);
186+
const properties: Record<string, ISchemaProperties> = {
187+
prop1: {
188+
type: 'string',
189+
title: 'Property 1',
190+
description: longDescription,
191+
},
192+
};
193+
194+
const result = shortenProperties(properties);
195+
196+
// Check that description was truncated
197+
expect(result.prop1.description.length).toBeLessThanOrEqual(ACTOR_MAX_DESCRIPTION_LENGTH + 3); // +3 for "..."
198+
expect(result.prop1.description.endsWith('...')).toBe(true);
199+
});
200+
201+
it('should not modify descriptions that are within limits', () => {
202+
const description = 'This is a normal description';
203+
const properties: Record<string, ISchemaProperties> = {
204+
prop1: {
205+
type: 'string',
206+
title: 'Property 1',
207+
description,
208+
},
209+
};
210+
211+
const result = shortenProperties(properties);
212+
213+
// Check that description was not modified
214+
expect(result.prop1.description).toBe(description);
215+
});
216+
217+
it('should shorten enum values if they exceed the limit', () => {
218+
// Create an enum with many values to exceed the character limit
219+
const enumValues = Array.from({ length: 50 }, (_, i) => `enum-value-${i}`);
220+
const properties: Record<string, ISchemaProperties> = {
221+
prop1: {
222+
type: 'string',
223+
title: 'Property 1',
224+
description: 'Property with enum',
225+
enum: enumValues,
226+
},
227+
};
228+
229+
const result = shortenProperties(properties);
230+
231+
// Check that enum was shortened
232+
expect(result.prop1.enum).toBeDefined();
233+
expect(result.prop1.enum!.length).toBeLessThan(enumValues.length);
234+
235+
// Calculate total character length of enum values
236+
const totalLength = result.prop1.enum!.reduce((sum, val) => sum + val.length, 0);
237+
expect(totalLength).toBeLessThanOrEqual(ACTOR_ENUM_MAX_LENGTH);
238+
});
239+
240+
it('should shorten items.enum values if they exceed the limit', () => {
241+
// Create an enum with many values to exceed the character limit
242+
const enumValues = Array.from({ length: 50 }, (_, i) => `enum-value-${i}`);
243+
const properties: Record<string, ISchemaProperties> = {
244+
prop1: {
245+
type: 'array',
246+
title: 'Property 1',
247+
description: 'Property with items.enum',
248+
items: {
249+
type: 'string',
250+
title: 'Item',
251+
description: 'Item description',
252+
enum: enumValues,
253+
},
254+
},
255+
};
256+
257+
const result = shortenProperties(properties);
258+
259+
// Check that items.enum was shortened
260+
expect(result.prop1.items?.enum).toBeDefined();
261+
expect(result.prop1.items!.enum!.length).toBeLessThan(enumValues.length);
262+
263+
// Calculate total character length of enum values
264+
const totalLength = result.prop1.items!.enum!.reduce((sum, val) => sum + val.length, 0);
265+
expect(totalLength).toBeLessThanOrEqual(ACTOR_ENUM_MAX_LENGTH);
266+
});
267+
268+
it('should handle properties without enum or items.enum', () => {
269+
const properties: Record<string, ISchemaProperties> = {
270+
prop1: {
271+
type: 'string',
272+
title: 'Property 1',
273+
description: 'Regular property',
274+
},
275+
prop2: {
276+
type: 'array',
277+
title: 'Property 2',
278+
description: 'Array property',
279+
items: {
280+
type: 'string',
281+
title: 'Item',
282+
description: 'Item description',
283+
},
284+
},
285+
};
286+
287+
const result = shortenProperties(properties);
288+
289+
// Check that properties were not modified
290+
expect(result).toEqual(properties);
291+
});
292+
293+
it('should handle empty enum arrays', () => {
294+
const properties: Record<string, ISchemaProperties> = {
295+
prop1: {
296+
type: 'string',
297+
title: 'Property 1',
298+
description: 'Property with empty enum',
299+
enum: [],
300+
},
301+
prop2: {
302+
type: 'array',
303+
title: 'Property 2',
304+
description: 'Array with empty items.enum',
305+
items: {
306+
type: 'string',
307+
title: 'Item',
308+
description: 'Item description',
309+
enum: [],
310+
},
311+
},
312+
};
313+
314+
const result = shortenProperties(properties);
315+
316+
// Check that properties were not modified
317+
expect(result).toEqual(properties);
318+
});
319+
});

0 commit comments

Comments
 (0)