2023-06-20 00:43:36 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
RSpec.describe Packages::Npm::MetadataCache, type: :model, feature_category: :package_registry do
|
2023-07-09 08:55:56 +05:30
|
|
|
let_it_be(:project) { create(:project) }
|
|
|
|
let_it_be(:package_name) { '@root/test' }
|
|
|
|
|
|
|
|
it { is_expected.to be_a FileStoreMounter }
|
2023-06-20 00:43:36 +05:30
|
|
|
|
|
|
|
describe 'relationships' do
|
|
|
|
it { is_expected.to belong_to(:project).inverse_of(:npm_metadata_caches) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'validations' do
|
|
|
|
it { is_expected.to validate_presence_of(:file) }
|
|
|
|
it { is_expected.to validate_presence_of(:project) }
|
|
|
|
it { is_expected.to validate_presence_of(:size) }
|
|
|
|
|
|
|
|
describe '#package_name' do
|
2023-07-09 08:55:56 +05:30
|
|
|
let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package_name, project: project) }
|
|
|
|
|
2023-06-20 00:43:36 +05:30
|
|
|
it { is_expected.to validate_presence_of(:package_name) }
|
2023-07-09 08:55:56 +05:30
|
|
|
|
|
|
|
describe 'uniqueness' do
|
|
|
|
it 'ensures the package name is unique within a given project' do
|
|
|
|
expect do
|
|
|
|
create(:npm_metadata_cache, package_name: package_name, project: project)
|
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Package name has already been taken')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows duplicate file names in different projects' do
|
|
|
|
expect do
|
|
|
|
create(:npm_metadata_cache, package_name: package_name, project: create(:project))
|
|
|
|
end.not_to raise_error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'format' do
|
|
|
|
it { is_expected.to allow_value('my.app-11.07.2018').for(:package_name) }
|
|
|
|
it { is_expected.to allow_value('@group-1/package').for(:package_name) }
|
|
|
|
it { is_expected.to allow_value('@any-scope/package').for(:package_name) }
|
|
|
|
it { is_expected.to allow_value('unscoped-package').for(:package_name) }
|
|
|
|
|
|
|
|
it { is_expected.not_to allow_value('my(dom$$$ain)com.my-app').for(:package_name) }
|
|
|
|
it { is_expected.not_to allow_value('@inv@lid-scope/package').for(:package_name) }
|
|
|
|
it { is_expected.not_to allow_value('@scope/../../package').for(:package_name) }
|
|
|
|
it { is_expected.not_to allow_value('@scope%2e%2e%fpackage').for(:package_name) }
|
|
|
|
it { is_expected.not_to allow_value('@scope/sub/package').for(:package_name) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.find_or_build' do
|
|
|
|
subject { described_class.find_or_build(package_name: package_name, project_id: project.id) }
|
|
|
|
|
|
|
|
context 'when a metadata cache exists' do
|
|
|
|
let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package_name, project: project) }
|
|
|
|
|
|
|
|
it 'finds an existing metadata cache' do
|
|
|
|
expect(subject).to eq(npm_metadata_cache)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when a metadata cache not found' do
|
|
|
|
let(:package_name) { 'not_existing' }
|
|
|
|
|
|
|
|
it 'builds a new instance', :aggregate_failures do
|
|
|
|
expect(subject).not_to be_persisted
|
|
|
|
expect(subject.package_name).to eq(package_name)
|
|
|
|
expect(subject.project_id).to eq(project.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'save callbacks' do
|
|
|
|
describe 'object_storage_key' do
|
|
|
|
let(:object_storage_key) do
|
|
|
|
Gitlab::HashedPath.new(
|
|
|
|
'packages', 'metadata_caches', 'npm', OpenSSL::Digest::SHA256.hexdigest(package_name),
|
|
|
|
root_hash: project.id
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(Gitlab::HashedPath).to receive(:new).and_return(object_storage_key)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the record is created' do
|
|
|
|
let(:npm_metadata_cache) { build(:npm_metadata_cache, package_name: package_name, project: project) }
|
|
|
|
|
|
|
|
it 'sets object_storage_key' do
|
|
|
|
npm_metadata_cache.save!
|
|
|
|
|
|
|
|
expect(npm_metadata_cache.object_storage_key).to eq(object_storage_key.to_s)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using `update!`' do
|
|
|
|
let(:metadata_content) { {}.to_json }
|
|
|
|
|
|
|
|
it 'sets object_storage_key' do
|
|
|
|
npm_metadata_cache.update!(
|
|
|
|
file: CarrierWaveStringFile.new(metadata_content),
|
|
|
|
size: metadata_content.bytesize
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(npm_metadata_cache.object_storage_key).to eq(object_storage_key.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the record is updated' do
|
|
|
|
let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache, package_name: package_name, project: project) }
|
|
|
|
|
|
|
|
let(:existing_object_storage_key) { npm_metadata_cache.object_storage_key }
|
|
|
|
let(:new_package_name) { 'updated_package_name' }
|
|
|
|
|
|
|
|
it 'does not update object_storage_key' do
|
|
|
|
existing_object_storage_key = npm_metadata_cache.object_storage_key
|
|
|
|
|
|
|
|
npm_metadata_cache.update!(package_name: new_package_name)
|
|
|
|
|
|
|
|
expect(npm_metadata_cache.object_storage_key).to eq(existing_object_storage_key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'readonly attributes' do
|
|
|
|
describe 'object_storage_key' do
|
|
|
|
let_it_be(:npm_metadata_cache) { create(:npm_metadata_cache) }
|
|
|
|
|
|
|
|
it 'sets object_storage_key' do
|
|
|
|
expect(npm_metadata_cache.object_storage_key).to be_present
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the record is persisted' do
|
|
|
|
let(:new_object_storage_key) { 'object/storage/updated_key' }
|
|
|
|
|
|
|
|
it 'does not re-set object_storage_key' do
|
|
|
|
npm_metadata_cache.object_storage_key = new_object_storage_key
|
|
|
|
|
|
|
|
npm_metadata_cache.save!
|
|
|
|
|
|
|
|
expect(npm_metadata_cache.object_storage_key).not_to eq(new_object_storage_key)
|
|
|
|
end
|
|
|
|
end
|
2023-06-20 00:43:36 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|