diff --git a/.github/workflows/obvious.yml b/.github/workflows/obvious.yml index 6e99095..f64d0c0 100644 --- a/.github/workflows/obvious.yml +++ b/.github/workflows/obvious.yml @@ -16,4 +16,4 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: | - bundle exec rspec + ruby -Ilib:test test/*_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index d259a46..068ec24 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,20 +6,6 @@ PATH GEM remote: https://rubygems.org/ specs: - diff-lcs (1.5.0) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.3) PLATFORMS ruby @@ -28,7 +14,6 @@ PLATFORMS DEPENDENCIES obvious! - rspec BUNDLED WITH 2.3.4 diff --git a/Rakefile b/Rakefile index 2995527..c686ef6 100644 --- a/Rakefile +++ b/Rakefile @@ -1 +1,9 @@ require "bundler/gem_tasks" +require "rake/testtask" + +Rake::TestTask.new(:test) do |t| + t.libs << "test" + t.libs << "lib" + t.test_files = FileList['test/**/*_test.rb'] +end +task :default => :test diff --git a/obvious.gemspec b/obvious.gemspec index 30491c6..2f63791 100644 --- a/obvious.gemspec +++ b/obvious.gemspec @@ -16,6 +16,4 @@ Gem::Specification.new do |gem| gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] - - gem.add_development_dependency "rspec" end diff --git a/spec/.spec_helper.rb.swp b/spec/.spec_helper.rb.swp deleted file mode 100644 index 2e2bda2..0000000 Binary files a/spec/.spec_helper.rb.swp and /dev/null differ diff --git a/spec/contract_spec.rb b/spec/contract_spec.rb deleted file mode 100644 index b33bced..0000000 --- a/spec/contract_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require_relative 'spec_helper' -require_relative '../lib/obvious/contract' - - -describe Hash do - describe '#has_shape?' do - it 'should return true for a valid shape' do - expect({ id: 1 }.has_shape?(id: Integer)).to be true - end - - it 'should return false for an invalid shape' do - expect({ id: 1 }.has_shape?(id: String)).to be false - end - - it 'should retrn the invalid field if return_field flag is set' do - expect({ id: 1 }.has_shape?({id: String}, true)).to eq [false, :id] - end - - it 'should allow for nil values to be returned' do - expect({ id: nil }.has_shape?({id: String})).to be true - end - end -end - -class TestContract < Obvious::Contract - contract_for :test, { - input: { id: Integer }, - output: { id: Integer, value: String } - } - - def test input - { id: 1, value: 'this is a test' } - end -end - -describe Obvious::Contract do - - describe "#call_method" do - it 'should return the correct output for valid input and output shapes' do - tc = TestContract.new - result = tc.test id: 1 - expect(result).to eq id: 1, value: 'this is a test' - end - - it 'should raise a contract input error with bad input' do - tc = TestContract.new - expect { tc.test Hash.new }.to raise_error Obvious::ContractInputError - end - - it 'should raise a DataNotFound error if {} is returned' do - tc = TestContract.new - allow(tc).to receive(:test_alias).and_return({}) - expect { tc.test id: 1 }.to raise_error Obvious::DataNotFoundError - end - - it 'should raise a contract output error if nil is returned' do - tc = TestContract.new - allow(tc).to receive(:test_alias).and_return(nil) - expect { tc.test id: 1 }.to raise_error Obvious::ContractOutputError - end - - end -end diff --git a/spec/entity_spec.rb b/spec/entity_spec.rb deleted file mode 100644 index eb0fa76..0000000 --- a/spec/entity_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -require_relative '../lib/obvious/entity' - -class Thing < Obvious::Entity - value :id, Integer - value :name, String -end - -class Thing2 < Obvious::Entity - value :foo , String - - validation :something, Proc.new { - if foo != "hello world" - msg = "Validation Error: Invalid value for foo, should be 'hello world'" - raise Obvious::ValidationError.new msg - end - } - - def modify_foo - @values[:foo] = 100 - end - -end - -class Thing3 < Obvious::Entity - value :foo , String - - validation :something, Proc.new { - @values[:foo] = 12 - } - -end - -# To test the entity, we are going to use classes that inherit from it instead -# of poking at it directly. In this case, I think that makes the most sense. -describe Thing do - it 'should create a valid object with valid input' do - input = { name: 'Thing', id: 1 } - t = Thing.new input - expect(t.name).to eq 'Thing' - expect(t.id).to eq 1 - end - - it 'should raise an error with invalid input types' do - input = { name: nil, id: nil } - expect { Thing.new input }.to raise_error Obvious::TypeError - end - - it 'should raise an error with extra fields in input' do - input = { name: 'Thing', id: 1, extra: 'should explode' } - expect { Thing.new input }.to raise_error Obvious::ShapeError - end - - it 'should raise an error when a method tries to modify a value' do - t = Thing2.new foo: 'hello world' - expect { t.modify_foo }.to raise_error RuntimeError - end - - describe '#to_hash' do - it 'should return a hash representation of the object' do - input = { name: 'Thing', id: 1 } - t = Thing.new input - expect(t.to_hash).to eq input - end - end - - describe 'validation' do - it 'should raise a validation error on a failed validation' do - expect { Thing2.new foo: 'not valid I promise!' }.to raise_error Obvious::ValidationError - end - - it 'should raise an error when trying to modify an object in a validation' do - expect { Thing3.new foo: 'hello world' }.to raise_error RuntimeError - end - end -end diff --git a/spec/obj_spec.rb b/spec/obj_spec.rb deleted file mode 100644 index 08a65a7..0000000 --- a/spec/obj_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require_relative '../lib/obvious/obj' - -class TestObj - include Obvious::Obj - - def initialize - @local = 'set!' - end - - define :defined_method, foo: String, bar: Integer do |input| - input - end - - define :defined_local do |input| - @local - end - - define :early_return_example do |input| - next true - false - end - -end - -describe Obvious::Obj do - - before do - @test = TestObj.new - end - - describe 'self.define' do - it 'should do the right thing with correct input' do - result = @test.defined_method foo: 'hello', bar: 12 - expect(result).to eq foo: 'hello', bar: 12 - end - - it 'should have access to instance variables' do - result = @test.defined_local - expect(result).to eq 'set!' - end - - it 'should raise an error for missing parameters' do - expect { @test.defined_method foo: 'hello' }.to raise_error { |error| - expect(error).to be_a ArgumentError - expect(error.message).to eq 'missing input field bar' - } - end - - it 'should raise an error for extra parameters' do - expect { @test.defined_method foo: 'hello', bar: 12, extra: 'this is extra!' }.to raise_error { |error| - expect(error).to be_a ArgumentError - expect(error.message).to eq 'invalid input field extra' - } - end - - it 'should raise an error for invalid types' do - expect { @test.defined_method foo: 1, bar: 12 }.to raise_error { |error| - expect(error).to be_a ArgumentError - expect(error.message).to eq 'invalid type for foo expected String' - } - - expect {@test.defined_method foo: 'hello', bar: nil }.to raise_error { |error| - expect(error).to be_a ArgumentError - expect(error.message).to eq 'invalid type for bar expected Integer' - } - end - - it 'should allow for early returns in control flow' do - result = @test.early_return_example - expect(result).to eq true - end - end - -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 0fd1652..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -RSpec.configure do |c| - c.mock_with :rspec -end diff --git a/test/contract_test.rb b/test/contract_test.rb new file mode 100644 index 0000000..23de74a --- /dev/null +++ b/test/contract_test.rb @@ -0,0 +1,62 @@ +require 'minitest/autorun' +require_relative '../lib/obvious/contract' + +class TestContract < Obvious::Contract + contract_for :test, { + input: { id: Integer }, + output: { id: Integer, value: String } + } + + def test input + { id: 1, value: 'this is a test' } + end +end + +class ContractTest < Minitest::Test + def test_valid_input + result = TestContract.new.test(id: 1) + assert_equal({id: 1, value: 'this is a test'}, result) + end + + def test_invalid_input + assert_raises Obvious::ContractInputError do + TestContract.new.test(Hash.new) + end + end + + def test_empty_hash_return + assert_raises Obvious::DataNotFoundError do + tc = TestContract.new + tc.stub :test_alias, {} do + tc.test(id: 1) + end + end + end + + def test_nil_return + assert_raises Obvious::ContractOutputError do + tc = TestContract.new + tc.stub :test_alias, nil do + tc.test(id: 1) + end + end + end +end + +class HashTest < Minitest::Test + def test_valid_has_shape + assert({id: 1}.has_shape?(id: Integer)) + end + + def test_invalid_has_shape + refute({id: 1}.has_shape?(id: String)) + end + + def test_has_shape_allow_nil_values + assert({id: nil}.has_shape?({id: String})) + end + + def test_has_shape_return_invalid_field + assert_equal([false, :id], { id: 1 }.has_shape?({id: String}, true)) + end +end diff --git a/test/entity_test.rb b/test/entity_test.rb new file mode 100644 index 0000000..11e968f --- /dev/null +++ b/test/entity_test.rb @@ -0,0 +1,76 @@ +require 'minitest/autorun' +require_relative '../lib/obvious/entity' + +class Thing < Obvious::Entity + value :id, Integer + value :name, String +end + +class Thing2 < Obvious::Entity + value :foo , String + + validation :something, -> { + if foo != "hello world" + msg = "Validation Error: Invalid value for foo, should be 'hello world'" + raise Obvious::ValidationError.new msg + end + } + + def modify_foo + @values[:foo] = 100 + end +end + +class Thing3 < Obvious::Entity + value :foo , String + + validation :something, -> { + @values[:foo] = 12 + } +end + + +# Test code begins here + +class EntityTest < Minitest::Test + def test_valid_input + t = Thing.new(name: 'Thing', id: 1) + assert_equal('Thing', t.name) + assert_equal(1, t.id) + end + + def test_invalid_input_types + assert_raises Obvious::TypeError do + Thing.new(name: nil, id: nil) + end + end + + def test_invalid_extra_field + assert_raises Obvious::ShapeError do + Thing.new(name: 'Thing', id: 1, extra: 'should explode') + end + end + + def test_method_modify_value + assert_raises RuntimeError do + Thing2.new(foo: 'hello world').modify_foo + end + end + + def test_to_hash + t = Thing.new(name: 'Thing', id: 1) + assert_equal({name: 'Thing', id: 1}, t.to_hash) + end + + def test_failed_validation + assert_raises Obvious::ValidationError do + Thing2.new(foo: 'not valid I promise!') + end + end + + def test_modify_value_inside_validation + assert_raises RuntimeError do + Thing3.new(foo: 'hello world') + end + end +end diff --git a/test/obj_test.rb b/test/obj_test.rb new file mode 100644 index 0000000..e4502a4 --- /dev/null +++ b/test/obj_test.rb @@ -0,0 +1,65 @@ +require 'minitest/autorun' +require_relative '../lib/obvious/obj' + +class TestObj + include Obvious::Obj + + def initialize + @local = 'set!' + end + + define :defined_method, foo: String, bar: Integer do |input| + input + end + + define :defined_local do |input| + @local + end + + define :early_return_example do |input| + next true + false + end +end + +class ObjTest < Minitest::Test + def test_valid_input + result = TestObj.new.defined_method foo: 'hello', bar: 12 + assert_equal({foo: 'hello', bar: 12}, result) + end + + def test_access_instance_variables + result = TestObj.new.defined_local + assert_equal('set!', result) + end + + def test_missing_parameters + error = assert_raises ArgumentError do + TestObj.new.defined_method foo: 'hello' + end + assert_equal('missing input field bar', error.message) + end + + def test_extra_parameters + error = assert_raises ArgumentError do + TestObj.new.defined_method foo: 'hello', bar: 12, extra: 'fail' + end + assert_equal('invalid input field extra', error.message) + end + + def test_invalid_types + error = assert_raises ArgumentError do + TestObj.new.defined_method foo: 1, bar: 12 + end + assert_equal('invalid type for foo expected String', error.message) + + error = assert_raises ArgumentError do + TestObj.new.defined_method foo: 'hello', bar: nil + end + assert_equal('invalid type for bar expected Integer', error.message) + end + + def test_early_return + assert(TestObj.new.early_return_example) + end +end