1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe RepositoryArchiveCleanUpService do
6  subject(:service) { described_class.new }
7
8  describe '#execute (new archive locations)' do
9    let(:sha) { "0" * 40 }
10
11    it 'removes outdated archives and directories in a new-style path' do
12      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
13        service.execute
14
15        files.each { |filename| expect(File.exist?(filename)).to be_falsy }
16        expect(File.directory?(dirname)).to be_falsy
17        expect(File.directory?(File.dirname(dirname))).to be_falsy
18      end
19    end
20
21    it 'removes outdated archives and directories in a versioned path' do
22      in_directory_with_files("project-#{non_existing_record_id}/#{sha}/@v2", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
23        service.execute
24
25        files.each { |filename| expect(File.exist?(filename)).to be_falsy }
26        expect(File.directory?(dirname)).to be_falsy
27        expect(File.directory?(File.dirname(dirname))).to be_falsy
28      end
29    end
30
31    it 'does not remove directories when they contain outdated non-archives' do
32      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar conf rb], 2.hours) do |dirname, files|
33        service.execute
34
35        expect(File.directory?(dirname)).to be_truthy
36      end
37    end
38
39    it 'does not remove in-date archives in a new-style path' do
40      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar tar.bz2 tar.gz zip], 1.hour) do |dirname, files|
41        service.execute
42
43        files.each { |filename| expect(File.exist?(filename)).to be_truthy }
44      end
45    end
46  end
47
48  describe '#execute (legacy archive locations)' do
49    context 'when the downloads directory does not exist' do
50      it 'does not remove any archives' do
51        path = '/invalid/path/'
52        stub_repository_downloads_path(path)
53
54        allow(File).to receive(:directory?).and_call_original
55        expect(File).to receive(:directory?).with(path).and_return(false)
56
57        expect(service).not_to receive(:clean_up_old_archives)
58        expect(service).not_to receive(:clean_up_empty_directories)
59
60        service.execute
61      end
62    end
63
64    context 'when the downloads directory exists' do
65      shared_examples 'invalid archive files' do |dirname, extensions, mtime|
66        it 'does not remove files and directory' do
67          in_directory_with_files(dirname, extensions, mtime) do |dir, files|
68            service.execute
69
70            files.each { |file| expect(File.exist?(file)).to eq true }
71            expect(File.directory?(dir)).to eq true
72          end
73        end
74      end
75
76      it 'removes files older than 2 hours that matches valid archive extensions' do
77        # In macOS, the the `mmin` parameter for `find` rounds up, so add a full
78        # minute to ensure these files are deemed old.
79        in_directory_with_files('sample.git', %w[tar tar.bz2 tar.gz zip], 121.minutes) do |dir, files|
80          service.execute
81
82          files.each { |file| expect(File.exist?(file)).to eq false }
83          expect(File.directory?(dir)).to eq false
84        end
85      end
86
87      context 'with files older than 2 hours that does not matches valid archive extensions' do
88        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb], 121.minutes
89      end
90
91      context 'with files older than 2 hours inside invalid directories' do
92        it_behaves_like 'invalid archive files', 'john/t/doe/sample.git', %w[conf rb tar tar.gz], 121.minutes
93      end
94
95      context 'with files newer than 2 hours that matches valid archive extensions' do
96        it_behaves_like 'invalid archive files', 'sample.git', %w[tar tar.bz2 tar.gz zip], 1.hour
97      end
98
99      context 'with files newer than 2 hours that does not matches valid archive extensions' do
100        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb], 1.hour
101      end
102
103      context 'with files newer than 2 hours inside invalid directories' do
104        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb tar tar.gz], 1.hour
105      end
106    end
107  end
108
109  def in_directory_with_files(dirname, extensions, mtime)
110    Dir.mktmpdir do |tmpdir|
111      stub_repository_downloads_path(tmpdir)
112      dir = File.join(tmpdir, dirname)
113      files = create_temporary_files(dir, extensions, mtime)
114
115      yield(dir, files)
116    end
117  end
118
119  def stub_repository_downloads_path(path)
120    allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
121  end
122
123  def create_temporary_files(dir, extensions, mtime)
124    FileUtils.mkdir_p(dir)
125    FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now.utc - mtime)
126  end
127end
128