Skip to content

Commit

Permalink
update tests, update README
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaGiorgadze committed Aug 22, 2023
1 parent 76c27fe commit 0ce6068
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 49 deletions.
70 changes: 43 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

[![PkgGoDev](https://pkg.go.dev/badge/github.com/lomsa-dev/gonull)](https://pkg.go.dev/github.com/lomsa-dev/gonull) ![mod-verify](https://github.com/lomsa-dev/gonull/workflows/mod-verify/badge.svg) ![golangci-lint](https://github.com/lomsa-dev/gonull/workflows/golangci-lint/badge.svg) ![staticcheck](https://github.com/lomsa-dev/gonull/workflows/staticcheck/badge.svg) ![gosec](https://github.com/lomsa-dev/gonull/workflows/gosec/badge.svg) [![codecov](https://codecov.io/gh/lomsa-dev/gonull/branch/main/graph/badge.svg?token=76089e7b-f137-4459-8eae-4b48007bd0d6)](https://codecov.io/gh/lomsa-dev/gonull)


## Go package simplifies nullable fields handling with Go Generics.

Package gonull provides a generic `Nullable` type for handling nullable values in a convenient way.
Expand All @@ -21,43 +20,30 @@ Unlike other nullable libraries, gonull leverages Go's generics feature, enablin
go get https://github.com/lomsa-dev/gonull
```

### Example

```go
type User struct {
Name gonull.Nullable[string]
Age gonull.Nullable[int]
}
package main

func main() {
// ...
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
import (
"encoding/json"
"fmt"

for rows.Next() {
var user User
err := rows.Scan( &user.Name, &user.Age)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %v, Age: %v\n", user.Name.Val, user.Age.Val)
}
// ...
}
```
"github.com/lomsa-dev/gonull"
)

Another example
type MyCustomInt int
type MyCustomFloat32 float32

```go
type Person struct {
Name string
Age int
Age gonull.Nullable[MyCustomInt]
Address gonull.Nullable[string]
Height gonull.Nullable[MyCustomFloat32]
}

func main() {
jsonData := []byte(`{"Name":"Alice","Age":30,"Address":null}`)
jsonData := []byte(`{"Name":"Alice","Age":15,"Address":null,"Height":null}`)

var person Person
err := json.Unmarshal(jsonData, &person)
Expand All @@ -72,4 +58,34 @@ func main() {
}
fmt.Printf("Marshalled JSON: %s\n", string(marshalledData))
}


```

### Database example

```go
type User struct {
Name gonull.Nullable[string]
Age gonull.Nullable[int]
}

func main() {
// ...
rows, err := db.Query("SELECT id, name, age FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
var user User
err := rows.Scan( &user.Name, &user.Age)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %v, Age: %v\n", user.Name.Val, user.Age.Val)
}
// ...
}
```
50 changes: 28 additions & 22 deletions gonull_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package gonull
package gonull_test

import (
"database/sql/driver"
"testing"

"github.com/lomsa-dev/gonull"
"github.com/stretchr/testify/assert"
)

func TestNewNullable(t *testing.T) {
value := "test"
n := NewNullable(value)
n := gonull.NewNullable(value)

assert.True(t, n.Valid)
assert.Equal(t, value, n.Val)
}

type NullableInt struct {
Int int
Null bool
}

func TestNullableScan(t *testing.T) {
tests := []struct {
name string
Expand All @@ -41,7 +47,7 @@ func TestNullableScan(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var n Nullable[string]
var n gonull.Nullable[string]
err := n.Scan(tt.value)

if tt.wantErr {
Expand All @@ -60,19 +66,19 @@ func TestNullableScan(t *testing.T) {
func TestNullableValue(t *testing.T) {
tests := []struct {
name string
nullable Nullable[string]
nullable gonull.Nullable[string]
wantValue driver.Value
wantErr error
}{
{
name: "valid value",
nullable: NewNullable("test"),
nullable: gonull.NewNullable("test"),
wantValue: "test",
wantErr: nil,
},
{
name: "unset value",
nullable: Nullable[string]{Valid: false},
nullable: gonull.Nullable[string]{Valid: false},
wantValue: nil,
wantErr: nil,
},
Expand Down Expand Up @@ -113,7 +119,7 @@ func TestNullableUnmarshalJSON(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var nullable Nullable[int]
var nullable gonull.Nullable[int]

err := nullable.UnmarshalJSON(tc.jsonData)
assert.NoError(t, err)
Expand All @@ -126,7 +132,7 @@ func TestNullableUnmarshalJSON(t *testing.T) {
func TestNullableUnmarshalJSON_Error(t *testing.T) {
jsonData := []byte(`"invalid_number"`)

var nullable Nullable[int]
var nullable gonull.Nullable[int]
err := nullable.UnmarshalJSON(jsonData)

assert.Error(t, err)
Expand All @@ -136,19 +142,19 @@ func TestNullableUnmarshalJSON_Error(t *testing.T) {
func TestNullableMarshalJSON(t *testing.T) {
type testCase struct {
name string
nullable Nullable[int]
nullable gonull.Nullable[int]
expectedJSON []byte
}

testCases := []testCase{
{
name: "ValuePresent",
nullable: NewNullable[int](123),
nullable: gonull.NewNullable[int](123),
expectedJSON: []byte(`123`),
},
{
name: "ValueNull",
nullable: Nullable[int]{Val: 0, Valid: false},
nullable: gonull.Nullable[int]{Val: 0, Valid: false},
expectedJSON: []byte(`null`),
},
}
Expand All @@ -165,7 +171,7 @@ func TestNullableMarshalJSON(t *testing.T) {
func TestNullableScan_UnconvertibleFromInt64(t *testing.T) {
value := int64(123456789012345)

var n Nullable[string]
var n gonull.Nullable[string]
err := n.Scan(value)

assert.Error(t, err)
Expand All @@ -188,39 +194,39 @@ func TestConvertToTypeFromInt64(t *testing.T) {
{name: "Convert int64 to uint16", targetType: "uint16", value: int64(7), expectedError: nil},
{name: "Convert int64 to uint32", targetType: "uint32", value: int64(8), expectedError: nil},
// Add more tests as necessary
{name: "Convert int64 to string (expected to fail)", targetType: "string", value: int64(9), expectedError: ErrUnsupportedConversion},
{name: "Convert int64 to string (expected to fail)", targetType: "string", value: int64(9), expectedError: gonull.ErrUnsupportedConversion},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var err error
switch tt.targetType {
case "int":
n := Nullable[int]{}
n := gonull.Nullable[int]{}
err = n.Scan(tt.value)
case "int8":
n := Nullable[int8]{}
n := gonull.Nullable[int8]{}
err = n.Scan(tt.value)
case "int16":
n := Nullable[int16]{}
n := gonull.Nullable[int16]{}
err = n.Scan(tt.value)
case "int32":
n := Nullable[int32]{}
n := gonull.Nullable[int32]{}
err = n.Scan(tt.value)
case "uint":
n := Nullable[uint]{}
n := gonull.Nullable[uint]{}
err = n.Scan(tt.value)
case "uint8":
n := Nullable[uint8]{}
n := gonull.Nullable[uint8]{}
err = n.Scan(tt.value)
case "uint16":
n := Nullable[uint16]{}
n := gonull.Nullable[uint16]{}
err = n.Scan(tt.value)
case "uint32":
n := Nullable[uint32]{}
n := gonull.Nullable[uint32]{}
err = n.Scan(tt.value)
case "string":
n := Nullable[string]{}
n := gonull.Nullable[string]{}
err = n.Scan(tt.value)
default:
t.Fatalf("Unsupported type: %s", tt.targetType)
Expand Down

0 comments on commit 0ce6068

Please sign in to comment.