main
rb 95 lines 3.3 KB
Raw
1 # frozen_string_literal: true
2
3 require "rails_helper"
4 require "tmpdir"
5 require "fileutils"
6 require "open3"
7
8 # Exercises the git mechanics against a small local fixture repo (no network):
9 # a mirror clone + push must carry every branch, every tag, and the full
10 # history, and the guardrails (size cap, timeout) must fire.
11 RSpec.describe RepositoryImportService do
12 # Builds a bare source repo with two branches (main, feature) and a tag (v1),
13 # returning its path. Yields nothing; caller cleans up the tmpdir.
14 def build_source(dir)
15 work = File.join(dir, "work")
16 FileUtils.mkdir_p(work)
17 run = ->(*a) { system(*a, exception: true) }
18 run.call("git", "init", "-q", "-b", "main", work)
19 run.call("git", "-C", work, "config", "user.email", "t@e.st")
20 run.call("git", "-C", work, "config", "user.name", "Test")
21 File.write(File.join(work, "README.md"), "# Fixture\n")
22 run.call("git", "-C", work, "add", ".")
23 run.call("git", "-C", work, "commit", "-qm", "initial")
24 run.call("git", "-C", work, "tag", "v1")
25 run.call("git", "-C", work, "checkout", "-q", "-b", "feature")
26 File.write(File.join(work, "f.txt"), "x\n")
27 run.call("git", "-C", work, "add", ".")
28 run.call("git", "-C", work, "commit", "-qm", "feature work")
29 run.call("git", "-C", work, "checkout", "-q", "main")
30
31 bare = File.join(dir, "source.git")
32 run.call("git", "clone", "-q", "--bare", work, bare)
33 bare
34 end
35
36 def empty_bare(dir)
37 dest = File.join(dir, "dest.git")
38 system("git", "init", "--bare", "-q", dest, exception: true)
39 dest
40 end
41
42 around do |example|
43 Dir.mktmpdir("import-svc-spec") do |tmp|
44 @tmp = tmp
45 example.run
46 end
47 end
48
49 it "imports every branch, tag, and the full history, reporting progress" do
50 source = build_source(@tmp)
51 dest = empty_bare(@tmp)
52 phases = []
53
54 result = described_class.clone_and_push(
55 source_url: source, dest_path: dest,
56 progress: ->(p) { phases << p }
57 )
58
59 branches = GitRepositoryService.branches(dest).sort
60 tags = Open3.capture3("git", "--git-dir", dest, "tag").first.split
61
62 expect(branches).to eq(%w[feature main])
63 expect(tags).to eq(%w[v1])
64 expect(GitRepositoryService.commit_count(dest, "main")).to eq(1)
65 expect(GitRepositoryService.commit_count(dest, "feature")).to eq(2)
66 expect(phases).to eq(%i[cloning pushing fetching_lfs])
67 expect(result.lfs_detected).to be(false)
68 expect(result.size_kb).to be_a(Integer)
69 end
70
71 it "rejects a repo larger than the size cap" do
72 source = build_source(@tmp)
73 dest = empty_bare(@tmp)
74
75 expect {
76 described_class.clone_and_push(source_url: source, dest_path: dest, max_size_kb: 0)
77 }.to raise_error(RepositoryImportService::SizeExceeded)
78 end
79
80 it "fails cleanly on an unreachable source instead of hanging" do
81 dest = empty_bare(@tmp)
82 expect {
83 described_class.clone_and_push(source_url: "https://192.0.2.1/nope.git",
84 dest_path: dest, timeout: 8)
85 }.to raise_error(RepositoryImportService::ImportError)
86 end
87
88 it "terminates a git call that overruns its timeout" do
89 source = build_source(@tmp)
90 dest = empty_bare(@tmp)
91 expect {
92 described_class.clone_and_push(source_url: source, dest_path: dest, timeout: 0.001)
93 }.to raise_error(RepositoryImportService::CloneTimeout)
94 end
95 end