Compare commits
8 Commits
20b223ea5a
...
ad1659835b
Author | SHA1 | Date |
---|---|---|
|
ad1659835b | 5 months ago |
|
e6701ab2fa | 5 months ago |
|
8e330d06d6 | 5 months ago |
|
893d96d805 | 5 months ago |
|
06d4a50b28 | 5 months ago |
|
1fa9669f1d | 5 months ago |
|
09e071d6c4 | 5 months ago |
|
e12c5bbdfb | 5 months ago |
@ -0,0 +1,7 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
eval "$(bin/load-rbenv "$1")" |
||||
BASE_DIR="$(dirname "$(dirname "$0")")" |
||||
shift |
||||
LD_PRELOAD="$BASE_DIR/build/libfake.so" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$BASE_DIR/build/" \ |
||||
exec rspec "$@" |
@ -1,52 +1,54 @@ |
||||
module CryptCheck |
||||
module Tls |
||||
class Curve |
||||
attr_reader :name |
||||
module Tls |
||||
class Curve |
||||
attr_reader :name |
||||
|
||||
def initialize(name) |
||||
name = name.to_sym if name.is_a? String |
||||
@name = name |
||||
end |
||||
def initialize(name) |
||||
name = name.to_sym if name.is_a? String |
||||
@name = name |
||||
end |
||||
|
||||
SUPPORTED = %i(secp256k1 sect283k1 sect283r1 secp384r1 |
||||
SUPPORTED = (%i(secp256k1 sect283k1 sect283r1 secp384r1 |
||||
sect409k1 sect409r1 secp521r1 sect571k1 sect571r1 |
||||
prime192v1 prime256v1 |
||||
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1 x25519).collect { |c| self.new c }.freeze |
||||
|
||||
extend Enumerable |
||||
|
||||
def self.each(&block) |
||||
SUPPORTED.each &block |
||||
end |
||||
|
||||
def to_s |
||||
@name |
||||
end |
||||
|
||||
def to_h |
||||
{ name: @name, states: self.states } |
||||
end |
||||
|
||||
def ==(other) |
||||
case other |
||||
when String |
||||
@name == other.to_sym |
||||
when Symbol |
||||
@name == other |
||||
else |
||||
@name == other.name |
||||
end |
||||
end |
||||
|
||||
protected |
||||
include State |
||||
|
||||
CHECKS = [].freeze |
||||
|
||||
protected |
||||
def available_checks |
||||
CHECKS |
||||
end |
||||
end |
||||
end |
||||
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1 x25519) & OpenSSL::PKey::EC::builtin_curves.collect { |c| c.first.to_sym }).collect { |c| self.new c }.freeze |
||||
|
||||
extend Enumerable |
||||
|
||||
def self.each(&block) |
||||
SUPPORTED.each &block |
||||
end |
||||
|
||||
def to_s |
||||
@name |
||||
end |
||||
|
||||
def to_h |
||||
{ name: @name, states: self.states } |
||||
end |
||||
|
||||
def ==(other) |
||||
case other |
||||
when String |
||||
@name == other.to_sym |
||||
when Symbol |
||||
@name == other |
||||
else |
||||
@name == other.name |
||||
end |
||||
end |
||||
|
||||
protected |
||||
|
||||
include State |
||||
|
||||
CHECKS = [].freeze |
||||
|
||||
protected |
||||
|
||||
def available_checks |
||||
CHECKS |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -1,67 +1,67 @@ |
||||
require 'httparty' |
||||
|
||||
module CryptCheck |
||||
module Tls |
||||
module Https |
||||
class Server < Tls::Server |
||||
attr_reader :hsts |
||||
module Tls |
||||
module Https |
||||
class Server < Tls::Server |
||||
attr_reader :hsts |
||||
|
||||
def initialize(hostname, ip, family, port = 443) |
||||
super |
||||
fetch_hsts |
||||
end |
||||
def initialize(hostname, ip, family, port = 443) |
||||
super |
||||
fetch_hsts |
||||
end |
||||
|
||||
def fetch_hsts |
||||
port = @port == 443 ? '' : ":#{@port}" |
||||
def fetch_hsts |
||||
port = @port == 443 ? '' : ":#{@port}" |
||||
|
||||
begin |
||||
response = ::HTTParty.head "https://#{@hostname}#{port}/", |
||||
{ |
||||
follow_redirects: false, |
||||
verify: false, |
||||
timeout: TLS_TIMEOUT, |
||||
ssl_version: @supported_methods.first.to_sym, |
||||
ciphers: Cipher::ALL |
||||
} |
||||
if header = response.headers['strict-transport-security'] |
||||
name, value = header.split '=' |
||||
if name == 'max-age' |
||||
@hsts = value.to_i |
||||
Logger.info { 'HSTS : ' + @hsts.to_s.colorize(hsts_long? ? :good : nil) } |
||||
return |
||||
end |
||||
end |
||||
rescue Exception => e |
||||
Logger.debug { e } |
||||
end |
||||
begin |
||||
response = ::HTTParty.head "https://#{@hostname}#{port}/", |
||||
{ |
||||
follow_redirects: false, |
||||
verify: false, |
||||
timeout: TLS_TIMEOUT, |
||||
ssl_version: @supported_methods.first.to_sym, |
||||
ciphers: Cipher::ALL |
||||
} |
||||
if header = response.headers['strict-transport-security'] |
||||
name, value = header.split '=' |
||||
if name == 'max-age' |
||||
@hsts = value.to_i |
||||
Logger.info { 'HSTS : ' + @hsts.to_s.colorize(hsts_long? ? :good : nil) } |
||||
return |
||||
end |
||||
end |
||||
rescue Exception => e |
||||
Logger.debug { e } |
||||
end |
||||
|
||||
Logger.info { 'No HSTS'.colorize :warning } |
||||
@hsts = nil |
||||
end |
||||
Logger.info { 'No HSTS'.colorize :warning } |
||||
@hsts = nil |
||||
end |
||||
|
||||
def hsts? |
||||
!@hsts.nil? |
||||
end |
||||
def hsts? |
||||
!@hsts.nil? |
||||
end |
||||
|
||||
LONG_HSTS = 6 * 30 * 24 * 60 * 60 |
||||
LONG_HSTS = 6 * 30 * 24 * 60 * 60 |
||||
|
||||
def hsts_long? |
||||
hsts? and @hsts >= LONG_HSTS |
||||
end |
||||
def hsts_long? |
||||
hsts? and @hsts >= LONG_HSTS |
||||
end |
||||
|
||||
def to_h |
||||
super.merge({ hsts: @hsts }) |
||||
end |
||||
def to_h |
||||
super.merge({ hsts: @hsts }) |
||||
end |
||||
|
||||
protected |
||||
protected |
||||
|
||||
def available_checks |
||||
super + [ |
||||
[:hsts, %i(warning good great), -> (s) { s.hsts_long? ? :great : s.hsts? ? :good : :warning }], |
||||
#[:must_staple, :best, -> (s) { s.must_staple? }], |
||||
] |
||||
end |
||||
end |
||||
end |
||||
end |
||||
def available_checks |
||||
super + [ |
||||
[:hsts, %i(warning good great), -> (s) { s.hsts_long? ? :great : s.hsts? ? :good : :warning }], |
||||
#[:must_staple, :best, -> (s) { s.must_staple? }], |
||||
] |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -1,149 +1,20 @@ |
||||
require 'awesome_print' |
||||
|
||||
module CryptCheck |
||||
describe Grade do |
||||
describe '#grade' do |
||||
def obj(trust: true, valid: true, **states) |
||||
Class.new do |
||||
def initialize(trust, valid, states) |
||||
@trust, @valid, @states = trust, valid, states |
||||
end |
||||
|
||||
include Grade |
||||
|
||||
def trusted? |
||||
@trust |
||||
end |
||||
|
||||
def valid? |
||||
@valid |
||||
end |
||||
|
||||
def states |
||||
State.empty.merge @states |
||||
end |
||||
end.new trust, valid, states |
||||
end |
||||
|
||||
it 'must return :V if not valid' do |
||||
obj = obj valid: false, critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :V |
||||
end |
||||
|
||||
it 'must return :T if not trusted' do |
||||
obj = obj trust: false, critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :T |
||||
end |
||||
|
||||
it 'must return :G if critical' do |
||||
obj = obj critical: { foo: false, bar: nil, baz: true }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :G |
||||
end |
||||
|
||||
it 'must return :F if error' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil, baz: true }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :F |
||||
end |
||||
|
||||
it 'must return :E if warning' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil, baz: true }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :E |
||||
end |
||||
|
||||
it 'must return :D if nor good nor bad' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: false, bar: nil }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :D |
||||
end |
||||
|
||||
it 'must return :C if some good' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: false, bar: nil, baz: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :C |
||||
end |
||||
|
||||
it 'must return :C+ if all good' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: false, bar: nil }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :'C+' |
||||
end |
||||
|
||||
it 'must return :B if some great' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: false, bar: nil, baz: true }, |
||||
best: { foo: true, bar: nil } |
||||
expect(obj.grade).to eq :B |
||||
end |
||||
|
||||
it 'must return :B+ if all great' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: false, bar: nil } |
||||
expect(obj.grade).to eq :'B+' |
||||
end |
||||
|
||||
it 'must return :A if some best' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: false, bar: nil, baz: true } |
||||
expect(obj.grade).to eq :A |
||||
end |
||||
|
||||
it 'must return :A+ if all best' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :'A+' |
||||
end |
||||
end |
||||
end |
||||
describe Grade do |
||||
describe '#compare' do |
||||
it 'must return correct order' do |
||||
expect(Grade.compare('A', 'B')).to be -1 |
||||
expect(Grade.compare('A', 'A')).to be 0 |
||||
expect(Grade.compare('B', 'A')).to be 1 |
||||
|
||||
expect(Grade.compare('A+', 'A')).to be -1 |
||||
expect(Grade.compare('A+', 'A+')).to be 0 |
||||
expect(Grade.compare('A', 'A+')).to be 1 |
||||
|
||||
expected = %i[A+ A B+ B C+ C D E F G V T X] |
||||
sorted = expected.shuffle |
||||
.sort &Grade.method(:compare) |
||||
expect(sorted).to eq expected |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -1,94 +1,94 @@ |
||||
module CryptCheck::Tls |
||||
describe Cert do |
||||
around :each do |example| |
||||
FakeTime.freeze(Time.utc 2000, 6, 1) { example.run } |
||||
end |
||||
|
||||
describe '::trusted?' do |
||||
it 'must accept valid certificate' do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq :trusted |
||||
end |
||||
|
||||
it 'must reject self signed certificate' do |
||||
cert, ca = chain(%w(self-signed ca)) |
||||
trust = Cert.trusted? cert, [], roots: ca |
||||
expect(trust).to eq 'self signed certificate' |
||||
|
||||
# Case for SSLv2 |
||||
cert, ca = chain(%w(self-signed ca)) |
||||
trust = Cert.trusted? cert, nil, roots: ca |
||||
expect(trust).to eq 'self signed certificate' |
||||
end |
||||
|
||||
it 'must reject unknown CA' do |
||||
cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: [] |
||||
expect(trust).to eq 'unable to get issuer certificate' |
||||
end |
||||
|
||||
it 'must reject missing intermediate chain' do |
||||
cert, ca = chain(%w(ecdsa-prime256v1 ca)) |
||||
chain = [] |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'unable to get local issuer certificate' |
||||
end |
||||
|
||||
it 'must reject expired certificate' do |
||||
FakeTime.freeze Time.utc(2002, 1, 1) do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'certificate has expired' |
||||
end |
||||
end |
||||
|
||||
it 'must reject not yet valid certificate' do |
||||
FakeTime.freeze Time.utc(1999, 1, 1) do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'certificate is not yet valid' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#md5?' do |
||||
it 'must detect md5 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.md5?).to be true |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.md5?).to be false |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.md5?).to be false |
||||
end |
||||
end |
||||
|
||||
describe '#sha1?' do |
||||
it 'must detect sha1 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.sha1?).to be false |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.sha1?).to be true |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.sha1?).to be false |
||||
end |
||||
end |
||||
|
||||
describe '#sha2?' do |
||||
it 'must detect sha2 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.sha2?).to be false |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.sha2?).to be false |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.sha2?).to be true |
||||
end |
||||
end |
||||
end |
||||
describe Cert do |
||||
around :each do |example| |
||||
Fake.freeze(Time.utc 2000, 6, 1) { example.run } |
||||
end |
||||
|
||||
describe '::trusted?' do |
||||
it 'must accept valid certificate' do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq :trusted |
||||
end |
||||
|
||||
it 'must reject self signed certificate' do |
||||
cert, ca = chain(%w(self-signed ca)) |
||||
trust = Cert.trusted? cert, [], roots: ca |
||||
expect(trust).to eq 'self signed certificate' |
||||
|
||||
# Case for SSLv2 |
||||
cert, ca = chain(%w(self-signed ca)) |
||||
trust = Cert.trusted? cert, nil, roots: ca |
||||
expect(trust).to eq 'self signed certificate' |
||||
end |
||||
|
||||
it 'must reject unknown CA' do |
||||
cert, *chain = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: [] |
||||
expect(trust).to eq 'unable to get issuer certificate' |
||||
end |
||||
|
||||
it 'must reject missing intermediate chain' do |
||||
cert, ca = chain(%w(ecdsa-prime256v1 ca)) |
||||
chain = [] |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'unable to get local issuer certificate' |
||||
end |
||||
|
||||
it 'must reject expired certificate' do |
||||
Fake.freeze Time.utc(2002, 1, 1) do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'certificate has expired' |
||||
end |
||||
end |
||||
|
||||
it 'must reject not yet valid certificate' do |
||||
Fake.freeze Time.utc(1999, 1, 1) do |
||||
cert, *chain, ca = chain(%w(ecdsa-prime256v1 intermediate ca)) |
||||
trust = Cert.trusted? cert, chain, roots: ca |
||||
expect(trust).to eq 'certificate is not yet valid' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#md5?' do |
||||
it 'must detect md5 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.md5?).to be true |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.md5?).to be false |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.md5?).to be false |
||||
end |
||||
end |
||||
|
||||
describe '#sha1?' do |
||||
it 'must detect sha1 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.sha1?).to be false |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.sha1?).to be true |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.sha1?).to be false |
||||
end |
||||
end |
||||
|
||||
describe '#sha2?' do |
||||
it 'must detect sha2 certificate' do |
||||
cert = Cert.new cert(:md5) |
||||
expect(cert.sha2?).to be false |
||||
|
||||
cert = Cert.new cert(:sha1) |
||||
expect(cert.sha2?).to be false |
||||
|
||||
cert = Cert.new cert(:ecdsa, :prime256v1) |
||||
expect(cert.sha2?).to be true |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,149 @@ |
||||
module CryptCheck |
||||
module Tls |
||||
describe Grade do |
||||
describe '#grade' do |
||||
def obj(trust: true, valid: true, **states) |
||||
Class.new do |
||||
def initialize(trust, valid, states) |
||||
@trust, @valid, @states = trust, valid, states |
||||
end |
||||
|
||||
include Grade |
||||
|
||||
def trusted? |
||||
@trust |
||||
end |
||||
|
||||
def valid? |
||||
@valid |
||||
end |
||||
|
||||
def states |
||||
State.empty.merge @states |
||||
end |
||||
end.new trust, valid, states |
||||
end |
||||
|
||||
it 'must return :V if not valid' do |
||||
obj = obj valid: false, critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :V |
||||
end |
||||
|
||||
it 'must return :T if not trusted' do |
||||
obj = obj trust: false, critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :T |
||||
end |
||||
|
||||
it 'must return :G if critical' do |
||||
obj = obj critical: { foo: false, bar: nil, baz: true }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :G |
||||
end |
||||
|
||||
it 'must return :F if error' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil, baz: true }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :F |
||||
end |
||||
|
||||
it 'must return :E if warning' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil, baz: true }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :E |
||||
end |
||||
|
||||
it 'must return :D if nor good nor bad' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: false, bar: nil }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :D |
||||
end |
||||
|
||||
it 'must return :C if some good' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: false, bar: nil, baz: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :C |
||||
end |
||||
|
||||
it 'must return :C+ if all good' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: false, bar: nil }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :'C+' |
||||
end |
||||
|
||||
it 'must return :B if some great' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: false, bar: nil, baz: true }, |
||||
best: { foo: true, bar: nil } |
||||
expect(obj.grade).to eq :B |
||||
end |
||||
|
||||
it 'must return :B+ if all great' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: false, bar: nil } |
||||
expect(obj.grade).to eq :'B+' |
||||
end |
||||
|
||||
it 'must return :A if some best' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: false, bar: nil, baz: true } |
||||
expect(obj.grade).to eq :A |
||||
end |
||||
|
||||
it 'must return :A+ if all best' do |
||||
obj = obj critical: { foo: false, bar: nil }, |
||||
error: { foo: false, bar: nil }, |
||||
warning: { foo: false, bar: nil }, |
||||
good: { foo: nil, bar: true }, |
||||
great: { foo: nil, bar: true }, |
||||
best: { foo: nil, bar: true } |
||||
expect(obj.grade).to eq :'A+' |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |