diff options
Diffstat (limited to 'spec/models/ml/candidate_spec.rb')
-rw-r--r-- | spec/models/ml/candidate_spec.rb | 127 |
1 files changed, 90 insertions, 37 deletions
diff --git a/spec/models/ml/candidate_spec.rb b/spec/models/ml/candidate_spec.rb index 374e49aea01..fa19b723ee2 100644 --- a/spec/models/ml/candidate_spec.rb +++ b/spec/models/ml/candidate_spec.rb @@ -3,48 +3,74 @@ require 'spec_helper' RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops do - let_it_be(:candidate) { create(:ml_candidates, :with_metrics_and_params, name: 'candidate0') } + let_it_be(:candidate) { create(:ml_candidates, :with_metrics_and_params, :with_artifact, name: 'candidate0') } let_it_be(:candidate2) do create(:ml_candidates, experiment: candidate.experiment, user: create(:user), name: 'candidate2') end - let_it_be(:candidate_artifact) do - FactoryBot.create(:generic_package, - name: candidate.package_name, - version: candidate.package_version, - project: candidate.project) - end - - let(:project) { candidate.experiment.project } + let(:project) { candidate.project } describe 'associations' do it { is_expected.to belong_to(:experiment) } + it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:package) } + it { is_expected.to belong_to(:ci_build).class_name('Ci::Build') } it { is_expected.to have_many(:params) } it { is_expected.to have_many(:metrics) } it { is_expected.to have_many(:metadata) } end + describe 'modules' do + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :internal_id } + let(:instance) { build(:ml_candidates, experiment: candidate.experiment) } + let(:scope) { :project } + let(:scope_attrs) { { project: instance.project } } + let(:usage) { :ml_candidates } + end + end + describe 'default values' do - it { expect(described_class.new.iid).to be_present } + it { expect(described_class.new.eid).to be_present } end - describe '.artifact_root' do - subject { candidate.artifact_root } + describe '.destroy' do + let_it_be(:candidate_to_destroy) do + create(:ml_candidates, :with_metrics_and_params, :with_metadata, :with_artifact) + end - it { is_expected.to eq("/ml_candidate_#{candidate.id}/-/") } + it 'destroys metrics, params and metadata, but not the artifact', :aggregate_failures do + expect { candidate_to_destroy.destroy! } + .to change { Ml::CandidateMetadata.count }.by(-2) + .and change { Ml::CandidateParam.count }.by(-2) + .and change { Ml::CandidateMetric.count }.by(-2) + .and not_change { Packages::Package.count } + end end - describe '.package_name' do - subject { candidate.package_name } + describe '.artifact_root' do + subject { candidate.artifact_root } - it { is_expected.to eq("ml_candidate_#{candidate.id}") } + it { is_expected.to eq("/#{candidate.package_name}/#{candidate.iid}/") } end describe '.package_version' do subject { candidate.package_version } - it { is_expected.to eq('-') } + it { is_expected.to eq(candidate.iid) } + end + + describe '.eid' do + let_it_be(:eid) { SecureRandom.uuid } + + let_it_be(:candidate3) do + build(:ml_candidates, :with_metrics_and_params, name: 'candidate0', eid: eid) + end + + subject { candidate3.eid } + + it { is_expected.to eq(eid) } end describe '.artifact' do @@ -52,10 +78,6 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d subject { tested_candidate.artifact } - before do - candidate_artifact - end - context 'when has logged artifacts' do it 'returns the package' do expect(subject.name).to eq(tested_candidate.package_name) @@ -69,21 +91,26 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d end end - describe '.artifact_lazy' do - context 'when candidates have same the same iid' do - before do - BatchLoader::Executor.clear_current - end + describe '#by_project_id_and_eid' do + let(:project_id) { candidate.experiment.project_id } + let(:eid) { candidate.eid } - it 'loads the correct artifacts', :aggregate_failures do - candidate.artifact_lazy - candidate2.artifact_lazy + subject { described_class.with_project_id_and_eid(project_id, eid) } - expect(Packages::Package).to receive(:joins).once.and_call_original # Only one database call + context 'when eid exists', 'and belongs to project' do + it { is_expected.to eq(candidate) } + end - expect(candidate.artifact.name).to eq(candidate.package_name) - expect(candidate2.artifact).to be_nil - end + context 'when eid exists', 'and does not belong to project' do + let(:project_id) { non_existing_record_id } + + it { is_expected.to be_nil } + end + + context 'when eid does not exist' do + let(:eid) { 'a' } + + it { is_expected.to be_nil } end end @@ -93,18 +120,18 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d subject { described_class.with_project_id_and_iid(project_id, iid) } - context 'when iid exists', 'and belongs to project' do + context 'when internal_id exists', 'and belongs to project' do it { is_expected.to eq(candidate) } end - context 'when iid exists', 'and does not belong to project' do + context 'when internal_id exists', 'and does not belong to project' do let(:project_id) { non_existing_record_id } it { is_expected.to be_nil } end - context 'when iid does not exist' do - let(:iid) { 'a' } + context 'when internal_id does not exist' do + let(:iid) { non_existing_record_id } it { is_expected.to be_nil } end @@ -130,6 +157,9 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d expect(subject.association_cached?(:latest_metrics)).to be(true) expect(subject.association_cached?(:params)).to be(true) expect(subject.association_cached?(:user)).to be(true) + expect(subject.association_cached?(:project)).to be(true) + expect(subject.association_cached?(:package)).to be(true) + expect(subject.association_cached?(:ci_build)).to be(true) end end @@ -161,6 +191,22 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d end end + describe 'from_ci?' do + subject { candidate } + + it 'is false if candidate does not have ci_build_id' do + allow(candidate).to receive(:ci_build_id).and_return(nil) + + is_expected.not_to be_from_ci + end + + it 'is true if candidate does has ci_build_id' do + allow(candidate).to receive(:ci_build_id).and_return(1) + + is_expected.to be_from_ci + end + end + describe '#order_by_metric' do let_it_be(:auc_metrics) do create(:ml_candidate_metrics, name: 'auc', value: 0.4, candidate: candidate) @@ -183,4 +229,11 @@ RSpec.describe Ml::Candidate, factory_default: :keep, feature_category: :mlops d end end end + + context 'with loose foreign key on ml_candidates.ci_build_id' do + it_behaves_like 'cleanup by a loose foreign key' do + let!(:parent) { create(:ci_build) } + let!(:model) { create(:ml_candidates, ci_build: parent) } + end + end end |