require './test/helper.rb' class TestDocument < Minitest::Test Document = CouchPillow::Document def mock_time return @time if @time @time = Time.now Time.stubs(:now).returns(@time) @time end def setup CouchPillow.db = FakeCouchbaseServer.new end def test_create d = Document.new d.save! end def test_timestamp d = Document.new({}, "1") d.save! assert d._created_at assert d._updated_at end def test_updated_at_doesnt_get_updated_at_initialize t = Time.parse("2008-06-21 13:30:00 UTC") d = Document.new({"_updated_at" => t}, "1") assert_equal t, d._updated_at end def test_default_type d = Document.new({}, "1") assert_equal "couchpillow", d.doc_type end def test_hash_with_proc_should_not_throw_error hsh = Hash.new { |hash,key| hash[key.to_s] if Symbol === key } hsh['foo'] = Hash.new { |hash,key| hash[key.to_s] if Symbol === key } hsh['bar'] = 200 d = Document.new(hsh, "1") d.save! end def test_type_subclasses d = Class.new(Document) do type 'test' end.new assert_equal "test", d.doc_type end def test_update d = Class.new(Document) do type 'test' attribute(:me) attribute(:batman) attribute(:apple) end.new refute d.me refute d.batman refute d.apple hash = { 'me' => 'too', :batman => 'robin', 'apple' => 123 } d.update(hash) assert_equal 'too', d.me assert_equal 'robin', d.batman assert_equal 123, d.apple assert d.save!, d.inspect end def test_update_existing_fields d = Class.new(Document) do type 'test' attribute(:a) attribute(:b) end.new({ a: 1, b: 2}) assert_equal 1, d[:a] assert_equal 1, d.a hash = { 'a' => 'too', } d.update(hash) assert_equal 'too', d[:a] assert_equal 2, d[:b] assert d.save! end def test_update_doesnt_trigger_validation d = Class.new(Document) do type 'test' attribute(:a).type(Integer) end.new({ a: 1, b: 2}) assert_equal 1, d[:a] assert_equal 1, d.a hash = { 'a' => 'too', } d.update(hash) assert_equal 'too', d.a assert_raises CouchPillow::ValidationError do assert d.save! end end def test_exclusion_other_keys d = Class.new(Document) do attribute :a attribute :b attribute :c end.new d.update({a: 1, b: 2, c: 3, d: 4}) assert d.save! assert_equal 1, d.a assert_equal 2, d.b assert_equal 3, d.c assert_equal false, d.has?(:d) end def test_to_json mock_time d = Document.new({}, "1") assert_equal "{\"_id\":\"1\",\"_type\":\"couchpillow\",\"_created_at\":\"#{mock_time.to_s}\",\"_updated_at\":\"#{mock_time.to_s}\"}", d.to_json end def test_to_hash mock_time d = Document.new({}, "1") assert_equal({ :_id => "1", :_created_at => mock_time, :_type => "couchpillow", :_updated_at => mock_time }, d.to_hash) end def test_brackets d = Document.new({}) d[123] = 'test' assert_equal 'test', d[123] d['foo'] = 'bar' assert_equal 'bar', d[:foo] d[:stuff] = [ 'john', 'mary' ] assert_equal ['john', 'mary'], d[:stuff] assert_equal nil, d[:something_else] end def test_rename_keys d = Class.new(Document) do attribute(:bar) attribute(:other) rename :foo => :bar end.new( { :foo => 123, :other => 'abc' } ) assert_equal 123, d[:bar] assert_equal 123, d.bar assert_equal nil, d[:foo] refute d.respond_to?(:foo) assert_equal 'abc', d.other end def test_rename_keys_multiple d = Class.new(Document) do attribute(:bar) attribute(:other) rename :foo => :bar, :dump => :other end.new( { :foo => 123, :dump => 'abc' } ) assert_equal 123, d[:bar] assert_equal 123, d.bar assert_equal nil, d[:foo] refute d.respond_to?(:foo) refute d.respond_to?(:dump) assert_equal 'abc', d.other end def test_rename_keys_reserved_keys assert_raises ArgumentError do d = Class.new(Document) do rename :_id => :bad end.new end assert_raises ArgumentError do d = Class.new(Document) do rename :_type => :err end.new end assert_raises ArgumentError do d = Class.new(Document) do rename :_created_at => :err end.new end assert_raises ArgumentError do d = Class.new(Document) do rename :bla => :_updated_at end.new end end def test_get klass = Class.new(Document) do type 'test' attribute(:foo) end k = klass.new k.foo = 100 k.save! k_id = k.id d = klass.get(k_id) assert_equal 100, d.foo end def test_get_returns_nil CouchPillow.db.expects(:get).with('couchpillow::123', { extended: true }).returns(nil) Document.expects(:_default_db).returns(CouchPillow.db) d = Document.get('123') assert_equal nil, d end def test_get_returns_a_document_of_a_different_type klass = Class.new(Document) do type 'test' end d = klass.get('123') assert_equal nil, d end def test_get_multiple_ids klass = Class.new(Document) do type 'test' attribute(:foo) end k = klass.new k.foo = 100 k.save! k2 = klass.new k2.foo = 200 k2.save! d = klass.get(k.id, k2.id) assert_equal 2, d.size assert_equal 100, d[0].foo assert_equal 200, d[1].foo end def test_get_multiple_ids_different_types_and_nils klassA = Class.new(Document) do type 'klassA' attribute(:foo) end klassB = Class.new(Document) do type 'klassB' attribute(:bar) end ka = klassA.new ka.foo = 100 ka.save! kb = klassB.new kb.bar = 200 kb.save! # kb is klassB, so it should be nil d = klassA.get(kb.id, ka.id) assert_equal 2, d.size assert_nil d[0] assert_equal 100, d[1].foo end def test_key_with_false_values klass = Class.new(Document) do attribute(:key) end d = klass.new({ :key => false }, "1") assert_equal false, d.key end def test_key_with_nil_values klass = Class.new(Document) do attribute(:key) end d = klass.new({ :key => nil }, "1") assert_equal nil, d.key end def test_attribute d = Class.new(Document) do type 'test' attribute(:foo) end.new d.foo = 12345 d.save! end def test_attribute_type d = Class.new(Document) do type 'test' attribute(:foo) .type(String) end.new d.foo = "test" d.save! end def test_attribute_auto_convert d = Class.new(Document) do type 'test' attribute(:foo) .type(String).auto_convert end.new d.foo = 1 d.save! assert_equal "1", d.foo end def test_attribute_with_directives d = Class.new(Document) do type 'test' attribute(:foo) .default { 100 } end.new d.save! assert_equal 100, d.foo end def test_attribute_no_default_but_required klass = Class.new(Document) do type 'test' attribute(:foo) .required end k = klass.new assert_raises CouchPillow::ValidationError do k.save! end end def test_attribute_with_default_but_also_required klass = Class.new(Document) do type 'test' attribute(:foo) .required .default { "batman" } end k = klass.new k.save! end def test_auto_convert_triggers_on_initialize d = Class.new(Document) do type 'test' attribute(:foo) .type(String).auto_convert attribute(:not_auto) .type(Integer) end.new( foo: 1, not_auto: "100" ) assert_equal "1", d.foo assert_equal "100", d.not_auto end def test_auto_convert_triggers_on_update d = Class.new(Document) do type 'test' attribute :foo do type(String) auto_convert end attribute :not_auto do type Integer end end.new( foo: 1, not_auto: "100" ) d.update( foo: 1776 ) assert_equal "1776", d.foo end def test_dsl_style d = Class.new(Document) do type 'test' attribute :foo do type String auto_convert required end attribute :bar do type Integer auto_convert required end end.new( foo: 1, bar: "100" ) assert_equal "1", d.foo assert_equal 100, d.bar end def test_assign_default_value_at_creation d = Class.new(Document) do type 'test' attribute :foo do type String auto_convert required default { "tester" } end end.new assert_equal "tester", d.foo end def test_ttl_support_from_opts d = Class.new(Document) do type 'test' attribute :foo do type String auto_convert required default { "tester" } end end.new d.save! ttl: 100 end def test_ttl_directive klass = Class.new(Document) do type 'test' ttl 10 attribute :foo do type String end end d = klass.new( foo: "bla" ) d.save! assert_equal 10, CouchPillow.db.ttl_value("test::#{d.id}") end def test_custom_db_connection conn1 = FakeCouchbaseServer.new klass = Class.new(Document) do type 'test' db conn1 attribute :foo do type String auto_convert required default { "tester" } end end d = klass.new({}, "1") d.foo = "hello" d.save! fromdefdb = CouchPillow.db.get("test::1") assert_nil fromdefdb fromprimdb = conn1.get("test::1") assert_equal "hello", fromprimdb[:foo] end def test_multiple_db_connections db1 = FakeCouchbaseServer.new db2 = FakeCouchbaseServer.new db3 = FakeCouchbaseServer.new klass = Class.new(Document) do type 'test' type_prefix false db db1 db db2 db db3 attribute :foo do type String auto_convert required default { "tester" } end end d = klass.new( {}, "1" ) d.foo = "hello" d.save! res = klass.wait data = db1.get("1") assert_equal "hello", data[:foo] data = db2.get("1") assert_equal "hello", data[:foo] data = db3.get("1") assert_equal "hello", data[:foo] end def test_cas_no_on_directive klass = Class.new(Document) do type 'test' attribute :foo do type String auto_convert required default { "tester" } end end d = klass.new( foo: "Hello" ) d.save! doc_id = d.id d1 = klass.get(doc_id) d2 = klass.get(doc_id) d2.foo = "Someone else" d2.save! assert_raises CouchPillow::CASError do d1.save! end end def test_cas_with_on_directive # must declare it here so Ruby can see the assert_equal methods cas_conflict_resolver = Proc.new do |original, theirs, mine| assert_equal "Hello", original[:foo] assert_equal "Someone else", theirs[:foo] assert_equal "This is mine", mine[:foo] mine end klass = Class.new(Document) do type 'test' attribute :foo do type String auto_convert required default { "tester" } end on :cas_conflict, &cas_conflict_resolver end d = klass.new( foo: "Hello" ) d.save! doc_id = d.id d1 = klass.get(doc_id) d1.foo = "This is mine" d2 = klass.get(doc_id) d2.foo = "Someone else" d2.save! d1.save! assert_equal "This is mine", klass.get(doc_id).foo end def test_type_prefix_directive_default assert Document._is_type_prefixed?, "type_prefix default is not true" end def test_type_prefix_directive_false klass = Class.new(Document) do type 'test' type_prefix false attribute :foo do type String end end d = klass.new( { foo: "Hello" } , "10" ) d.save! dataindb = CouchPillow.db.get("10") assert dataindb assert_equal "Hello", dataindb[:foo] end def test_type_prefix_directive_true klass = Class.new(Document) do type 'test' attribute :foo do type String end end d = klass.new( { foo: "Hello" } , "10" ) assert_equal "10", d.id d.save! d1 = klass.get("10") assert_equal "Hello", d1.foo dataindb = CouchPillow.db.get("test::10") assert dataindb assert_equal "Hello", dataindb[:foo] end def test_new_with_type_prefix klass = Class.new(Document) do type 'test' attribute :foo do type String end end d = klass.new( { foo: "Hello" } , "test::10" ) assert_equal "10", d.id d = klass.new( { foo: "Hello" } , "wrong::10" ) assert_equal "wrong::10", d.id klass = Class.new(Document) do type 'test' type_prefix false attribute :foo do type String end end d = klass.new( { foo: "Hello" } , "test::10" ) assert_equal "test::10", d.id d = klass.new( { foo: "Hello" } , "wrong::10" ) assert_equal "wrong::10", d.id end def test_content_directive klass = Class.new(Document) do type 'test' attribute :foo do type String content { |v| v == "must" } end end d = klass.new( foo: "Hello" ) assert_raises CouchPillow::ValidationError do d.save! end d.foo = "must" d.save! end def test_validate_content_alias klass = Class.new(Document) do type 'test' attribute :foo do type String validate_content { |v| v == "must" } end end d = klass.new( foo: "Hello" ) assert_raises CouchPillow::ValidationError do d.save! end d.foo = "must" d.save! end def test_migrate klass = Class.new(Document) do type 'test' attribute :foo do type String end migrate :foo do |v| if v.class == Hash "omg" elsif v.class == Array 123 else "yes" end end end d = klass.new( foo: "Hello" ) assert_equal "yes", d.foo d = klass.new( foo: { :complex => "data" } ) assert_equal "omg", d.foo d = klass.new( foo: ["some", "array"] ) assert_equal 123, d.foo end end