rspec-remote_fixtures 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e56645e72d9aed2902fafbf7da230bb87e9a149b9ab501b340588143e506afee
4
+ data.tar.gz: 109d60b0288b9d4968f7507f5943fd22fe834bbd1bf2a4d718bcba5182992261
5
+ SHA512:
6
+ metadata.gz: 4a58f66e38610dbe75cd1601b05c0622e2c30ca986a863642b8df92ca4d640491d9df9c665b9283dba0e33368064c15a066df00e3e45ed41f5f09585fcd0862c
7
+ data.tar.gz: '091bd17bcfd4ddf520df8270433ef5c38f6d4a33a7414fb9632c2875db7082ced63afafd133cc8cf284f1fe263f41f35d48f26f00795214c6dbac935facef621'
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --order rand
4
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,27 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+ Exclude:
5
+ <% `git status --ignored --porcelain`.lines.grep(/^!! /).each do |path| %>
6
+ - <%= path.sub(/^!! /, '').sub(/\/$/, '/**/*') %>
7
+ <% end %>
8
+
9
+ Style/StringLiterals:
10
+ Enabled: true
11
+ EnforcedStyle: single_quotes
12
+
13
+ Style/StringLiteralsInInterpolation:
14
+ Enabled: true
15
+ EnforcedStyle: single_quotes
16
+
17
+ Layout/LineLength:
18
+ Max: 120
19
+
20
+ Metrics/MethodLength:
21
+ Max: 20
22
+ Exclude:
23
+ - 'spec/**/*'
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - '**/*.rake'
27
+ - 'spec/**/*'
data/.simplecov ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.start do
4
+ enable_coverage :branch
5
+ add_filter '/spec/'
6
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## [0.2.0] - 2023-01-09
2
+
3
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in rspec-remote-fixtures.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+ gem 'rbs'
10
+
11
+ gem 'rubocop', '~> 1.21'
12
+ gem 'simplecov', require: false, group: :test
13
+
14
+ gem 'byebug', '~> 11.1'
data/Gemfile.lock ADDED
@@ -0,0 +1,99 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rspec-remote_fixtures (0.2.0)
5
+ activesupport (>= 6.1)
6
+ aws-sdk-s3 (~> 1.0)
7
+ rspec (~> 3.12)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (7.0.4)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 1.6, < 2)
15
+ minitest (>= 5.1)
16
+ tzinfo (~> 2.0)
17
+ ast (2.4.2)
18
+ aws-eventstream (1.2.0)
19
+ aws-partitions (1.688.0)
20
+ aws-sdk-core (3.168.4)
21
+ aws-eventstream (~> 1, >= 1.0.2)
22
+ aws-partitions (~> 1, >= 1.651.0)
23
+ aws-sigv4 (~> 1.5)
24
+ jmespath (~> 1, >= 1.6.1)
25
+ aws-sdk-kms (1.61.0)
26
+ aws-sdk-core (~> 3, >= 3.165.0)
27
+ aws-sigv4 (~> 1.1)
28
+ aws-sdk-s3 (1.117.2)
29
+ aws-sdk-core (~> 3, >= 3.165.0)
30
+ aws-sdk-kms (~> 1)
31
+ aws-sigv4 (~> 1.4)
32
+ aws-sigv4 (1.5.2)
33
+ aws-eventstream (~> 1, >= 1.0.2)
34
+ byebug (11.1.3)
35
+ concurrent-ruby (1.1.10)
36
+ diff-lcs (1.5.0)
37
+ docile (1.4.0)
38
+ i18n (1.12.0)
39
+ concurrent-ruby (~> 1.0)
40
+ jmespath (1.6.2)
41
+ json (2.6.3)
42
+ minitest (5.17.0)
43
+ parallel (1.22.1)
44
+ parser (3.2.0.0)
45
+ ast (~> 2.4.1)
46
+ rainbow (3.1.1)
47
+ rake (13.0.6)
48
+ rbs (2.8.3)
49
+ regexp_parser (2.6.1)
50
+ rexml (3.2.5)
51
+ rspec (3.12.0)
52
+ rspec-core (~> 3.12.0)
53
+ rspec-expectations (~> 3.12.0)
54
+ rspec-mocks (~> 3.12.0)
55
+ rspec-core (3.12.0)
56
+ rspec-support (~> 3.12.0)
57
+ rspec-expectations (3.12.1)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.12.0)
60
+ rspec-mocks (3.12.1)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.12.0)
63
+ rspec-support (3.12.0)
64
+ rubocop (1.42.0)
65
+ json (~> 2.3)
66
+ parallel (~> 1.10)
67
+ parser (>= 3.1.2.1)
68
+ rainbow (>= 2.2.2, < 4.0)
69
+ regexp_parser (>= 1.8, < 3.0)
70
+ rexml (>= 3.2.5, < 4.0)
71
+ rubocop-ast (>= 1.24.1, < 2.0)
72
+ ruby-progressbar (~> 1.7)
73
+ unicode-display_width (>= 1.4.0, < 3.0)
74
+ rubocop-ast (1.24.1)
75
+ parser (>= 3.1.1.0)
76
+ ruby-progressbar (1.11.0)
77
+ simplecov (0.22.0)
78
+ docile (~> 1.1)
79
+ simplecov-html (~> 0.11)
80
+ simplecov_json_formatter (~> 0.1)
81
+ simplecov-html (0.12.3)
82
+ simplecov_json_formatter (0.1.4)
83
+ tzinfo (2.0.5)
84
+ concurrent-ruby (~> 1.0)
85
+ unicode-display_width (2.4.2)
86
+
87
+ PLATFORMS
88
+ x86_64-linux
89
+
90
+ DEPENDENCIES
91
+ byebug (~> 11.1)
92
+ rake (~> 13.0)
93
+ rbs
94
+ rspec-remote_fixtures!
95
+ rubocop (~> 1.21)
96
+ simplecov
97
+
98
+ BUNDLED WITH
99
+ 2.3.26
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # RSpec::RemoteFixtures
2
+
3
+ RemoteFixtures is a plugin for RSpec that lets you store test fixture files in s3 to avoid unnecessary overhead.
4
+
5
+ Why would I ever want to use this gem?
6
+ =========================================
7
+ This gem lets you stop committing large fixture files, without having to worry about git-lfs.
8
+ Furthermore, a great many applications use docker images to run in production and CI. If you have
9
+ hundreds of MB worth of fixture files, these files are first downloaded to wherever the image is being built,
10
+ then uploaded, over the network, likely to many CI workers and production instances.
11
+ Once the files are there, it's likely only a small proportion of these workers actually *need* any particular file.
12
+
13
+ Thus, rspec-remote-fixtures: A gem that sticks your rspec fixtures in S3, and downloads them transparently
14
+
15
+
16
+ ## Installation
17
+
18
+ Install the gem and add to the application's Gemfile by executing:
19
+
20
+ $ bundle add rspec-remote_fixtures
21
+
22
+ If bundler is not being used to manage dependencies, install the gem by executing:
23
+
24
+ $ gem install rspec-remote_fixtures
25
+
26
+ You will likely want to add the gem to the `develop` and `test` groups in your application's Gemfile
27
+ ## Configuration
28
+
29
+ Create an initializer `rspec_remote_fixtures.rb`:
30
+
31
+ ```ruby
32
+ if Rails.env.test? || Rails.env.development?
33
+ RSpec::RemoteFixtures::Config.backend_path = 's3://my-s3-bucket/some-prefix-path/'
34
+ # the following are defaults, only set if you want to override them
35
+ RSpec::RemoteFixtures::Config.manifest_path = 'spec/fixtures.json'
36
+ RSpec::RemoteFixtures::Config.backend = RSpec::RemoteFixtures::Backend::S3Backend
37
+ RSpec::RemoteFixtures::Config.fixture_path = Rails.root.join('spec/fixtures')
38
+ # use to set/override s3 auth if you need different credentials for the above bucket
39
+ RSpec::RemoteFixtures::Config.s3_client = Aws::S3::Client.new
40
+
41
+ # When should we validate the digest of a file fixture?
42
+ # always: any time the file is used in a spec
43
+ # download: whenever the file is downloaded to this runner for the first time
44
+ # never: <--
45
+ RSpec::RemoteFixtures::Config.check_remote_fixture_digest = :download
46
+ end
47
+ ```
48
+
49
+ In your `rails_helper.rb`:
50
+
51
+ ```ruby
52
+ require 'rspec/rails'
53
+ # Important: we hook into some of the helpers provided by rspec/rails so this must come after requiring it:
54
+ RSpec::RemoteFixtures.setup_rspec!
55
+ ```
56
+
57
+ After the gem is configured, you will want to generate a manifest of your existing fixtures:
58
+ ```shell
59
+ rails generate rspec:fixture_manifest
60
+ ```
61
+
62
+ If the s3 object in question has an `etag` which matches the local digest of the file, the file will not
63
+ be re-uploaded.
64
+
65
+ Future files can be added or updated by calling the generator again with the `--files` parameter:
66
+
67
+ ```shell
68
+ rails generate rspec:fixture_manifest --files spec/fixtures/bob.txt
69
+ ```
70
+
71
+ Once the manifest has been set up, you can remove the fixture files from version control, and commit the manifest file.
72
+
73
+ ## Usage
74
+
75
+ There are two main ways to use this tool in your specs:
76
+
77
+ ```ruby
78
+ # fixture_file_path returns an absolute Pathname to a fixture located in RSpec::RemoteFixtures::Config.fixture_path
79
+ # The following invocation will ensure the file is present,
80
+ # and return Pathname.new('/my/rails/root/spec/fixtures/bob.txt')
81
+ # This method is available to FactoryBot factories as well as RSpec examples.
82
+ fixture_file_path('bob.txt')
83
+
84
+ # fixture_file_upload hooks into rspec/rails's method of the same name,
85
+ # downloading the file if not present and then calling super
86
+ fixture_file_upload('bob.txt')
87
+ ```
88
+
89
+ ## Warnings
90
+
91
+ S3 authentication relies on an accurate date. If you are using Timecop, RemoteFixtures will attempt to unfreeze for the
92
+ while downloading the file, and return, by calling `Timecop.unfreeze` in a block. Other libraries that freeze time may
93
+ cause downloads to fail.
94
+
95
+ ## Design
96
+
97
+ RSpec::RemoteFixtures creates a manifest JSON file of the following form:
98
+ ```json
99
+ {
100
+ "some/path.txt": {
101
+ "digest": "d8e8fca2dc0f896fd7cb4cb0031ba249",
102
+ "remote_path": "s3://some-bucket/prefix/d8e8fca2dc0f896fd7cb4cb0031ba249_path.txt"
103
+ }
104
+ }
105
+ ```
106
+
107
+ When `fixture_file_path` is called, the gem checks to see if the file is present on the local filesystem,
108
+ and conditionally verifies the digest of the local copy (see Configuration section). If the file is not present,
109
+ it retrieves the file, again potentially verifying the digest.
110
+
111
+ ## Development
112
+
113
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
114
+
115
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
116
+
117
+ ## Contributing
118
+
119
+ Bug reports and pull requests are welcome on GitHub at https://github.com/aleksclark/rspec-remote-fixtures.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../rspec/remote_fixtures'
4
+
5
+ module Rspec
6
+ module Generators
7
+ # @private
8
+ class FixtureManifestGenerator < ::Rails::Generators::Base
9
+ desc <<~DESC
10
+ Description:
11
+ Generate a fixture manifest for rspec/remote_fixtures.
12
+ DESC
13
+ class_option :files, type: :array, default: [], optional: true
14
+ class_option :force, type: :boolean, default: false, optional: true
15
+
16
+ def generate_manifest
17
+ create_file config.manifest_path, '{}' if !File.exist?(config.manifest_path) || options.force
18
+
19
+ add_files
20
+
21
+ say "Persisting manifest to #{config.manifest_path}"
22
+ RSpec::RemoteFixtures::Manifest.persist_manifest!
23
+ end
24
+
25
+ private
26
+
27
+ def add_files
28
+ if options.files.any?
29
+ options.files.each { |file| add_file(file) }
30
+ else
31
+ say "#{fixture_files.count} fixtures found..."
32
+ fixture_files.each { |path| add_file(path) }
33
+ end
34
+ end
35
+
36
+ def fixture_files
37
+ return @fixture_files if defined? @fixture_files
38
+
39
+ @fixture_files = []
40
+ Dir.chdir do
41
+ Dir.glob("#{config.fixture_path}/**/*", File::FNM_DOTMATCH) do |path|
42
+ next if File.directory?(path)
43
+
44
+ fixture_files << path
45
+ end
46
+ end
47
+
48
+ @fixture_files
49
+ end
50
+
51
+ def add_file(path)
52
+ path = Pathname.new(path)
53
+ path = Pathname.new(Dir.pwd).join(path) unless path.absolute?
54
+ path = path.relative_path_from(config.fixture_path)
55
+ say "Adding #{path} to the manifest.."
56
+ RSpec::RemoteFixtures::Manifest.add_fixture!(path)
57
+ end
58
+
59
+ def config
60
+ RSpec::RemoteFixtures::Config
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-s3'
4
+ require_relative '../config'
5
+
6
+ module RSpec
7
+ module RemoteFixtures
8
+ module Backend
9
+ # An S3 backend for RemoteFixtures
10
+ # Will only re-upload if the local digest doesn't match the remote object's etag
11
+ class S3Backend
12
+ attr_reader :s3_path, :s3_bucket, :s3_prefix, :bucket_name
13
+
14
+ def initialize
15
+ @s3_path = RemoteFixtures::Config.backend_path
16
+ raise Error, 'S3 path has not been configured!' unless @s3_path
17
+
18
+ components = s3_path.gsub('s3://', '').split('/')
19
+ @bucket_name = components[0]
20
+ @s3_bucket = s3.bucket(bucket_name)
21
+ @s3_prefix = components[1..].join('/')
22
+ super
23
+ end
24
+
25
+ def s3
26
+ @s3 ||= Aws::S3::Resource.new(client: RemoteFixtures::Config.s3_client)
27
+ end
28
+
29
+ def upload(path, digest)
30
+ file_name = "#{digest}_#{File.basename(path)}"
31
+ bucket_path = "#{s3_prefix}/#{file_name}"
32
+ obj = s3_bucket.object(bucket_path)
33
+ obj.upload_file(path) unless digest_match?(obj, digest)
34
+
35
+ "s3://#{bucket_name}/#{bucket_path}"
36
+ end
37
+
38
+ def download(remote_path, local_path)
39
+ report_download(remote_path, local_path)
40
+ if defined? Timecop && Timecop&.frozen? # rubocop:disable Style/SafeNavigation
41
+ Timecop.unfreeze do
42
+ perform_download(remote_path, local_path)
43
+ end
44
+ else
45
+ perform_download(remote_path, local_path)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def perform_download(remote_path, local_path)
52
+ bucket_path = remote_path.to_s.gsub('s3://', '').split('/')[1..].join('/')
53
+ obj = s3_bucket.object(bucket_path)
54
+ # byebug
55
+ obj.download_file(local_path.to_s)
56
+ end
57
+
58
+ def report_download(remote_path, local_path)
59
+ msg = "#{local_path} not present locally, retrieving from #{remote_path}"
60
+ RSpec.configuration.reporter.message(msg)
61
+ end
62
+
63
+ def digest_match?(obj, digest)
64
+ digest == JSON.parse(obj.etag)
65
+ rescue StandardError
66
+ false
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module RemoteFixtures
5
+ # Storage backends for RemoteFixtures
6
+ module Backend
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative 'backend/s3_backend'
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-s3'
4
+ require_relative 'backend'
5
+
6
+ module RSpec
7
+ module RemoteFixtures
8
+ # Configuration namespace for RemoteFixtures
9
+ #
10
+ # Defaults:
11
+ # `manifest_path`: `spec/fixtures.json`
12
+ # `fixture_path`: `spec/fixtures/`
13
+ # `backend`: `RSpec::RemoteFixtures::Backend::S3Backend`
14
+ # `backend_path`: None, must be configured
15
+ # `check_remote_fixture_path`: ``:download``
16
+ module Config
17
+ def self.manifest_path=(value)
18
+ @manifest_path = value
19
+ end
20
+
21
+ def self.manifest_path
22
+ @manifest_path || 'spec/fixtures.json'
23
+ end
24
+
25
+ def self.fixture_path=(value)
26
+ value = Pathname.new(value) unless value.is_a? Pathname
27
+ @fixture_path = value
28
+ end
29
+
30
+ def self.fixture_path
31
+ @fixture_path ||= Pathname.new('spec/fixtures/')
32
+ end
33
+
34
+ def self.backend=(value)
35
+ @backend = value
36
+ end
37
+
38
+ def self.backend
39
+ @backend || Backend::S3Backend
40
+ end
41
+
42
+ def self.backend_path=(value)
43
+ @backend_path = value
44
+ end
45
+
46
+ def self.backend_path
47
+ @backend_path
48
+ end
49
+
50
+ def self.s3_client
51
+ @s3_client ||= Aws::S3::Client.new
52
+ end
53
+
54
+ def self.s3_client=(value)
55
+ @s3_client = value
56
+ end
57
+
58
+ def self.check_remote_fixture_digest=(value)
59
+ @check_remote_fixture_digest = value
60
+ end
61
+
62
+ def self.check_remote_fixture_digest
63
+ @check_remote_fixture_digest || :download
64
+ end
65
+
66
+ def self.reset!
67
+ @backend_path = nil
68
+ @check_remote_fixture_digest = nil
69
+ @fixture_path = nil
70
+ @backend = nil
71
+ @manifest_path = nil
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module RemoteFixtures
5
+ # Hooks into RSpec so that RemoteFixtures is available in examples
6
+ module ExampleGroup
7
+ def remote_fixture_path(path)
8
+ RemoteFixtures.ensure_file(path)
9
+ end
10
+
11
+ # need to maintain compatibility with rspec-rails
12
+ # rubocop:disable Style/OptionalBooleanParameter
13
+ def fixture_file_upload(path, mime = nil, binary = false)
14
+ RemoteFixtures.ensure_file(path)
15
+ super(path, mime, binary)
16
+ end
17
+ # rubocop:enable Style/OptionalBooleanParameter
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
6
+
7
+ module RSpec
8
+ module RemoteFixtures
9
+ # Keep track of fixture files, their digest, and their remote locations
10
+ module Manifest
11
+ # Support accepting a remote path and digest in case upload has already been performed
12
+ def self.add_fixture!(relative_path, remote_path = nil, digest = nil)
13
+ digest ||= compute_digest(relative_path)
14
+ remote_path ||= RemoteFixtures.upload(relative_path, digest)
15
+ manifest[relative_path] = { 'digest' => digest, 'remote_path' => remote_path }
16
+ end
17
+
18
+ def self.persist_manifest!
19
+ File.write(manifest_path, manifest.to_json)
20
+ end
21
+
22
+ def self.entry_for(relative_path)
23
+ manifest[relative_path]
24
+ end
25
+
26
+ def self.manifest
27
+ return @manifest if defined?(@manifest)
28
+
29
+ init_manifest! unless File.exist?(manifest_path)
30
+ @manifest = JSON.parse(File.read(manifest_path))
31
+ end
32
+
33
+ def self.init_manifest!
34
+ puts "Initializing rspec-remote-fixtures manifest #{manifest_path}"
35
+ File.write(manifest_path, {}.to_json)
36
+ end
37
+
38
+ def self.manifest_path
39
+ Config.manifest_path
40
+ end
41
+
42
+ def self.compute_digest(path)
43
+ File.open(RemoteFixtures.full_path_from_relative(path), 'rb') { |f| Digest::MD5.hexdigest(f.read) }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module RemoteFixtures
5
+ VERSION = '0.2.0'
6
+ end
7
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'remote_fixtures/version'
4
+ require_relative 'remote_fixtures/manifest'
5
+ require_relative 'remote_fixtures/example_group'
6
+ require_relative 'remote_fixtures/backend'
7
+ require_relative 'remote_fixtures/config'
8
+
9
+ # Why would I ever want to use this gem?
10
+ # =========================================
11
+ # This gem lets you stop committing large fixture files, without having to worry about git-lfs.
12
+ # Furthermore, a great many applications use docker images to run in production and CI. If you have
13
+ # hundreds of MB worth of fixture files, these files are first downloaded to wherever the image is being built,
14
+ # then uploaded, over the network, likely to many different CI workers and production instances.
15
+ #
16
+ # Once the files are there, it's likely only a small proportion of these workers actually *need* any particular file.
17
+ #
18
+ # Thus, rspec-remote-fixtures: A gem that sticks your rspec fixtures in S3, and downloads them transparently
19
+
20
+ module RSpec
21
+ # An on-demand-fixture-downloader for RSpec
22
+ module RemoteFixtures
23
+ class Error < StandardError; end
24
+
25
+ def self.backend_inst
26
+ @backend_inst ||= Config.backend.new
27
+ end
28
+
29
+ def self.ensure_file(relative_path)
30
+ full_path = full_path_from_relative(relative_path)
31
+ entry = Manifest.entry_for(relative_path)
32
+ log_not_found(relative_path) unless entry
33
+
34
+ verify_checksum(entry, full_path) if Config.check_remote_fixture_digest == :always
35
+ file_present = File.exist?(full_path)
36
+
37
+ retrieve_entry(entry, full_path) unless file_present
38
+ maybe_verify(entry, full_path, file_present)
39
+
40
+ full_path
41
+ end
42
+
43
+ def self.full_path_from_relative(relative_path)
44
+ Pathname.new(Config.fixture_path).join(relative_path)
45
+ end
46
+
47
+ def self.retrieve_entry(entry, dest)
48
+ raise Error, "Attempted to retrieve #{dest} but it wasn't present in the manifest" unless entry
49
+
50
+ FileUtils.mkdir_p(dest.dirname)
51
+ backend_inst.download(entry['remote_path'], dest)
52
+ end
53
+
54
+ def self.maybe_verify(entry, path, file_present)
55
+ config_val = Config.check_remote_fixture_digest
56
+ return if config_val == :never
57
+ return if config_val == :download && file_present
58
+
59
+ raise Error, "Unable to verify digest for #{path} - entry not found" unless entry
60
+
61
+ digest = Manifest.compute_digest(path)
62
+ raise Error, "Digest for #{path} did not match manifest entry, aborting!" unless digest == entry['digest']
63
+ end
64
+
65
+ def self.setup_examples!
66
+ return if @setup_done
67
+
68
+ @setup_done = true
69
+ RSpec.configuration.prepend(ExampleGroup)
70
+ end
71
+
72
+ def self.log_not_found(relative_path)
73
+ msg = "Warning: fixture #{relative_path} not found in manifest, this spec may fail elsewhere!"
74
+ RSpec.configuration.reporter.message(msg)
75
+ end
76
+
77
+ def self.upload(relative_path, digest)
78
+ full_path = full_path_from_relative(relative_path)
79
+ backend_inst.upload(full_path, digest)
80
+ end
81
+
82
+ def self.setup_rspec!
83
+ RSpec.configuration.fixture_path = Config.fixture_path if RSpec.configuration.respond_to?(:fixture_path=)
84
+ if RSpec.configuration.respond_to?(:file_fixture_path=)
85
+ RSpec.configuration.file_fixture_path = Config.fixture_path
86
+ end
87
+
88
+ RSpec::Core::World.prepend(World)
89
+ FactoryBot::SyntaxRunner.include(ExampleGroup) if defined? FactoryBot::SyntaxRunner
90
+ end
91
+
92
+ # RSpec uses this for global data that's not configuration
93
+ module World
94
+ def ordered_example_groups
95
+ RSpec::RemoteFixtures.setup_examples!
96
+ super
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/rspec/remote_fixtures/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'rspec-remote_fixtures'
7
+ spec.version = RSpec::RemoteFixtures::VERSION
8
+ spec.authors = ['Aleks Clark']
9
+ spec.email = ['aleks.clark@gmail.com']
10
+
11
+ spec.summary = 'Allow rspec to fetch fixture files on demand'
12
+ spec.description = 'Allow rspec to fetch fixture files on demand.'
13
+ spec.homepage = 'https://github.com/aleksclark/rspec-remote_fixtures'
14
+ spec.required_ruby_version = '>= 2.6.0'
15
+
16
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/aleksclark/rspec-remote_fixtures'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/aleksclark/rspec-remote_fixtures/blob/main/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'activesupport', '>= 6.1'
34
+ spec.add_dependency 'aws-sdk-s3', '~> 1.0'
35
+ spec.add_dependency 'rspec', '~> 3.12'
36
+
37
+ # For more information and examples about making a new gem, check out our
38
+ # guide at: https://bundler.io/guides/creating_gem.html
39
+ spec.metadata['rubygems_mfa_required'] = 'true'
40
+ end
@@ -0,0 +1,6 @@
1
+ module RSpec
2
+ module RemoteFixtures
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-remote_fixtures
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleks Clark
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-s3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ description: Allow rspec to fetch fixture files on demand.
56
+ email:
57
+ - aleks.clark@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".rspec"
63
+ - ".rubocop.yml"
64
+ - ".simplecov"
65
+ - CHANGELOG.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - README.md
69
+ - Rakefile
70
+ - lib/generators/rspec/fixture_manifest_generator.rb
71
+ - lib/rspec/remote_fixtures.rb
72
+ - lib/rspec/remote_fixtures/backend.rb
73
+ - lib/rspec/remote_fixtures/backend/s3_backend.rb
74
+ - lib/rspec/remote_fixtures/config.rb
75
+ - lib/rspec/remote_fixtures/example_group.rb
76
+ - lib/rspec/remote_fixtures/manifest.rb
77
+ - lib/rspec/remote_fixtures/version.rb
78
+ - rspec-remote_fixtures.gemspec
79
+ - sig/rspec/remote_fixtures.rbs
80
+ homepage: https://github.com/aleksclark/rspec-remote_fixtures
81
+ licenses: []
82
+ metadata:
83
+ allowed_push_host: https://rubygems.org
84
+ homepage_uri: https://github.com/aleksclark/rspec-remote_fixtures
85
+ source_code_uri: https://github.com/aleksclark/rspec-remote_fixtures
86
+ changelog_uri: https://github.com/aleksclark/rspec-remote_fixtures/blob/main/CHANGELOG.md
87
+ rubygems_mfa_required: 'true'
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 2.6.0
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubygems_version: 3.3.26
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Allow rspec to fetch fixture files on demand
107
+ test_files: []