github.com/mpontillo/pop@v4.13.1+incompatible/nulls/types_test.go (about)

     1  package nulls_test
     2  
     3  import (
     4  	"database/sql"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/gobuffalo/nulls"
    13  
    14  	. "github.com/gobuffalo/pop/nulls"
    15  	"github.com/gofrs/uuid"
    16  	"github.com/jmoiron/sqlx"
    17  	_ "github.com/mattn/go-sqlite3"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  type Foo struct {
    22  	ID         Int64     `json:"id" db:"id"`
    23  	Name       String    `json:"name" db:"name"`
    24  	Alive      Bool      `json:"alive" db:"alive"`
    25  	Price      Float64   `json:"price" db:"price"`
    26  	Birth      Time      `json:"birth" db:"birth"`
    27  	Price32    Float32   `json:"price32" db:"price32"`
    28  	Bytes      ByteSlice `json:"bytes" db:"bytes"`
    29  	IntType    Int       `json:"intType" db:"int_type"`
    30  	Int32Type  Int32     `json:"int32Type" db:"int32_type"`
    31  	UInt32Type UInt32    `json:"uint32Type" db:"uint32_type"`
    32  	UID        UUID      `json:"uid" db:"uid"`
    33  }
    34  
    35  const schema = `CREATE TABLE "main"."foos" (
    36  	 "id" integer,
    37  	 "name" text,
    38  	 "alive" integer,
    39  	 "price" float,
    40  	 "birth" timestamp,
    41  	 "price32" float,
    42  	 "bytes"  blob,
    43  	 "int_type" integer,
    44  	 "int32_type" integer,
    45  	 "uint32_type" integer,
    46  	 "uid" uuid
    47  );`
    48  
    49  // Ensure legacy package is still compatible with new package types.
    50  var _ nulls.Bool = NewBool(true)
    51  var uid, _ = uuid.NewV4()
    52  var now = time.Now()
    53  
    54  func newValidFoo() Foo {
    55  	return Foo{
    56  		ID:         NewInt64(1),
    57  		Name:       NewString("Mark"),
    58  		Alive:      NewBool(true),
    59  		Price:      NewFloat64(9.99),
    60  		Birth:      NewTime(now),
    61  		Price32:    NewFloat32(3.33),
    62  		Bytes:      NewByteSlice([]byte("Byte Slice")),
    63  		IntType:    NewInt(2),
    64  		Int32Type:  NewInt32(3),
    65  		UInt32Type: NewUInt32(5),
    66  		UID:        NewUUID(uid),
    67  	}
    68  }
    69  
    70  func Test_TypesMarshalProperly(t *testing.T) {
    71  	t.Parallel()
    72  
    73  	a := require.New(t)
    74  	f := newValidFoo()
    75  
    76  	ti, _ := json.Marshal(now)
    77  	ba, _ := json.Marshal(f.Bytes)
    78  	jsonString := fmt.Sprintf(`{"id":1,"name":"Mark","alive":true,"price":9.99,"birth":%s,"price32":3.33,"bytes":%s,"intType":2,"int32Type":3,"uint32Type":5,"uid":"%s"}`, ti, ba, uid.String())
    79  
    80  	// check marshalling to json works:
    81  	data, _ := json.Marshal(f)
    82  	a.Equal(string(data), jsonString)
    83  
    84  	// check unmarshalling from json works:
    85  	f = Foo{}
    86  	json.NewDecoder(strings.NewReader(jsonString)).Decode(&f)
    87  	a.Equal(f.ID.Int64, int64(1))
    88  	a.Equal(f.Name.String, "Mark")
    89  	a.Equal(f.Alive.Bool, true)
    90  	a.Equal(f.Price.Float64, 9.99)
    91  	a.Equal(f.Birth.Time.Nanosecond(), now.Nanosecond())
    92  	a.Equal(f.Price32.Float32, float32(3.33))
    93  	a.Equal(f.Bytes.ByteSlice, ba)
    94  	a.Equal(f.IntType.Int, 2)
    95  	a.Equal(f.Int32Type.Int32, int32(3))
    96  	a.Equal(f.UInt32Type.UInt32, uint32(5))
    97  	a.Equal(uid.String(), f.UID.UUID.String())
    98  
    99  	// check marshalling nulls works:
   100  	f = Foo{}
   101  	jsonString = `{"id":null,"name":null,"alive":null,"price":null,"birth":null,"price32":null,"bytes":null,"intType":null,"int32Type":null,"uint32Type":null,"uid":null}`
   102  	data, _ = json.Marshal(f)
   103  	a.Equal(string(data), jsonString)
   104  
   105  	f = Foo{}
   106  	json.NewDecoder(strings.NewReader(jsonString)).Decode(&f)
   107  	a.Equal(f.ID.Int64, int64(0))
   108  	a.False(f.ID.Valid)
   109  	a.Equal(f.Name.String, "")
   110  	a.False(f.Name.Valid)
   111  	a.Equal(f.Alive.Bool, false)
   112  	a.False(f.Alive.Valid)
   113  	a.Equal(f.Price.Float64, float64(0))
   114  	a.False(f.Price.Valid)
   115  	a.Equal(f.Birth.Time, time.Time{})
   116  	a.False(f.Birth.Valid)
   117  	a.Equal(f.Price32.Float32, float32(0))
   118  	a.False(f.Price32.Valid)
   119  	a.Equal(f.Bytes.ByteSlice, []byte(nil))
   120  	a.False(f.Bytes.Valid)
   121  	a.Equal(f.IntType.Int, 0)
   122  	a.False(f.IntType.Valid)
   123  	a.Equal(f.Int32Type.Int32, int32(0))
   124  	a.False(f.Int32Type.Valid)
   125  	a.Equal(f.UInt32Type.UInt32, uint32(0))
   126  	a.False(f.UInt32Type.Valid)
   127  	a.Equal(f.UID.UUID, uuid.Nil)
   128  	a.False(f.UID.Valid)
   129  }
   130  
   131  func Test_TypeSaveAndRetrieveProperly(t *testing.T) {
   132  	t.Parallel()
   133  
   134  	a := require.New(t)
   135  
   136  	initDB(func(db *sqlx.DB) {
   137  		// Test with invalid INSERT query
   138  		tx, err := db.Beginx()
   139  		a.NoError(err)
   140  		_, err = tx.Exec("insert into foos")
   141  		a.Error(err)
   142  
   143  		f := Foo{}
   144  		a.Equal(sql.ErrNoRows, tx.Get(&f, "select * from foos"))
   145  		a.False(f.Alive.Valid)
   146  		a.False(f.Birth.Valid)
   147  		a.False(f.ID.Valid)
   148  		a.False(f.Name.Valid)
   149  		a.False(f.Price.Valid)
   150  		a.False(f.Alive.Bool)
   151  		a.False(f.Price32.Valid)
   152  		a.False(f.Bytes.Valid)
   153  		a.False(f.IntType.Valid)
   154  		a.False(f.Int32Type.Valid)
   155  		a.False(f.UInt32Type.Valid)
   156  		a.Equal(f.Birth.Time.UnixNano(), time.Time{}.UnixNano())
   157  		a.Equal(f.ID.Int64, int64(0))
   158  		a.Equal(f.Name.String, "")
   159  		a.Equal(f.Price.Float64, float64(0))
   160  		a.Equal(f.Price32.Float32, float32(0))
   161  		a.Equal(f.Bytes.ByteSlice, []byte(nil))
   162  		a.Equal(f.IntType.Int, 0)
   163  		a.Equal(f.Int32Type.Int32, int32(0))
   164  		a.Equal(f.UInt32Type.UInt32, uint32(0))
   165  		a.NoError(tx.Rollback())
   166  
   167  		// Test with valid INSERT query
   168  		tx, err = db.Beginx()
   169  		a.NoError(err)
   170  
   171  		f = newValidFoo()
   172  		_, err = tx.NamedExec("INSERT INTO foos (id, name, alive, price, birth, price32, bytes, int_type, int32_type, uint32_type, uid) VALUES (:id, :name, :alive, :price, :birth, :price32, :bytes, :int_type, :int32_type, :uint32_type, :uid)", &f)
   173  		a.NoError(err)
   174  		f = Foo{}
   175  		a.NoError(tx.Get(&f, "select * from foos"))
   176  		a.True(f.Alive.Valid)
   177  		a.True(f.Birth.Valid)
   178  		a.True(f.ID.Valid)
   179  		a.True(f.Name.Valid)
   180  		a.True(f.Price.Valid)
   181  		a.True(f.Alive.Bool)
   182  		a.True(f.Price32.Valid)
   183  		a.True(f.Bytes.Valid)
   184  		a.True(f.IntType.Valid)
   185  		a.True(f.Int32Type.Valid)
   186  		a.True(f.UInt32Type.Valid)
   187  		a.Equal(f.Birth.Time.UnixNano(), now.UnixNano())
   188  		a.Equal(f.ID.Int64, int64(1))
   189  		a.Equal(f.Name.String, "Mark")
   190  		a.Equal(f.Price.Float64, 9.99)
   191  		a.Equal(f.Price32.Float32, float32(3.33))
   192  		a.Equal(f.Bytes.ByteSlice, []byte("Byte Slice"))
   193  		a.Equal(f.IntType.Int, 2)
   194  		a.Equal(f.Int32Type.Int32, int32(3))
   195  		a.Equal(f.UInt32Type.UInt32, uint32(5))
   196  		a.Equal(uid.String(), f.UID.UUID.String())
   197  
   198  		a.NoError(tx.Rollback())
   199  	})
   200  }
   201  
   202  func initDB(f func(db *sqlx.DB)) {
   203  	os.Remove("./foo.db")
   204  	db, _ := sqlx.Open("sqlite3", "./foo.db")
   205  	db.MustExec(schema)
   206  	f(db)
   207  	os.Remove("./foo.db")
   208  }