tfa 0.0.14 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 165a204703e889ae75bcd6592b7c0461acab1ab105b938d8d0211e50f74b96d8
4
- data.tar.gz: 1af5e8d49c650b8caf622d83ef1693177da89b1df433b24f267c1132d40e869f
3
+ metadata.gz: 4986f1db7a233768d23cc39c31e8c7f7257c5291b99edc2bb62256dee75c75a6
4
+ data.tar.gz: 9b1421af345917ceee7775fb3f74abef84e7e33394af904199c4a1fbe66fbbb0
5
5
  SHA512:
6
- metadata.gz: 252e06668172e271dd76af579c888fe4906dfe57ec0e2c1f02fec0ac82cf1152c847c16e01e77416cdb1e7c5b4562e96df64de7c3765a57a2813904f58b5e47b
7
- data.tar.gz: 21b407384b87ae6cd58f5ad0593c274880231830772c939fc234208d88082cd88edb0fd1907df195e15067a1ddd9f47502da0682ad7447b80c05f5b267d588e8
6
+ metadata.gz: 32a5d58eddc632ed57af8f8701bd65b61fd3fcc1373ea8ad121f9dbdc650a5351bf62217a66e05a41a57fd6936a67f5855d4322e6243390150452c9cc30a8695
7
+ data.tar.gz: 347a0f07c968bb4b295f0c2caac950a71b68baee36b677efd91d2e8fb360ed9a15201acc9ee8fb26cb1e028d89e51b1508314e60f96d7bf5c719ec385ebd2755
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/mokhan/tfa.svg?branch=v0.0.2)](https://travis-ci.org/mokhan/tfa)
4
4
  [![Code Climate](https://codeclimate.com/github/mokhan/tfa.png)](https://codeclimate.com/github/mokhan/tfa)
5
- [![Gitter chat](https://badges.gitter.im/mokhan/tfa.png)](https://gitter.im/mokhan/tfa)
6
5
 
7
6
  This CLI helps to manage your one time passwords for different accounts/environments.
8
7
  The goal of this tool is to help you generate one time passwords quickly
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require 'tfa'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/tfa ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tfa'
3
+
4
+ begin
5
+ result = TFA::CLI.start(ARGV)
6
+ puts result unless result.is_a?(IO)
7
+ rescue OpenSSL::Cipher::CipherError => error
8
+ puts error.message
9
+ end
data/lib/tfa.rb CHANGED
@@ -1,7 +1,13 @@
1
+ require "base64"
2
+ require "digest"
3
+ require "json"
4
+ require "openssl"
1
5
  require "pstore"
2
6
  require "rotp"
3
- require "openssl"
4
- require "tfa/version"
5
- require "tfa/totp_command"
6
- require "tfa/storage"
7
+ require "yaml/store"
8
+
7
9
  require "tfa/cli"
10
+ require "tfa/secure_storage"
11
+ require "tfa/storage"
12
+ require "tfa/totp_command"
13
+ require "tfa/version"
data/lib/tfa/cli.rb CHANGED
@@ -4,11 +4,12 @@ module TFA
4
4
  class CLI < Thor
5
5
  package_name "TFA"
6
6
  class_option :filename
7
+ class_option :directory
8
+ class_option :passphrase
7
9
 
8
10
  desc "add NAME SECRET", "add a new secret to the database"
9
11
  def add(name, secret)
10
- secret = clean(secret)
11
- storage.save(name, secret)
12
+ storage.save(name, clean(secret))
12
13
  "Added #{name}"
13
14
  end
14
15
 
@@ -32,10 +33,70 @@ module TFA
32
33
  TotpCommand.new(storage).run('', secret)
33
34
  end
34
35
 
36
+ desc "upgrade", "upgrade the database."
37
+ def upgrade
38
+ if !File.exist?(pstore_path)
39
+ say_status :error, "Unable to detect #{pstore_path}", :red
40
+ return
41
+ end
42
+ if File.exist?(secure_path)
43
+ say_status :error, "The new database format was detected.", :red
44
+ return
45
+ end
46
+
47
+ if yes? "Upgrade to #{secure_path}?"
48
+ secure_storage
49
+ pstore_storage.each do |row|
50
+ row.each do |name, secret|
51
+ secure_storage.save(name, secret) if yes?("Migrate `#{name}`?")
52
+ end
53
+ end
54
+ File.delete(pstore_path) if yes?("Delete `#{pstore_path}`?")
55
+ end
56
+ end
57
+
58
+ desc "encrypt", "encrypts the tfa database"
59
+ def encrypt
60
+ return unless ensure_upgraded!
61
+
62
+ secure_storage.encrypt!
63
+ end
64
+
65
+ desc "decrypt", "decrypts the tfa database"
66
+ def decrypt
67
+ return unless ensure_upgraded!
68
+
69
+ secure_storage.decrypt!
70
+ end
71
+
35
72
  private
36
73
 
37
74
  def storage
38
- @storage ||= Storage.new(filename: options[:filename] || 'tfa')
75
+ File.exist?(pstore_path) ? pstore_storage : secure_storage
76
+ end
77
+
78
+ def pstore_storage
79
+ @pstore_storage ||= Storage.new(pstore_path)
80
+ end
81
+
82
+ def secure_storage
83
+ @secure_storage ||= SecureStorage.new(Storage.new(secure_path), ->{ passphrase })
84
+ end
85
+
86
+ def filename
87
+ options[:filename] || '.tfa'
88
+ end
89
+
90
+ def directory
91
+ options[:directory] || Dir.home
92
+ end
93
+
94
+ def pstore_path
95
+ File.join(directory, "#{filename}.pstore")
96
+ end
97
+
98
+ def secure_path
99
+ File.join(directory, filename)
39
100
  end
40
101
 
41
102
  def clean(secret)
@@ -45,5 +106,26 @@ module TFA
45
106
  secret
46
107
  end
47
108
  end
109
+
110
+ def passphrase
111
+ @passphrase ||=
112
+ begin
113
+ result = options[:passphrase] || ask("Enter passphrase:\n", echo: false)
114
+ raise "Invalid Passphrase" if result.nil? || result.strip.empty?
115
+ result
116
+ end
117
+ end
118
+
119
+ def ensure_upgraded!
120
+ return true if upgraded?
121
+
122
+ error = "Use the `upgrade` command to upgrade your database."
123
+ say_status :error, error, :red
124
+ false
125
+ end
126
+
127
+ def upgraded?
128
+ !File.exist?(pstore_path) && File.exist?(secure_path)
129
+ end
48
130
  end
49
131
  end
@@ -0,0 +1,72 @@
1
+ module TFA
2
+ class SecureStorage
3
+ def initialize(original, passphrase_request)
4
+ @original = original
5
+ @passphrase_request = passphrase_request
6
+ end
7
+
8
+ def encrypt!(algorithm = "AES-256-CBC")
9
+ cipher = OpenSSL::Cipher.new(algorithm)
10
+ cipher.encrypt
11
+ cipher.key = digest
12
+ cipher.iv = iv = cipher.random_iv
13
+ plain_text = IO.read(@original.path)
14
+ json = JSON.generate(
15
+ algorithm: algorithm,
16
+ iv: Base64.encode64(iv),
17
+ cipher_text: Base64.encode64(cipher.update(plain_text) + cipher.final),
18
+ )
19
+ IO.write(@original.path, json)
20
+ end
21
+
22
+ def decrypt!
23
+ data = JSON.parse(IO.read(@original.path), symbolize_names: true)
24
+ decipher = OpenSSL::Cipher.new(data[:algorithm])
25
+ decipher.decrypt
26
+ decipher.key = digest
27
+ decipher.iv = Base64.decode64(data[:iv])
28
+ plain_text = decipher.update(Base64.decode64(data[:cipher_text]))
29
+ IO.write(@original.path, plain_text + decipher.final)
30
+ end
31
+
32
+ def encrypted?
33
+ return false unless File.exist?(@original.path)
34
+ JSON.parse(IO.read(@original.path))
35
+ true
36
+ rescue JSON::ParserError
37
+ false
38
+ end
39
+
40
+ private
41
+
42
+ def method_missing(name, *args, &block)
43
+ super unless @original.respond_to?(name)
44
+
45
+ was_encrypted = encrypted?
46
+ if was_encrypted
47
+ encrypted_content = IO.read(@original.path)
48
+ decrypt!
49
+ original_sha256 = Digest::SHA256.file(@original.path)
50
+ end
51
+ result = @original.public_send(name, *args, &block)
52
+ if was_encrypted
53
+ new_sha256 = Digest::SHA256.file(@original.path)
54
+
55
+ if original_sha256 == new_sha256
56
+ IO.write(@original.path, encrypted_content)
57
+ else
58
+ encrypt!
59
+ end
60
+ end
61
+ result
62
+ end
63
+
64
+ def respond_to_missing?(method, *)
65
+ @original.respond_to?(method)
66
+ end
67
+
68
+ def digest
69
+ @digest ||= Digest::SHA256.digest(@passphrase_request.call)
70
+ end
71
+ end
72
+ end
data/lib/tfa/storage.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  module TFA
2
2
  class Storage
3
3
  include Enumerable
4
-
5
- def initialize(options)
6
- @storage = PStore.new(File.join(Dir.home, ".#{options[:filename]}.pstore"))
4
+ attr_reader :path
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ @storage =
9
+ if ".pstore" == File.extname(path)
10
+ PStore.new(path)
11
+ else
12
+ YAML::Store.new(path)
13
+ end
7
14
  end
8
15
 
9
16
  def each
data/lib/tfa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TFA
2
- VERSION = "0.0.14".freeze
2
+ VERSION = "0.0.15".freeze
3
3
  end
data/tfa.gemspec CHANGED
@@ -13,14 +13,18 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/mokhan/tfa/"
14
14
  spec.license = "MIT"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
22
  spec.require_paths = ["lib"]
23
+ spec.required_ruby_version = "~> 2.0"
20
24
 
21
- spec.add_dependency "rotp"
22
- spec.add_dependency "thor"
25
+ spec.add_dependency "rotp", "~> 3.3"
26
+ spec.add_dependency "thor", "~> 0.20"
23
27
  spec.add_development_dependency "bundler", "~> 1.6"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "rake", "~> 12.3"
29
+ spec.add_development_dependency "rspec", "~> 3.7"
26
30
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tfa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-09 00:00:00.000000000 Z
11
+ date: 2018-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rotp
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '3.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '3.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: thor
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '0.20'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '0.20'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,30 +56,30 @@ dependencies:
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '12.3'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '12.3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '3.7'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: '3.7'
83
83
  description: A CLI to manage your time based one time passwords.
84
84
  email:
85
85
  - mo@mokhan.ca
@@ -95,16 +95,15 @@ files:
95
95
  - LICENSE.txt
96
96
  - README.md
97
97
  - Rakefile
98
- - bin/tfa
98
+ - bin/console
99
+ - bin/setup
100
+ - exe/tfa
99
101
  - lib/tfa.rb
100
102
  - lib/tfa/cli.rb
103
+ - lib/tfa/secure_storage.rb
101
104
  - lib/tfa/storage.rb
102
105
  - lib/tfa/totp_command.rb
103
106
  - lib/tfa/version.rb
104
- - spec/lib/cli_spec.rb
105
- - spec/lib/console_spec.rb
106
- - spec/lib/totp_command_spec.rb
107
- - spec/spec_helper.rb
108
107
  - tfa.gemspec
109
108
  homepage: https://github.com/mokhan/tfa/
110
109
  licenses:
@@ -116,9 +115,9 @@ require_paths:
116
115
  - lib
117
116
  required_ruby_version: !ruby/object:Gem::Requirement
118
117
  requirements:
119
- - - ">="
118
+ - - "~>"
120
119
  - !ruby/object:Gem::Version
121
- version: '0'
120
+ version: '2.0'
122
121
  required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  requirements:
124
123
  - - ">="
@@ -126,12 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
125
  version: '0'
127
126
  requirements: []
128
127
  rubyforge_project:
129
- rubygems_version: 2.7.3
128
+ rubygems_version: 2.7.5
130
129
  signing_key:
131
130
  specification_version: 4
132
131
  summary: A CLI to manage your time based one time passwords.
133
- test_files:
134
- - spec/lib/cli_spec.rb
135
- - spec/lib/console_spec.rb
136
- - spec/lib/totp_command_spec.rb
137
- - spec/spec_helper.rb
132
+ test_files: []
data/bin/tfa DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'tfa'
3
-
4
- puts TFA::CLI.start(ARGV)
data/spec/lib/cli_spec.rb DELETED
@@ -1,87 +0,0 @@
1
- module TFA
2
- describe CLI do
3
- subject { CLI.new }
4
-
5
- def code_for(secret)
6
- ::ROTP::TOTP.new(secret).now
7
- end
8
-
9
- let(:dev_secret) { ::ROTP::Base32.random_base32 }
10
- let(:prod_secret) { ::ROTP::Base32.random_base32 }
11
-
12
- describe "#add" do
13
- context "when a secret is added" do
14
- it "adds the secret" do
15
- subject.add("development", dev_secret)
16
- expect(subject.show("development")).to eql(dev_secret)
17
- end
18
- end
19
-
20
- context "when a full otpauth string is added" do
21
- it "strips out the url for just the secret" do
22
- url = "otpauth://totp/email@email.com?secret=#{dev_secret}&issuer="
23
-
24
- subject.add("development", url)
25
- expect(subject.show("development")).to eql(dev_secret)
26
- end
27
- end
28
- end
29
-
30
- describe "#show" do
31
- context "when a single key is given" do
32
- it "returns the secret" do
33
- subject.add("development", dev_secret)
34
- expect(subject.show("development")).to eql(dev_secret)
35
- end
36
- end
37
-
38
- context "when no key is given" do
39
- it "returns the secret for all keys" do
40
- subject.add("development", dev_secret)
41
- subject.add("production", prod_secret)
42
-
43
- result = subject.show.to_s
44
- expect(result).to include(dev_secret)
45
- expect(result).to include(prod_secret)
46
- end
47
- end
48
- end
49
-
50
- describe "#totp" do
51
- context "when a single key is given" do
52
- it "returns a time based one time password" do
53
- subject.add("development", dev_secret)
54
- expect(subject.totp("development")).to eql(code_for(dev_secret))
55
- end
56
- end
57
-
58
- context "when no key is given" do
59
- it "returns a time based one time password for all keys" do
60
- subject.add("development", dev_secret)
61
- subject.add("production", prod_secret)
62
-
63
- result = subject.totp.to_s
64
- expect(result).to include(code_for(dev_secret))
65
- expect(result).to include(code_for(prod_secret))
66
- end
67
- end
68
- end
69
-
70
- describe "#destroy" do
71
- let(:name) { "development" }
72
-
73
- it "removes the secret with the given name" do
74
- subject.add(name, dev_secret)
75
- subject.destroy(name)
76
-
77
- expect(subject.show(name)).to be_nil
78
- end
79
- end
80
-
81
- describe "#now" do
82
- it "returns a time based one time password for the given secret" do
83
- expect(subject.now(dev_secret)).to eql(code_for(dev_secret))
84
- end
85
- end
86
- end
87
- end
@@ -1,22 +0,0 @@
1
- module TFA
2
- describe CLI do
3
- subject { CLI.new }
4
- let(:secret) { ::ROTP::Base32.random_base32 }
5
-
6
- describe "#run" do
7
- context "when adding a key" do
8
- it "saves a new secret" do
9
- subject.add("development", secret)
10
- expect(subject.show("development")).to eql(secret)
11
- end
12
- end
13
-
14
- context "when getting a one time password" do
15
- it "creates a totp for a certain key" do
16
- subject.add("development", secret)
17
- expect(subject.totp("development")).to_not be_nil
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,55 +0,0 @@
1
- module TFA
2
- describe TotpCommand do
3
- subject { TotpCommand.new(storage) }
4
- let(:storage) { Storage.new(filename: SecureRandom.uuid) }
5
-
6
- def code_for(secret)
7
- ::ROTP::TOTP.new(secret).now
8
- end
9
-
10
- describe "#run" do
11
- context "when a single key is given" do
12
- let(:secret) { ::ROTP::Base32.random_base32 }
13
-
14
- it "returns a time based one time password for the authentication secret given" do
15
- storage.save("development", secret)
16
- expect(subject.run("development")).to eql(code_for(secret))
17
- end
18
- end
19
-
20
- context "when no arguments are given" do
21
- let(:development_secret) { ::ROTP::Base32.random_base32 }
22
- let(:staging_secret) { ::ROTP::Base32.random_base32 }
23
-
24
- it "returns the one time password for all keys" do
25
- storage.save("development", development_secret)
26
- storage.save("staging", staging_secret)
27
-
28
- expect(subject.run(nil)).to match_array([
29
- { "development" => code_for(development_secret) },
30
- { "staging" => code_for(staging_secret) }
31
- ])
32
- end
33
- end
34
-
35
- context "when the key is not known" do
36
- it "returns with nothing" do
37
- expect(subject.run(["blah"])).to be_empty
38
- end
39
- end
40
-
41
- context "when the secret is invalid" do
42
- let(:invalid_secret) { "hello world" }
43
-
44
- before :each do
45
- storage.save("development", invalid_secret)
46
- end
47
-
48
- it "returns an error message" do
49
- expected = { "development" => "INVALID SECRET" }
50
- expect(subject.run(["development"])).to match_array([expected])
51
- end
52
- end
53
- end
54
- end
55
- end
data/spec/spec_helper.rb DELETED
@@ -1,81 +0,0 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
- # file to always be loaded, without a need to explicitly require it in any files.
5
- #
6
- # Given that it is always loaded, you are encouraged to keep this file as
7
- # light-weight as possible. Requiring heavyweight dependencies from this file
8
- # will add to the boot time of your test suite on EVERY test run, even for an
9
- # individual file that may not need all of that loaded. Instead, make a
10
- # separate helper file that requires this one and then use it only in the specs
11
- # that actually need it.
12
- #
13
- # The `.rspec` file also contains a few flags that are not defaults but that
14
- # users commonly want.
15
- #
16
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
- require 'tfa'
18
- require 'securerandom'
19
- require 'tempfile'
20
- RSpec.configure do |config|
21
- # The settings below are suggested to provide a good initial experience
22
- # with RSpec, but feel free to customize to your heart's content.
23
- =begin
24
- # These two settings work together to allow you to limit a spec run
25
- # to individual examples or groups you care about by tagging them with
26
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
27
- # get run.
28
- config.filter_run :focus
29
- config.run_all_when_everything_filtered = true
30
-
31
- # Many RSpec users commonly either run the entire suite or an individual
32
- # file, and it's useful to allow more verbose output when running an
33
- # individual spec file.
34
- if config.files_to_run.one?
35
- # Use the documentation formatter for detailed output,
36
- # unless a formatter has already been configured
37
- # (e.g. via a command-line flag).
38
- config.default_formatter = 'doc'
39
- end
40
-
41
- # Print the 10 slowest examples and example groups at the
42
- # end of the spec run, to help surface which specs are running
43
- # particularly slow.
44
- config.profile_examples = 10
45
-
46
- # Run specs in random order to surface order dependencies. If you find an
47
- # order dependency and want to debug it, you can fix the order by providing
48
- # the seed, which is printed after each run.
49
- # --seed 1234
50
- config.order = :random
51
-
52
- # Seed global randomization in this process using the `--seed` CLI option.
53
- # Setting this allows you to use `--seed` to deterministically reproduce
54
- # test failures related to randomization by passing the same `--seed` value
55
- # as the one that triggered the failure.
56
- Kernel.srand config.seed
57
-
58
- # rspec-expectations config goes here. You can use an alternate
59
- # assertion/expectation library such as wrong or the stdlib/minitest
60
- # assertions if you prefer.
61
- config.expect_with :rspec do |expectations|
62
- # Enable only the newer, non-monkey-patching expect syntax.
63
- # For more details, see:
64
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
65
- expectations.syntax = :expect
66
- end
67
-
68
- # rspec-mocks config goes here. You can use an alternate test double
69
- # library (such as bogus or mocha) by changing the `mock_with` option here.
70
- config.mock_with :rspec do |mocks|
71
- # Enable only the newer, non-monkey-patching expect syntax.
72
- # For more details, see:
73
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
74
- mocks.syntax = :expect
75
-
76
- # Prevents you from mocking or stubbing a method that does not exist on
77
- # a real object. This is generally recommended.
78
- mocks.verify_partial_doubles = true
79
- end
80
- =end
81
- end