11import {
2+ CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE ,
23 CODAMA_ERROR__UNEXPECTED_NODE_KIND ,
34 CODAMA_ERROR__VISITORS__CYCLIC_DEPENDENCY_DETECTED_WHEN_RESOLVING_INSTRUCTION_DEFAULT_VALUES ,
45 CodamaError ,
@@ -19,7 +20,9 @@ import {
1920 instructionStatusNode ,
2021 integerTypeNode ,
2122 integerValueNode ,
23+ optionTypeNode ,
2224 pluginNode ,
25+ programLinkNode ,
2326 programNode ,
2427 ProgramNode ,
2528 providedNode ,
@@ -426,3 +429,166 @@ test('it reports text nodes without plugins', () => {
426429 ] ) ;
427430 expect ( translatedItems ) . toEqual ( [ ] ) ;
428431} ) ;
432+
433+ test ( 'it reports cyclic defined types without a finite value' , ( ) => {
434+ // Given a loop whose every value nests another one.
435+ const loop = definedTypeNode ( {
436+ identifier : 'loop' ,
437+ type : structTypeNode ( [ structFieldTypeNode ( { identifier : 'next' , type : definedTypeLinkNode ( 'loop' ) } ) ] ) ,
438+ } ) ;
439+ const program = programNode ( { definedTypes : [ loop ] , identifier : 'myProgram' , publicKey : '1111' , version : '1.0.0' } ) ;
440+ const root = rootNode ( program ) ;
441+
442+ const loopCause = new CodamaError ( CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE , {
443+ name : loop . identifier ,
444+ path : [ root , program , loop ] ,
445+ } ) ;
446+
447+ // When we validate it.
448+ const items = visit ( root , getValidationItemsVisitor ( ) ) ;
449+
450+ // Then the loop is reported.
451+ expect ( items ) . toStrictEqual ( [ validationItem ( 'error' , loopCause . message , [ root , program , loop ] , loopCause ) ] ) ;
452+ } ) ;
453+
454+ test ( 'it reports defined types aliasing themselves' , ( ) => {
455+ // Given a type aliasing itself.
456+ const alias = definedTypeNode ( { identifier : 'alias' , type : definedTypeLinkNode ( 'alias' ) } ) ;
457+ const program = programNode ( {
458+ definedTypes : [ alias ] ,
459+ identifier : 'myProgram' ,
460+ publicKey : '1111' ,
461+ version : '1.0.0' ,
462+ } ) ;
463+ const root = rootNode ( program ) ;
464+
465+ const aliasCause = new CodamaError ( CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE , {
466+ name : alias . identifier ,
467+ path : [ root , program , alias ] ,
468+ } ) ;
469+
470+ // When we validate it.
471+ const items = visit ( root , getValidationItemsVisitor ( ) ) ;
472+
473+ // Then the alias is reported.
474+ expect ( items ) . toStrictEqual ( [ validationItem ( 'error' , aliasCause . message , [ root , program , alias ] , aliasCause ) ] ) ;
475+ } ) ;
476+
477+ test ( 'it accepts cyclic defined types with a finite value' , ( ) => {
478+ // Given a linked list that may end with `None`.
479+ const list = definedTypeNode ( {
480+ identifier : 'list' ,
481+ type : structTypeNode ( [
482+ structFieldTypeNode ( {
483+ identifier : 'next' ,
484+ type : optionTypeNode ( definedTypeLinkNode ( 'list' ) , { prefix : integerTypeNode ( 'u8' ) } ) ,
485+ } ) ,
486+ ] ) ,
487+ } ) ;
488+ const program = programNode ( { definedTypes : [ list ] , identifier : 'myProgram' , publicKey : '1111' , version : '1.0.0' } ) ;
489+
490+ // When we validate it, then nothing is reported.
491+ expect ( visit ( rootNode ( program ) , getValidationItemsVisitor ( ) ) ) . toStrictEqual ( [ ] ) ;
492+ } ) ;
493+
494+ test ( 'it only reports the cyclic defined type when another one links to it' , ( ) => {
495+ // Given a type linking to a loop.
496+ const loop = definedTypeNode ( {
497+ identifier : 'loop' ,
498+ type : structTypeNode ( [ structFieldTypeNode ( { identifier : 'next' , type : definedTypeLinkNode ( 'loop' ) } ) ] ) ,
499+ } ) ;
500+ const usesLoop = definedTypeNode ( { identifier : 'usesLoop' , type : definedTypeLinkNode ( 'loop' ) } ) ;
501+ const program = programNode ( {
502+ definedTypes : [ loop , usesLoop ] ,
503+ identifier : 'myProgram' ,
504+ publicKey : '1111' ,
505+ version : '1.0.0' ,
506+ } ) ;
507+ const root = rootNode ( program ) ;
508+
509+ const loopCause = new CodamaError ( CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE , {
510+ name : loop . identifier ,
511+ path : [ root , program , loop ] ,
512+ } ) ;
513+
514+ // When we validate it.
515+ const items = visit ( root , getValidationItemsVisitor ( ) ) ;
516+
517+ // Then only the loop is reported.
518+ expect ( items ) . toStrictEqual ( [ validationItem ( 'error' , loopCause . message , [ root , program , loop ] , loopCause ) ] ) ;
519+ } ) ;
520+
521+ test ( 'it reports every defined type of a cycle without a finite value' , ( ) => {
522+ // Given two types nesting each other.
523+ const a = definedTypeNode ( {
524+ identifier : 'a' ,
525+ type : structTypeNode ( [ structFieldTypeNode ( { identifier : 'b' , type : definedTypeLinkNode ( 'b' ) } ) ] ) ,
526+ } ) ;
527+ const b = definedTypeNode ( {
528+ identifier : 'b' ,
529+ type : structTypeNode ( [ structFieldTypeNode ( { identifier : 'a' , type : definedTypeLinkNode ( 'a' ) } ) ] ) ,
530+ } ) ;
531+ const program = programNode ( { definedTypes : [ a , b ] , identifier : 'myProgram' , publicKey : '1111' , version : '1.0.0' } ) ;
532+ const root = rootNode ( program ) ;
533+
534+ const aCause = new CodamaError ( CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE , {
535+ name : a . identifier ,
536+ path : [ root , program , a ] ,
537+ } ) ;
538+
539+ const bCause = new CodamaError ( CODAMA_ERROR__DEFINED_TYPE_HAS_NO_FINITE_VALUE , {
540+ name : b . identifier ,
541+ path : [ root , program , b ] ,
542+ } ) ;
543+
544+ // When we validate them.
545+ const items = visit ( root , getValidationItemsVisitor ( ) ) ;
546+
547+ // Then both are reported.
548+ expect ( items ) . toStrictEqual ( [
549+ validationItem ( 'error' , aCause . message , [ root , program , a ] , aCause ) ,
550+ validationItem ( 'error' , bCause . message , [ root , program , b ] , bCause ) ,
551+ ] ) ;
552+ } ) ;
553+
554+ test ( 'it does not mistake links to same-named defined types of other programs for cycles' , ( ) => {
555+ // Given a type of program A linking to a same-named type of program B.
556+ const typeA = definedTypeNode ( {
557+ identifier : 'config' ,
558+ type : definedTypeLinkNode ( 'config' , { program : programLinkNode ( 'programB' ) } ) ,
559+ } ) ;
560+ const typeB = definedTypeNode ( { identifier : 'config' , type : integerTypeNode ( 'u32' ) } ) ;
561+ const programA = programNode ( {
562+ definedTypes : [ typeA ] ,
563+ identifier : 'programA' ,
564+ publicKey : '1111' ,
565+ version : '1.0.0' ,
566+ } ) ;
567+ const programB = programNode ( {
568+ definedTypes : [ typeB ] ,
569+ identifier : 'programB' ,
570+ publicKey : '2222' ,
571+ version : '1.0.0' ,
572+ } ) ;
573+
574+ // When we validate them, then nothing is reported.
575+ expect ( visit ( rootNode ( programA , { additionalPrograms : [ programB ] } ) , getValidationItemsVisitor ( ) ) ) . toStrictEqual (
576+ [ ] ,
577+ ) ;
578+ } ) ;
579+
580+ test ( 'it does not check the values of defined types outside a program' , ( ) => {
581+ // Given a loop validated on its own.
582+ const link = definedTypeLinkNode ( 'loop' ) ;
583+ const field = structFieldTypeNode ( { identifier : 'next' , type : link } ) ;
584+ const struct = structTypeNode ( [ field ] ) ;
585+ const loop = definedTypeNode ( { identifier : 'loop' , type : struct } ) ;
586+
587+ // When we validate it.
588+ const items = visit ( loop , getValidationItemsVisitor ( ) ) ;
589+
590+ // Then it does not throw, and only reports the link it cannot resolve without a program.
591+ expect ( items ) . toStrictEqual ( [
592+ validationItem ( 'error' , 'Pointing to a missing defined type named "loop"' , [ loop , struct , field , link ] ) ,
593+ ] ) ;
594+ } ) ;
0 commit comments