Skip to content
This repository has been archived by the owner on Jul 29, 2020. It is now read-only.

Scanner #2

Merged
merged 7 commits into from
Jul 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
dist: trusty

sudo: required

addons:
postgresql: "9.6"

language: go

os:
- linux

go:
- 1.7
- 1.8
- tip

matrix:
allow_failures:
- go: tip

env:
- PREST_PG_USER=postgres PREST_PG_DATABASE=prest PREST_PG_PORT=5432 PREST_CONF=$TRAVIS_BUILD_DIR/testdata/prest.toml

before_script:
- sh testdata/schema.sh
- go get -v -d ./...

script:
- sh test.sh
99 changes: 99 additions & 0 deletions postgres/internal/scanner/scanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package scanner

import (
"bytes"
"encoding/json"
"errors"
"reflect"
)

var (
errPtr = errors.New("item to input data is not a pointer")
errUnsupTyp = errors.New("item to input data has an unsupported type")
errLength = errors.New("rows returned is not 1")
supType = map[reflect.Kind]bool{
reflect.Slice: true,
reflect.Struct: true,
reflect.Map: true,
}
)

func validateType(i interface{}) (ref reflect.Value, err error) {
ref = reflect.ValueOf(i)
if ref.Kind() != reflect.Ptr {
err = errPtr
return
}
if _, ok := supType[ref.Elem().Kind()]; !ok {
err = errUnsupTyp
return
}
return
}

// PrestScanner is a default implementation of postgres.Scanner
type PrestScanner struct {
Buff *bytes.Buffer
Error error
IsQuery bool
}

// Scan put prest response into a struct or map
func (p *PrestScanner) Scan(i interface{}) (err error) {
var ref reflect.Value
if ref, err = validateType(i); err != nil {
return
}
if p.IsQuery {
err = p.scanQuery(ref, i)
return
}
err = p.scanNotQuery(ref, i)
return
}

func (p *PrestScanner) scanQuery(ref reflect.Value, i interface{}) (err error) {
decoder := json.NewDecoder(p.Buff)
if ref.Elem().Kind() == reflect.Slice {
err = decoder.Decode(&i)
return
}
ret := make([]map[string]interface{}, 0)
if err = decoder.Decode(&ret); err != nil {
return
}
if len(ret) != 1 {
err = errLength
return
}
var byt []byte
byt, err = json.Marshal(ret[0])
if err != nil {
return
}
err = json.Unmarshal(byt, &i)
return
}

func (p *PrestScanner) scanNotQuery(ref reflect.Value, i interface{}) (err error) {
if ref.Elem().Kind() == reflect.Slice {
err = errUnsupTyp
return
}
err = json.NewDecoder(p.Buff).Decode(&i)
return
}

// Bytes return prest response in bytes
func (p *PrestScanner) Bytes() (byt []byte) {
if p.Buff != nil {
byt = p.Buff.Bytes()
}
return
}

// Err return prest response error
func (p *PrestScanner) Err() (err error) {
err = p.Error
return
}
176 changes: 176 additions & 0 deletions postgres/internal/scanner/scanner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package scanner

import (
"bytes"
"encoding/json"
"errors"
"reflect"
"testing"
)

func TestValidateType(t *testing.T) {
var tmap map[string]interface{}
var tint int
var testCases = []struct {
name string
input interface{}
err error
}{
{"is not a pointer", 1, errPtr},
{"is not a valid type", &tint, errUnsupTyp},
{"is a valid type", &tmap, nil},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err := validateType(tc.input)
if err != tc.err {
t.Errorf("expected %v, but got %v", tc.err, err)
}
})
}
}

func TestPrestScanQuery(t *testing.T) {
type ComplexType struct {
Name string `json:"name,omitempty"`
}
act := make([]ComplexType, 0)
tac := []ComplexType{ComplexType{Name: "test"}}
byt, err := json.Marshal(tac)
if err != nil {
t.Errorf("expected no errors but got %v", err)
}
var tmap map[string]interface{}
var testCases = []struct {
name string
stInput *bytes.Buffer
stErr error
scInput interface{}
scErr error
}{
{"scan Query map", bytes.NewBuffer(byt), nil, &tmap, nil},
{"scan Query struct", bytes.NewBuffer(byt), nil, &ComplexType{}, nil},
{"scan Query slice", bytes.NewBuffer(byt), nil, &act, nil},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := &PrestScanner{
Buff: tc.stInput,
Error: tc.stErr,
}
if s.Err() != tc.stErr {
t.Errorf("expected %v, but got %v", tc.stErr, s.Err())
}
err := s.scanQuery(reflect.ValueOf(tc.scInput), tc.scInput)
if err != tc.scErr {
t.Errorf("expected %v, but got %v", tc.scErr, err)
}
if string(s.Bytes()) != tc.stInput.String() {
t.Errorf("expected %v, but got %v", tc.stInput.Bytes(), s.Bytes())
}
})
}
}

func TestPrestScanNotQuery(t *testing.T) {
type ComplexType struct {
Name string `json:"name,omitempty"`
}
act := make([]ComplexType, 0)
tac := ComplexType{Name: "test"}
byt, err := json.Marshal(tac)
if err != nil {
t.Errorf("expected no errors but got %v", err)
}
var tmap map[string]interface{}
errJSON := errors.New("json: cannot unmarshal array into Go value of type scanner.ComplexType")
var testCases = []struct {
name string
stInput *bytes.Buffer
stErr error
scInput interface{}
scErr error
}{
{"scan Not Query map", bytes.NewBuffer(byt), nil, &tmap, nil},
{"scan Not Query struct", bytes.NewBuffer(byt), nil, &ComplexType{}, nil},
{"scan Not Query slice", bytes.NewBuffer(byt), errJSON, &act, errUnsupTyp},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := &PrestScanner{
Buff: tc.stInput,
Error: tc.stErr,
}
if s.Err() != tc.stErr {
t.Errorf("expected %v, but got %v", tc.stErr, s.Err())
}
err := s.scanNotQuery(reflect.ValueOf(tc.scInput), tc.scInput)
if err != tc.scErr {
t.Errorf("expected %v, but got %v", tc.scErr, err)
}
if string(s.Bytes()) != tc.stInput.String() {
t.Errorf("expected %v, but got %v", tc.stInput.Bytes(), s.Bytes())
}
})
}
}

func TestPrestScan(t *testing.T) {
type ComplexType struct {
Name string `json:"name,omitempty"`
}
act := make([]ComplexType, 0)
tac := []ComplexType{ComplexType{Name: "test"}}
byt, err := json.Marshal(tac)
if err != nil {
t.Errorf("expected no errors but got %v", err)
}
tacs := []ComplexType{
ComplexType{Name: "test"},
ComplexType{Name: "Test"},
}
byts, err := json.Marshal(tacs)
if err != nil {
t.Errorf("expected no errors but got %v", err)
}
ta := ComplexType{Name: "test"}
b, err := json.Marshal(ta)
if err != nil {
t.Errorf("expected no errors but got %v", err)
}
var tmap map[string]interface{}
var testCases = []struct {
name string
stInput *bytes.Buffer
stErr error
scInput interface{}
scErr error
isQuery bool
}{
{"scan error", &bytes.Buffer{}, errors.New("test error"), tmap, errPtr, true},
{"scan err length", bytes.NewBuffer(byts), nil, &ComplexType{}, errLength, true},
{"scan not slice", bytes.NewBuffer(byt), nil, &ComplexType{}, nil, true},
{"scan slice", bytes.NewBuffer(byt), nil, &act, nil, true},
{"scan using map", bytes.NewBuffer(byt), nil, &tmap, nil, true},
{"scan not query", bytes.NewBuffer(b), nil, &tmap, nil, false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := &PrestScanner{
Buff: tc.stInput,
Error: tc.stErr,
IsQuery: tc.isQuery,
}
if s.Err() != tc.stErr {
t.Errorf("expected %v, but got %v", tc.stErr, s.Err())
}
err := s.Scan(tc.scInput)
if err != tc.scErr {
t.Errorf("expected %v, but got %v", tc.scErr, err)
}
if string(s.Bytes()) != tc.stInput.String() {
t.Errorf("expected %v, but got %v", tc.stInput.Bytes(), s.Bytes())
}
})
}
}
Loading