# frozen_string_literal: true
require "spec_helper"

describe GraphQL::Schema::Directive::Feature do
  class FeatureSchema < GraphQL::Schema
    class Feature < GraphQL::Schema::Directive::Feature
      def self.enabled?(flag_name, obj, ctx)
        !!ctx[flag_name.to_sym]
      end
    end

    class Query < GraphQL::Schema::Object
      field :int, Integer, null: false

      def int
        context[:int] ||= 0
        context[:int] += 1
      end
    end

    directive(Feature)
    query(Query)
    # only supported by the interpreter
    use GraphQL::Execution::Interpreter
    use GraphQL::Analysis::AST
  end


  it "skips or runs fields" do
    str = '{
      int1: int
      int2: int @feature(flag: "flag1")
      int3: int @feature(flag: "flag2")
    }'

    res = FeatureSchema.execute(str, context: { flag2: true })
    # Int2 was not called, so `int3` is actually 2
    assert_equal({"int1" => 1, "int3" => 2}, res["data"])
  end

  it "skips or runs fragment spreads" do
    str = '{
      ...F1
      ...F2 @feature(flag: "flag1")
      ...F3 @feature(flag: "flag2")
      int4: int
    }

    fragment F1 on Query { int1: int }
    fragment F2 on Query { int2: int }
    fragment F3 on Query { int3: int }
    '

    res = FeatureSchema.execute(str, context: { flag1: true })
    # `int3` was skipped
    assert_equal({"int1" => 1, "int2" => 2, "int4" => 3}, res["data"])
  end
  it "skips or runs inline fragments" do
    str = '{
      ... { int1: int }
      ... @feature(flag: "flag1") { int2: int }
      ... @feature(flag: "flag2") { int3: int }
      int4: int
    }
    '

    res = FeatureSchema.execute(str, context: { flag2: true })
    # `int2` was skipped
    assert_equal({"int1" => 1, "int3" => 2, "int4" => 3}, res["data"])
  end

  it "returns an error if it's in the wrong place" do
    str = '
    query @feature(flag: "x") {
      int
    }
    '
    res = FeatureSchema.execute(str)
    assert_equal ["'@feature' can't be applied to queries (allowed: fields, fragment spreads, inline fragments)"], res["errors"].map { |e| e["message"] }
  end
end
