DEV: Optimize specs (#238)

Git repositories will be initialized less often and then copied for each
spec. This reduces the calls to 'git' and the total execution time of
the specs.
This commit is contained in:
Bianca Nenciu 2024-09-06 09:45:34 +03:00 committed by GitHub
parent 3a43aa6be7
commit 957867798e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 58 deletions

View File

@ -38,7 +38,7 @@ RSpec.describe DockerManager::GitRepo do
subject(:repo) do subject(:repo) do
prepare_repos prepare_repos
repo = described_class.new(@local_repo.path) repo = described_class.new(@local_git_repo.path)
repo.update_remote! unless @skip_update_remote repo.update_remote! unless @skip_update_remote
repo repo
end end
@ -47,7 +47,7 @@ RSpec.describe DockerManager::GitRepo do
before do before do
@skip_update_remote = false @skip_update_remote = false
@local_repo = @remote_git_repo = nil @local_git_repo = @remote_git_repo = nil
@before_local_repo_clone = [] @before_local_repo_clone = []
@after_local_repo_clone = [] @after_local_repo_clone = []
end end
@ -55,32 +55,41 @@ RSpec.describe DockerManager::GitRepo do
after { @remote_git_repo.destroy } after { @remote_git_repo.destroy }
def prepare_repos def prepare_repos
return if @local_repo && @remote_git_repo return if @local_git_repo && @remote_git_repo
@remote_git_repo = GitHelpers::RemoteGitRepo.new(initial_branch: initial_branch) cache_key =
@remote_git_repo.commit( Digest::SHA1.hexdigest(
filename: "foo.txt", "#{initial_branch}-" + @before_local_repo_clone.map(&:source_location).flatten.join(","),
commits: [ )
{ content: "A", date: "2023-03-06T20:31:17Z" },
{
content: "B",
date: "2023-03-06T21:08:52Z",
tags: %w[v3.1.0.beta1 beta latest-release],
},
{ content: "C", date: "2023-03-06T22:48:29Z" },
],
)
@remote_git_repo.create_branches("tests-passed")
@before_local_repo_clone.each { |callback| callback.call } @remote_git_repo =
@local_repo = @remote_git_repo.create_local_clone(method: clone_method) GitHelpers::RemoteGitRepo.new(initial_branch:, cache_key:) do |repo|
@after_local_repo_clone.each { |callback| callback.call } repo.commit(
filename: "foo.txt",
commits: [
{ content: "A", date: "2023-03-06T20:31:17Z" },
{
content: "B",
date: "2023-03-06T21:08:52Z",
tags: %w[v3.1.0.beta1 beta latest-release],
},
{ content: "C", date: "2023-03-06T22:48:29Z" },
],
)
repo.create_branches("tests-passed")
@before_local_repo_clone.each { |callback| callback.call(repo) }
end
@local_git_repo = @remote_git_repo.create_local_clone(method: clone_method)
@after_local_repo_clone.each { |callback| callback.call(@remote_git_repo, @local_git_repo) }
@remote_git_repo.in_remote_repo { |git| git.call("log --pretty=oneline") } @remote_git_repo.in_remote_repo { |git| git.call("log --pretty=oneline") }
end end
def add_new_commits def add_new_commits(remote_repo, local_repo)
@remote_git_repo.commit( remote_repo.commit(
filename: "foo.txt", filename: "foo.txt",
commits: [ commits: [
{ content: "D", date: "2023-03-07T10:11:19Z" }, { content: "D", date: "2023-03-07T10:11:19Z" },
@ -92,12 +101,16 @@ RSpec.describe DockerManager::GitRepo do
{ content: "F", date: "2023-03-07T15:22:23Z" }, { content: "F", date: "2023-03-07T15:22:23Z" },
], ],
) )
@remote_git_repo.rebase(source_branch: "main", target_branch: "tests-passed") remote_repo.rebase(source_branch: "main", target_branch: "tests-passed")
end end
shared_examples "common tests" do shared_examples "common tests" do
context "when tracking `tests-passed` branch" do context "when tracking `tests-passed` branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("tests-passed") } } before do
@after_local_repo_clone << ->(remote_repo, local_repo) do
local_repo.checkout("tests-passed")
end
end
describe "#has_local_main?" do describe "#has_local_main?" do
context "with existing `main` branch" do context "with existing `main` branch" do
@ -125,15 +138,19 @@ RSpec.describe DockerManager::GitRepo do
context "with `master` as initial branch" do context "with `master` as initial branch" do
let(:initial_branch) { "master" } let(:initial_branch) { "master" }
before { @after_local_repo_clone << -> { @local_repo.checkout("master") } } before do
@after_local_repo_clone << ->(remote_repo, local_repo) do
local_repo.checkout("master")
end
end
it "returns `origin/master` if a repo hasn't been renamed" do it "returns `origin/master` if a repo hasn't been renamed" do
expect(repo.tracking_ref).to eq("origin/master") expect(repo.tracking_ref).to eq("origin/master")
end end
it "returns `origin/main` if a repo has been renamed but still tracks `master`" do it "returns `origin/main` if a repo has been renamed but still tracks `master`" do
@after_local_repo_clone << -> do @after_local_repo_clone << ->(remote_repo, local_repo) do
@remote_git_repo.rename_branch(old_name: "master", new_name: "main") remote_repo.rename_branch(old_name: "master", new_name: "main")
end end
expect(repo.tracking_ref).to eq("origin/main") expect(repo.tracking_ref).to eq("origin/main")
@ -141,7 +158,9 @@ RSpec.describe DockerManager::GitRepo do
end end
context "with `main` as current local branch" do context "with `main` as current local branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("main") } } before do
@after_local_repo_clone << ->(remote_repo, local_repo) { local_repo.checkout("main") }
end
it "returns `origin/main` if a repo points at `origin/main`" do it "returns `origin/main` if a repo points at `origin/main`" do
expect(repo.tracking_ref).to eq("origin/main") expect(repo.tracking_ref).to eq("origin/main")
@ -157,15 +176,19 @@ RSpec.describe DockerManager::GitRepo do
context "with `master` as initial branch" do context "with `master` as initial branch" do
let(:initial_branch) { "master" } let(:initial_branch) { "master" }
before { @after_local_repo_clone << -> { @local_repo.checkout("master") } } before do
@after_local_repo_clone << ->(remote_repo, local_repo) do
local_repo.checkout("master")
end
end
it "returns `origin/master` if a repo hasn't been renamed" do it "returns `origin/master` if a repo hasn't been renamed" do
expect(repo.upstream_branch).to eq("origin/master") expect(repo.upstream_branch).to eq("origin/master")
end end
it "returns `origin/master` if a repo has been renamed but still tracks `master`" do it "returns `origin/master` if a repo has been renamed but still tracks `master`" do
@after_local_repo_clone << -> do @after_local_repo_clone << ->(remote_repo, local_repo) do
@remote_git_repo.rename_branch(old_name: "master", new_name: "main") remote_repo.rename_branch(old_name: "master", new_name: "main")
end end
expect(repo.upstream_branch).to eq("origin/master") expect(repo.upstream_branch).to eq("origin/master")
@ -173,7 +196,9 @@ RSpec.describe DockerManager::GitRepo do
end end
context "with `main` as current local branch" do context "with `main` as current local branch" do
before { @after_local_repo_clone << -> { @local_repo.checkout("main") } } before do
@after_local_repo_clone << ->(remote_repo, local_repo) { local_repo.checkout("main") }
end
it "returns `origin/main` if a repo points at `origin/main`" do it "returns `origin/main` if a repo points at `origin/main`" do
expect(repo.upstream_branch).to eq("origin/main") expect(repo.upstream_branch).to eq("origin/main")
@ -187,7 +212,9 @@ RSpec.describe DockerManager::GitRepo do
end end
it "returns false when upstream branch doesn't exist" do it "returns false when upstream branch doesn't exist" do
@after_local_repo_clone << -> { @remote_git_repo.delete_branches("tests-passed") } @after_local_repo_clone << ->(remote_repo, local_repo) do
remote_repo.delete_branches("tests-passed")
end
expect(repo.upstream_branch_exist?).to eq(false) expect(repo.upstream_branch_exist?).to eq(false)
end end
end end
@ -225,8 +252,8 @@ RSpec.describe DockerManager::GitRepo do
context "with no tags" do context "with no tags" do
before do before do
@before_local_repo_clone << -> do @before_local_repo_clone << ->(repo) do
@remote_git_repo.delete_tags("beta", "latest-release", "v3.1.0.beta1") repo.delete_tags("beta", "latest-release", "v3.1.0.beta1")
end end
end end
@ -262,7 +289,7 @@ RSpec.describe DockerManager::GitRepo do
context "with `beta` and version tags on HEAD~1" do context "with `beta` and version tags on HEAD~1" do
before do before do
skip_for_shallow_clone skip_for_shallow_clone
@before_local_repo_clone << -> { @remote_git_repo.delete_tags("latest-release") } @before_local_repo_clone << ->(repo) { repo.delete_tags("latest-release") }
end end
describe "#latest_local_tag_version" do describe "#latest_local_tag_version" do
@ -287,7 +314,7 @@ RSpec.describe DockerManager::GitRepo do
describe "#update_remote!" do describe "#update_remote!" do
it "fetches the correct amount of new commits" do it "fetches the correct amount of new commits" do
prepare_repos prepare_repos
expect { repo.update_remote! }.to not_change { @local_repo.commit_count } expect { repo.update_remote! }.to not_change { @local_git_repo.commit_count }
end end
end end
end end
@ -343,7 +370,7 @@ RSpec.describe DockerManager::GitRepo do
describe "#update_remote!" do describe "#update_remote!" do
it "fetches the correct amount of new commits" do it "fetches the correct amount of new commits" do
prepare_repos prepare_repos
expect { repo.update_remote! }.to change { @local_repo.commit_count }.by( expect { repo.update_remote! }.to change { @local_git_repo.commit_count }.by(
fetch_commit_count, fetch_commit_count,
) )
end end
@ -353,13 +380,13 @@ RSpec.describe DockerManager::GitRepo do
context "when tracking `beta` tag" do context "when tracking `beta` tag" do
before do before do
@after_local_repo_clone << -> do @after_local_repo_clone << ->(remote_repo, local_repo) do
unless shallow_clone? unless shallow_clone?
@local_repo.checkout("beta") local_repo.checkout("beta")
# Mimics the behavior of `web.template.yml` where we store the value of the `$version` variable # Mimics the behavior of `web.template.yml` where we store the value of the `$version` variable
# as a user-defined config value in git. # as a user-defined config value in git.
# See https://github.com/discourse/discourse_docker/blob/main/templates/web.template.yml # See https://github.com/discourse/discourse_docker/blob/main/templates/web.template.yml
@local_repo.git("config user.discourse-version beta") local_repo.git("config user.discourse-version beta")
end end
end end
end end
@ -462,7 +489,7 @@ RSpec.describe DockerManager::GitRepo do
describe "#update_remote!" do describe "#update_remote!" do
it "fetches the correct amount of new commits" do it "fetches the correct amount of new commits" do
prepare_repos prepare_repos
expect { repo.update_remote! }.to not_change { @local_repo.commit_count } expect { repo.update_remote! }.to not_change { @local_git_repo.commit_count }
end end
end end
end end
@ -522,7 +549,7 @@ RSpec.describe DockerManager::GitRepo do
describe "#update_remote!" do describe "#update_remote!" do
it "fetches the correct amount of new commits" do it "fetches the correct amount of new commits" do
prepare_repos prepare_repos
expect { repo.update_remote! }.to change { @local_repo.commit_count }.by( expect { repo.update_remote! }.to change { @local_git_repo.commit_count }.by(
fetch_commit_count, fetch_commit_count,
) )
end end
@ -532,14 +559,14 @@ RSpec.describe DockerManager::GitRepo do
context "when tracking deleted branch" do context "when tracking deleted branch" do
before do before do
@before_local_repo_clone << -> do @before_local_repo_clone << ->(repo) do
@remote_git_repo.delete_tags("beta") repo.delete_tags("beta")
@remote_git_repo.create_branches("beta") repo.create_branches("beta")
end end
@after_local_repo_clone << -> do @after_local_repo_clone << ->(remote_repo, local_repo) do
@local_repo.checkout("beta") local_repo.checkout("beta")
@remote_git_repo.delete_branches("beta") remote_repo.delete_branches("beta")
@remote_git_repo.in_working_directory do |git| remote_repo.in_working_directory do |git|
git.call("tag -m 'latest beta release' -a beta latest-release^{}") git.call("tag -m 'latest beta release' -a beta latest-release^{}")
git.call("push origin beta") git.call("push origin beta")
end end
@ -595,7 +622,9 @@ RSpec.describe DockerManager::GitRepo do
describe "#url" do describe "#url" do
before do before do
@skip_update_remote = true @skip_update_remote = true
@after_local_repo_clone << -> { @local_repo.git("remote set-url origin #{remote_url}") } @after_local_repo_clone << ->(remote_repo, local_repo) do
local_repo.git("remote set-url origin #{remote_url}")
end
end end
context "with GitHub HTTPS URL" do context "with GitHub HTTPS URL" do
@ -657,8 +686,8 @@ RSpec.describe DockerManager::GitRepo do
let!(:clone_method) { GitHelpers::CLONE_SHALLOW } let!(:clone_method) { GitHelpers::CLONE_SHALLOW }
before do before do
@before_local_repo_clone << -> do @before_local_repo_clone << ->(repo) do
@remote_git_repo.commit( repo.commit(
filename: ".discourse-compatibility", filename: ".discourse-compatibility",
commits: [{ content: "<= 1000.0.0: twoPointFiveBranch", date: "2023-03-06T20:31:17Z" }], commits: [{ content: "<= 1000.0.0: twoPointFiveBranch", date: "2023-03-06T20:31:17Z" }],
) )

View File

@ -6,16 +6,25 @@ module GitHelpers
CLONE_TREELESS = :treeless CLONE_TREELESS = :treeless
class RemoteGitRepo class RemoteGitRepo
attr_reader :url, :work_path, :remote_path attr_reader :url, :root_path, :work_path, :remote_path
def initialize(initial_branch: "main") @@caches = {}
def initialize(initial_branch: "main", cache_key: nil, force: false, &blk)
@initial_branch = initial_branch @initial_branch = initial_branch
@local_clone_count = 0 @local_clone_count = 0
@root_path = Dir.mktmpdir @root_path = Dir.mktmpdir
@remote_path = File.join(@root_path, "remote.git") @remote_path = File.join(@root_path, "remote.git")
@work_path = File.join(@root_path, "work")
@url = "file://#{@remote_path}" @url = "file://#{@remote_path}"
if !force
@@caches[cache_key] ||= self.class.new(initial_branch:, cache_key:, force: true, &blk)
FileUtils.cp_r(@@caches[cache_key].root_path + "/.", @root_path)
Dir.chdir(@work_path) { git "remote set-url origin #{@url}" }
return
end
Dir.mkdir(@remote_path) Dir.mkdir(@remote_path)
Dir.chdir(@remote_path) do Dir.chdir(@remote_path) do
git "init --bare --initial-branch=#{initial_branch}" git "init --bare --initial-branch=#{initial_branch}"
@ -23,12 +32,13 @@ module GitHelpers
git "config receive.denyNonFastforwards true" git "config receive.denyNonFastforwards true"
git "config receive.denyCurrentBranch ignore" git "config receive.denyCurrentBranch ignore"
git "config uploadpack.allowFilter true" git "config uploadpack.allowFilter true"
git "config commit.gpgsign false"
end end
@work_path = File.join(@root_path, "work")
Dir.mkdir(@work_path) Dir.mkdir(@work_path)
Dir.chdir(@work_path) do Dir.chdir(@work_path) do
git "init . --initial-branch=#{initial_branch}" git "init . --initial-branch=#{initial_branch}"
git "config commit.gpgsign false"
git "remote add origin #{@url}" git "remote add origin #{@url}"
File.write("README.md", "This is a git repo for testing docker_manager.") File.write("README.md", "This is a git repo for testing docker_manager.")
@ -38,6 +48,8 @@ module GitHelpers
git "commit -m 'Initial commit'" git "commit -m 'Initial commit'"
git "push --set-upstream origin #{initial_branch}" git "push --set-upstream origin #{initial_branch}"
end end
yield(self) if block_given?
end end
def destroy def destroy
@ -162,7 +174,7 @@ module GitHelpers
if status.success? || !raise_exception if status.success? || !raise_exception
stdout.presence stdout.presence
else else
raise RuntimeError raise RuntimeError.new("stderr while running #{command}: #{stderr}")
end end
end end