require "./spec_helper" require "base64" require "json" class SimpleModel include DB::Serializable property c0 : Int32 property c1 : String end class NonStrictModel include DB::Serializable include DB::Serializable::NonStrict property c1 : Int32 property c2 : String end class ModelWithDefaults include DB::Serializable property c0 : Int32 = 10 property c1 : String = "c" end class ModelWithNilables include DB::Serializable property c0 : Int32? = 10 property c1 : String? end class ModelWithNilUnionTypes include DB::Serializable property c0 : Int32 | Nil = 10 property c1 : String | Nil end class ModelWithKeys include DB::Serializable @[DB::Field(key: "c0")] property foo : Int32 @[DB::Field(key: "c1")] property bar : String end class ModelWithConverter module Base64Converter def self.from_rs(rs) Base64.decode(rs.read(String)) end end include DB::Serializable @[DB::Field(converter: ModelWithConverter::Base64Converter)] property c0 : Slice(UInt8) property c1 : String end class ModelWithInitialize include DB::Serializable property c0 : Int32 property c1 : String def_equals c0, c1 def initialize(@c0, @c1) end end class ModelWithJSON include JSON::Serializable include DB::Serializable property c0 : Int32 property c1 : String end struct ModelWithEnum include DB::Serializable getter c0 : Int32 getter c1 : MyEnum # Ensure multiple enum types work together getter c2 : MyOtherEnum enum MyEnum Foo = 0 Bar = 1 Baz = 2 Quux = 3 end enum MyOtherEnum OMG LOL WTF BBQ end end macro from_dummy(query, type) with_dummy do |db| rs = db.query({{ query }}) rs.move_next %obj = {{ type }}.new(rs) rs.close %obj end end macro expect_model(query, t, values) %obj = from_dummy({{ query }}, {{ t }}) %obj.should be_a({{ t }}) {% for key, value in values %} %obj.{{key.id}}.should eq({{value}}) {% end %} end describe "DB::Serializable" do it "should initialize a simple model" do expect_model("1,a", SimpleModel, {c0: 1, c1: "a"}) end it "should fail to initialize a simple model if types do not match" do expect_raises DB::MappingException, /Invalid Int32: "?b"?\n deserializing SimpleModel#c0/ do from_dummy("b,a", SimpleModel) end end it "should fail to initialize a simple model if there is a missing column" do expect_raises DB::MappingException, "Missing column c1\n deserializing SimpleModel#c1" do from_dummy("1", SimpleModel) end end it "should fail to initialize a simple model if there is an unexpected column" do expect_raises DB::MappingException, "Unknown column: c2\n deserializing SimpleModel" do from_dummy("1,a,b", SimpleModel) end end it "should initialize a non-strict model if there is an unexpected column" do expect_model("1,2,a,b", NonStrictModel, {c1: 2, c2: "a"}) end it "should initialize a model with default values" do expect_model("1,a", ModelWithDefaults, {c0: 1, c1: "a"}) end it "should initialize a model using default values if columns are missing" do expect_model("1", ModelWithDefaults, {c0: 1, c1: "c"}) end it "should initialize a model using default values if values are nil and types are non nilable" do expect_model("1,NULL", ModelWithDefaults, {c0: 1, c1: "c"}) end it "should initialize a model with nilables if columns are missing" do expect_model("1", ModelWithNilables, {c0: 1, c1: nil}) end it "should initialize a model with nilables ignoring default value if NULL" do expect_model("NULL,a", ModelWithNilables, {c0: nil, c1: "a"}) end it "should initialize a model with nil union types if columns are missing" do expect_model("1", ModelWithNilUnionTypes, {c0: 1, c1: nil}) end it "should initialize a model with nil union types ignoring default value if NULL" do expect_model("NULL,a", ModelWithNilUnionTypes, {c0: nil, c1: "a"}) end it "should initialize a model with different keys" do expect_model("1,a", ModelWithKeys, {foo: 1, bar: "a"}) end it "should initialize a model with a value converter" do expect_model("Zm9v,a", ModelWithConverter, {c0: "foo".to_slice, c1: "a"}) end it "should initialize a model with an initialize" do obj1 = from_dummy("1,a", ModelWithInitialize) obj2 = ModelWithInitialize.new(1, "a") obj1.should eq obj2 end it "should initialize a model with JSON serialization also defined" do expect_model("1,a", ModelWithJSON, {c0: 1, c1: "a"}) end it "should initialize a model with an enum property" do expect_model("1,2,LOL", ModelWithEnum, { c0: 1, c1: ModelWithEnum::MyEnum::Baz, c2: ModelWithEnum::MyOtherEnum::LOL, }) expect_raises DB::MappingException, "Unknown enum ModelWithEnum::MyEnum value: adsf" do from_dummy("1,adsf,BBQ", ModelWithEnum) end end it "should initialize multiple instances from a single resultset" do with_dummy do |db| db.query("1,a 2,b") do |rs| objs = SimpleModel.from_rs(rs) objs.size.should eq(2) objs[0].c0.should eq(1) objs[0].c1.should eq("a") objs[1].c0.should eq(2) objs[1].c1.should eq("b") end end end it "Class.from_rs should close resultset" do with_dummy do |db| rs = db.query("1,a 2,b") objs = SimpleModel.from_rs(rs) rs.closed?.should be_true objs.size.should eq(2) objs[0].c0.should eq(1) objs[0].c1.should eq("a") objs[1].c0.should eq(2) objs[1].c1.should eq("b") end end it "should initialize from a query_one" do with_dummy do |db| obj = db.query_one "1,a", as: SimpleModel obj.c0.should eq(1) obj.c1.should eq("a") end end it "should initialize from a query_all" do with_dummy do |db| objs = db.query_all "1,a 2,b", as: SimpleModel objs.size.should eq(2) objs[0].c0.should eq(1) objs[0].c1.should eq("a") objs[1].c0.should eq(2) objs[1].c1.should eq("b") end end end