spec/integration/graphql_spec.rb (178 lines of code) (raw):

# Licensed to Elasticsearch B.V. under one or more contributor # license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright # ownership. Elasticsearch B.V. licenses this file to you under # the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # frozen_string_literal: true require 'integration_helper' if defined?(Rails) enabled = true else puts '[INFO] Skipping Rails spec' end if enabled require 'active_record' require 'action_controller/railtie' require 'graphql' RSpec.describe 'GraphQL', :allow_running_agent, :spec_logger, :mock_intake do include Rack::Test::Methods def setup_database ActiveRecord::Base.logger = Logger.new(SpecLogger) ActiveRecord::Base.logger = Logger.new(nil) ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: '/tmp/graphql.sqlite3' ) ActiveRecord::Migration.suppress_messages do ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.string :slug, null: false t.string :title, null: false t.timestamps end create_table :comments, force: true do |t| t.text :body, null: false t.belongs_to :post, null: false, index: true t.timestamps end end end a = Post.create!(slug: 'a', title: 'A') Post.create!(slug: 'b', title: 'B') Post.create!(slug: 'c', title: 'C') Comment.create!(post: a, body: 'So good') end let(:app) { Rails.application } before :all do module Types class CommentType < GraphQL::Schema::Object field :body, String, null: false end class PostType < GraphQL::Schema::Object field :slug, String, null: false field :title, String, null: false field :comments, [CommentType], null: false end class QueryType < GraphQL::Schema::Object field :posts, [PostType], null: false field :post, PostType, null: false do argument :slug, String, required: true end def posts Post.all end def post(slug:) Post.where(slug: slug).first! end end class GraphQLTestAppSchema < GraphQL::Schema query QueryType tracer ElasticAPM::GraphQL end end class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post end setup_database module GraphQLTestApp class Application < Rails::Application RailsTestHelpers.setup_rails_test_config(config) config.elastic_apm.disable_metrics = '*' config.elastic_apm.api_request_time = '200ms' config.logger = Logger.new(SpecLogger) # config.logger = Logger.new(nil) end end class ::ApplicationController < ActionController::Base def index render plain: 'ok' end end class ::GraphqlController < ApplicationController def execute context_ = {} result = if (multi = params[:multi]) Types::GraphQLTestAppSchema.multiplex( multi.map do |q| { query: q[:query], variables: q[:variables], context: context_ } end ) else Types::GraphQLTestAppSchema.execute( params[:query], variables: params[:variables], context: context_, operation_name: params[:operation_name] ) end render json: result rescue StandardError => e logger.error e.message render( status: 500, json: { error: { message: e.message }, data: {} } ) end end MockIntake.stub! GraphQLTestApp::Application.initialize! GraphQLTestApp::Application.routes.draw do post '/graphql', to: 'graphql#execute' root to: 'application#index' end end after :all do ElasticAPM.stop end context 'a query with an Operation Name' do it 'adds spans and renames transaction' do resp = post '/graphql', query: ' query PostsWithComments { posts { title comments { body } } } ' wait_for timeout: 10, transactions: 1, spans: 12 expect(resp.status).to be 200 transaction, = @mock_intake.transactions expect(transaction['name']).to eq 'GraphQL: PostsWithComments' end end context 'with an unnamed query' do it 'renames to [unnamed]' do resp = post '/graphql', query: '{ posts { title } }' wait_for transactions: 1 expect(resp.status).to be 200 transaction, = @mock_intake.transactions expect(transaction['name']).to eq 'GraphQL: [unnamed]' end end context 'with multiple queries' do it 'renames and concattenates' do resp = post '/graphql', multi: [ { query: 'query Posts { posts { title } }' }, { query: 'query PostA($slug: String!) { post(slug: $slug) { title } }', variables: { slug: 'a' } } ] wait_for transactions: 1 expect(resp.status).to be 200 transaction, = @mock_intake.transactions expect(transaction['name']).to eq 'GraphQL: Posts+PostA' end end context 'with too many queries to list' do it 'renames and concattenates' do resp = post '/graphql', multi: [ { query: 'query Posts { posts { title } }' }, { query: 'query PostsWithComments { posts { title comments { body } } }' }, { query: 'query PostA($a: String!) { post(slug: $a) { title } }', variables: { a: 'a' } }, { query: 'query PostB($b: String!) { post(slug: $b) { title } }', variables: { b: 'b' } }, { query: 'query PostC($c: String!) { post(slug: $c) { title } }', variables: { c: 'c' } }, { query: 'query MorePosts { posts { title } }' } ] wait_for transactions: 1 expect(resp.status).to be 200 transaction, = @mock_intake.transactions expect(transaction['name']).to eq 'GraphQL: [multiple-queries]' end end end end