412 lines
12 KiB
Ruby
412 lines
12 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event do
|
|
describe '#commit_title' do
|
|
it 'returns nil when there are no commits' do
|
|
expect(described_class.new.commit_title).to be_nil
|
|
end
|
|
|
|
it 'returns nil when there are commits without commit messages' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:commits).and_return([{ id: '123' }])
|
|
|
|
expect(event.commit_title).to be_nil
|
|
end
|
|
|
|
it 'returns the commit message when it is less than 70 characters long' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:commits).and_return([{ message: 'Hello world' }])
|
|
|
|
expect(event.commit_title).to eq('Hello world')
|
|
end
|
|
|
|
it 'returns the first line of a commit message if multiple lines are present' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:commits).and_return([{ message: "Hello\n\nworld" }])
|
|
|
|
expect(event.commit_title).to eq('Hello')
|
|
end
|
|
|
|
it 'truncates the commit to 70 characters when it is too long' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:commits).and_return([{ message: 'a' * 100 }])
|
|
|
|
expect(event.commit_title).to eq(('a' * 67) + '...')
|
|
end
|
|
end
|
|
|
|
describe '#commit_from_sha' do
|
|
it 'returns nil when pushing to a new ref' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:create?).and_return(true)
|
|
|
|
expect(event.commit_from_sha).to be_nil
|
|
end
|
|
|
|
it 'returns the ID of the first commit when pushing to an existing ref' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:create?).and_return(false)
|
|
allow(event).to receive(:data).and_return(before: '123')
|
|
|
|
expect(event.commit_from_sha).to eq('123')
|
|
end
|
|
end
|
|
|
|
describe '#commit_to_sha' do
|
|
it 'returns nil when removing an existing ref' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:remove?).and_return(true)
|
|
|
|
expect(event.commit_to_sha).to be_nil
|
|
end
|
|
|
|
it 'returns the ID of the last commit when pushing to an existing ref' do
|
|
event = described_class.new
|
|
|
|
allow(event).to receive(:remove?).and_return(false)
|
|
allow(event).to receive(:data).and_return(after: '123')
|
|
|
|
expect(event.commit_to_sha).to eq('123')
|
|
end
|
|
end
|
|
|
|
describe '#data' do
|
|
it 'returns the deserialized data' do
|
|
event = described_class.new(data: { before: '123' })
|
|
|
|
expect(event.data).to eq(before: '123')
|
|
end
|
|
|
|
it 'returns an empty hash when no data is present' do
|
|
event = described_class.new
|
|
|
|
expect(event.data).to eq({})
|
|
end
|
|
end
|
|
|
|
describe '#commits' do
|
|
it 'returns an Array of commits' do
|
|
event = described_class.new(data: { commits: [{ id: '123' }] })
|
|
|
|
expect(event.commits).to eq([{ id: '123' }])
|
|
end
|
|
|
|
it 'returns an empty array when no data is present' do
|
|
event = described_class.new
|
|
|
|
expect(event.commits).to eq([])
|
|
end
|
|
end
|
|
|
|
describe '#commit_count' do
|
|
it 'returns the number of commits' do
|
|
event = described_class.new(data: { total_commits_count: 2 })
|
|
|
|
expect(event.commit_count).to eq(2)
|
|
end
|
|
|
|
it 'returns 0 when no data is present' do
|
|
event = described_class.new
|
|
|
|
expect(event.commit_count).to eq(0)
|
|
end
|
|
end
|
|
|
|
describe '#ref' do
|
|
it 'returns the name of the ref' do
|
|
event = described_class.new(data: { ref: 'refs/heads/master' })
|
|
|
|
expect(event.ref).to eq('refs/heads/master')
|
|
end
|
|
end
|
|
|
|
describe '#trimmed_ref_name' do
|
|
it 'returns the trimmed ref name for a branch' do
|
|
event = described_class.new(data: { ref: 'refs/heads/master' })
|
|
|
|
expect(event.trimmed_ref_name).to eq('master')
|
|
end
|
|
|
|
it 'returns the trimmed ref name for a tag' do
|
|
event = described_class.new(data: { ref: 'refs/tags/v1.2' })
|
|
|
|
expect(event.trimmed_ref_name).to eq('v1.2')
|
|
end
|
|
end
|
|
|
|
describe '#create?' do
|
|
it 'returns true when creating a new ref' do
|
|
event = described_class.new(data: { before: described_class::BLANK_REF })
|
|
|
|
expect(event.create?).to eq(true)
|
|
end
|
|
|
|
it 'returns false when pushing to an existing ref' do
|
|
event = described_class.new(data: { before: '123' })
|
|
|
|
expect(event.create?).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe '#remove?' do
|
|
it 'returns true when removing an existing ref' do
|
|
event = described_class.new(data: { after: described_class::BLANK_REF })
|
|
|
|
expect(event.remove?).to eq(true)
|
|
end
|
|
|
|
it 'returns false when pushing to an existing ref' do
|
|
event = described_class.new(data: { after: '123' })
|
|
|
|
expect(event.remove?).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe '#push_action' do
|
|
let(:event) { described_class.new }
|
|
|
|
it 'returns :created when creating a new ref' do
|
|
allow(event).to receive(:create?).and_return(true)
|
|
|
|
expect(event.push_action).to eq(:created)
|
|
end
|
|
|
|
it 'returns :removed when removing an existing ref' do
|
|
allow(event).to receive(:create?).and_return(false)
|
|
allow(event).to receive(:remove?).and_return(true)
|
|
|
|
expect(event.push_action).to eq(:removed)
|
|
end
|
|
|
|
it 'returns :pushed when pushing to an existing ref' do
|
|
allow(event).to receive(:create?).and_return(false)
|
|
allow(event).to receive(:remove?).and_return(false)
|
|
|
|
expect(event.push_action).to eq(:pushed)
|
|
end
|
|
end
|
|
|
|
describe '#ref_type' do
|
|
let(:event) { described_class.new }
|
|
|
|
it 'returns :tag for a tag' do
|
|
allow(event).to receive(:ref).and_return('refs/tags/1.2')
|
|
|
|
expect(event.ref_type).to eq(:tag)
|
|
end
|
|
|
|
it 'returns :branch for a branch' do
|
|
allow(event).to receive(:ref).and_return('refs/heads/1.2')
|
|
|
|
expect(event.ref_type).to eq(:branch)
|
|
end
|
|
end
|
|
end
|
|
|
|
##
|
|
# The background migration relies on a temporary table, hence we're migrating
|
|
# to a specific version of the database where said table is still present.
|
|
#
|
|
describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170608152748 do
|
|
let(:migration) { described_class.new }
|
|
let(:project) { create(:project_empty_repo) }
|
|
let(:author) { create(:user) }
|
|
|
|
# We can not rely on FactoryGirl as the state of Event may change in ways that
|
|
# the background migration does not expect, hence we use the Event class of
|
|
# the migration itself.
|
|
def create_push_event(project, author, data = nil)
|
|
klass = Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event
|
|
|
|
klass.create!(
|
|
action: klass::PUSHED,
|
|
project_id: project.id,
|
|
author_id: author.id,
|
|
data: data
|
|
)
|
|
end
|
|
|
|
describe '#perform' do
|
|
it 'returns if data should not be migrated' do
|
|
allow(migration).to receive(:migrate?).and_return(false)
|
|
|
|
expect(migration).not_to receive(:find_events)
|
|
|
|
migration.perform(1, 10)
|
|
end
|
|
|
|
it 'migrates the range of events if data is to be migrated' do
|
|
event1 = create_push_event(project, author, { commits: [] })
|
|
event2 = create_push_event(project, author, { commits: [] })
|
|
|
|
allow(migration).to receive(:migrate?).and_return(true)
|
|
|
|
expect(migration).to receive(:process_event).twice
|
|
|
|
migration.perform(event1.id, event2.id)
|
|
end
|
|
end
|
|
|
|
describe '#process_event' do
|
|
it 'processes a regular event' do
|
|
event = double(:event, push_event?: false)
|
|
|
|
expect(migration).to receive(:replicate_event)
|
|
expect(migration).not_to receive(:create_push_event_payload)
|
|
|
|
migration.process_event(event)
|
|
end
|
|
|
|
it 'processes a push event' do
|
|
event = double(:event, push_event?: true)
|
|
|
|
expect(migration).to receive(:replicate_event)
|
|
expect(migration).to receive(:create_push_event_payload)
|
|
|
|
migration.process_event(event)
|
|
end
|
|
end
|
|
|
|
describe '#replicate_event' do
|
|
it 'replicates the event to the "events_for_migration" table' do
|
|
event = create_push_event(
|
|
project,
|
|
author,
|
|
data: { commits: [] },
|
|
title: 'bla'
|
|
)
|
|
|
|
attributes = event
|
|
.attributes.with_indifferent_access.except(:title, :data)
|
|
|
|
expect(described_class::EventForMigration)
|
|
.to receive(:create!)
|
|
.with(attributes)
|
|
|
|
migration.replicate_event(event)
|
|
end
|
|
end
|
|
|
|
describe '#create_push_event_payload' do
|
|
let(:push_data) do
|
|
{
|
|
commits: [],
|
|
ref: 'refs/heads/master',
|
|
before: '156e0e9adc587a383a7eeb5b21ddecb9044768a8',
|
|
after: '0' * 40,
|
|
total_commits_count: 1
|
|
}
|
|
end
|
|
|
|
let(:event) do
|
|
create_push_event(project, author, push_data)
|
|
end
|
|
|
|
before do
|
|
# The foreign key in push_event_payloads at this point points to the
|
|
# "events_for_migration" table so we need to make sure a row exists in
|
|
# said table.
|
|
migration.replicate_event(event)
|
|
end
|
|
|
|
it 'creates a push event payload for an event' do
|
|
payload = migration.create_push_event_payload(event)
|
|
|
|
expect(PushEventPayload.count).to eq(1)
|
|
expect(payload.valid?).to eq(true)
|
|
end
|
|
|
|
it 'does not create push event payloads for removed events' do
|
|
allow(event).to receive(:id).and_return(-1)
|
|
|
|
payload = migration.create_push_event_payload(event)
|
|
|
|
expect(payload).to be_nil
|
|
expect(PushEventPayload.count).to eq(0)
|
|
end
|
|
|
|
it 'encodes and decodes the commit IDs from and to binary data' do
|
|
payload = migration.create_push_event_payload(event)
|
|
packed = migration.pack(push_data[:before])
|
|
|
|
expect(payload.commit_from).to eq(packed)
|
|
expect(payload.commit_to).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#find_events' do
|
|
it 'returns the events for the given ID range' do
|
|
event1 = create_push_event(project, author, { commits: [] })
|
|
event2 = create_push_event(project, author, { commits: [] })
|
|
event3 = create_push_event(project, author, { commits: [] })
|
|
events = migration.find_events(event1.id, event2.id)
|
|
|
|
expect(events.length).to eq(2)
|
|
expect(events.pluck(:id)).not_to include(event3.id)
|
|
end
|
|
end
|
|
|
|
describe '#migrate?' do
|
|
it 'returns true when data should be migrated' do
|
|
allow(described_class::Event)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
allow(described_class::PushEventPayload)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
allow(described_class::EventForMigration)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
expect(migration.migrate?).to eq(true)
|
|
end
|
|
|
|
it 'returns false if the "events" table does not exist' do
|
|
allow(described_class::Event)
|
|
.to receive(:table_exists?).and_return(false)
|
|
|
|
expect(migration.migrate?).to eq(false)
|
|
end
|
|
|
|
it 'returns false if the "push_event_payloads" table does not exist' do
|
|
allow(described_class::Event)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
allow(described_class::PushEventPayload)
|
|
.to receive(:table_exists?).and_return(false)
|
|
|
|
expect(migration.migrate?).to eq(false)
|
|
end
|
|
|
|
it 'returns false when the "events_for_migration" table does not exist' do
|
|
allow(described_class::Event)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
allow(described_class::PushEventPayload)
|
|
.to receive(:table_exists?).and_return(true)
|
|
|
|
allow(described_class::EventForMigration)
|
|
.to receive(:table_exists?).and_return(false)
|
|
|
|
expect(migration.migrate?).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe '#pack' do
|
|
it 'packs a SHA1 into a 20 byte binary string' do
|
|
packed = migration.pack('156e0e9adc587a383a7eeb5b21ddecb9044768a8')
|
|
|
|
expect(packed.bytesize).to eq(20)
|
|
end
|
|
|
|
it 'returns nil if the input value is nil' do
|
|
expect(migration.pack(nil)).to be_nil
|
|
end
|
|
end
|
|
end
|