@@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest';
22
33import { ACTOR_ENUM_MAX_LENGTH , ACTOR_MAX_DESCRIPTION_LENGTH } from '../../src/const.js' ;
44import { buildNestedProperties , decodeDotPropertyNames , encodeDotPropertyNames ,
5- markInputPropertiesAsRequired , shortenProperties } from '../../src/tools/utils.js' ;
5+ markInputPropertiesAsRequired , shortenProperties ,
6+ transformActorInputSchemaProperties } from '../../src/tools/utils.js' ;
67import type { IActorInputSchema , ISchemaProperties } from '../../src/types.js' ;
78
89describe ( 'buildNestedProperties' , ( ) => {
@@ -231,7 +232,13 @@ describe('shortenProperties', () => {
231232
232233 // Check that enum was shortened
233234 expect ( result . prop1 . enum ) . toBeDefined ( ) ;
234- expect ( result . prop1 . enum ! . length ) . toBeLessThan ( enumValues . length ) ;
235+ if ( result . prop1 . enum ) {
236+ expect ( result . prop1 . enum . length ) . toBeLessThan ( 30 ) ;
237+ const totalEnumLen = result . prop1 . enum . reduce ( ( sum , v ) => sum + v . length , 0 ) ;
238+ expect ( totalEnumLen ) . toBeLessThanOrEqual ( ACTOR_ENUM_MAX_LENGTH ) ;
239+ } else {
240+ expect ( result . prop1 . enum ) . toBeUndefined ( ) ;
241+ }
235242
236243 // Calculate total character length of enum values
237244 const totalLength = result . prop1 . enum ! . reduce ( ( sum , val ) => sum + val . length , 0 ) ;
@@ -259,7 +266,13 @@ describe('shortenProperties', () => {
259266
260267 // Check that items.enum was shortened
261268 expect ( result . prop1 . items ?. enum ) . toBeDefined ( ) ;
262- expect ( result . prop1 . items ! . enum ! . length ) . toBeLessThan ( enumValues . length ) ;
269+ if ( result . prop1 . items ?. enum ) {
270+ expect ( result . prop1 . items . enum . length ) . toBeLessThan ( enumValues . length ) ;
271+ const totalLength = result . prop1 . items . enum . reduce ( ( sum , val ) => sum + val . length , 0 ) ;
272+ expect ( totalLength ) . toBeLessThanOrEqual ( ACTOR_ENUM_MAX_LENGTH ) ;
273+ } else {
274+ expect ( result . prop1 . items ?. enum ) . toBeUndefined ( ) ;
275+ }
263276
264277 // Calculate total character length of enum values
265278 const totalLength = result . prop1 . items ! . enum ! . reduce ( ( sum , val ) => sum + val . length , 0 ) ;
@@ -368,3 +381,123 @@ describe('decodeDotPropertyNames', () => {
368381 expect ( result ) . toEqual ( input ) ;
369382 } ) ;
370383} ) ;
384+
385+ // ----------------------
386+ // Tests for transformActorInputSchemaProperties
387+ // ----------------------
388+ describe ( 'transformActorInputSchemaProperties' , ( ) => {
389+ it ( 'should apply all transformations in the correct order' , ( ) => {
390+ const input = {
391+ title : 'Test' ,
392+ type : 'object' ,
393+ required : [ 'foo.bar' , 'enumProp' ] ,
394+ properties : {
395+ 'foo.bar' : {
396+ type : 'string' ,
397+ title : 'Foo Bar' ,
398+ description : 'desc' ,
399+ } ,
400+ proxy : {
401+ type : 'object' ,
402+ editor : 'proxy' ,
403+ title : 'Proxy' ,
404+ description : 'Proxy desc' ,
405+ properties : { } ,
406+ } ,
407+ sources : {
408+ type : 'array' ,
409+ editor : 'requestListSources' ,
410+ title : 'Sources' ,
411+ description : 'Sources desc' ,
412+ } ,
413+ enumProp : {
414+ type : 'string' ,
415+ title : 'Enum' ,
416+ description : 'Enum desc' ,
417+ enum : Array . from ( { length : 30 } , ( _ , i ) => `val${ i } ` ) ,
418+ } ,
419+ longDesc : {
420+ type : 'string' ,
421+ title : 'Long' ,
422+ description : 'a' . repeat ( ACTOR_MAX_DESCRIPTION_LENGTH + 10 ) ,
423+ } ,
424+ } ,
425+ } ;
426+ const result = transformActorInputSchemaProperties ( input ) ;
427+ // 1. markInputPropertiesAsRequired: required fields get **REQUIRED** in description
428+ expect ( result [ 'foo-dot-bar' ] . description ) . toContain ( '**REQUIRED**' ) ;
429+ expect ( result . enumProp . description ) . toContain ( '**REQUIRED**' ) ;
430+ // 2. buildNestedProperties: proxy gets useApifyProxy, sources gets url
431+ expect ( result . proxy . properties ) . toBeDefined ( ) ;
432+ expect ( result . proxy . properties ?. useApifyProxy ) . toBeDefined ( ) ;
433+ expect ( result . sources . items ) . toBeDefined ( ) ;
434+ expect ( result . sources . items ?. properties ?. url ) . toBeDefined ( ) ;
435+ // 3. filterSchemaProperties: only allowed fields present
436+ expect ( Object . keys ( result [ 'foo-dot-bar' ] ) ) . toEqual (
437+ expect . arrayContaining ( [ 'title' , 'description' , 'type' , 'default' , 'prefill' , 'properties' , 'items' , 'required' , 'enum' ] ) ,
438+ ) ;
439+ // 4. shortenProperties: longDesc is truncated, enumProp.enum is shortened
440+ expect ( result . longDesc . description . length ) . toBeLessThanOrEqual ( ACTOR_MAX_DESCRIPTION_LENGTH + 3 ) ;
441+ if ( result . enumProp . enum ) {
442+ expect ( result . enumProp . enum . length ) . toBeLessThanOrEqual ( 30 ) ;
443+ const totalEnumLen = result . enumProp . enum . reduce ( ( sum , v ) => sum + v . length , 0 ) ;
444+ expect ( totalEnumLen ) . toBeLessThanOrEqual ( ACTOR_ENUM_MAX_LENGTH ) ;
445+ } else {
446+ // If enum is too long, it may be set to undefined
447+ expect ( result . enumProp . enum ) . toBeUndefined ( ) ;
448+ }
449+ // 5. addEnumsToDescriptionsWithExamples: enum values in description
450+ expect ( result . enumProp . description ) . toMatch ( / P o s s i b l e v a l u e s : / ) ;
451+ // 6. encodeDotPropertyNames: foo.bar becomes foo-dot-bar
452+ expect ( result [ 'foo-dot-bar' ] ) . toBeDefined ( ) ;
453+ expect ( result [ 'foo.bar' ] ) . toBeUndefined ( ) ;
454+ } ) ;
455+
456+ it ( 'should handle input with no required, no enums, no dots' , ( ) => {
457+ const input = {
458+ title : 'Simple' ,
459+ type : 'object' ,
460+ properties : {
461+ simple : {
462+ type : 'string' ,
463+ title : 'Simple' ,
464+ description : 'desc' ,
465+ } ,
466+ } ,
467+ } ;
468+ const result = transformActorInputSchemaProperties ( input ) ;
469+ expect ( result . simple . description ) . toBe ( 'desc' ) ;
470+ expect ( result . simple . enum ) . toBeUndefined ( ) ;
471+ expect ( result . simple ) . toBeDefined ( ) ;
472+ } ) ;
473+
474+ it ( 'should encode all dotted property names' , ( ) => {
475+ const input = {
476+ title : 'Dots' ,
477+ type : 'object' ,
478+ properties : {
479+ 'a.b' : { type : 'string' , title : 'A B' , description : 'desc' } ,
480+ 'c.d.e' : { type : 'number' , title : 'CDE' , description : 'desc2' } ,
481+ } ,
482+ } ;
483+ const result = transformActorInputSchemaProperties ( input ) ;
484+ expect ( result [ 'a-dot-b' ] ) . toBeDefined ( ) ;
485+ expect ( result [ 'c-dot-d-dot-e' ] ) . toBeDefined ( ) ;
486+ expect ( result [ 'a.b' ] ) . toBeUndefined ( ) ;
487+ expect ( result [ 'c.d.e' ] ) . toBeUndefined ( ) ;
488+ } ) ;
489+
490+ it ( 'should not mutate the input object' , ( ) => {
491+ const input = {
492+ title : 'Immut' ,
493+ type : 'object' ,
494+ required : [ 'foo' ] ,
495+ properties : {
496+ foo : { type : 'string' , title : 'Foo' , description : 'desc' } ,
497+ } ,
498+ } ;
499+ const inputCopy = JSON . parse ( JSON . stringify ( input ) ) ;
500+ transformActorInputSchemaProperties ( input ) ;
501+ expect ( input ) . toEqual ( inputCopy ) ;
502+ } ) ;
503+ } ) ;
0 commit comments