import { TypeAssertion , ValidationContext } from '../types' ; import { validate , getType } from '../validator' ; import { compile } from '../compiler' ; import { serialize , deserialize } from '../serializer' ; describe ( "compiler-2" , function ( ) { it ( "compiler-interface-1" , function ( ) { const schema = compile ( `
            type X = string;
            interface Foo {
                a1: number;
                a2: bigint;
                a3: string;
                a4: boolean;
                a5: null;
                a6: undefined;
                a7: X;
                a8: integer;
            }
        ` ) ; { expect ( Array . from ( schema . keys ( ) ) ) . toEqual ( [ 'X' , 'Foo' , ] ) ; } { const rhs = { name : 'Foo' , typeName : 'Foo' , kind : 'object' , members : [ [ 'a1' , { name : 'a1' , kind : 'primitive' , primitiveName : 'number' , } ] , [ 'a2' , { name : 'a2' , kind : 'primitive' , primitiveName : 'bigint' , } ] , [ 'a3' , { name : 'a3' , kind : 'primitive' , primitiveName : 'string' , } ] , [ 'a4' , { name : 'a4' , kind : 'primitive' , primitiveName : 'boolean' , } ] , [ 'a5' , { name : 'a5' , kind : 'primitive' , primitiveName : 'null' , } ] , [ 'a6' , { name : 'a6' , kind : 'primitive' , primitiveName : 'undefined' , } ] , [ 'a7' , { name : 'a7' , typeName : 'X' , kind : 'primitive' , primitiveName : 'string' , } ] , [ 'a8' , { name : 'a8' , kind : 'primitive' , primitiveName : 'integer' , } ] , ] , } ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Foo' ) , getType ( schema , 'Foo' ) ] ) { expect ( ty ) . toEqual ( rhs ) ; { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( { value : v } ) ; } { const v = { a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : BigInt ( 99 ) , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : 99 , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 7 , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : '' , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : void 0 , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : null , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : 99 , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5.5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( null ) ; } } } } ) ; it ( "compiler-interface-2 (optional types)" , function ( ) { const schemas = [ compile ( `
            type X = string;
            interface Foo {
                a1?: number;
                a2?: bigint;
                a3?: string;
                a4?: boolean;
                a5?: null;
                a6?: undefined;
                a7?: X;
                a8?: integer;
            }
        ` ) , compile ( `
            type X = string;
            type Foo = Partial<{
                a1: number,
                a2: bigint,
                a3: string,
                a4: boolean,
                a5: null,
                a6: undefined,
                a7: X,
                a8: integer,
            }>;
        ` ) , compile ( `
            type X = string;
            type Foo = Partial<{
                a1?: number,
                a2?: bigint,
                a3?: string,
                a4?: boolean,
                a5?: null,
                a6?: undefined,
                a7?: X,
                a8?: integer,
            }>;
        ` ) , compile ( `
            type X = string;
            interface Y {
                a1: number;
                a2: bigint;
                a3: string;
                a4: boolean;
                a5: null;
                a6: undefined;
                a7: X;
                a8: integer;
            }
            type Foo = Partial<Y>;
        ` ) , compile ( `
            type X = string;
            interface Y {
                a1?: number;
                a2?: bigint;
                a3?: string;
                a4?: boolean;
                a5?: null;
                a6?: undefined;
                a7?: X;
                a8?: integer;
            }
            type Foo = Partial<Y>;
        ` ) ] ; { expect ( Array . from ( schemas [ 0 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'Foo' , ] ) ; expect ( Array . from ( schemas [ 1 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'Foo' , ] ) ; expect ( Array . from ( schemas [ 2 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'Foo' , ] ) ; expect ( Array . from ( schemas [ 3 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'Y' , 'Foo' , ] ) ; expect ( Array . from ( schemas [ 4 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'Y' , 'Foo' , ] ) ; } for ( const schema of schemas ) { { const rhs = { name : 'Foo' , typeName : 'Foo' , kind : 'object' , members : [ [ 'a1' , { name : 'a1' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'number' , } } ] , [ 'a2' , { name : 'a2' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'bigint' , } } ] , [ 'a3' , { name : 'a3' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'string' , } } ] , [ 'a4' , { name : 'a4' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'boolean' , } } ] , [ 'a5' , { name : 'a5' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'null' , } } ] , [ 'a6' , { name : 'a6' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'undefined' , } } ] , [ 'a7' , { name : 'a7' , typeName : 'X' , kind : 'optional' , optional : { name : 'X' , typeName : 'X' , kind : 'primitive' , primitiveName : 'string' , } } ] , [ 'a8' , { name : 'a8' , kind : 'optional' , optional : { kind : 'primitive' , primitiveName : 'integer' , } } ] , ] , } ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Foo' ) , getType ( schema , 'Foo' ) ] ) { expect ( ty ) . toEqual ( rhs ) ; { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( { value : v } ) ; } { const v = { } ; expect ( validate < any > ( v , ty ) ) . toEqual ( { value : v } ) ; } } } } } ) ; it ( "compiler-interface-3 (extends)" , function ( ) { const schemas = [ compile ( `
            type X = string;
            interface P {
                a1: number;
                a2: bigint;
            }
            interface Q {
                a3: string;
                a4: boolean;
            }
            interface R extends Q {
                a5: null;
                a6: undefined;
            }
            interface Foo extends P, R {
                a7: X;
                a8: integer;
            }
        ` ) , compile ( `
            interface Foo extends P, R {
                a7: X;
                a8: integer;
            }
            interface R extends Q {
                a5: null;
                a6: undefined;
            }
            interface Q {
                a3: string;
                a4: boolean;
            }
            interface P {
                a1: number;
                a2: bigint;
            }
            type X = string;
        ` ) ] ; { expect ( Array . from ( schemas [ 0 ] . keys ( ) ) ) . toEqual ( [ 'X' , 'P' , 'Q' , 'R' , 'Foo' , ] ) ; } { expect ( Array . from ( schemas [ 1 ] . keys ( ) ) ) . toEqual ( [ 'Foo' , 'R' , 'Q' , 'P' , 'X' , ] ) ; } for ( const schema of schemas ) { { const tyP = { name : 'P' , typeName : 'P' , kind : 'object' , members : [ [ 'a1' , { name : 'a1' , kind : 'primitive' , primitiveName : 'number' , } ] , [ 'a2' , { name : 'a2' , kind : 'primitive' , primitiveName : 'bigint' , } ] , ] , } ; const tyQ = { name : 'Q' , typeName : 'Q' , kind : 'object' , members : [ [ 'a3' , { name : 'a3' , kind : 'primitive' , primitiveName : 'string' , } ] , [ 'a4' , { name : 'a4' , kind : 'primitive' , primitiveName : 'boolean' , } ] , ] , } ; const tyR = { name : 'R' , typeName : 'R' , kind : 'object' , baseTypes : [ tyQ ] , members : [ ... ( [ [ 'a5' , { name : 'a5' , kind : 'primitive' , primitiveName : 'null' , } ] , [ 'a6' , { name : 'a6' , kind : 'primitive' , primitiveName : 'undefined' , } ] , ] as any [ ] ) , ... tyQ . members . map ( x => [ x [ 0 ] , x [ 1 ] , true ] ) , ] , } ; const rhs = { name : 'Foo' , typeName : 'Foo' , kind : 'object' , baseTypes : [ tyP , tyR ] , members : [ ... ( [ [ 'a7' , { name : 'a7' , typeName : 'X' , kind : 'primitive' , primitiveName : 'string' , } ] , [ 'a8' , { name : 'a8' , kind : 'primitive' , primitiveName : 'integer' , } ] , ] as any [ ] ) , ... tyP . members . map ( x => [ x [ 0 ] , x [ 1 ] , true ] ) , ... tyR . members . map ( x => [ x [ 0 ] , x [ 1 ] , true ] ) , ] , } ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Foo' ) , getType ( schema , 'Foo' ) ] ) { expect ( ty ) . toEqual ( rhs ) ; { const v = { a1 : 3 , a2 : BigInt ( 5 ) , a3 : 'C' , a4 : true , a5 : null , a6 : void 0 , a7 : '' , a8 : 5 , } ; expect ( validate < any > ( v , ty ) ) . toEqual ( { value : v } ) ; } } } } } ) ; it ( "compiler-interface-4 (recursive members (repeated))" , function ( ) { const schema = compile ( `
            interface EntryBase {
                name: string;
            }
            interface File extends EntryBase {
                type: 'file';
            }
            interface Folder extends EntryBase {
                type: 'folder';
                entries: Entry[];
            }
            type Entry = File | Folder;
        ` ) ; { expect ( Array . from ( schema . keys ( ) ) ) . toEqual ( [ 'EntryBase' , 'File' , 'Folder' , 'Entry' , ] ) ; } { const tyBase = { name : 'EntryBase' , typeName : 'EntryBase' , kind : 'object' , members : [ [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } ] , ] , } ; const tyEntry = { name : 'Entry' , typeName : 'Entry' , kind : 'one-of' , oneOf : [ { name : 'File' , typeName : 'File' , kind : 'object' , baseTypes : [ tyBase ] , members : [ [ 'type' , { name : 'type' , kind : 'primitive-value' , value : 'file' , } ] , [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } , true ] , ] , } , { name : 'Folder' , typeName : 'Folder' , kind : 'symlink' , symlinkTargetName : 'Folder' , } ] , } ; const rhs = { name : 'Folder' , typeName : 'Folder' , kind : 'object' , baseTypes : [ tyBase ] , members : [ [ 'type' , { name : 'type' , kind : 'primitive-value' , value : 'folder' , } ] , [ 'entries' , { name : 'entries' , kind : 'repeated' , min : null , max : null , repeated : { name : 'Entry' , typeName : 'Entry' , kind : 'symlink' , symlinkTargetName : 'Entry' , } } ] , [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } , true ] , ] , } ; tyEntry . oneOf [ 1 ] = rhs ; let cnt = 0 ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Entry' ) , getType ( schema , 'Entry' ) ] ) { if ( cnt !== 0 ) { expect ( ty ) . toEqual ( tyEntry ) ; } cnt ++ ; } cnt = 0 ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Folder' ) , getType ( schema , 'Folder' ) ] ) { if ( cnt !== 0 ) { expect ( ty ) . toEqual ( rhs ) ; } { const v = { type : 'folder' , name : '/' , entries : [ { type : 'file' , name : 'a' , } , { type : 'folder' , name : 'b' , entries : [ { type : 'file' , name : 'c' , } ] , } ] , } ; try { validate < any > ( v , ty ) ; expect ( 0 ) . toEqual ( 1 ) ; } catch ( e ) { if ( cnt === 0 ) { expect ( e . message ) . toEqual ( 'Unresolved symbol \'Folder\' is appeared.' ) ; } else { expect ( e . message ) . toEqual ( 'Unresolved symbol \'Entry\' is appeared.' ) ; } } const ctx1 = { } ; expect ( ( ) => validate < any > ( v , ty , ctx1 ) ) . toThrow ( ) ; expect ( ctx1 . errors ) . toEqual ( [ ... ( cnt === 0 ? [ { code : 'ValueUnmatched' , message : '"type" of "File" value should be "file".' , dataPath : 'Folder:entries.(1:repeated).Entry:File:type' , value : 'folder' , constraints : { } , } ] : [ ] ) , { code : 'InvalidDefinition' , message : cnt === 0 ? '"Folder" of "Entry" type definition is invalid.' : '"entries" of "Folder" type definition is invalid.' , dataPath : cnt === 0 ? 'Folder:entries.(1:repeated).Entry:Folder' : 'Folder:entries.(0:repeated).Entry' , constraints : { } } ] ) ; expect ( validate < any > ( v , ty , { schema } ) ) . toEqual ( { value : v } ) ; } cnt ++ ; } } } ) ; it ( "compiler-interface-5 (recursive members (spread))" , function ( ) { const schema = compile ( `
            interface EntryBase {
                name: string;
            }
            interface File extends EntryBase {
                type: 'file';
            }
            interface Folder extends EntryBase {
                type: 'folder';
                entries: [...<Entry>];
            }
            type Entry = File | Folder;
        ` ) ; { expect ( Array . from ( schema . keys ( ) ) ) . toEqual ( [ 'EntryBase' , 'File' , 'Folder' , 'Entry' , ] ) ; } { const tyBase = { name : 'EntryBase' , typeName : 'EntryBase' , kind : 'object' , members : [ [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } ] , ] , } ; const tyEntry = { name : 'Entry' , typeName : 'Entry' , kind : 'one-of' , oneOf : [ { name : 'File' , typeName : 'File' , kind : 'object' , baseTypes : [ tyBase ] , members : [ [ 'type' , { name : 'type' , kind : 'primitive-value' , value : 'file' , } ] , [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } , true ] , ] , } , { name : 'Folder' , typeName : 'Folder' , kind : 'symlink' , symlinkTargetName : 'Folder' , } ] , } ; const rhs = { name : 'Folder' , typeName : 'Folder' , kind : 'object' , baseTypes : [ tyBase ] , members : [ [ 'type' , { name : 'type' , kind : 'primitive-value' , value : 'folder' , } ] , [ 'entries' , { name : 'entries' , kind : 'sequence' , sequence : [ { kind : 'spread' , min : null , max : null , spread : { name : 'Entry' , typeName : 'Entry' , kind : 'symlink' , symlinkTargetName : 'Entry' , } , } ] } ] , [ 'name' , { name : 'name' , kind : 'primitive' , primitiveName : 'string' , } , true ] , ] , } ; tyEntry . oneOf [ 1 ] = rhs ; let cnt = 0 ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Entry' ) , getType ( schema , 'Entry' ) ] ) { if ( cnt !== 0 ) { expect ( ty ) . toEqual ( tyEntry ) ; } cnt ++ ; } cnt = 0 ; for ( const ty of [ getType ( deserialize ( serialize ( schema ) ) , 'Folder' ) , getType ( schema , 'Folder' ) ] ) { if ( cnt !== 0 ) { expect ( ty ) . toEqual ( rhs ) ; } { const v = { type : 'folder' , name : '/' , entries : [ { type : 'file' , name : 'a' , } , { type : 'folder' , name : 'b' , entries : [ { type : 'file' , name : 'c' , } ] , } ] , } ; try { validate < any > ( v , ty ) ; expect ( 0 ) . toEqual ( 1 ) ; } catch ( e ) { if ( cnt === 0 ) { expect ( e . message ) . toEqual ( 'Unresolved symbol \'Folder\' is appeared.' ) ; } else { expect ( e . message ) . toEqual ( 'Unresolved symbol \'Entry\' is appeared.' ) ; } } const ctx1 = { } ; expect ( ( ) => validate < any > ( v , ty , ctx1 ) ) . toThrow ( ) ; expect ( ctx1 . errors ) . toEqual ( [ ... ( cnt === 0 ? [ { code : 'ValueUnmatched' , message : '"type" of "File" value should be "file".' , dataPath : 'Folder:entries.(1:sequence).Entry:File:type' , value : 'folder' , constraints : { } , } ] : [ ] ) , { code : 'InvalidDefinition' , message : cnt === 0 ? '"Folder" of "Entry" type definition is invalid.' : '"entries" of "Folder" type definition is invalid.' , dataPath : cnt === 0 ? 'Folder:entries.(1:sequence).Entry:Folder' : 'Folder:entries.(0:sequence).Entry' , constraints : { } } ] ) ; expect ( validate < any > ( v , ty , { schema } ) ) . toEqual ( { value : v } ) ; } cnt ++ ; } } } ) ; } ) ;