try refiniments for constructing formulas in specs

This commit is contained in:
Ivan Kuchin
2025-08-22 20:15:43 +02:00
parent d855def3ba
commit 997b740cfc
4 changed files with 83 additions and 41 deletions
@@ -31,6 +31,8 @@
require "spec_helper"
RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_value_project_attribute: true } do
using CustomFieldFormulaReferencing
let(:model_class) do
Class.new do
include ActsAsCustomizable::CalculatedValue
@@ -47,8 +49,6 @@ RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_valu
allow(instance).to receive(:custom_field_values=)
end
def ref(custom_field) = "{{cf_#{custom_field.id}}}"
describe "#calculate_custom_fields" do
context "when calling with empty array" do
let(:custom_field_values) { [:foo] }
@@ -143,8 +143,8 @@ RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_valu
let(:cf_a) { build_stubbed(:integer_project_custom_field) }
let(:cf_b) { build_stubbed(:integer_project_custom_field) }
let(:cf1) { build_stubbed(:calculated_value_project_custom_field, formula: "#{ref cf_a} + #{ref cf_b}") }
let(:cf2) { build_stubbed(:calculated_value_project_custom_field, formula: "#{ref cf_a} * #{ref cf_b}") }
let(:cf1) { build_stubbed(:calculated_value_project_custom_field, formula: "#{cf_a} + #{cf_b}") }
let(:cf2) { build_stubbed(:calculated_value_project_custom_field, formula: "#{cf_a} * #{cf_b}") }
let(:custom_field_values) do
{
@@ -168,7 +168,7 @@ RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_valu
context "when calling with custom fields referencing other calculated fields" do
let(:cf1) { build_stubbed(:calculated_value_project_custom_field, formula: "1 + 1") }
let(:cf2) { build_stubbed(:calculated_value_project_custom_field, formula: "1 + 2") }
let(:cf3) { build_stubbed(:calculated_value_project_custom_field, formula: "#{ref cf1} * #{ref cf2}") }
let(:cf3) { build_stubbed(:calculated_value_project_custom_field, formula: "#{cf1} * #{cf2}") }
let(:custom_field_values) do
{
@@ -206,8 +206,8 @@ RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_valu
let(:cf_missing) { build_stubbed(:integer_project_custom_field) }
let(:cf_unavailable) { build_stubbed(:integer_project_custom_field) }
let(:cf_using_missing) { build_stubbed(:calculated_value_project_custom_field, formula: "1 + #{ref cf_missing}") }
let(:cf_using_unavailable) { build_stubbed(:calculated_value_project_custom_field, formula: "2 + #{ref cf_unavailable}") }
let(:cf_using_missing) { build_stubbed(:calculated_value_project_custom_field, formula: "1 + #{cf_missing}") }
let(:cf_using_unavailable) { build_stubbed(:calculated_value_project_custom_field, formula: "2 + #{cf_unavailable}") }
let(:cf_other) { build_stubbed(:calculated_value_project_custom_field, formula: "1 + 2") }
let(:custom_field_values) do
@@ -252,10 +252,10 @@ RSpec.describe ActsAsCustomizable::CalculatedValue, with_flag: { calculated_valu
before do
{
cf1 => "#{ref cf_a} * #{ref cf2}",
cf2 => "#{ref cf_b} * #{ref cf3}",
cf3 => "#{ref cf1} * #{ref cf4}",
cf4 => "#{ref cf_c} * #{ref cf_d}"
cf1 => "#{cf_a} * #{cf2}",
cf2 => "#{cf_b} * #{cf3}",
cf3 => "#{cf1} * #{cf4}",
cf4 => "#{cf_c} * #{cf_d}"
}.each do |cf, formula|
cf.formula = formula
end
@@ -31,17 +31,17 @@
require "spec_helper"
RSpec.describe CustomField::CalculatedValue, with_flag: { calculated_value_project_attribute: true } do
subject(:custom_field) { create(:calculated_value_project_custom_field, formula: "1 + 1") }
using CustomFieldFormulaReferencing
def ref(custom_field) = "{{cf_#{custom_field.id}}}"
subject(:custom_field) { create(:calculated_value_project_custom_field, formula: "1 + 1") }
describe ".with_formula_referencing", :aggregate_failures do
shared_let(:cf_a) { create(:integer_project_custom_field, default_value: 1) }
shared_let(:cf_b) { create(:integer_project_custom_field, default_value: 2) }
shared_let(:cf_c) { create(:integer_project_custom_field, default_value: 3) }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_a} + #{ref cf_b}") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_b} + #{ref cf1}") }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_a} + #{cf_b}") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_b} + #{cf1}") }
let(:scope) { CustomField }
@@ -70,10 +70,10 @@ RSpec.describe CustomField::CalculatedValue, with_flag: { calculated_value_proje
shared_let(:cf_b) { create(:integer_project_custom_field, default_value: 2) }
shared_let(:cf_c) { create(:integer_project_custom_field, default_value: 3) }
shared_let(:cf_d) { create(:integer_project_custom_field, default_value: 4) }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_a} * 2") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_a} + #{ref cf_b}") }
shared_let(:cf3) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_b} * 2") }
shared_let(:cf4) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_c} * 2") }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_a} * 2") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_a} + #{cf_b}") }
shared_let(:cf3) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_b} * 2") }
shared_let(:cf4) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_c} * 2") }
it "returns an empty array if argument is empty" do
expect(scope.affected_calculated_fields([])).to eq([])
@@ -141,9 +141,9 @@ RSpec.describe CustomField::CalculatedValue, with_flag: { calculated_value_proje
shared_let(:cf_c) { create(:integer_project_custom_field, default_value: 3) }
shared_let(:cf_d) { create(:integer_project_custom_field, default_value: 4) }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf_a} + #{ref cf_b}") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf1} * #{ref cf_c}") }
shared_let(:cf3) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{ref cf2} * #{ref cf_d}") }
shared_let(:cf1) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf_a} + #{cf_b}") }
shared_let(:cf2) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf1} * #{cf_c}") }
shared_let(:cf3) { create(:calculated_value_project_custom_field, :skip_validations, formula: "#{cf2} * #{cf_d}") }
it "returns referencing fields when passing referenced constant field ids" do
expect(scope.affected_calculated_fields([cf_a.id])).to contain_exactly(cf1, cf2, cf3)
@@ -193,9 +193,9 @@ RSpec.describe CustomField::CalculatedValue, with_flag: { calculated_value_proje
before do
{
cf1 => "#{ref cf_a} + #{ref cf2}",
cf2 => "#{ref cf_b} + #{ref cf3}",
cf3 => "#{ref cf_c} + #{ref cf1}"
cf1 => "#{cf_a} + #{cf2}",
cf2 => "#{cf_b} + #{cf3}",
cf3 => "#{cf_c} + #{cf1}"
}.each do |cf, formula|
cf.formula = formula
cf.save!(validate: false)
@@ -322,7 +322,7 @@ RSpec.describe Projects::SetAttributesService, type: :model do
end
end
def ref(custom_field) = "{{cf_#{custom_field.id}}}"
using CustomFieldFormulaReferencing
context "when trying to explicitly set values of calculated custom fields" do
let!(:cf_static) { create(:integer_project_custom_field, projects: [project]) }
@@ -355,12 +355,12 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_calculated1) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_static} * 7")
formula: "#{cf_static} * 7")
end
let!(:cf_calculated2) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_calculated1} * 11")
formula: "#{cf_calculated1} * 11")
end
let(:call_attributes) do
@@ -391,12 +391,12 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_calculated1) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_static} * 7")
formula: "#{cf_static} * 7")
end
let!(:cf_calculated2) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_calculated1} * 11")
formula: "#{cf_calculated1} * 11")
end
let(:call_attributes) do
@@ -428,15 +428,15 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_c) { create(:integer_project_custom_field, projects: [project]) }
let!(:cf_calculated1) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project], formula: "#{ref cf_a} * 7")
projects: [project], formula: "#{cf_a} * 7")
end
let!(:cf_calculated2) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project], formula: "#{ref cf_b} * 11")
projects: [project], formula: "#{cf_b} * 11")
end
let!(:cf_calculated3) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project], formula: "#{ref cf_c} * 13")
projects: [project], formula: "#{cf_c} * 13")
end
let(:call_attributes) do
@@ -474,16 +474,16 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_calculated1) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_static} * 7")
formula: "#{cf_static} * 7")
end
let!(:cf_calculated2) do
create(:calculated_value_project_custom_field, :skip_validations,
formula: "#{ref cf_calculated1} * 11")
formula: "#{cf_calculated1} * 11")
end
let!(:cf_calculated3) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_calculated2} * 13")
formula: "#{cf_calculated2} * 13")
end
let(:call_attributes) do
@@ -522,17 +522,17 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_calculated1) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_static} * 7")
formula: "#{cf_static} * 7")
end
let!(:cf_calculated2) do
create(:calculated_value_project_custom_field, :skip_validations, :admin_only,
projects: [project],
formula: "#{ref cf_calculated1} * 11")
formula: "#{cf_calculated1} * 11")
end
let!(:cf_calculated3) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_calculated2} * 13")
formula: "#{cf_calculated2} * 13")
end
let(:call_attributes) do
@@ -565,7 +565,7 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_calculated) do
create(:calculated_value_project_custom_field, :skip_validations,
projects: [project],
formula: "#{ref cf_static} * #{ref cf_referenced}")
formula: "#{cf_static} * #{cf_referenced}")
end
let(:call_attributes) do
@@ -632,7 +632,7 @@ RSpec.describe Projects::SetAttributesService, type: :model do
let!(:cf_referenced) do
create(:calculated_value_project_custom_field, :skip_validations, :admin_only,
projects: [project],
formula: "21 * #{ref cf_referenced1}")
formula: "21 * #{cf_referenced1}")
end
before do
@@ -0,0 +1,42 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
# Allows cleaner building of calculated value formulas in specs:
#
# using CustomFieldFormulaReferencing
#
# create(:calculated_value_project_custom_field, formula: "#{cf_1} * #{cf_2} + #{cf_3}")
module CustomFieldFormulaReferencing
refine CustomField do
def to_s
"{{cf_#{id}}}"
end
end
end