Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 14:59:07 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 14:59:07 +0300
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4 /spec/support_specs
parent4b1de649d0168371549608993deac953eb692019 (diff)
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'spec/support_specs')
-rw-r--r--spec/support_specs/graphql/arguments_spec.rb71
-rw-r--r--spec/support_specs/graphql/field_selection_spec.rb62
-rw-r--r--spec/support_specs/graphql/var_spec.rb34
-rw-r--r--spec/support_specs/helpers/graphql_helpers_spec.rb274
-rw-r--r--spec/support_specs/helpers/stub_feature_flags_spec.rb25
-rw-r--r--spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb226
6 files changed, 683 insertions, 9 deletions
diff --git a/spec/support_specs/graphql/arguments_spec.rb b/spec/support_specs/graphql/arguments_spec.rb
new file mode 100644
index 00000000000..ffb58503a0e
--- /dev/null
+++ b/spec/support_specs/graphql/arguments_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Graphql::Arguments do
+ it 'returns a blank string if the arguments are blank' do
+ args = described_class.new({})
+
+ expect("#{args}").to be_blank
+ end
+
+ it 'returns a serialized arguments if the arguments are not blank' do
+ units = described_class.new({ temp: :CELSIUS, time: :MINUTES })
+ args = described_class.new({ temp: 180, time: 45, units: units })
+
+ expect("#{args}").to eq('temp: 180, time: 45, units: {temp: CELSIUS, time: MINUTES}')
+ end
+
+ it 'supports merge with +' do
+ lhs = described_class.new({ a: 1, b: 2 })
+ rhs = described_class.new({ b: 3, c: 4 })
+
+ expect(lhs + rhs).to eq({ a: 1, b: 3, c: 4 })
+ end
+
+ it 'supports merge with + and a string' do
+ lhs = described_class.new({ a: 1, b: 2 })
+ rhs = 'x: no'
+
+ expect(lhs + rhs).to eq('a: 1, b: 2, x: no')
+ end
+
+ it 'supports merge with + and a string when empty' do
+ lhs = described_class.new({})
+ rhs = 'x: no'
+
+ expect(lhs + rhs).to eq('x: no')
+ end
+
+ it 'supports merge with + and an empty string' do
+ lhs = described_class.new({ a: 1 })
+ rhs = ''
+
+ expect(lhs + rhs).to eq({ a: 1 })
+ end
+
+ it 'serializes all values correctly' do
+ args = described_class.new({
+ array: [1, 2.5, "foo", nil, true, false, :BAR, { power: :on }],
+ hash: { a: 1, b: 2, c: 3 },
+ int: 42,
+ float: 2.7,
+ string: %q[he said "no"],
+ enum: :OFF,
+ null: nil, # we expect this to be omitted - absence is the same as explicit nullness
+ bool_true: true,
+ bool_false: false,
+ var: ::Graphql::Var.new('x', 'Int')
+ })
+
+ expect(args.to_s).to eq([
+ %q(array: [1,2.5,"foo",null,true,false,BAR,{power: on}]),
+ %q(hash: {a: 1, b: 2, c: 3}),
+ 'int: 42, float: 2.7',
+ %q(string: "he said \\"no\\""),
+ 'enum: OFF',
+ 'boolTrue: true, boolFalse: false',
+ 'var: $x'
+ ].join(', '))
+ end
+end
diff --git a/spec/support_specs/graphql/field_selection_spec.rb b/spec/support_specs/graphql/field_selection_spec.rb
new file mode 100644
index 00000000000..8818e59e598
--- /dev/null
+++ b/spec/support_specs/graphql/field_selection_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Graphql::FieldSelection do
+ it 'can report on the paths that are selected' do
+ selection = described_class.new({
+ 'foo' => nil,
+ 'bar' => nil,
+ 'quux' => {
+ 'a' => nil,
+ 'b' => { 'x' => nil, 'y' => nil }
+ },
+ 'qoox' => {
+ 'q' => nil,
+ 'r' => { 's' => { 't' => nil } }
+ }
+ })
+
+ expect(selection.paths).to include(
+ %w[foo],
+ %w[quux a],
+ %w[quux b x],
+ %w[qoox r s t]
+ )
+ end
+
+ it 'can serialize a field selection nicely' do
+ selection = described_class.new({
+ 'foo' => nil,
+ 'bar' => nil,
+ 'quux' => {
+ 'a' => nil,
+ 'b' => { 'x' => nil, 'y' => nil }
+ },
+ 'qoox' => {
+ 'q' => nil,
+ 'r' => { 's' => { 't' => nil } }
+ }
+ })
+
+ expect(selection.to_s).to eq(<<~FRAG.strip)
+ foo
+ bar
+ quux {
+ a
+ b {
+ x
+ y
+ }
+ }
+ qoox {
+ q
+ r {
+ s {
+ t
+ }
+ }
+ }
+ FRAG
+ end
+end
diff --git a/spec/support_specs/graphql/var_spec.rb b/spec/support_specs/graphql/var_spec.rb
new file mode 100644
index 00000000000..f708f01a11e
--- /dev/null
+++ b/spec/support_specs/graphql/var_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Graphql::Var do
+ subject(:var) { described_class.new('foo', 'Int') }
+
+ it 'associates a name with a type and an initially empty value' do
+ expect(var).to have_attributes(
+ name: 'foo',
+ type: 'Int',
+ value: be_nil
+ )
+ end
+
+ it 'has a correct signature' do
+ expect(var).to have_attributes(sig: '$foo: Int')
+ end
+
+ it 'implements to_graphql_value as $name' do
+ expect(var.to_graphql_value).to eq('$foo')
+ end
+
+ it 'can set a value using with, returning a new object' do
+ with_value = var.with(42)
+
+ expect(with_value).to have_attributes(name: 'foo', type: 'Int', value: 42)
+ expect(var).to have_attributes(value: be_nil)
+ end
+
+ it 'returns an object suitable for passing to post_graphql(variables:)' do
+ expect(var.with(17).to_h).to eq('foo' => 17)
+ end
+end
diff --git a/spec/support_specs/helpers/graphql_helpers_spec.rb b/spec/support_specs/helpers/graphql_helpers_spec.rb
index bc777621674..a9fe5b8d196 100644
--- a/spec/support_specs/helpers/graphql_helpers_spec.rb
+++ b/spec/support_specs/helpers/graphql_helpers_spec.rb
@@ -5,6 +5,278 @@ require 'spec_helper'
RSpec.describe GraphqlHelpers do
include GraphqlHelpers
+ # Normalize irrelevant whitespace to make comparison easier
+ def norm(query)
+ query.tr("\n", ' ').gsub(/\s+/, ' ').strip
+ end
+
+ describe 'graphql_dig_at' do
+ it 'transforms symbol keys to graphql field names' do
+ data = { 'camelCased' => 'names' }
+
+ expect(graphql_dig_at(data, :camel_cased)).to eq('names')
+ end
+
+ it 'supports integer indexing' do
+ data = { 'array' => [:boom, { 'id' => :hooray! }, :boom] }
+
+ expect(graphql_dig_at(data, :array, 1, :id)).to eq(:hooray!)
+ end
+
+ it 'gracefully degrades to nil' do
+ data = { 'project' => { 'mergeRequest' => nil } }
+
+ expect(graphql_dig_at(data, :project, :merge_request, :id)).to be_nil
+ end
+
+ it 'supports implicitly flat-mapping traversals' do
+ data = {
+ 'foo' => {
+ 'nodes' => [
+ { 'bar' => { 'nodes' => [{ 'id' => 1 }, { 'id' => 2 }] } },
+ { 'bar' => { 'nodes' => [{ 'id' => 3 }, { 'id' => 4 }] } },
+ { 'bar' => nil }
+ ]
+ },
+ 'irrelevant' => 'the field is a red-herring'
+ }
+
+ expect(graphql_dig_at(data, :foo, :nodes, :bar, :nodes, :id)).to eq([1, 2, 3, 4])
+ end
+ end
+
+ describe 'var' do
+ it 'allocates a fresh name for each var' do
+ a = var('Int')
+ b = var('Int')
+
+ expect(a.name).not_to eq(b.name)
+ end
+
+ it 'can be used to construct correct signatures' do
+ a = var('Int')
+ b = var('String!')
+
+ q = with_signature([a, b], '{ foo bar }')
+
+ expect(q).to eq("query(#{a.to_graphql_value}: Int, #{b.to_graphql_value}: String!) { foo bar }")
+ end
+
+ it 'can be used to pass arguments to fields' do
+ a = var('ID!')
+
+ q = graphql_query_for(:project, { full_path: a }, :id)
+
+ expect(norm(q)).to eq("{ project(fullPath: #{a.to_graphql_value}){ id } }")
+ end
+
+ it 'can associate values with variables' do
+ a = var('Int')
+
+ expect(a.with(3).to_h).to eq(a.name => 3)
+ end
+
+ it 'does not mutate the variable when providing a value' do
+ a = var('Int')
+ three = a.with(3)
+
+ expect(three.value).to eq(3)
+ expect(a.value).to be_nil
+ end
+
+ it 'can associate many values with variables' do
+ a = var('Int').with(3)
+ b = var('String').with('foo')
+
+ expect(serialize_variables([a, b])).to eq({ a.name => 3, b.name => 'foo' }.to_json)
+ end
+ end
+
+ describe '.query_nodes' do
+ it 'can produce a basic connection selection' do
+ selection = query_nodes(:users)
+
+ expected = query_graphql_path([:users, :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'allows greater depth' do
+ selection = query_nodes(:users, max_depth: 2)
+
+ expected = query_graphql_path([:users, :nodes], all_graphql_fields_for('User', max_depth: 2))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts fields' do
+ selection = query_nodes(:users, :id)
+
+ expected = query_graphql_path([:users, :nodes], :id)
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts arguments' do
+ args = { username: 'foo' }
+ selection = query_nodes(:users, args: args)
+
+ expected = query_graphql_path([[:users, args], :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts arguments and fields' do
+ selection = query_nodes(:users, :id, args: { username: 'foo' })
+
+ expected = query_graphql_path([[:users, { username: 'foo' }], :nodes], :id)
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'accepts explicit type name' do
+ selection = query_nodes(:members, of: 'User')
+
+ expected = query_graphql_path([:members, :nodes], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+
+ it 'can optionally provide pagination info' do
+ selection = query_nodes(:users, include_pagination_info: true)
+
+ expected = query_graphql_path([:users, "#{page_info_selection} nodes"], all_graphql_fields_for('User', max_depth: 1))
+
+ expect(selection).to eq(expected)
+ end
+ end
+
+ describe '.query_graphql_path' do
+ it 'can build nested paths' do
+ selection = query_graphql_path(%i[foo bar wibble_wobble], :id)
+
+ expected = norm(<<-GQL)
+ foo{
+ bar{
+ wibbleWobble{
+ id
+ }
+ }
+ }
+ GQL
+
+ expect(norm(selection)).to eq(expected)
+ end
+
+ it 'can insert arguments at any point' do
+ selection = query_graphql_path(
+ [:foo, [:bar, { quux: true }], [:wibble_wobble, { eccentricity: :HIGH }]],
+ :id
+ )
+
+ expected = norm(<<-GQL)
+ foo{
+ bar(quux: true){
+ wibbleWobble(eccentricity: HIGH){
+ id
+ }
+ }
+ }
+ GQL
+
+ expect(norm(selection)).to eq(expected)
+ end
+ end
+
+ describe '.attributes_to_graphql' do
+ it 'can serialize hashes to literal arguments' do
+ x = var('Int')
+ args = {
+ an_array: [1, nil, "foo", true, [:foo, :bar]],
+ a_hash: {
+ nested: true,
+ value: "bar"
+ },
+ an_int: 42,
+ a_float: 0.1,
+ a_string: "wibble",
+ an_enum: :LOW,
+ null: nil,
+ a_bool: false,
+ a_var: x
+ }
+
+ literal = attributes_to_graphql(args)
+
+ expect(norm(literal)).to eq(norm(<<~EXP))
+ anArray: [1,null,"foo",true,[foo,bar]],
+ aHash: {nested: true, value: "bar"},
+ anInt: 42,
+ aFloat: 0.1,
+ aString: "wibble",
+ anEnum: LOW,
+ aBool: false,
+ aVar: #{x.to_graphql_value}
+ EXP
+ end
+ end
+
+ describe '.all_graphql_fields_for' do
+ it 'returns a FieldSelection' do
+ selection = all_graphql_fields_for('User', max_depth: 1)
+
+ expect(selection).to be_a(::Graphql::FieldSelection)
+ end
+
+ it 'returns nil if the depth is too shallow' do
+ selection = all_graphql_fields_for('User', max_depth: 0)
+
+ expect(selection).to be_nil
+ end
+
+ it 'can select just the scalar fields' do
+ selection = all_graphql_fields_for('User', max_depth: 1)
+ paths = selection.paths.map(&:join)
+
+ # A sample, tested using include to save churn as fields are added
+ expect(paths)
+ .to include(*%w[avatarUrl email groupCount id location name state username webPath webUrl])
+
+ expect(selection.paths).to all(have_attributes(size: 1))
+ end
+
+ it 'selects only as far as 3 levels by default' do
+ selection = all_graphql_fields_for('User')
+
+ expect(selection.paths).to all(have_attributes(size: (be <= 3)))
+
+ # Representative sample
+ expect(selection.paths).to include(
+ %w[userPermissions createSnippet],
+ %w[todos nodes id],
+ %w[starredProjects nodes name],
+ %w[authoredMergeRequests count],
+ %w[assignedMergeRequests pageInfo startCursor]
+ )
+ end
+
+ it 'selects only as far as requested' do
+ selection = all_graphql_fields_for('User', max_depth: 2)
+
+ expect(selection.paths).to all(have_attributes(size: (be <= 2)))
+ end
+
+ it 'omits fields that have required arguments' do
+ selection = all_graphql_fields_for('DesignCollection', max_depth: 3)
+
+ expect(selection.paths).not_to be_empty
+
+ expect(selection.paths).not_to include(
+ %w[designAtVersion id]
+ )
+ end
+ end
+
describe '.graphql_mutation' do
shared_examples 'correct mutation definition' do
it 'returns correct mutation definition' do
@@ -15,7 +287,7 @@ RSpec.describe GraphqlHelpers do
}
}
MUTATION
- variables = %q({"updateAlertStatusInput":{"projectPath":"test/project"}})
+ variables = { "updateAlertStatusInput" => { "projectPath" => "test/project" } }
is_expected.to eq(GraphqlHelpers::MutationDefinition.new(query, variables))
end
diff --git a/spec/support_specs/helpers/stub_feature_flags_spec.rb b/spec/support_specs/helpers/stub_feature_flags_spec.rb
index 57dd3015f5e..8629e895fd1 100644
--- a/spec/support_specs/helpers/stub_feature_flags_spec.rb
+++ b/spec/support_specs/helpers/stub_feature_flags_spec.rb
@@ -5,19 +5,20 @@ require 'spec_helper'
RSpec.describe StubFeatureFlags do
let_it_be(:dummy_feature_flag) { :dummy_feature_flag }
- # We inject dummy feature flag defintion
- # to ensure that we strong validate it's usage
- # as well
- before(:all) do
- definition = Feature::Definition.new(
+ let_it_be(:dummy_definition) do
+ Feature::Definition.new(
nil,
name: dummy_feature_flag,
type: 'development',
- # we allow ambigious usage of `default_enabled:`
- default_enabled: [false, true]
+ default_enabled: false
)
+ end
- Feature::Definition.definitions[dummy_feature_flag] = definition
+ # We inject dummy feature flag defintion
+ # to ensure that we strong validate it's usage
+ # as well
+ before(:all) do
+ Feature::Definition.definitions[dummy_feature_flag] = dummy_definition
end
after(:all) do
@@ -47,6 +48,10 @@ RSpec.describe StubFeatureFlags do
it { expect(Feature.disabled?(feature_name)).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
+ before do
+ allow(dummy_definition).to receive(:default_enabled).and_return(true)
+ end
+
it { expect(Feature.enabled?(feature_name, default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, default_enabled: true)).not_to eq(expected_result) }
end
@@ -79,6 +84,10 @@ RSpec.describe StubFeatureFlags do
it { expect(Feature.disabled?(feature_name, actor(tested_actor))).not_to eq(expected_result) }
context 'default_enabled does not impact feature state' do
+ before do
+ allow(dummy_definition).to receive(:default_enabled).and_return(true)
+ end
+
it { expect(Feature.enabled?(feature_name, actor(tested_actor), default_enabled: true)).to eq(expected_result) }
it { expect(Feature.disabled?(feature_name, actor(tested_actor), default_enabled: true)).not_to eq(expected_result) }
end
diff --git a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
index 15b846f28cb..6d8d9ba0754 100644
--- a/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
+++ b/spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb
@@ -22,6 +22,232 @@ RSpec.describe ExceedQueryLimitHelpers do
end
end
+ describe '#diff_query_group_message' do
+ it 'prints a group helpfully' do
+ test_matcher = TestMatcher.new
+ suffixes = {
+ 'WHERE x = z' => [1, 1],
+ 'WHERE x = y' => [1, 2],
+ 'LIMIT 1' => [1, 0]
+ }
+
+ message = test_matcher.diff_query_group_message('SELECT * FROM foo', suffixes)
+
+ expect(message).to eq(<<~MSG.chomp)
+ SELECT * FROM foo...
+ -- (expected: 1, got: 1)
+ WHERE x = z
+ -- (expected: 1, got: 2)
+ WHERE x = y
+ -- (expected: 1, got: 0)
+ LIMIT 1
+ MSG
+ end
+ end
+
+ describe '#diff_query_counts' do
+ let(:expected) do
+ ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.first
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.where(version: 'z').delete_all
+ end
+ end
+
+ let(:actual) do
+ ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.create!(version: 'x')
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.count
+ TestQueries.where(version: 'y').update_all(version: 'z')
+ TestQueries.where(version: 'z').delete_all
+ end
+ end
+
+ it 'merges two query counts' do
+ test_matcher = TestMatcher.new
+
+ diff = test_matcher.diff_query_counts(
+ test_matcher.count_queries(expected),
+ test_matcher.count_queries(actual)
+ )
+
+ expect(diff).to eq({
+ "SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
+ "ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
+ },
+ "SELECT COUNT(*) FROM \"schema_migrations\"" => { "" => [1, 2] },
+ "UPDATE \"schema_migrations\"" => {
+ "SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
+ },
+ "SAVEPOINT active_record_1" => { "" => [0, 1] },
+ "INSERT INTO \"schema_migrations\" (\"version\")" => {
+ "VALUES ('x') RETURNING \"version\"" => [0, 1]
+ },
+ "RELEASE SAVEPOINT active_record_1" => { "" => [0, 1] }
+ })
+ end
+
+ it 'can show common queries if so desired' do
+ test_matcher = TestMatcher.new.show_common_queries
+
+ diff = test_matcher.diff_query_counts(
+ test_matcher.count_queries(expected),
+ test_matcher.count_queries(actual)
+ )
+
+ expect(diff).to eq({
+ "SELECT \"schema_migrations\".* FROM \"schema_migrations\"" => {
+ "WHERE \"schema_migrations\".\"version\" = 'foobar'" => [2, 2],
+ "WHERE \"schema_migrations\".\"version\" = 'also foobar and baz'" => [1, 1],
+ "ORDER BY \"schema_migrations\".\"version\" ASC LIMIT 1" => [1, 0]
+ },
+ "SELECT COUNT(*) FROM \"schema_migrations\"" => {
+ "" => [1, 2],
+ "WHERE \"schema_migrations\".\"version\" = 'foobar'" => [1, 1]
+ },
+ "UPDATE \"schema_migrations\"" => {
+ "SET \"version\" = 'y' WHERE \"schema_migrations\".\"version\" = 'x'" => [1, 1],
+ "SET \"version\" = 'z' WHERE \"schema_migrations\".\"version\" = 'y'" => [0, 1]
+ },
+ "DELETE FROM \"schema_migrations\"" => {
+ "WHERE \"schema_migrations\".\"version\" = 'z'" => [1, 1]
+ },
+ "SAVEPOINT active_record_1" => {
+ "" => [0, 1]
+ },
+ "INSERT INTO \"schema_migrations\" (\"version\")" => {
+ "VALUES ('x') RETURNING \"version\"" => [0, 1]
+ },
+ "RELEASE SAVEPOINT active_record_1" => {
+ "" => [0, 1]
+ }
+ })
+ end
+ end
+
+ describe '#count_queries' do
+ it 'handles queries with suffixes over multiple lines' do
+ test_matcher = TestMatcher.new
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ TestQueries.find_by(version: %w(foo bar baz).join("\n"))
+ TestQueries.find_by(version: %w(foo biz baz).join("\n"))
+ TestQueries.find_by(version: %w(foo bar baz).join("\n"))
+ end
+
+ recorder.count
+
+ expect(test_matcher.count_queries(recorder)).to eq({
+ 'SELECT "schema_migrations".* FROM "schema_migrations"' => {
+ %Q[WHERE "schema_migrations"."version" = 'foo\nbar\nbaz' LIMIT 1] => 2,
+ %Q[WHERE "schema_migrations"."version" = 'foo\nbiz\nbaz' LIMIT 1] => 1
+ }
+ })
+ end
+
+ it 'can aggregate queries' do
+ test_matcher = TestMatcher.new
+
+ recorder = ActiveRecord::QueryRecorder.new do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.count
+ TestQueries.create!(version: 'x')
+ TestQueries.first
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'x').update_all(version: 'y')
+ TestQueries.where(version: 'foobar').count
+ TestQueries.count
+ TestQueries.where(version: 'y').update_all(version: 'z')
+ TestQueries.where(version: 'z').delete_all
+ end
+
+ recorder.count
+
+ expect(test_matcher.count_queries(recorder)).to eq({
+ 'SELECT "schema_migrations".* FROM "schema_migrations"' => {
+ %q[WHERE "schema_migrations"."version" = 'foobar'] => 2,
+ %q[WHERE "schema_migrations"."version" = 'also foobar and baz'] => 1,
+ %q[ORDER BY "schema_migrations"."version" ASC LIMIT 1] => 1
+ },
+ 'SELECT COUNT(*) FROM "schema_migrations"' => {
+ "" => 2,
+ %q[WHERE "schema_migrations"."version" = 'foobar'] => 1
+ },
+ 'SAVEPOINT active_record_1' => { "" => 1 },
+ 'INSERT INTO "schema_migrations" ("version")' => {
+ %q[VALUES ('x') RETURNING "version"] => 1
+ },
+ 'RELEASE SAVEPOINT active_record_1' => { "" => 1 },
+ 'UPDATE "schema_migrations"' => {
+ %q[SET "version" = 'y' WHERE "schema_migrations"."version" = 'x'] => 1,
+ %q[SET "version" = 'z' WHERE "schema_migrations"."version" = 'y'] => 1
+ },
+ 'DELETE FROM "schema_migrations"' => {
+ %q[WHERE "schema_migrations"."version" = 'z'] => 1
+ }
+ })
+ end
+ end
+
+ it 'can count queries' do
+ test_matcher = TestMatcher.new
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(4)
+ end
+
+ it 'can select specific queries' do
+ test_matcher = TestMatcher.new.for_query(/foobar/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(2)
+ end
+
+ it 'can ignore specific queries' do
+ test_matcher = TestMatcher.new.ignoring(/foobar/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ end
+
+ expect(test_matcher.actual_count).to eq(1)
+ end
+
+ it 'can perform inclusion and exclusion' do
+ test_matcher = TestMatcher.new.for_query(/foobar/).ignoring(/baz/)
+ test_matcher.verify_count do
+ TestQueries.where(version: 'foobar').to_a
+ TestQueries.where(version: 'also foobar and baz').to_a
+ TestQueries.first
+ TestQueries.count
+ end
+
+ expect(test_matcher.actual_count).to eq(1)
+ end
+
it 'does not contain marginalia annotations' do
test_matcher = TestMatcher.new
test_matcher.verify_count do