import { describe, it } from 'mocha';

import { getIntrospectionQuery } from '../../utilities/getIntrospectionQuery';

import { MaxIntrospectionDepthRule } from '../rules/MaxIntrospectionDepthRule';

import { expectValidationErrors } from './harness';

function expectErrors(queryStr: string) {
  return expectValidationErrors(MaxIntrospectionDepthRule, queryStr);
}

function expectValid(queryStr: string) {
  expectErrors(queryStr).toDeepEqual([]);
}

describe('Validate: Max introspection nodes rule', () => {
  it('default introspection query', () => {
    expectValid(getIntrospectionQuery());
  });

  it('all options introspection query', () => {
    expectValid(
      getIntrospectionQuery({
        descriptions: true,
        specifiedByUrl: true,
        directiveIsRepeatable: true,
        schemaDescription: true,
        inputValueDeprecation: true,
      }),
    );
  });

  it('3 flat fields introspection query', () => {
    expectValid(`
    {
      __type(name: "Query") {
        trueFields: fields(includeDeprecated: true) {
          name
        }
        falseFields: fields(includeDeprecated: false) {
          name
        }
        omittedFields: fields {
          name
        }
      }
    }
    `);
  });

  it('3 fields deep introspection query from __schema', () => {
    expectErrors(`
    {
      __schema {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 interfaces deep introspection query from __schema', () => {
    expectErrors(`
    {
      __schema {
        types {
          interfaces {
            interfaces {
              interfaces {
                name
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 possibleTypes deep introspection query from __schema', () => {
    expectErrors(`
    {
      __schema {
        types {
          possibleTypes {
            possibleTypes {
              possibleTypes {
                name
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 inputFields deep introspection query from __schema', () => {
    expectErrors(`
    {
      __schema {
        types {
          inputFields {
            type {
              inputFields {
                type {
                  inputFields {
                    type {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 fields deep introspection query from multiple __schema', () => {
    expectErrors(`
    {
      one: __schema {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
      two: __schema {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
      three: __schema {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
      {
        locations: [
          {
            column: 7,
            line: 18,
          },
        ],
        message: 'Maximum introspection depth exceeded',
      },
      {
        locations: [
          {
            column: 7,
            line: 33,
          },
        ],
        message: 'Maximum introspection depth exceeded',
      },
    ]);
  });

  it('3 fields deep introspection query from __type', () => {
    expectErrors(`
    {
      __type(name: "Query") {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 fields deep introspection query from multiple __type', () => {
    expectErrors(`
    {
      one: __type(name: "Query") {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
      two: __type(name: "Query") {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
      three: __type(name: "Query") {
        types {
          fields {
            type {
              fields {
                type {
                  fields {
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
      {
        locations: [
          {
            column: 7,
            line: 18,
          },
        ],
        message: 'Maximum introspection depth exceeded',
      },
      {
        locations: [
          {
            column: 7,
            line: 33,
          },
        ],
        message: 'Maximum introspection depth exceeded',
      },
    ]);
  });

  it('1 fields deep with 3 fields introspection query', () => {
    expectValid(`
    {
      __schema {
        types {
          fields {
            type {
              oneFields: fields {
                name
              }
              twoFields: fields {
                name
              }
              threeFields: fields {
                name
              }
            }
          }
        }
      }
    }
    `);
  });

  it('3 fields deep from varying parents introspection query', () => {
    expectErrors(`
    {
      __schema {
        types {
          fields {
            type {
              fields {
                type {
                  ofType {
                    fields {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 fields deep introspection query with inline fragments', () => {
    expectErrors(`
    query test {
      __schema {
        types {
          ... on __Type {
            fields {
              type {
                ... on __Type {
                  ofType {
                    fields {
                      type {
                        ... on __Type {
                          fields {
                            name
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 fields deep introspection query with fragments', () => {
    expectErrors(`
    query test {
      __schema {
        types {
          ...One
        }
      }
    }

    fragment One on __Type {
      fields {
        type {
          ...Two
        }
      }
    }

    fragment Two on __Type {
      fields {
        type {
          ...Three
        }
      }
    }

    fragment Three on __Type {
      fields {
        name
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 7,
            line: 3,
          },
        ],
      },
    ]);
  });

  it('3 fields deep inside inline fragment on query', () => {
    expectErrors(`
    {
      ... {
        __schema { types { fields { type { fields { type { fields { name } } } } } } }
      }
    }
    `).toDeepEqual([
      {
        message: 'Maximum introspection depth exceeded',
        locations: [
          {
            column: 9,
            line: 4,
          },
        ],
      },
    ]);
  });

  it('opts out if fragment is missing', () => {
    expectValid(`
    query test {
      __schema {
        types {
          ...Missing
        }
      }
    }
    `);
  });

  it("doesn't infinitely recurse on fragment cycle", () => {
    expectValid(`
    query test {
      __schema {
        types {
          ...Cycle
        }
      }
    }
    fragment Cycle on __Type {
      ...Cycle
    }
    `);
  });
});