# frozen_string_literal: true
require "spec_helper"

describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
  include StaticValidationHelpers
  let(:query_string) { "
    query getCheese {
      notDefinedField { name }
      cheese(id: 1) { nonsenseField, flavor ...cheeseFields }
      fromSource(source: COW) { bogusField }
    }

    fragment cheeseFields on Cheese { fatContent, hogwashField }
  "}

  it "finds fields that are requested on types that don't have that field" do
    expected_errors = [
      "Field 'notDefinedField' doesn't exist on type 'Query'",  # from query root
      "Field 'nonsenseField' doesn't exist on type 'Cheese'",   # from another field
      "Field 'bogusField' doesn't exist on type 'Cheese'",      # from a list
      "Field 'hogwashField' doesn't exist on type 'Cheese'",    # from a fragment
    ]
    assert_equal(expected_errors, error_messages)
  end

  describe "on objects" do
    let(:query_string) { "query getStuff { notDefinedField }"}

    it "finds invalid fields" do
      expected_errors = [
        {
          "message"=>"Field 'notDefinedField' doesn't exist on type 'Query'",
          "locations"=>[{"line"=>1, "column"=>18}],
          "path"=>["query getStuff", "notDefinedField"],
          "extensions"=>{"code"=>"undefinedField", "typeName"=>"Query", "fieldName"=>"notDefinedField"}
        }
      ]
      assert_equal(expected_errors, errors)
    end
  end

  describe "on interfaces" do
    let(:query_string) { "query getStuff { favoriteEdible { amountThatILikeIt } }"}

    it "finds invalid fields" do
      expected_errors = [
        {
          "message"=>"Field 'amountThatILikeIt' doesn't exist on type 'Edible'",
          "locations"=>[{"line"=>1, "column"=>35}],
          "path"=>["query getStuff", "favoriteEdible", "amountThatILikeIt"],
          "extensions"=>{"code"=>"undefinedField", "typeName"=>"Edible", "fieldName"=>"amountThatILikeIt"}
        }
      ]
      assert_equal(expected_errors, errors)
    end
  end

  describe "on unions" do
    let(:query_string) { "
      query notOnUnion { favoriteEdible { ...dpFields ...dpIndirectFields } }
      fragment dpFields on DairyProduct { source }
      fragment dpIndirectFields on DairyProduct { ... on Cheese { source } }
    "}

    it "doesnt allow selections on unions" do
      expected_errors = [
        {
          "message"=>"Selections can't be made directly on unions (see selections on DairyProduct)",
          "locations"=>[
            {"line"=>3, "column"=>7}
          ],
          "path"=>["fragment dpFields", "source"],
          "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"DairyProduct"}
        }
      ]
      assert_equal(expected_errors, errors)
    end
  end

  describe "__typename" do
    describe "on existing unions" do
      let(:query_string) { "
        query { favoriteEdible { ...dpFields } }
        fragment dpFields on DairyProduct { __typename }
      "}

      it "is allowed" do
        assert_equal([], errors)
      end
    end

    describe "on existing objects" do
      let(:query_string) { "
        query { cheese(id: 1) { __typename } }
      "}

      it "is allowed" do
        assert_equal([], errors)
      end
    end
  end

  describe "__schema" do
    describe "on query root" do
      let(:query_string) { "
        query { __schema { queryType { name } } }
      "}

      it "is allowed" do
        assert_equal([], errors)
      end
    end

    describe "on non-query root" do
      let(:query_string) { "
        query { cheese(id: 1) { __schema { queryType { name } } } }
      "}

      it "is not allowed" do
        expected_errors = [
          {
            "message"=>"Field '__schema' doesn't exist on type 'Cheese'",
            "locations"=>[
              {"line"=>2, "column"=>33}
            ],
            "path"=>["query", "cheese", "__schema"],
            "extensions"=>{"code"=>"undefinedField", "typeName"=>"Cheese", "fieldName"=>"__schema"}
          }
        ]
        assert_equal(expected_errors, errors)
      end
    end
  end

  describe "__type" do
    describe "on query root" do
      let(:query_string) { %|
        query { __type(name: "Cheese") { name } }
      |}

      it "is allowed" do
        assert_equal([], errors)
      end
    end

    describe "on non-query root" do
      let(:query_string) { %|
        query { cheese(id: 1) { __type(name: "Cheese") { name } } }
      |}

      it "is not allowed" do
        expected_errors = [
          {
            "message"=>"Field '__type' doesn't exist on type 'Cheese'",
            "locations"=>[
              {"line"=>2, "column"=>33}
            ],
            "path"=>["query", "cheese", "__type"],
            "extensions"=>{"code"=>"undefinedField", "typeName"=>"Cheese", "fieldName"=>"__type"}
          }
        ]
        assert_equal(expected_errors, errors)
      end
    end
  end

end
