rspec-remote_fixtures 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []