195 lines
5.5 KiB
Ruby
195 lines
5.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Ml::Experiment, feature_category: :mlops do
|
|
let_it_be(:exp) { create(:ml_experiments) }
|
|
let_it_be(:exp2) { create(:ml_experiments, project: exp.project) }
|
|
let_it_be(:model) { create(:ml_models, project: exp.project) }
|
|
let_it_be(:model_experiment) { model.default_experiment }
|
|
|
|
let(:iid) { exp.iid }
|
|
let(:exp_name) { exp.name }
|
|
|
|
describe 'associations' do
|
|
it { is_expected.to belong_to(:project) }
|
|
it { is_expected.to belong_to(:user) }
|
|
it { is_expected.to have_many(:candidates) }
|
|
it { is_expected.to have_many(:metadata) }
|
|
it { is_expected.to belong_to(:model).class_name('Ml::Model') }
|
|
end
|
|
|
|
describe '#destroy' do
|
|
it 'allow experiment without model to be destroyed' do
|
|
experiment = create(:ml_experiments, project: exp.project)
|
|
|
|
expect { experiment.destroy! }.to change { Ml::Experiment.count }.by(-1)
|
|
end
|
|
|
|
it 'throws error when destroying experiment with model' do
|
|
experiment = create(:ml_models, project: exp.project).default_experiment
|
|
|
|
expect { experiment.destroy! }.to raise_error(ActiveRecord::ActiveRecordError)
|
|
expect(experiment.errors.full_messages).to include('Cannot delete an experiment associated to a model')
|
|
end
|
|
end
|
|
|
|
describe '.package_name' do
|
|
it { expect(exp.package_name).to eq("ml_experiment_#{exp.iid}") }
|
|
|
|
context 'when model belongs to package' do
|
|
it 'is the model name' do
|
|
expect(model_experiment.package_name).to eq(model.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.for_model?' do
|
|
it 'is false if it is not the default experiment for a model' do
|
|
expect(exp.for_model?).to be(false)
|
|
end
|
|
|
|
it 'is true if it is not the default experiment for a model' do
|
|
expect(model_experiment.for_model?).to be(true)
|
|
end
|
|
end
|
|
|
|
describe '.by_project' do
|
|
subject { described_class.by_project(exp.project) }
|
|
|
|
it { is_expected.to match_array([exp, exp2, model_experiment]) }
|
|
end
|
|
|
|
describe '.including_project' do
|
|
subject { described_class.including_project }
|
|
|
|
it 'loads latest version' do
|
|
expect(subject.first.association_cached?(:project)).to be(true)
|
|
end
|
|
end
|
|
|
|
describe '.including_user' do
|
|
subject { described_class.including_user }
|
|
|
|
it 'loads latest version' do
|
|
expect(subject.first.association_cached?(:user)).to be(true)
|
|
end
|
|
end
|
|
|
|
describe '#by_project_id_and_iid' do
|
|
subject { described_class.by_project_id_and_iid(exp.project_id, iid) }
|
|
|
|
context 'if exists' do
|
|
it { is_expected.to eq(exp) }
|
|
end
|
|
|
|
context 'if does not exist' do
|
|
let(:iid) { non_existing_record_id }
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
|
|
describe '#by_project_id_and_name' do
|
|
subject { described_class.by_project_id_and_name(exp.project_id, exp_name) }
|
|
|
|
context 'if exists' do
|
|
it { is_expected.to eq(exp) }
|
|
end
|
|
|
|
context 'if does not exist' do
|
|
let(:exp_name) { 'hello' }
|
|
|
|
it { is_expected.to be_nil }
|
|
end
|
|
end
|
|
|
|
describe '.find_or_create' do
|
|
let(:name) { exp.name }
|
|
let(:project) { exp.project }
|
|
|
|
subject(:find_or_create) { described_class.find_or_create(project, name, exp.user) }
|
|
|
|
context 'when experiments exists' do
|
|
it 'fetches existing experiment', :aggregate_failures do
|
|
expect { find_or_create }.not_to change { Ml::Experiment.count }
|
|
|
|
expect(find_or_create).to eq(exp)
|
|
end
|
|
end
|
|
|
|
context 'when experiments does not exist' do
|
|
let(:name) { 'a new experiment' }
|
|
|
|
it 'creates the experiment', :aggregate_failures do
|
|
expect { find_or_create }.to change { Ml::Experiment.count }.by(1)
|
|
|
|
expect(find_or_create.name).to eq(name)
|
|
expect(find_or_create.user).to eq(exp.user)
|
|
expect(find_or_create.project).to eq(project)
|
|
end
|
|
end
|
|
|
|
context 'when experiment name exists but project is different' do
|
|
let(:project) { create(:project) }
|
|
|
|
it 'creates a model', :aggregate_failures do
|
|
expect { find_or_create }.to change { Ml::Experiment.count }.by(1)
|
|
|
|
expect(find_or_create.name).to eq(name)
|
|
expect(find_or_create.user).to eq(exp.user)
|
|
expect(find_or_create.project).to eq(project)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#with_candidate_count' do
|
|
let_it_be(:exp3) do
|
|
create(:ml_experiments, project: exp.project).tap do |e|
|
|
create_list(:ml_candidates, 3, experiment: e, user: nil)
|
|
create(:ml_candidates, experiment: exp2, user: nil)
|
|
end
|
|
end
|
|
|
|
subject { described_class.with_candidate_count.to_h { |e| [e.id, e.candidate_count] } }
|
|
|
|
it 'fetches the candidate count', :aggregate_failures do
|
|
expect(subject[exp.id]).to eq(0)
|
|
expect(subject[exp2.id]).to eq(1)
|
|
expect(subject[exp3.id]).to eq(3)
|
|
end
|
|
end
|
|
|
|
describe '#package_for_experiment?' do
|
|
using RSpec::Parameterized::TableSyntax
|
|
|
|
subject { described_class.package_for_experiment?(package_name) }
|
|
|
|
where(:package_name, :id) do
|
|
'ml_experiment_1234' | true
|
|
'ml_experiment_1234abc' | false
|
|
'ml_experiment_abc' | false
|
|
'ml_experiment_' | false
|
|
'blah' | false
|
|
end
|
|
|
|
with_them do
|
|
it { is_expected.to be(id) }
|
|
end
|
|
end
|
|
|
|
describe "#exclude_experiments_for_models" do
|
|
subject { described_class.by_project(exp.project).exclude_experiments_for_models }
|
|
|
|
it 'excludes experiments that belongs to a model' do
|
|
is_expected.to match_array([exp, exp2])
|
|
end
|
|
end
|
|
|
|
describe '.count_for_project' do
|
|
subject { described_class.count_for_project(exp.project_id) }
|
|
|
|
it { is_expected.to be(3) }
|
|
end
|
|
end
|