summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ci/build_spec.rb6
-rw-r--r--spec/models/ci/pipeline_spec.rb81
-rw-r--r--spec/models/ci/stage_spec.rb4
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb5
-rw-r--r--spec/models/instance_configuration_spec.rb6
-rw-r--r--spec/models/internal_id_spec.rb3
-rw-r--r--spec/models/issue_spec.rb39
-rw-r--r--spec/models/label_spec.rb36
-rw-r--r--spec/models/license_template_spec.rb2
-rw-r--r--spec/models/note_spec.rb67
-rw-r--r--spec/models/project_auto_devops_spec.rb27
-rw-r--r--spec/models/project_feature_spec.rb31
-rw-r--r--spec/models/project_spec.rb57
-rw-r--r--spec/models/todo_spec.rb125
14 files changed, 409 insertions, 80 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 31bcfe1c6b1..a046541031e 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -261,7 +261,7 @@ describe Ci::Build do
it 'schedules BuildScheduleWorker at the right time' do
Timecop.freeze do
expect(Ci::BuildScheduleWorker)
- .to receive(:perform_at).with(1.minute.since, build.id)
+ .to receive(:perform_at).with(be_like_time(1.minute.since), build.id)
subject
end
@@ -1852,6 +1852,7 @@ describe Ci::Build do
describe '#variables' do
let(:container_registry_enabled) { false }
+ let(:gitlab_version_info) { Gitlab::VersionInfo.parse(Gitlab::VERSION) }
let(:predefined_variables) do
[
{ key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
@@ -1869,6 +1870,9 @@ describe Ci::Build do
{ key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
+ { key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s, public: true },
+ { key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b19e75a956d..3b01b39ecab 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -837,6 +837,57 @@ describe Ci::Pipeline, :mailer do
end
end
+ describe '#branch_updated?' do
+ context 'when pipeline has before SHA' do
+ before do
+ pipeline.update_column(:before_sha, 'a1b2c3d4')
+ end
+
+ it 'runs on a branch update push' do
+ expect(pipeline.before_sha).not_to be Gitlab::Git::BLANK_SHA
+ expect(pipeline.branch_updated?).to be true
+ end
+ end
+
+ context 'when pipeline does not have before SHA' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'does not run on a branch updating push' do
+ expect(pipeline.branch_updated?).to be false
+ end
+ end
+ end
+
+ describe '#modified_paths' do
+ context 'when old and new revisions are set' do
+ let(:project) { create(:project, :repository) }
+
+ before do
+ pipeline.update(before_sha: '1234abcd', sha: '2345bcde')
+ end
+
+ it 'fetches stats for changes between commits' do
+ expect(project.repository)
+ .to receive(:diff_stats).with('1234abcd', '2345bcde')
+ .and_call_original
+
+ pipeline.modified_paths
+ end
+ end
+
+ context 'when either old or new revision is missing' do
+ before do
+ pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ end
+
+ it 'raises an error' do
+ expect { pipeline.modified_paths }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
describe '#has_kubernetes_active?' do
context 'when kubernetes is active' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
@@ -1993,4 +2044,34 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe '#default_branch?' do
+ let(:default_branch) { 'master'}
+
+ subject { pipeline.default_branch? }
+
+ before do
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ context 'when pipeline ref is the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: default_branch)
+ end
+
+ it "returns true" do
+ expect(subject).to be_truthy
+ end
+ end
+
+ context 'when pipeline ref is not the default branch of the project' do
+ let(:pipeline) do
+ build(:ci_empty_pipeline, status: :created, project: project, ref: 'another_branch')
+ end
+
+ it "returns false" do
+ expect(subject).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 060a1d95293..5076f7faeac 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -200,8 +200,8 @@ describe Ci::Stage, :models do
end
end
- describe '#schedule' do
- subject { stage.schedule }
+ describe '#delay' do
+ subject { stage.delay }
let(:stage) { create(:ci_stage_entity, status: :created) }
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index da26d802688..f8d50e89d40 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -331,11 +331,12 @@ describe CacheMarkdownField do
end
context 'with a project' do
- let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: :project_value) }
+ let(:project) { create(:project, group: create(:group)) }
+ let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: project) }
it 'sets the project in the context' do
is_expected.to have_key(:project)
- expect(context[:project]).to eq(:project_value)
+ expect(context[:project]).to eq(project)
end
it 'invalidates the cache when project changes' do
diff --git a/spec/models/instance_configuration_spec.rb b/spec/models/instance_configuration_spec.rb
index 34db94920f3..cb3d6c7cda2 100644
--- a/spec/models/instance_configuration_spec.rb
+++ b/spec/models/instance_configuration_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
-RSpec.describe InstanceConfiguration do
+describe InstanceConfiguration do
context 'without cache' do
describe '#settings' do
describe '#ssh_algorithms_hashes' do
- let(:md5) { '54:e0:f8:70:d6:4f:4c:b1:b3:02:44:77:cf:cd:0d:fc' }
- let(:sha256) { '9327f0d15a48c4d9f6a3aee65a1825baf9a3412001c98169c5fd022ac27762fc' }
+ let(:md5) { '5a:65:6c:4d:d4:4c:6d:e6:59:25:b8:cf:ba:34:e7:64' }
+ let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do
stub_pub_file(exist: false)
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index f2aad455d5f..52c00a74b4b 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -65,7 +65,8 @@ describe InternalId do
context 'with an insufficient schema version' do
before do
described_class.reset_column_information
- expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ # Project factory will also call the current_version
+ expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 19bc2713ef5..6f900a60213 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -268,45 +268,6 @@ describe Issue do
end
end
- describe '#related_branches' do
- let(:user) { create(:admin) }
-
- before do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
-
- # Without this stub, the `create(:merge_request)` above fails because it can't find
- # the source branch. This seems like a reasonable compromise, in comparison with
- # setting up a full repo here.
- allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff)
- end
-
- it "selects the right branches when there are no referenced merge requests" do
- expect(subject.related_branches(user)).to eq([subject.to_branch_name, "#{subject.iid}-branch"])
- end
-
- it "selects the right branches when there is a referenced merge request" do
- merge_request = create(:merge_request, { description: "Closes ##{subject.iid}",
- source_project: subject.project,
- source_branch: "#{subject.iid}-branch" })
- merge_request.create_cross_references!(user)
-
- referenced_merge_requests = Issues::ReferencedMergeRequestsService
- .new(subject.project, user)
- .referenced_merge_requests(subject)
-
- expect(referenced_merge_requests).not_to be_empty
- expect(subject.related_branches(user)).to eq([subject.to_branch_name])
- end
-
- it 'excludes stable branches from the related branches' do
- allow(subject.project.repository).to receive(:branch_names)
- .and_return(["#{subject.iid}-0-stable"])
-
- expect(subject.related_branches(user)).to eq []
- end
- end
-
describe '#suggested_branch_name' do
let(:repository) { double }
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 99670af786a..3fc6c06b7fa 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -155,4 +155,40 @@ describe Label do
expect(described_class.search('feature')).to be_empty
end
end
+
+ describe '.subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns nothing' do
+ expect(described_class.subscribed_by(0)).to be_empty
+ end
+ end
+
+ describe '.optionally_subscribed_by' do
+ let!(:user) { create(:user) }
+ let!(:label) { create(:label) }
+ let!(:label2) { create(:label) }
+
+ before do
+ label.subscribe(user)
+ end
+
+ it 'returns subscribed labels' do
+ expect(described_class.optionally_subscribed_by(user.id)).to eq([label])
+ end
+
+ it 'returns all labels if user_id is nil' do
+ expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2])
+ end
+ end
end
diff --git a/spec/models/license_template_spec.rb b/spec/models/license_template_spec.rb
index c633e1908d4..dd912eefac1 100644
--- a/spec/models/license_template_spec.rb
+++ b/spec/models/license_template_spec.rb
@@ -54,6 +54,6 @@ describe LicenseTemplate do
end
def build_template(content)
- described_class.new(id: 'foo', name: 'foo', category: :Other, content: content)
+ described_class.new(key: 'foo', name: 'foo', category: :Other, content: content)
end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 947be44c903..1783dd3206b 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -231,33 +231,60 @@ describe Note do
let(:ext_proj) { create(:project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
- let(:note) do
- create :note,
- noteable: ext_issue, project: ext_proj,
- note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
- system: true
- end
+ shared_examples "checks references" do
+ it "returns true" do
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
- it "returns true" do
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
- end
+ it "returns false" do
+ expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ end
- it "returns false" do
- expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+ it "returns false if user visible reference count set" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 1
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ end
+
+ it "returns true if ref count is 0" do
+ note.user_visible_reference_count = 0
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
- it "returns false if user visible reference count set" do
- note.user_visible_reference_count = 1
+ context "when there is one reference in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
+ system: true
+ end
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_falsy
+ it_behaves_like "checks references"
end
- it "returns true if ref count is 0" do
- note.user_visible_reference_count = 0
+ context "when there are two references in note" do
+ let(:note) do
+ create :note,
+ noteable: ext_issue, project: ext_proj,
+ note: "mentioned in issue #{private_issue.to_reference(ext_proj)} and " \
+ "public issue #{ext_issue.to_reference(ext_proj)}",
+ system: true
+ end
+
+ it_behaves_like "checks references"
- expect(note).not_to receive(:reference_mentionables)
- expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ it "returns true if user visible reference count set and there is a private reference" do
+ note.user_visible_reference_count = 1
+ note.total_reference_count = 2
+
+ expect(note).not_to receive(:reference_mentionables)
+ expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+ end
end
end
@@ -269,7 +296,7 @@ describe Note do
end
context 'when the note might contain cross references' do
- SystemNoteMetadata::TYPES_WITH_CROSS_REFERENCES.each do |type|
+ SystemNoteMetadata.new.cross_reference_types.each do |type|
let(:note) { create(:note, :system) }
let!(:metadata) { create(:system_note_metadata, note: note, action: type) }
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 797d767465a..342798f730b 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -70,24 +70,31 @@ describe ProjectAutoDevops do
end
context 'when deploy_strategy is manual' do
- let(:domain) { 'example.com' }
-
- before do
- auto_devops.deploy_strategy = 'manual'
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :manual_deployment, project: project) }
+ let(:expected_variables) do
+ [
+ { key: 'INCREMENTAL_ROLLOUT_MODE', value: 'manual' },
+ { key: 'STAGING_ENABLED', value: '1' },
+ { key: 'INCREMENTAL_ROLLOUT_ENABLED', value: '1' }
+ ]
end
+ it { expect(auto_devops.predefined_variables).to include(*expected_variables) }
+ end
+
+ context 'when deploy_strategy is continuous' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :continuous_deployment, project: project) }
+
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
- .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
+ .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
end
end
- context 'when deploy_strategy is continuous' do
- let(:domain) { 'example.com' }
+ context 'when deploy_strategy is timed_incremental' do
+ let(:auto_devops) { build_stubbed(:project_auto_devops, :timed_incremental_deployment, project: project) }
- before do
- auto_devops.deploy_strategy = 'continuous'
- end
+ it { expect(auto_devops.predefined_variables).to include(key: 'INCREMENTAL_ROLLOUT_MODE', value: 'timed') }
it do
expect(auto_devops.predefined_variables.map { |var| var[:key] })
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index cd7f77024da..fee7d65c217 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -17,7 +17,7 @@ describe ProjectFeature do
end
describe '#feature_available?' do
- let(:features) { %w(issues wiki builds merge_requests snippets repository) }
+ let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
context 'when features are disabled' do
it "returns false" do
@@ -73,6 +73,22 @@ describe ProjectFeature do
end
end
end
+
+ context 'when feature is disabled by a feature flag' do
+ it 'returns false' do
+ stub_feature_flags(issues: false)
+
+ expect(project.feature_available?(:issues, user)).to eq(false)
+ end
+ end
+
+ context 'when feature is enabled by a feature flag' do
+ it 'returns true' do
+ stub_feature_flags(issues: true)
+
+ expect(project.feature_available?(:issues, user)).to eq(true)
+ end
+ end
end
context 'repository related features' do
@@ -96,6 +112,19 @@ describe ProjectFeature do
end
end
+ context 'public features' do
+ it "does not allow public for other than pages" do
+ features = %w(issues wiki builds merge_requests snippets repository)
+ project_feature = project.project_feature
+
+ features.each do |feature|
+ field = "#{feature}_access_level".to_sym
+ project_feature.update_attribute(field, ProjectFeature::PUBLIC)
+ expect(project_feature.valid?).to be_falsy
+ end
+ end
+ end
+
describe '#*_enabled?' do
let(:features) { %w(wiki builds merge_requests) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8b71919544e..3fecddefff2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4039,6 +4039,63 @@ describe Project do
end
end
+ describe "#find_or_initialize_services" do
+ subject { build(:project) }
+
+ it 'returns only enabled services' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ services = subject.find_or_initialize_services
+
+ expect(services.count).to eq 1
+ expect(services).to include(PushoverService)
+ end
+ end
+
+ describe "#find_or_initialize_service" do
+ subject { build(:project) }
+
+ it 'avoids N+1 database queries' do
+ allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+
+ control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count
+
+ allow(Service).to receive(:available_services_names).and_call_original
+
+ expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
+ end
+
+ it 'returns nil if service is disabled' do
+ allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+
+ expect(subject.find_or_initialize_service('prometheus')).to be_nil
+ end
+ end
+
+ describe '.find_without_deleted' do
+ it 'returns nil if the project is about to be removed' do
+ project = create(:project, pending_delete: true)
+
+ expect(described_class.find_without_deleted(project.id)).to be_nil
+ end
+
+ it 'returns a project when it is not about to be removed' do
+ project = create(:project)
+
+ expect(described_class.find_without_deleted(project.id)).to eq(project)
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the projects for a given group' do
+ group = create(:group)
+ project = create(:project, namespace: group)
+
+ expect(described_class.for_group(group)).to eq([project])
+ end
+ end
+
def rugged_config
rugged_repo(project.repository).config
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index f29abcf536e..2c01578aaca 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -173,4 +173,129 @@ describe Todo do
expect(subject).not_to be_self_assigned
end
end
+
+ describe '.for_action' do
+ it 'returns the todos for a given action' do
+ create(:todo, action: Todo::MENTIONED)
+
+ todo = create(:todo, action: Todo::ASSIGNED)
+
+ expect(described_class.for_action(Todo::ASSIGNED)).to eq([todo])
+ end
+ end
+
+ describe '.for_author' do
+ it 'returns the todos for a given author' do
+ user1 = create(:user)
+ user2 = create(:user)
+ todo = create(:todo, author: user1)
+
+ create(:todo, author: user2)
+
+ expect(described_class.for_author(user1)).to eq([todo])
+ end
+ end
+
+ describe '.for_project' do
+ it 'returns the todos for a given project' do
+ project1 = create(:project)
+ project2 = create(:project)
+ todo = create(:todo, project: project1)
+
+ create(:todo, project: project2)
+
+ expect(described_class.for_project(project1)).to eq([todo])
+ end
+ end
+
+ describe '.for_group' do
+ it 'returns the todos for a given group' do
+ group1 = create(:group)
+ group2 = create(:group)
+ todo = create(:todo, group: group1)
+
+ create(:todo, group: group2)
+
+ expect(described_class.for_group(group1)).to eq([todo])
+ end
+ end
+
+ describe '.for_type' do
+ it 'returns the todos for a given target type' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_type(Issue)).to eq([todo])
+ end
+ end
+
+ describe '.for_target' do
+ it 'returns the todos for a given target' do
+ todo = create(:todo, target: create(:issue))
+
+ create(:todo, target: create(:merge_request))
+
+ expect(described_class.for_target(todo.target)).to eq([todo])
+ end
+ end
+
+ describe '.for_commit' do
+ it 'returns the todos for a commit ID' do
+ todo = create(:todo, commit_id: '123')
+
+ create(:todo, commit_id: '456')
+
+ expect(described_class.for_commit('123')).to eq([todo])
+ end
+ end
+
+ describe '.for_group_and_descendants' do
+ it 'returns the todos for a group and its descendants' do
+ parent_group = create(:group)
+ child_group = create(:group, parent: parent_group)
+
+ todo1 = create(:todo, group: parent_group)
+ todo2 = create(:todo, group: child_group)
+ todos = described_class.for_group_and_descendants(parent_group)
+
+ expect(todos).to include(todo1)
+
+ # Nested groups only work on PostgreSQL, so on MySQL todo2 won't be
+ # present.
+ expect(todos).to include(todo2) if Gitlab::Database.postgresql?
+ end
+ end
+
+ describe '.any_for_target?' do
+ it 'returns true if there are todos for a given target' do
+ todo = create(:todo)
+
+ expect(described_class.any_for_target?(todo.target)).to eq(true)
+ end
+
+ it 'returns false if there are no todos for a given target' do
+ issue = create(:issue)
+
+ expect(described_class.any_for_target?(issue)).to eq(false)
+ end
+ end
+
+ describe '.update_state' do
+ it 'updates the state of todos' do
+ todo = create(:todo, :pending)
+ ids = described_class.update_state(:done)
+
+ todo.reload
+
+ expect(ids).to eq([todo.id])
+ expect(todo.state).to eq('done')
+ end
+
+ it 'does not update todos that already have the given state' do
+ create(:todo, :pending)
+
+ expect(described_class.update_state(:pending)).to be_empty
+ end
+ end
end