import { Reader } from '@preserves/core';
import { Meta, readSchema } from '../src/index';
import './test-utils';

describe('checker', () => {
    describe('simplest invertibility tests', () => {
        it('passes simple invertibility test', () => {
            expect(readSchema('version 1 . A = <a @x string @y symbol> .')).not.toBeNull();
        });
        it('passes invertibility check for literal field', () => {
            expect(readSchema('version 1 . A = <a "string" @y symbol> .')).not.toBeNull();
        });
        it('detects non-invertibility for string field', () => {
            expect(() => readSchema('version 1 . A = <a string @y symbol> .')).toThrow(/item 0 of fields of A/);
        });
        it('detects non-invertibility for symbol field', () => {
            expect(() => readSchema('version 1 . A = <a @x string symbol> .')).toThrow(/item 1 of fields of A/);
        });
        it('is OK with no names in simple seqof', () => {
            expect(readSchema('version 1 . A = [string ...].')).not.toBeNull();
        });
    });

    describe('extensible record', () => {
        it('is happy with extensible record', () => {
            expect(Meta.fromSchema(readSchema(
                'version 1 . ExtensibleRecord = <foo @a string @b string @extra any ...>.')))
                .is(new Reader(
                    `<schema {
                       version: 1,
                       embeddedType: #f,
                       definitions: {
                         ExtensibleRecord:
                           <rec <lit foo>
                                <tuplePrefix [<named a <atom String>>, <named b <atom String>>]
                                             <named extra <seqof any>>>>}}>`).next());
        });
        it('non-invertibility tail', () => {
            expect(() => readSchema(
                'version 1 . ExtensibleRecord = <foo @a string @b string any ...>.'))
                .toThrow(/tail of fields of ExtensibleRecord/);
        });
    });

    describe('duplicate bindings', () => {
        it('complains about duplicates in tuples', () => {
            expect(() => readSchema('version 1 . A = [@a string @a string].'))
                .toThrow(/duplicate binding named "a" in item 1 of A/);
        });
        it('complains about duplicates in dicts', () => {
            expect(() => readSchema('version 1 . A = { x: @a string , y: @a string }.'))
                .toThrow(/duplicate binding named "a" in entry y in dictionary in A/);
        });
        it('complains about duplicates in tuplePrefixes', () => {
            expect(() => readSchema('version 1 . A = [@a string @b string @a int @rest any ...].'))
                .toThrow(/duplicate binding named "a" in item 2 of A/);
        });
        it('complains about duplicates in tuplePrefix tails', () => {
            expect(() => readSchema('version 1 . A = [@a string @b string @c int @a any ...].'))
                .toThrow(/duplicate binding named "a" in tail of A/);
        });
        describe('in records', () => {
            it('complains about duplicates in recs (1)', () => {
                expect(() => readSchema('version 1 . A = <a @a string @a int>.'))
                    .toThrow(/duplicate binding named "a" in item 1 of fields of A/);
            });
            it('complains about duplicates in recs (2)', () => {
                expect(() => readSchema('version 1 . A = <a @a string <x @y int @a int>>.'))
                    .toThrow(/duplicate binding named "a" in item 1 of fields of item 1 of fields of A/);
            });
            it('complains about duplicates in recs (3)', () => {
                expect(() => readSchema('version 1 . A = <a @a string <<rec> =x [@y int @a int]>>.'))
                    .toThrow(/duplicate binding named "a" in item 1 of fields of item 1 of fields of A/);
            });
            it('complains about duplicates in recs (4)', () => {
                expect(() => readSchema('version 1 . A = <a @a string <<rec> @a =x [@y int @z int]>>.'))
                    .toThrow(/duplicate binding named "a" in label of item 1 of fields of A/);
            });
            it('complains about duplicates in recs (5)', () => {
                expect(() => readSchema('version 1 . A = <a @a string <<rec> @a any [@y int @z int]>>.'))
                    .toThrow(/duplicate binding named "a" in label of item 1 of fields of A/);
            });
        });
        describe('in unions', () => {
            it('is OK with non-duplicate but duplicate-seeming bindings across branches', () => {
                expect(readSchema('version 1 . A = <a @a string> / <b @a string>.')).not.toBeNull();
            });
            it('complains about duplicates within branches', () => {
                expect(() => readSchema('version 1 . A = <a @a string @a int> / <b @a string>.'))
                    .toThrow(/in item 1 of fields of variant a of A/);
            });
            it('complains about duplicate branch names', () => {
                expect(() => readSchema('version 1 . A = @x <a @a string> / @x <b @a string>.'))
                    .toThrow(/duplicate variant label in variant x of A/);
            });
        });
        describe('in intersections', () => {
            it('is OK with non-duplicate bindings across branches', () => {
                expect(readSchema('version 1 . A = <a @a string> & <a @b string>.')).not.toBeNull();
            });
            it('complains about duplicates within branches', () => {
                expect(() => readSchema(
                    `version 1 . A = <a @a string @a int> & <a @b string @c int>.`))
                    .toThrow(/in item 1 of fields of A/);
            });
            it('complains about duplicates across branches', () => {
                expect(() => readSchema(
                    `version 1 . A = <a @a string @b int> & <a @a string @c int>.`))
                    .toThrow(/in item 0 of fields of A/);
            });
            it('complains about duplicates within named branches', () => {
                expect(() => readSchema(
                    `version 1 .
                     AAA = <a @a string @a int>.
                     ABC = <a @b string @c int>.
                     A = @x AAA & @y ABC .`))
                    .toThrow(/in item 1 of fields of AAA/);
            });
            it('is OK with seeming- but non-duplicates across named branches', () => {
                expect(readSchema(
                    `version 1 .
                     AAA = <a @a string @b int>.
                     ABC = <a @a string @c int>.
                     A = @x AAA & @y ABC .`))
                    .not.toBeNull();
            });
            it('complains about duplicate branch names', () => {
                expect(() => readSchema(
                    `version 1 .
                     AAB = <a @a string @b int>.
                     ACD = <a @c string @d int>.
                     A = @x AAB & @x ACD .`))
                    .toThrow(/in A/);
            });
        });
    });
});
