2020-03-13 15:44:24 +05:30
# frozen_string_literal: true
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec . describe ActiveRecord :: QueryRecorder do
2020-05-24 23:13:21 +05:30
before do
stub_const ( 'TestQueries' , Class . new ( ActiveRecord :: Base ) )
TestQueries . class_eval do
self . table_name = 'schema_migrations'
end
2020-03-13 15:44:24 +05:30
end
2021-04-29 21:17:54 +05:30
describe 'printing to the log' do
let ( :backtrace ) { %r{ QueryRecorder backtrace: --> ( \ w+/)* \ w+ \ .rb: \ d+:in `.*' } }
let ( :duration_line ) { %r{ QueryRecorder DURATION: --> \ d+ \ . \ d+ } }
def expect_section ( query , lines )
query_lines = lines . take ( query . size )
# the query comes first
expect ( query_lines ) . to match ( query )
# followed by the duration
expect ( lines [ query . size ] ) . to match ( duration_line )
# and then one or more lines of backtrace
backtrace_lines = lines . drop ( query . size + 1 ) . take_while { | line | line . match ( backtrace ) }
expect ( backtrace_lines ) . not_to be_empty
# Advance to the next section
lines . drop ( query . size + 1 + backtrace_lines . size )
end
it 'prints SQL, duration and backtrace, all prefixed with QueryRecorder' , :aggregate_failures do
io = StringIO . new
control = ActiveRecord :: QueryRecorder . new ( log_file : io , query_recorder_debug : true ) do
TestQueries . count
TestQueries . first
TestQueries . where ( << ~ FRAGMENT ) . to_a # tests multi-line SQL
version = 'foo'
OR
version = 'bar'
FRAGMENT
end
query_a = start_with ( %q[ QueryRecorder SQL: --> SELECT COUNT(*) FROM "schema_migrations" ] )
query_b = start_with ( %q[ QueryRecorder SQL: --> SELECT "schema_migrations".* FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC LIMIT 1 ] )
query_c_a = eq ( %q[ QueryRecorder SQL: --> SELECT "schema_migrations".* FROM "schema_migrations" WHERE (version = 'foo' ] )
query_c_b = eq ( %q( QueryRecorder SQL: --> OR ) )
query_c_c = eq ( %q( QueryRecorder SQL: --> version = 'bar' ) )
query_c_d = start_with ( " QueryRecorder SQL: --> ) " )
expect ( control . count ) . to eq ( 3 )
lines = io . string . lines . map ( & :chomp )
expect ( lines ) . to all ( start_with ( 'QueryRecorder' ) )
lines = expect_section ( [ query_a ] , lines )
lines = expect_section ( [ query_b ] , lines )
lines = expect_section ( [ query_c_a , query_c_b , query_c_c , query_c_d ] , lines )
expect ( lines ) . to be_empty
end
end
it 'includes duration information' do
control = ActiveRecord :: QueryRecorder . new do
TestQueries . count
TestQueries . first
end
expect ( control . count ) . to eq ( 2 )
expect ( control . data . values . flat_map { _1 [ :durations ] } ) . to match ( [ be > 0 , be > 0 ] )
end
2020-03-13 15:44:24 +05:30
describe 'detecting the right number of calls and their origin' do
it 'detects two separate queries' do
control = ActiveRecord :: QueryRecorder . new query_recorder_debug : true do
2 . times { TestQueries . count }
TestQueries . first
end
# Check #find_query
expect ( control . find_query ( / .* / , 0 ) . size )
. to eq ( control . data . keys . size )
# Ensure exactly 2 COUNT queries were detected
expect ( control . occurrences_by_line_method . last [ 1 ] [ :occurrences ]
2021-04-29 21:17:54 +05:30
. count { | str | str . start_with? ( 'SELECT COUNT' ) } ) . to eq ( 2 )
2020-03-13 15:44:24 +05:30
# Ensure exactly 1 LIMIT 1 (#first)
expect ( control . occurrences_by_line_method . first [ 1 ] [ :occurrences ]
2021-04-29 21:17:54 +05:30
. count { | str | str . match ( / ORDER BY.* #{ TestQueries . table_name } .*LIMIT 1 / ) } ) . to eq ( 1 )
2020-03-13 15:44:24 +05:30
# Ensure 3 DB calls overall were executed
expect ( control . log . size ) . to eq ( 3 )
# Ensure memoization value match the raw value above
expect ( control . count ) . to eq ( control . log . size )
# Ensure we have only two sources of queries
expect ( control . data . keys . size ) . to eq ( 1 )
end
end
end