require 'rails_helper' RSpec.describe Template, type: :model do context "validations" do it { is_expected.to validate_presence_of(:title) } it { is_expected.to allow_values(true, false).for(:published) } # This is currently being set in the defaults before validation # it { is_expected.not_to allow_value(nil).for(:published) } it { is_expected.to validate_presence_of(:org) } it { is_expected.to validate_presence_of(:locale) } it { is_expected.to allow_values(true, false).for(:is_default) } # This is currently being set in the defaults before validation # it { is_expected.not_to allow_value(nil).for(:is_default) } # This is currently being set in the defaults before validation # it { is_expected.to validate_presence_of(:version) } # This is currently being set in the defaults before validation # it { is_expected.to validate_presence_of(:visibility) } # This is currently being set in the defaults before validation # it { is_expected.to validate_presence_of(:family_id) } it { is_expected.to allow_values(true, false).for(:archived) } # This is currently being set in the defaults before validation # it { is_expected.not_to allow_value(nil).for(:archived) } end context "associations" do it { is_expected.to belong_to :org } it { is_expected.to have_many :plans } it { is_expected.to have_many :phases } it { is_expected.to have_many :sections } it { is_expected.to have_many :questions } it { is_expected.to have_many :annotations } end describe '.archived' do subject { Template.archived } context "when template is archived" do let!(:template) { create(:template, archived: true) } it { is_expected.to include(template) } end context "when template is archived" do let!(:template) { create(:template, archived: false) } it { is_expected.not_to include(template) } end end describe '.unarchived' do subject { Template.unarchived } context "when template is archived" do let!(:template) { create(:template, archived: true) } it { is_expected.not_to include(template) } end context "when template is archived" do let!(:template) { create(:template, archived: false) } it { is_expected.to include(template) } end end describe '.default' do subject { Template.default } context "when default published template exists" do before do @a = create(:template, :default, :published) @b = create(:template, :default, :published) end it "returns the latest record" do expect(subject).to eql(@b) end end context "when default template is not published" do before do create(:template, :default, :unpublished) end it "returns nil" do is_expected.to be_nil end end context "when none of the published templates are default" do before do create(:template, :published, is_default: false) end it "returns nil" do is_expected.to be_nil end end end describe ".latest_version" do let!(:family_id) { nil } subject { Template.latest_version(family_id) } it "returns an ActiveRecord::Relation" do expect(subject).to be_a(ActiveRecord::Relation) end context "when family_id is present" do let!(:family_id) { "1235" } let!(:template) do create(:template, :unpublished, family_id: "1235", version: 12) end before do create(:template, :unpublished, family_id: "1235", version: 11) create(:template, :unpublished, family_id: "9999", version: 13) end it "filters results by family_id" do expect(subject).to include(template) end end context "when family_id is absent" do let!(:family_id) { nil } let!(:template) { create(:template, :unpublished, version: 12) } before do create(:template, :unpublished, version: 11) create(:template, :unpublished, version: 10) end it "returns the " do expect(subject).to include(template) end end context "when template is archived" do let!(:family_id) { nil } let!(:template) { create(:template, :archived, :unpublished, version: 12) } before do @a = create(:template, :unpublished, version: 11) @b = create(:template, :unpublished, version: 10) end it "excludes from the results" do expect(subject).not_to include(template) end end end describe ".published" do subject { Template.published(family_id) } before do @a = create(:template, :published, family_id: family_id, version: 1) @b = create(:template, :published, version: 3) @c = create(:template, :unpublished, family_id: family_id, version: 2) @d = create(:template, :unpublished, version: 5) end context "when family_id is present" do let!(:family_id) { "1234" } it "includes records with family id" do expect(subject).to include(@a) end it "excludes records without family id" do expect(subject).not_to include(@b) end it "excludes unpublished records" do expect(subject).not_to include(@c) expect(subject).not_to include(@d) end end context "when family_id is absent" do let!(:family_id) { nil } it "includes all published records" do expect(subject).to include(@a) expect(subject).to include(@b) end it "excludes all published records" do expect(subject).not_to include(@c) expect(subject).not_to include(@d) end end end describe ".latest_customized_version" do let!(:original_template) { create(:template) } let!(:family_id) { original_template.family_id } let!(:org) { create(:org) } subject { Template.latest_customized_version(family_id, org.id) } context "when latest version present" do before do create(:template, customization_of: family_id, org: org, version: 5) create(:template, customization_of: family_id, org: org, version: 6) end it "returns a single record" do expect(subject).to be_one end it "returns the correct version" do expect(subject.last.version).to eql(6) end end context "when latest version absent" do before do create(:template, customization_of: "1234", org: org, version: 5) create(:template, customization_of: "1234", org: org, version: 6) end it "returns an empty set" do expect(subject).to be_empty end end context "when latest version belongs to different Org" do before do create(:template, customization_of: family_id, version: 5) create(:template, customization_of: family_id, version: 6) end it "returns an empty set" do expect(subject).to be_empty end end context "when latest version is archived" do before do create(:template, customization_of: family_id, org: org, version: 5) create(:template, :archived, customization_of: family_id, org: org, version: 6) end # TODO: Confirm: Is this the desired behaviour? it "returns an empty set" do expect(subject).to be_empty end end end describe ".latest_version_per_org" do context "when org_id is an Integer" do let!(:org) { create(:org) } subject { Template.latest_version_per_org(org.id) } before do @a = create(:template, org: org, version: 1, family_id: 123) @b = create(:template, org: org, version: 2, family_id: 123) @c = create(:template, org: org, version: 2, family_id: 456) @d = create(:template, org: org, version: 1, family_id: 456) end it { is_expected.not_to include(@a) } it { is_expected.to include(@b) } it { is_expected.to include(@c) } it { is_expected.not_to include(@d) } end context "when org_id is an Array" do let!(:org_a) { create(:org) } let!(:org_b) { create(:org) } subject { Template.latest_version_per_org([org_a.id, org_b.id]) } before do @a = create(:template, org: org_a, version: 1, family_id: 123) @b = create(:template, org: org_a, version: 2, family_id: 123) @c = create(:template, org: org_b, version: 2, family_id: 456) @d = create(:template, org: org_b, version: 1, family_id: 456) end it { is_expected.not_to include(@a) } it { is_expected.to include(@b) } it { is_expected.to include(@c) } it { is_expected.not_to include(@d) } end end describe ".latest_customized_version_per_org" do let!(:original_template) { create(:template) } let!(:family_id) { original_template.family_id } let!(:org) { create(:org) } subject { Template.latest_customized_version_per_org(org.id) } context "when latest version present" do before do create(:template, customization_of: family_id, org: org, version: 5) create(:template, customization_of: family_id, org: org, version: 6) end it "returns a single record" do expect(subject).to be_one end it "returns the correct version" do expect(subject.last.version).to eql(6) end end context "when latest version absent" do before do create(:template, customization_of: "1234", org: org, version: 5) create(:template, customization_of: "1234", org: org, version: 6) end it "returns an empty set" do expect(subject).to be_empty end end context "when latest version belongs to different Org" do before do create(:template, customization_of: family_id, version: 5) create(:template, customization_of: family_id, version: 6) end it "returns an empty set" do expect(subject).to be_empty end end context "when latest version is archived" do before do create(:template, customization_of: family_id, org: org, version: 5) create(:template, :archived, customization_of: family_id, org: org, version: 6) end # TODO: Confirm: Is this the desired behaviour? it "returns an empty set" do expect(subject).to be_empty end end end describe ".families" do context "when org_id is given" do let!(:org) { create(:org) } subject { Template.families([org.id]) } before do @a = create(:template, customization_of: nil) @b = create(:template, customization_of: nil, org: org) @c = create(:template, :archived, customization_of: nil, org: org) end it "excludes Templates belonging to other Orgs" do expect(subject).not_to include(@a) end it "includes Templates that are not customisations" do expect(subject).to include(@b) end it "excludes archived Templates" do expect(subject).not_to include(@c) end end context "when org_id is nil" do subject { Template.families(nil) } before do @a = create(:template, customization_of: nil) @b = create(:template, customization_of: "123") @c = create(:template, :archived, customization_of: nil) end it "includes Templates belonging to all Orgs" do expect(subject).to include(@a) end it "excludes Templates that are customizations" do expect(subject).not_to include(@b) end it "excludes archived Templates" do expect(subject).not_to include(@c) end end end describe ".latest_customizable" do before do create(:template, :default, :published) end let!(:template) { create(:template, :published, :publicly_visible, org: org) } subject { Template.latest_customizable } context "when Org is an institution" do let!(:org) { create(:org, :institution) } it { is_expected.not_to include(template) } end context "when Org is a funder" do let!(:org) { create(:org, :funder) } it { is_expected.to include(template) } end context "when Org is an organisation" do let!(:org) { create(:org, :organisation) } it { is_expected.not_to include(template) } end context "when Org is a research_institute" do let!(:org) { create(:org, :research_institute) } it { is_expected.not_to include(template) } end context "when Org is a project" do let!(:org) { create(:org, :project) } it { is_expected.not_to include(template) } end context "when Org is a school" do let!(:org) { create(:org, :school) } it { is_expected.not_to include(template) } end context "when template is default and published" do let!(:template) { create(:template, :default, :published) } it { is_expected.to include(template) } end context "when template is default and unpublished" do let!(:template) { create(:template, :default, :unpublished) } it { is_expected.not_to include(template) } end end describe ".publicly_visible" do subject { Template.publicly_visible } before do @a = create(:template, :archived, :publicly_visible) @b = create(:template, :publicly_visible) @c = create(:template, :organisationally_visible) end it "excludes archived Templates" do # The enum is currently overwriting this scope expect(subject).not_to include(@a) end it "includes publicly_visible Templates" do expect(subject).to include(@b) end it "excludes organisationally_visible Templates" do expect(subject).not_to include(@c) end end describe ".organisationally_visible" do subject { Template.organisationally_visible } before do @a = create(:template, :archived, :organisationally_visible) @b = create(:template, :publicly_visible) @c = create(:template, :organisationally_visible) end it "excludes archived Templates" do # The enum is currently overwriting this scope expect(subject).not_to include(@a) end it "excludes publicly_visible Templates" do expect(subject).not_to include(@b) end it "includes organisationally_visible Templates" do expect(subject).to include(@c) end end describe ".search" do subject { Template.search("foo") } before do @a = create(:template, :archived, title: "foo bar") @b = create(:template, title: "foo bar") @c = create(:template, description: "<p>foo bar</p>") @d = create(:template, org: create(:org, name: "foo org")) end it "excludes archived Templates" do expect(subject).not_to include(@a) end it "includes Templates with a matching title" do expect(subject).to include(@b) end it "excludes Templates with a matching description" do expect(subject).not_to include(@c) end it "includes Templates with a matching Org name" do expect(subject).to include(@d) end end describe ".current" do subject { Template.current("1234") } before do @a = create(:template, :archived, family_id: "1234", version: 5) @b = create(:template, family_id: "5555", version: 2) @c = create(:template, family_id: "1234", version: 2) end it "excludes archived Templates" do expect(subject).not_to eql(@a) end it "excludes Templates with a different family_id" do expect(subject).not_to eql(@b) end it "orders results by DESC version" do expect(subject).to eql(@c) end end describe ".live" do context "when family ID is an Array" do subject { Template.live([1234, 1235]) } before do @a = create(:template, :archived, :published, family_id: 1234) @b = create(:template, :published, family_id: 1234) @c = create(:template, :unpublished, family_id: 1234) @d = create(:template, :published, family_id: 1235) end it "returns an enumerable" do expect(subject).to be_a(ActiveRecord::Relation) end it "excludes archived Templates" do expect(subject).not_to include(@a) end it "includes published Templates" do expect(subject).to include(@b) end it "excludes unpublished Templates" do expect(subject).not_to include(@c) end it "includes published Templates" do expect(subject).to include(@d) end end context "when family ID is an Integer" do subject { Template.live(1234) } before do @a = create(:template, :archived, :published, family_id: 1234) @b = create(:template, :published, family_id: 1234) @c = create(:template, :unpublished, family_id: 1234) @d = create(:template, :published, family_id: 1235) end it "excludes archived Templates" do expect(subject).not_to eql(@a) end it "includes published Templates" do expect(subject).to eql(@b) end it "excludes unpublished Templates" do expect(subject).not_to eql(@c) end it "excludes published Templates with other family_id" do expect(subject).not_to eql(@d) end end end describe ".find_or_generate_version!" do subject { Template.find_or_generate_version!(template) } context "when template is not latest?" do let!(:template) { create(:template) } before do template.expects(:latest?).at_least(1).returns(false) end it "raises an error" do expect { subject }.to raise_error(StandardError) end end context "when template is latest and generate_version? is true" do let!(:template) { create(:template, :published) } before do template.expects(:latest?).at_least(1).returns(true) end it "returns a different Template" do expect(subject).not_to eql(template) end it "creates a persisted Template" do expect(subject).to be_persisted end end context "when template is latest and generate_version? is false" do let!(:template) { create(:template, :unpublished) } before do template.expects(:latest?).at_least(1).returns(true) end it "returns the same Template" do expect(subject).to eql(template) end end end describe "#deep_copy" do context "when attributes is provided" do let!(:template) { create(:template, :published, phases: 2) } subject do template.deep_copy(attributes: { title: "foo", description: "bar" }) end it "updates title with the provided value" do expect(subject.title).to eql("foo") end it "updates describe with the provided value" do expect(subject.description).to eql("bar") end end context "when options save is true" do let!(:template) { create(:template, :published, phases: 2) } subject { template.deep_copy(attributes: { family_id: 123 }, save: true) } it "returns a persisted record" do expect(subject).to be_persisted end it "creates phases" do expect(subject.phases).to be_many end it "sets template_id on phases to the new template" do new_temp = subject expect(new_temp.phases.map(&:template_id).uniq).to eql([new_temp.id]) end end context "when options save is false" do let!(:template) { create(:template, :published, phases: 2) } subject { template.deep_copy(attributes: { family_id: 123 }, save: false) } it "returns a new record" do expect(subject).to be_new_record end it "builds phases" do expect(subject.phases).to be_many end it "doesn't set template_id on phases" do expect(subject.phases.map(&:template_id).compact).to be_empty end end end describe "#base_org" do subject { template.base_org } context "when customization_of is present" do let!(:source_template) { create(:template) } let!(:template) do create(:template, customization_of: source_template.family_id) end it "returns the source Template org" do expect(subject).to eql(source_template.org) end end context "when customization_of is not present" do let!(:template) { create(:template, customization_of: nil) } it "returns the base Template org" do expect(subject).to eql(template.org) end end end describe "#latest?" do context "when Template is the latest in its family" do let!(:template) do create(:template, :published, version: 5, family_id: 123) end it "returns true" do expect(template).to be_latest end end context "when Template is not the latest in its family" do let!(:template) do create(:template, :published, version: 5, family_id: 123) end before do create(:template, :published, version: 6, family_id: 123) end it "returns false" do expect(template).not_to be_latest end end end describe "#generate_version?" do let!(:template) { build(:template) } subject { template.generate_version? } context "when published is true" do before do template.published = true end it { is_expected.to eql(true) } end context "when published is false" do before do template.published = false end it { is_expected.to eql(false) } end end describe "#customize?" do subject { template.customize?(org) } context "when param is Org, org is funder, customization doesn't exist" do let!(:org) { create(:org, :funder) } let!(:template) { create(:template, org: org) } it { is_expected.to eql(true) } end context "when param is Org, template default, customization doesn't exist" do let!(:org) { create(:org) } let!(:template) { create(:template, :default, org: org) } it { is_expected.to eql(true) } end context "when param is Org, org is funder, customization exists" do let!(:org) { create(:org, :funder) } let!(:template) { create(:template, org: org) } before do create(:template, customization_of: template.family_id, org: org) end it { is_expected.to eql(false) } end context "when param is Org, template default, customization exists" do let!(:org) { create(:org) } let!(:template) { create(:template, :default, org: org) } before do create(:template, customization_of: template.family_id, org: org) end it { is_expected.to eql(false) } end context "when param not Org" do let!(:org) { stub(:banana) } let!(:template) { create(:template, org: create(:org, :funder)) } it { is_expected.to eql(false) } end end describe "#upgrade_customization?" do let!(:org) { create(:org, :funder) } context "when not a customization of another template" do let!(:template) do create(:template, :published, customization_of: nil, org: org) end it { expect(template).not_to be_upgrade_customization } end context "when customization of another template and source is newer" do let!(:source) do create(:template, :published, family_id: 123, org: org, created_at: 1.minutes.from_now) end let!(:template) do create(:template, :published, customization_of: source.family_id) end it { expect(template).to be_upgrade_customization } end context "when customization of another template and source is older" do let!(:source) do create(:template, :published, family_id: 123, org: org, created_at: 1.minutes.ago) end let!(:template) do create(:template, :published, customization_of: source.family_id) end it { expect(template).not_to be_upgrade_customization } end end describe "#draft?" do subject { template.draft? } context "when published and family has published template" do let!(:template) { create(:template, :published) } before do create(:template, :published, family_id: template.family_id) end it { is_expected.to eql(false) } end context "when published and family has no published template" do let!(:template) { create(:template, :published) } before do create(:template, :unpublished, family_id: template.family_id) end it { is_expected.to eql(false) } end context "when unpublished and family has published template" do let!(:template) { create(:template, :unpublished) } before do create(:template, :published, family_id: template.family_id) end it { is_expected.to eql(true) } end context "when unpublished and family has no published template" do let!(:template) { create(:template, :unpublished) } before do create(:template, :unpublished, family_id: template.family_id) end it { is_expected.to eql(false) } end end describe "#removable?" do let!(:template) { create(:template) } context "when there are no Plans using this Template" do it { expect(template).to be_removable } end context "when there Plans using this Template" do before do create(:plan, template: template) end it { expect(template).not_to be_removable } end context "when there are Plans, but using different Templates" do before do create(:plan) end it { expect(template).to be_removable } end end describe "#generate_copy!" do subject { template.generate_copy!(org) } let!(:template) { create(:template) } context "when org is not an Org" do let!(:org) { stub(:banana) } it "raises a StandardError" do expect { subject }.to raise_error(StandardError) end end context "when org is an Org record" do let!(:org) { create(:org) } it "sets the version to zero" do expect(subject.version).to be_zero end it "sets published to false" do expect(subject.published).to eql(false) end it "sets family_id to a new integer" do expect(subject.family_id).to be_a(Integer) end it "sets org to new org" do expect(subject.org).to eql(org) end it "sets is_default to false" do expect(subject.is_default).to eql(false) end it "sets title to include 'Copy of'" do expect(subject.title).to include('Copy of') end it "sets title to include original title" do expect(subject.title).to include(template.title) end end end describe "#generate_version!" do subject { template.generate_version! } context "when template is not published" do let!(:template) { create(:template, :unpublished) } it "raises a StandardError" do expect { subject }.to raise_error(StandardError) end end context "when Template is published" do let!(:template) do create(:template, :published, version: 4) end it "sets the version to current version plus one" do expect(subject.version).to eql(5) end it "sets published to false" do expect(subject.published).to eql(false) end it "sets org to new org" do expect(subject.org).to eql(template.org) end end end describe "#customize!" do subject { template.customize!(org) } # Added an additional type to Org so that funder_only? fails let!(:org) { create(:org, :funder, :organisation) } let!(:template) { create(:template, :default, org: org) } it "sets the version to 0" do expect(subject.version).to be_zero end it "sets the published to false" do expect(subject.published).to eql(false) end it "sets the family_id to a new Integer" do expect(subject.family_id).not_to eql(template.family_id) end it "sets customization of to Template's family_id" do expect(subject.customization_of).to eql(template.family_id) end it "sets org to Template's org" do expect(subject.org).to eql(template.org) end it "sets visibility to Organisationally visible" do expect(subject.visibility).to eql("organisationally_visible") end it "sets is_default to false" do expect(subject.is_default).to eql(false) end context "when org is not an Org" do let!(:org) { stubs(:org) } let!(:template) { create(:template) } it "raises an exception" do expect { subject }.to raise_error(StandardError) end end context "when org is not funder only" do let!(:org) { create(:org, :school) } let!(:template) { create(:template, org: org) } it "raises an exception" do expect { subject }.to raise_error(StandardError) end end end describe "#upgrade_customization!" do subject { template.upgrade_customization! } let!(:source) do create(:template, :published, phases: 3) end let!(:template) do create(:template, :published, version: 5, customization_of: source.family_id) end it "returns a Template" do expect(subject).to be_a(Template) end it "returns a persisted Template" do expect(subject).to be_persisted end it "sets the version to template version plus one" do expect(subject.version).to eql(6) end it "sets the published to false" do expect(subject.published).to eql(false) end it "sets the family_id to template's family ID" do expect(subject.family_id).to eql(template.family_id) end it "sets the customization_of to template's customization_of" do expect(subject.customization_of).to eql(template.customization_of) end it "sets the org to Template's org" do expect(subject.org).to eql(template.org) end it "sets the visibility to Organisationally visible" do expect(subject.visibility).to eql("organisationally_visible") end it "sets is_default to false" do expect(subject.is_default).to eql(false) end it "sets phases modifiable to false" do subject.phases.each do |phase| expect(phase.modifiable).to eql(false) end end context "when customization_of is blank" do let!(:template) { create(:template, customization_of: nil) } it "raises an error" do expect { subject }.to raise_error(StandardError) end end context "when source Template is not present" do let!(:template) do create(:template, :published, customization_of: 456) end it "raises an error" do expect { subject }.to raise_error(StandardError) end end end describe "#publishability" do subject { template.publishability } # Added an additional type to Org so that funder_only? fails let!(:org) { create(:org, :funder, :organisation) } #let!(:template) { create(:template, :default, org: org) } # case when template is correctly generated context "When the Template has all components, is latest, and unpublished" do let(:template) { create(:template, :default, published: false, org: org, phases: 1, sections: 1, questions: 1)} it "returns true" do expect(subject[0]).to eql(true) end it "has no errors" do expect(subject[1]).to eql("") end end # case when the template is historical context "When the template is an historical version" do let(:template) { create(:template, :default, published: true, org: org, phases: 3, version: 1, sections: 1, questions: 1)} before do template.generate_version! template.update_column(:published, false) end it "returns false" do expect(subject[0]).to eql(false) end it "has error_message" do expect(subject[1]).to include("You can not publish a historical version of this template. ") end end # case when the template is published context "When the Template has all components, is latest, and already published" do let(:template) { create(:template, :default, published: true, org: org, phases: 1, sections: 1, questions: 1)} it "is not publishable" do expect(subject[0]).to eql(false) end it "has error_message" do expect(subject[1]).to include("You can not publish a published template.") end end # case when template has no phases context "When the Template has no phases" do let(:template) { create(:template, :default, published: true, org: org, phases: 0)} it "is not publishable" do expect(subject[0]).to eql(false) end it "has error_message" do expect(subject[1]).to include("You can not publish a template without phases") end end # case when a template has no sections context "When the Template has no sections" do let(:template) { create(:template, :default, published: true, org: org, phases: 1, sections: 0)} it "is not publishable" do expect(subject[0]).to eql(false) end it "has error_message" do expect(subject[1]).to include("You can not publish a template without sections in a phase") end end # case when a section has no questions context "When the Template has no questions" do let(:template) { create(:template, :default, published: true, org: org, phases: 1, sections: 1, questions: 0)} it "is not publishable" do expect(subject[0]).to eql(false) end it "has error_message" do expect(subject[1]).to include("You can not publish a template without questions in a section") end end end end