* feature extend readthrough for cache module (#5116) * feature 增加readthrough * feature: add write though for cache mode (#5117) * feature: add writethough for cache mode * feature add singleflight cache (#5119) * build(deps): bump go.opentelemetry.io/otel/trace from 1.8.0 to 1.11.2 Bumps [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) from 1.8.0 to 1.11.2. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.8.0...v1.11.2) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/trace dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * fix 5129: must set formatter after init the logger * remove beego.vip * build(deps): bump actions/stale from 5 to 7 Bumps [actions/stale](https://github.com/actions/stale) from 5 to 7. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix 5079: only log msg when the channel is not closed (#5132) * optimize test * upgrade otel dependencies to v1.11.2 * format code * Bloom filter cache (#5126) * feature: add bloom filter cache * feature upload remove all temp file * bugfix Controller SaveToFile remove all temp file * rft: motify BeeLogger signalChan (#5139) * add non-block write log in asynchronous mode (#5150) * add non-block write log in asynchronous mode --------- Co-authored-by: chenhaokun <chenhaokun@itiger.com> * fix the docsite URL (#5173) * Unified gopkg.in/yaml version to v2 (#5169) * Unified gopkg.in/yaml version to v2 and go mod tidy * update CHANGELOG * bugfix: protect field access with lock to avoid possible data race (#5211) * fix some comments (#5194) Signed-off-by: cui fliter <imcusg@gmail.com> * build(deps): bump github.com/prometheus/client_golang (#5213) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.14.0...v1.15.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump go.etcd.io/etcd/client/v3 from 3.5.4 to 3.5.9 (#5209) Bumps [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) from 3.5.4 to 3.5.9. - [Release notes](https://github.com/etcd-io/etcd/releases) - [Commits](https://github.com/etcd-io/etcd/compare/v3.5.4...v3.5.9) --- updated-dependencies: - dependency-name: go.etcd.io/etcd/client/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * cache: fix typo and optimize the naming * Release 2.1.0 change log * bugfix: beegoAppConfig String and Strings function has bug * httplib: fix unstable test, do not use httplib.org * chore: pkg imported more than once * chore: fmt modify * chore: Use github.com/go-kit/log * chore: unnecessary use of fmt.Sprintf * fix: golangci-lint error * orm: refactor ORM introducing internal/models pkg * remove adapter package * build(deps): bump github.com/bits-and-blooms/bloom/v3 Bumps [github.com/bits-and-blooms/bloom/v3](https://github.com/bits-and-blooms/bloom) from 3.3.1 to 3.5.0. - [Release notes](https://github.com/bits-and-blooms/bloom/releases) - [Commits](https://github.com/bits-and-blooms/bloom/compare/v3.3.1...v3.5.0) --- updated-dependencies: - dependency-name: github.com/bits-and-blooms/bloom/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * feat: add write-delete cache mode * fix: unnecessary assignment to the blank identifier * fix: add change into .CHANGELOG file * build(deps): bump golang.org/x/sync from 0.1.0 to 0.3.0 Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.3.0. - [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.3.0) --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * build(deps): bump golang.org/x/crypto Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20220315160706-3147a52a75dd to 0.10.0. - [Commits](https://github.com/golang/crypto/commits/v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * remove golang--lint-ci * Beego web.Run() runs the server twice * fix 5255: Check the rows.Err() if rows.Next() is false * closes 5254: %COL% should be a common placeholder * build(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * fix: use of ioutil package (#5261) * fix ioutil.NopCloser * fix ioutil.ReadAll * fix ioutil.ReadFile * fix ioutil.WriteFile * run goimports -w -format-only ./ * update CHANGELOG.md * feature: add write-double-delete cache mode (#5263) * cache/redis: support skipEmptyPrefix option (#5264) * fix: refactor InsertValue method (#5267) * fix: refactor insertValue method and add the test * fix: exec goimports and add Licence file header * fix: modify construct method of dbBase * fix: add modify record into CHANGELOG * fix: modify InsertOrUpdate method (#5269) * fix: modify InsertOrUpdate method, Remove the isMulti variable and its associated code * fix: Delete unnecessary judgment branches * fix: add modify record into CHANGELOG * cache/redis: use redisConfig to receive incoming JSON (previously using a map) (#5268) * refactor cache/redis: Use redisConfig to receive incoming JSON (previously using a map). * refactor cache/redis: Use the string type to receive JSON parameters. --------- Co-authored-by: Tan <tanqianheng@gmail.com> * fix: refactor Delete method (#5271) * fix: refactor Delete method and add test * fix: add modify record into CHANGELOG * fix: refactor update sql (#5274) * fix: refactor UpdateSQL method and add test * fix: add modify record into CHANGELOG * fix: modify url in the CHANGELOG * fix: modify pr url in the CHANGELOG * Fix setPK function for table without primary key (#5276) --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: cui fliter <imcusg@gmail.com> Co-authored-by: Stone-afk <73482944+Stone-afk@users.noreply.github.com> Co-authored-by: hookokoko <hooko@tju.edu.cn> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hookokoko <648646891@qq.com> Co-authored-by: Stone-afk <1711865140@qq.com> Co-authored-by: chenhaokun <chenhaokun@itiger.com> Co-authored-by: Xuing <admin@xuing.cn> Co-authored-by: cui fliter <imcusg@gmail.com> Co-authored-by: guoguangwu <guoguangwu@magic-shield.com> Co-authored-by: uzziah <uzziahlin@gmail.com> Co-authored-by: Hanjiang Yu <delacroix.yu@gmail.com> Co-authored-by: Kota <mdryzk64smsh@gmail.com> Co-authored-by: Uzziah <120019273+uzziahlin@users.noreply.github.com> Co-authored-by: Handkerchiefs-t <59816423+Handkerchiefs-t@users.noreply.github.com> Co-authored-by: Tan <tanqianheng@gmail.com> Co-authored-by: mlgd <mlgd17@gmail.com>
2946 lines
79 KiB
Go
2946 lines
79 KiB
Go
// Copyright 2014 beego Author. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package orm
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/beego/beego/v2/client/orm/internal/logs"
|
|
|
|
"github.com/beego/beego/v2/client/orm/internal/utils"
|
|
|
|
"github.com/beego/beego/v2/client/orm/internal/models"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
|
|
"github.com/beego/beego/v2/client/orm/hints"
|
|
)
|
|
|
|
var _ = os.PathSeparator
|
|
|
|
var (
|
|
testDate = utils.FormatDate + " -0700"
|
|
testDateTime = utils.FormatDateTime + " -0700"
|
|
testTime = utils.FormatTime + " -0700"
|
|
)
|
|
|
|
type argAny []interface{}
|
|
|
|
// get interface by index from interface slice
|
|
func (a argAny) Get(i int, args ...interface{}) (r interface{}) {
|
|
if i >= 0 && i < len(a) {
|
|
r = a[i]
|
|
}
|
|
if len(args) > 0 {
|
|
r = args[0]
|
|
}
|
|
return
|
|
}
|
|
|
|
func ValuesCompare(is bool, a interface{}, args ...interface{}) (ok bool, err error) {
|
|
if len(args) == 0 {
|
|
return false, fmt.Errorf("miss args")
|
|
}
|
|
b := args[0]
|
|
arg := argAny(args)
|
|
|
|
switch v := a.(type) {
|
|
case reflect.Kind:
|
|
ok = reflect.ValueOf(b).Kind() == v
|
|
case time.Time:
|
|
if v2, vo := b.(time.Time); vo {
|
|
if arg.Get(1) != nil {
|
|
format := utils.ToStr(arg.Get(1))
|
|
a = v.Format(format)
|
|
b = v2.Format(format)
|
|
ok = a == b
|
|
} else {
|
|
err = fmt.Errorf("compare datetime miss format")
|
|
goto wrongArg
|
|
}
|
|
}
|
|
default:
|
|
ok = utils.ToStr(a) == utils.ToStr(b)
|
|
}
|
|
ok = is && ok || !is && !ok
|
|
if !ok {
|
|
err = fmt.Errorf("expected: `%v`, get `%v`", b, a)
|
|
}
|
|
|
|
wrongArg:
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func AssertIs(a interface{}, args ...interface{}) error {
|
|
if ok, err := ValuesCompare(true, a, args...); !ok {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func AssertNot(a interface{}, args ...interface{}) error {
|
|
if ok, err := ValuesCompare(false, a, args...); !ok {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getCaller(skip int) string {
|
|
pc, file, line, _ := runtime.Caller(skip)
|
|
fun := runtime.FuncForPC(pc)
|
|
_, fn := filepath.Split(file)
|
|
data, err := os.ReadFile(file)
|
|
var codes []string
|
|
if err == nil {
|
|
lines := bytes.Split(data, []byte{'\n'})
|
|
n := 10
|
|
for i := 0; i < n; i++ {
|
|
o := line - n
|
|
if o < 0 {
|
|
continue
|
|
}
|
|
cur := o + i + 1
|
|
flag := " "
|
|
if cur == line {
|
|
flag = ">>"
|
|
}
|
|
ls := formatLines(string(lines[o+i]))
|
|
code := fmt.Sprintf(" %s %5d: %s", flag, cur, ls)
|
|
if code != "" {
|
|
codes = append(codes, code)
|
|
}
|
|
}
|
|
}
|
|
funName := fun.Name()
|
|
if i := strings.LastIndex(funName, "."); i > -1 {
|
|
funName = funName[i+1:]
|
|
}
|
|
return fmt.Sprintf("%s:%s:%d: \n%s", fn, funName, line, strings.Join(codes, "\n"))
|
|
}
|
|
|
|
func formatLines(s string) string {
|
|
return strings.ReplaceAll(s, "\t", " ")
|
|
}
|
|
|
|
// Deprecated: Using stretchr/testify/assert
|
|
func throwFail(t *testing.T, err error, args ...interface{}) {
|
|
if err != nil {
|
|
con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
|
|
if len(args) > 0 {
|
|
parts := make([]string, 0, len(args))
|
|
for _, arg := range args {
|
|
parts = append(parts, fmt.Sprintf("%v", arg))
|
|
}
|
|
con += " " + strings.Join(parts, ", ")
|
|
}
|
|
t.Error(con)
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
// deprecated using assert.XXX
|
|
func throwFailNow(t *testing.T, err error, args ...interface{}) {
|
|
if err != nil {
|
|
con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
|
|
if len(args) > 0 {
|
|
parts := make([]string, 0, len(args))
|
|
for _, arg := range args {
|
|
parts = append(parts, fmt.Sprintf("%v", arg))
|
|
}
|
|
con += " " + strings.Join(parts, ", ")
|
|
}
|
|
t.Error(con)
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
func TestGetDB(t *testing.T) {
|
|
if db, err := GetDB(); err != nil {
|
|
throwFailNow(t, err)
|
|
} else {
|
|
err = db.Ping()
|
|
throwFailNow(t, err)
|
|
}
|
|
}
|
|
|
|
func TestSyncDb(t *testing.T) {
|
|
RegisterModel(new(Data), new(DataNull), new(DataCustom))
|
|
RegisterModel(new(User))
|
|
RegisterModel(new(Profile))
|
|
RegisterModel(new(Post))
|
|
RegisterModel(new(NullValue))
|
|
RegisterModel(new(Tag))
|
|
RegisterModel(new(Comment))
|
|
RegisterModel(new(UserBig))
|
|
RegisterModel(new(PostTags))
|
|
RegisterModel(new(Group))
|
|
RegisterModel(new(Permission))
|
|
RegisterModel(new(GroupPermissions))
|
|
RegisterModel(new(InLine))
|
|
RegisterModel(new(InLineOneToOne))
|
|
RegisterModel(new(IntegerPk))
|
|
RegisterModel(new(UintPk))
|
|
RegisterModel(new(PtrPk))
|
|
RegisterModel(new(Index))
|
|
RegisterModel(new(StrPk))
|
|
RegisterModel(new(TM))
|
|
RegisterModel(new(DeptInfo))
|
|
|
|
err := RunSyncdb("default", true, Debug)
|
|
throwFail(t, err)
|
|
|
|
defaultModelCache.clean()
|
|
}
|
|
|
|
func TestRegisterModels(_ *testing.T) {
|
|
RegisterModel(new(Data), new(DataNull), new(DataCustom))
|
|
RegisterModel(new(User))
|
|
RegisterModel(new(Profile))
|
|
RegisterModel(new(Post))
|
|
RegisterModel(new(NullValue))
|
|
RegisterModel(new(Tag))
|
|
RegisterModel(new(Comment))
|
|
RegisterModel(new(UserBig))
|
|
RegisterModel(new(PostTags))
|
|
RegisterModel(new(Group))
|
|
RegisterModel(new(Permission))
|
|
RegisterModel(new(GroupPermissions))
|
|
RegisterModel(new(InLine))
|
|
RegisterModel(new(InLineOneToOne))
|
|
RegisterModel(new(IntegerPk))
|
|
RegisterModel(new(UintPk))
|
|
RegisterModel(new(PtrPk))
|
|
RegisterModel(new(Index))
|
|
RegisterModel(new(StrPk))
|
|
RegisterModel(new(TM))
|
|
RegisterModel(new(DeptInfo))
|
|
|
|
BootStrap()
|
|
|
|
dORM = NewOrm()
|
|
dDbBaser = getDbAlias("default").DbBaser
|
|
}
|
|
|
|
func TestModelSyntax(t *testing.T) {
|
|
user := &User{}
|
|
ind := reflect.ValueOf(user).Elem()
|
|
fn := models.GetFullName(ind.Type())
|
|
_, ok := defaultModelCache.getByFullName(fn)
|
|
throwFail(t, AssertIs(ok, true))
|
|
|
|
mi, ok := defaultModelCache.get("user")
|
|
throwFail(t, AssertIs(ok, true))
|
|
if ok {
|
|
throwFail(t, AssertIs(mi.Fields.GetByName("ShouldSkip") == nil, true))
|
|
}
|
|
}
|
|
|
|
var DataValues = map[string]interface{}{
|
|
"Boolean": true,
|
|
"Char": "char",
|
|
"Text": "text",
|
|
"JSON": `{"name":"json"}`,
|
|
"Jsonb": `{"name": "jsonb"}`,
|
|
"Time": time.Now(),
|
|
"Date": time.Now(),
|
|
"DateTime": time.Now(),
|
|
"Byte": byte(1<<8 - 1),
|
|
"Rune": rune(1<<31 - 1),
|
|
"Int": int(1<<31 - 1),
|
|
"Int8": int8(1<<7 - 1),
|
|
"Int16": int16(1<<15 - 1),
|
|
"Int32": int32(1<<31 - 1),
|
|
"Int64": int64(1<<63 - 1),
|
|
"Uint": uint(1<<32 - 1),
|
|
"Uint8": uint8(1<<8 - 1),
|
|
"Uint16": uint16(1<<16 - 1),
|
|
"Uint32": uint32(1<<32 - 1),
|
|
"Uint64": uint64(1<<63 - 1), // uint64 values with high bit set are not supported
|
|
"Float32": float32(100.1234),
|
|
"Float64": float64(100.1234),
|
|
"Decimal": float64(100.1234),
|
|
}
|
|
|
|
func TestDataTypes(t *testing.T) {
|
|
d := Data{}
|
|
ind := reflect.Indirect(reflect.ValueOf(&d))
|
|
|
|
for name, value := range DataValues {
|
|
if name == "JSON" {
|
|
continue
|
|
}
|
|
e := ind.FieldByName(name)
|
|
e.Set(reflect.ValueOf(value))
|
|
}
|
|
id, err := dORM.Insert(&d)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
d = Data{ID: 1}
|
|
err = dORM.Read(&d)
|
|
throwFail(t, err)
|
|
|
|
ind = reflect.Indirect(reflect.ValueOf(&d))
|
|
|
|
for name, value := range DataValues {
|
|
e := ind.FieldByName(name)
|
|
vu := e.Interface()
|
|
switch name {
|
|
case "Date":
|
|
case "DateTime":
|
|
case "Time":
|
|
assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
|
|
default:
|
|
assert.Equal(t, value, vu)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTM(t *testing.T) {
|
|
// The precision of sqlite is not implemented
|
|
if dORM.Driver().Type() == 2 {
|
|
return
|
|
}
|
|
var recTM TM
|
|
tm := NewTM()
|
|
tm.TMPrecision1 = time.Unix(1596766024, 123456789)
|
|
tm.TMPrecision2 = time.Unix(1596766024, 123456789)
|
|
_, err := dORM.Insert(tm)
|
|
throwFail(t, err)
|
|
|
|
err = dORM.QueryTable("tm").One(&recTM)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(recTM.TMPrecision1.String(), "2020-08-07 02:07:04.123 +0000 UTC"))
|
|
throwFail(t, AssertIs(recTM.TMPrecision2.String(), "2020-08-07 02:07:04.1235 +0000 UTC"))
|
|
}
|
|
|
|
func TestUnregisterModel(t *testing.T) {
|
|
data := []*DeptInfo{
|
|
{
|
|
DeptName: "A",
|
|
EmployeeName: "A1",
|
|
Salary: 1000,
|
|
},
|
|
{
|
|
DeptName: "A",
|
|
EmployeeName: "A2",
|
|
Salary: 2000,
|
|
},
|
|
{
|
|
DeptName: "B",
|
|
EmployeeName: "B1",
|
|
Salary: 2000,
|
|
},
|
|
{
|
|
DeptName: "B",
|
|
EmployeeName: "B2",
|
|
Salary: 4000,
|
|
},
|
|
{
|
|
DeptName: "B",
|
|
EmployeeName: "B3",
|
|
Salary: 3000,
|
|
},
|
|
}
|
|
qs := dORM.QueryTable("dept_info")
|
|
i, _ := qs.PrepareInsert()
|
|
for _, d := range data {
|
|
_, err := i.Insert(d)
|
|
if err != nil {
|
|
throwFail(t, err)
|
|
}
|
|
}
|
|
|
|
f := func() {
|
|
var res []UnregisterModel
|
|
n, err := dORM.QueryTable("dept_info").All(&res)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(n, 5))
|
|
throwFail(t, AssertIs(res[0].EmployeeName, "A1"))
|
|
|
|
type Sum struct {
|
|
DeptName string
|
|
Total int
|
|
}
|
|
var sun []Sum
|
|
qs.Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").OrderBy("dept_name").All(&sun)
|
|
throwFail(t, AssertIs(sun[0].DeptName, "A"))
|
|
throwFail(t, AssertIs(sun[0].Total, 3000))
|
|
|
|
type Max struct {
|
|
DeptName string
|
|
Max float64
|
|
}
|
|
var max []Max
|
|
qs.Aggregate("dept_name,max(salary) as max").GroupBy("dept_name").OrderBy("dept_name").All(&max)
|
|
throwFail(t, AssertIs(max[1].DeptName, "B"))
|
|
throwFail(t, AssertIs(max[1].Max, 4000))
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
f()
|
|
}
|
|
}
|
|
|
|
func TestNullDataTypes(t *testing.T) {
|
|
d := DataNull{}
|
|
|
|
if IsPostgres {
|
|
// can removed when this fixed
|
|
// https://github.com/lib/pq/pull/125
|
|
d.DateTime = time.Now()
|
|
}
|
|
|
|
id, err := dORM.Insert(&d)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
data := `{"ok":1,"data":{"arr":[1,2],"msg":"gopher"}}`
|
|
d = DataNull{ID: 1, JSON: data}
|
|
num, err := dORM.Update(&d)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
d = DataNull{ID: 1}
|
|
err = dORM.Read(&d)
|
|
throwFail(t, err)
|
|
|
|
throwFail(t, AssertIs(d.JSON, data))
|
|
|
|
throwFail(t, AssertIs(d.NullBool.Valid, false))
|
|
throwFail(t, AssertIs(d.NullString.Valid, false))
|
|
throwFail(t, AssertIs(d.NullInt64.Valid, false))
|
|
throwFail(t, AssertIs(d.NullFloat64.Valid, false))
|
|
|
|
throwFail(t, AssertIs(d.BooleanPtr, nil))
|
|
throwFail(t, AssertIs(d.CharPtr, nil))
|
|
throwFail(t, AssertIs(d.TextPtr, nil))
|
|
throwFail(t, AssertIs(d.BytePtr, nil))
|
|
throwFail(t, AssertIs(d.RunePtr, nil))
|
|
throwFail(t, AssertIs(d.IntPtr, nil))
|
|
throwFail(t, AssertIs(d.Int8Ptr, nil))
|
|
throwFail(t, AssertIs(d.Int16Ptr, nil))
|
|
throwFail(t, AssertIs(d.Int32Ptr, nil))
|
|
throwFail(t, AssertIs(d.Int64Ptr, nil))
|
|
throwFail(t, AssertIs(d.UintPtr, nil))
|
|
throwFail(t, AssertIs(d.Uint8Ptr, nil))
|
|
throwFail(t, AssertIs(d.Uint16Ptr, nil))
|
|
throwFail(t, AssertIs(d.Uint32Ptr, nil))
|
|
throwFail(t, AssertIs(d.Uint64Ptr, nil))
|
|
throwFail(t, AssertIs(d.Float32Ptr, nil))
|
|
throwFail(t, AssertIs(d.Float64Ptr, nil))
|
|
throwFail(t, AssertIs(d.DecimalPtr, nil))
|
|
throwFail(t, AssertIs(d.TimePtr, nil))
|
|
throwFail(t, AssertIs(d.DatePtr, nil))
|
|
throwFail(t, AssertIs(d.DateTimePtr, nil))
|
|
|
|
_, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
|
|
throwFail(t, err)
|
|
|
|
d = DataNull{ID: 2}
|
|
err = dORM.Read(&d)
|
|
throwFail(t, err)
|
|
|
|
booleanPtr := true
|
|
charPtr := string("test")
|
|
textPtr := string("test")
|
|
bytePtr := byte('t')
|
|
runePtr := rune('t')
|
|
intPtr := int(42)
|
|
int8Ptr := int8(42)
|
|
int16Ptr := int16(42)
|
|
int32Ptr := int32(42)
|
|
int64Ptr := int64(42)
|
|
uintPtr := uint(42)
|
|
uint8Ptr := uint8(42)
|
|
uint16Ptr := uint16(42)
|
|
uint32Ptr := uint32(42)
|
|
uint64Ptr := uint64(42)
|
|
float32Ptr := float32(42.0)
|
|
float64Ptr := float64(42.0)
|
|
decimalPtr := float64(42.0)
|
|
timePtr := time.Now()
|
|
datePtr := time.Now()
|
|
dateTimePtr := time.Now()
|
|
|
|
d = DataNull{
|
|
DateTime: time.Now(),
|
|
NullString: sql.NullString{String: "test", Valid: true},
|
|
NullBool: sql.NullBool{Bool: true, Valid: true},
|
|
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
|
|
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
|
|
BooleanPtr: &booleanPtr,
|
|
CharPtr: &charPtr,
|
|
TextPtr: &textPtr,
|
|
BytePtr: &bytePtr,
|
|
RunePtr: &runePtr,
|
|
IntPtr: &intPtr,
|
|
Int8Ptr: &int8Ptr,
|
|
Int16Ptr: &int16Ptr,
|
|
Int32Ptr: &int32Ptr,
|
|
Int64Ptr: &int64Ptr,
|
|
UintPtr: &uintPtr,
|
|
Uint8Ptr: &uint8Ptr,
|
|
Uint16Ptr: &uint16Ptr,
|
|
Uint32Ptr: &uint32Ptr,
|
|
Uint64Ptr: &uint64Ptr,
|
|
Float32Ptr: &float32Ptr,
|
|
Float64Ptr: &float64Ptr,
|
|
DecimalPtr: &decimalPtr,
|
|
TimePtr: &timePtr,
|
|
DatePtr: &datePtr,
|
|
DateTimePtr: &dateTimePtr,
|
|
}
|
|
|
|
id, err = dORM.Insert(&d)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 3))
|
|
|
|
d = DataNull{ID: 3}
|
|
err = dORM.Read(&d)
|
|
throwFail(t, err)
|
|
|
|
throwFail(t, AssertIs(d.NullBool.Valid, true))
|
|
throwFail(t, AssertIs(d.NullBool.Bool, true))
|
|
|
|
throwFail(t, AssertIs(d.NullString.Valid, true))
|
|
throwFail(t, AssertIs(d.NullString.String, "test"))
|
|
|
|
throwFail(t, AssertIs(d.NullInt64.Valid, true))
|
|
throwFail(t, AssertIs(d.NullInt64.Int64, 42))
|
|
|
|
throwFail(t, AssertIs(d.NullFloat64.Valid, true))
|
|
throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))
|
|
|
|
throwFail(t, AssertIs(*d.BooleanPtr, booleanPtr))
|
|
throwFail(t, AssertIs(*d.CharPtr, charPtr))
|
|
throwFail(t, AssertIs(*d.TextPtr, textPtr))
|
|
throwFail(t, AssertIs(*d.BytePtr, bytePtr))
|
|
throwFail(t, AssertIs(*d.RunePtr, runePtr))
|
|
throwFail(t, AssertIs(*d.IntPtr, intPtr))
|
|
throwFail(t, AssertIs(*d.Int8Ptr, int8Ptr))
|
|
throwFail(t, AssertIs(*d.Int16Ptr, int16Ptr))
|
|
throwFail(t, AssertIs(*d.Int32Ptr, int32Ptr))
|
|
throwFail(t, AssertIs(*d.Int64Ptr, int64Ptr))
|
|
throwFail(t, AssertIs(*d.UintPtr, uintPtr))
|
|
throwFail(t, AssertIs(*d.Uint8Ptr, uint8Ptr))
|
|
throwFail(t, AssertIs(*d.Uint16Ptr, uint16Ptr))
|
|
throwFail(t, AssertIs(*d.Uint32Ptr, uint32Ptr))
|
|
throwFail(t, AssertIs(*d.Uint64Ptr, uint64Ptr))
|
|
throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
|
|
throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
|
|
throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
|
|
|
|
// in mysql, there are some precision problem, (*d.TimePtr).UTC() != timePtr.UTC()
|
|
assert.True(t, (*d.TimePtr).UTC().Sub(timePtr.UTC()) <= time.Second)
|
|
assert.True(t, (*d.DatePtr).UTC().Sub(datePtr.UTC()) <= time.Second)
|
|
assert.True(t, (*d.DateTimePtr).UTC().Sub(dateTimePtr.UTC()) <= time.Second)
|
|
|
|
// test support for pointer Fields using RawSeter.QueryRows()
|
|
var dnList []*DataNull
|
|
Q := dDbBaser.TableQuote()
|
|
num, err = dORM.Raw(fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q), 3).QueryRows(&dnList)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
equal := reflect.DeepEqual(*dnList[0], d)
|
|
throwFailNow(t, AssertIs(equal, true))
|
|
}
|
|
|
|
func TestDataCustomTypes(t *testing.T) {
|
|
d := DataCustom{}
|
|
ind := reflect.Indirect(reflect.ValueOf(&d))
|
|
|
|
for name, value := range DataValues {
|
|
e := ind.FieldByName(name)
|
|
if !e.IsValid() {
|
|
continue
|
|
}
|
|
e.Set(reflect.ValueOf(value).Convert(e.Type()))
|
|
}
|
|
|
|
id, err := dORM.Insert(&d)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
d = DataCustom{ID: 1}
|
|
err = dORM.Read(&d)
|
|
throwFail(t, err)
|
|
|
|
ind = reflect.Indirect(reflect.ValueOf(&d))
|
|
|
|
for name, value := range DataValues {
|
|
e := ind.FieldByName(name)
|
|
if !e.IsValid() {
|
|
continue
|
|
}
|
|
vu := e.Interface()
|
|
value = reflect.ValueOf(value).Convert(e.Type()).Interface()
|
|
throwFail(t, AssertIs(vu == value, true), value, vu)
|
|
}
|
|
}
|
|
|
|
func TestCRUD(t *testing.T) {
|
|
profile := NewProfile()
|
|
profile.Age = 30
|
|
profile.Money = 1234.12
|
|
id, err := dORM.Insert(profile)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
user := NewUser()
|
|
user.UserName = "slene"
|
|
user.Email = "vslene@gmail.com"
|
|
user.Password = "pass"
|
|
user.Status = 3
|
|
user.IsStaff = true
|
|
user.IsActive = true
|
|
|
|
id, err = dORM.Insert(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
u := &User{ID: user.ID}
|
|
err = dORM.Read(u)
|
|
throwFail(t, err)
|
|
|
|
throwFail(t, AssertIs(u.UserName, "slene"))
|
|
throwFail(t, AssertIs(u.Email, "vslene@gmail.com"))
|
|
throwFail(t, AssertIs(u.Password, "pass"))
|
|
throwFail(t, AssertIs(u.Status, 3))
|
|
throwFail(t, AssertIs(u.IsStaff, true))
|
|
throwFail(t, AssertIs(u.IsActive, true))
|
|
|
|
assert.True(t, u.Created.In(DefaultTimeLoc).Sub(user.Created.In(DefaultTimeLoc)) <= time.Second)
|
|
assert.True(t, u.Updated.In(DefaultTimeLoc).Sub(user.Updated.In(DefaultTimeLoc)) <= time.Second)
|
|
|
|
user.UserName = "astaxie"
|
|
user.Profile = profile
|
|
num, err := dORM.Update(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
u = &User{ID: user.ID}
|
|
err = dORM.Read(u)
|
|
throwFailNow(t, err)
|
|
throwFail(t, AssertIs(u.UserName, "astaxie"))
|
|
throwFail(t, AssertIs(u.Profile.ID, profile.ID))
|
|
|
|
u = &User{UserName: "astaxie", Password: "pass"}
|
|
err = dORM.Read(u, "UserName")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(id, 1))
|
|
|
|
u.UserName = "QQ"
|
|
u.Password = "111"
|
|
num, err = dORM.Update(u, "UserName")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
u = &User{ID: user.ID}
|
|
err = dORM.Read(u)
|
|
throwFailNow(t, err)
|
|
throwFail(t, AssertIs(u.UserName, "QQ"))
|
|
throwFail(t, AssertIs(u.Password, "pass"))
|
|
|
|
num, err = dORM.Delete(profile)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
u = &User{ID: user.ID}
|
|
err = dORM.Read(u)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(true, u.Profile == nil))
|
|
|
|
num, err = dORM.Delete(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
u = &User{ID: 100}
|
|
err = dORM.Read(u)
|
|
throwFail(t, AssertIs(err, ErrNoRows))
|
|
|
|
ub := UserBig{}
|
|
ub.Name = "name"
|
|
id, err = dORM.Insert(&ub)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
ub = UserBig{ID: 1}
|
|
err = dORM.Read(&ub)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(ub.Name, "name"))
|
|
|
|
num, err = dORM.Delete(&ub, "name")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestInsertTestData(t *testing.T) {
|
|
var users []*User
|
|
|
|
profile := NewProfile()
|
|
profile.Age = 28
|
|
profile.Money = 1234.12
|
|
|
|
id, err := dORM.Insert(profile)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 2))
|
|
|
|
user := NewUser()
|
|
user.UserName = "slene"
|
|
user.Email = "vslene@gmail.com"
|
|
user.Password = "pass"
|
|
user.Status = 1
|
|
user.IsStaff = false
|
|
user.IsActive = true
|
|
user.Profile = profile
|
|
|
|
users = append(users, user)
|
|
|
|
id, err = dORM.Insert(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 2))
|
|
|
|
profile = NewProfile()
|
|
profile.Age = 30
|
|
profile.Money = 4321.09
|
|
|
|
id, err = dORM.Insert(profile)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 3))
|
|
|
|
user = NewUser()
|
|
user.UserName = "astaxie"
|
|
user.Email = "astaxie@gmail.com"
|
|
user.Password = "password"
|
|
user.Status = 2
|
|
user.IsStaff = true
|
|
user.IsActive = false
|
|
user.Profile = profile
|
|
|
|
users = append(users, user)
|
|
|
|
id, err = dORM.Insert(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 3))
|
|
|
|
user = NewUser()
|
|
user.UserName = "nobody"
|
|
user.Email = "nobody@gmail.com"
|
|
user.Password = "nobody"
|
|
user.Status = 3
|
|
user.IsStaff = false
|
|
user.IsActive = false
|
|
|
|
users = append(users, user)
|
|
|
|
id, err = dORM.Insert(user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 4))
|
|
|
|
tags := []*Tag{
|
|
{Name: "golang", BestPost: &Post{ID: 2}},
|
|
{Name: "example"},
|
|
{Name: "format"},
|
|
{Name: "c++"},
|
|
}
|
|
|
|
posts := []*Post{
|
|
{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand.
|
|
This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`},
|
|
{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`},
|
|
{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide.
|
|
With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`},
|
|
{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.
|
|
The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`},
|
|
}
|
|
|
|
comments := []*Comment{
|
|
{Post: posts[0], Content: "a comment"},
|
|
{Post: posts[1], Content: "yes"},
|
|
{Post: posts[1]},
|
|
{Post: posts[1]},
|
|
{Post: posts[2]},
|
|
{Post: posts[2]},
|
|
}
|
|
|
|
for _, tag := range tags {
|
|
id, err := dORM.Insert(tag)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
}
|
|
|
|
for _, post := range posts {
|
|
id, err := dORM.Insert(post)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
|
|
num := len(post.Tags)
|
|
if num > 0 {
|
|
nums, err := dORM.QueryM2M(post, "tags").Add(post.Tags)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(nums, num))
|
|
}
|
|
}
|
|
|
|
for _, comment := range comments {
|
|
id, err := dORM.Insert(comment)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
}
|
|
|
|
permissions := []*Permission{
|
|
{Name: "writePosts"},
|
|
{Name: "readComments"},
|
|
{Name: "readPosts"},
|
|
}
|
|
|
|
groups := []*Group{
|
|
{
|
|
Name: "admins",
|
|
Permissions: []*Permission{permissions[0], permissions[1], permissions[2]},
|
|
},
|
|
{
|
|
Name: "users",
|
|
Permissions: []*Permission{permissions[1], permissions[2]},
|
|
},
|
|
}
|
|
|
|
for _, permission := range permissions {
|
|
id, err := dORM.Insert(permission)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
}
|
|
|
|
for _, group := range groups {
|
|
_, err := dORM.Insert(group)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
|
|
num := len(group.Permissions)
|
|
if num > 0 {
|
|
nums, err := dORM.QueryM2M(group, "permissions").Add(group.Permissions)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(nums, num))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCustomField(t *testing.T) {
|
|
user := User{ID: 2}
|
|
err := dORM.Read(&user)
|
|
throwFailNow(t, err)
|
|
|
|
user.Langs = append(user.Langs, "zh-CN", "en-US")
|
|
user.Extra.Name = "beego"
|
|
user.Extra.Data = "orm"
|
|
_, err = dORM.Update(&user, "Langs", "Extra")
|
|
throwFailNow(t, err)
|
|
|
|
user = User{ID: 2}
|
|
err = dORM.Read(&user)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(len(user.Langs), 2))
|
|
throwFailNow(t, AssertIs(user.Langs[0], "zh-CN"))
|
|
throwFailNow(t, AssertIs(user.Langs[1], "en-US"))
|
|
|
|
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
|
throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
|
|
|
|
var users []User
|
|
Q := dDbBaser.TableQuote()
|
|
n, err := dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRows(&users)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(n, 1))
|
|
throwFailNow(t, AssertIs(users[0].Extra.Name, "beego"))
|
|
throwFailNow(t, AssertIs(users[0].Extra.Data, "orm"))
|
|
|
|
user = User{}
|
|
err = dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRow(&user)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
|
|
throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
|
|
}
|
|
|
|
func TestExpr(t *testing.T) {
|
|
user := &User{}
|
|
var qs QuerySeter
|
|
assert.NotPanics(t, func() { qs = dORM.QueryTable(user) })
|
|
assert.NotPanics(t, func() { qs = dORM.QueryTable((*User)(nil)) })
|
|
assert.NotPanics(t, func() { qs = dORM.QueryTable("User") })
|
|
assert.NotPanics(t, func() { qs = dORM.QueryTable("user") })
|
|
num, err := qs.Filter("UserName", "slene").Filter("user_name", "slene").Filter("profile__Age", 28).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("created", time.Now()).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
// num, err = qs.Filter("created", time.Now().Format(format_Date)).Count()
|
|
// throwFail(t, err)
|
|
// throwFail(t, AssertIs(num, 3))
|
|
}
|
|
|
|
func TestSpecifyIndex(t *testing.T) {
|
|
var index *Index
|
|
index = &Index{
|
|
F1: 1,
|
|
F2: 2,
|
|
}
|
|
_, _ = dORM.Insert(index)
|
|
throwFailNow(t, AssertIs(index.Id, 1))
|
|
|
|
index = &Index{
|
|
F1: 3,
|
|
F2: 4,
|
|
}
|
|
_, _ = dORM.Insert(index)
|
|
throwFailNow(t, AssertIs(index.Id, 2))
|
|
|
|
_ = dORM.QueryTable(&Index{}).Filter(`f1`, `1`).ForceIndex(`index_f1`).One(index)
|
|
throwFailNow(t, AssertIs(index.F2, 2))
|
|
|
|
_ = dORM.QueryTable(&Index{}).Filter(`f2`, `4`).UseIndex(`index_f2`).One(index)
|
|
throwFailNow(t, AssertIs(index.F1, 3))
|
|
|
|
_ = dORM.QueryTable(&Index{}).Filter(`f1`, `1`).IgnoreIndex(`index_f1`, `index_f2`).One(index)
|
|
throwFailNow(t, AssertIs(index.F2, 2))
|
|
}
|
|
|
|
func TestOperators(t *testing.T) {
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.Filter("user_name", "slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name__exact", String("slene")).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name__exact", "slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name__iexact", "Slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
if IsMysql {
|
|
// Now only mysql support `strictexact`
|
|
num, err = qs.Filter("user_name__strictexact", "Slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 0))
|
|
|
|
num, err = qs.Filter("user_name__strictexact", "slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
num, err = qs.Filter("user_name__contains", "e").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
var shouldNum int
|
|
|
|
if IsSqlite || IsTidb {
|
|
shouldNum = 2
|
|
} else {
|
|
shouldNum = 0
|
|
}
|
|
|
|
num, err = qs.Filter("user_name__contains", "E").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, shouldNum))
|
|
|
|
num, err = qs.Filter("user_name__icontains", "E").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("user_name__icontains", "E").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("status__gt", 1).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("status__gte", 1).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
num, err = qs.Filter("status__lt", Uint(3)).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("status__lte", Int(3)).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
num, err = qs.Filter("user_name__startswith", "s").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
if IsSqlite || IsTidb {
|
|
shouldNum = 1
|
|
} else {
|
|
shouldNum = 0
|
|
}
|
|
|
|
num, err = qs.Filter("user_name__startswith", "S").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, shouldNum))
|
|
|
|
num, err = qs.Filter("user_name__istartswith", "S").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name__endswith", "e").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
if IsSqlite || IsTidb {
|
|
shouldNum = 2
|
|
} else {
|
|
shouldNum = 0
|
|
}
|
|
|
|
num, err = qs.Filter("user_name__endswith", "E").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, shouldNum))
|
|
|
|
num, err = qs.Filter("user_name__iendswith", "E").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("profile__isnull", true).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("status__in", 1, 2).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("status__in", []int{1, 2}).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
n1, n2 := 1, 2
|
|
num, err = qs.Filter("status__in", []*int{&n1}, &n2).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("id__between", 2, 3).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Filter("id__between", []int{2, 3}).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.FilterRaw("user_name", "= 'slene'").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.FilterRaw("status", "IN (1, 2)").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.FilterRaw("profile_id", "IN (SELECT id FROM user_profile WHERE age=30)").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestSetCond(t *testing.T) {
|
|
cond := NewCondition()
|
|
cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
|
|
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.SetCond(cond1).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
cond2 := cond.AndCond(cond1).OrCond(cond.And("user_name", "slene"))
|
|
num, err = qs.SetCond(cond2).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
cond3 := cond.AndNotCond(cond.And("status__in", 1))
|
|
num, err = qs.SetCond(cond3).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene"))
|
|
num, err = qs.SetCond(cond4).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
cond5 := cond.Raw("user_name", "= 'slene'").OrNotCond(cond.And("user_name", "slene"))
|
|
num, err = qs.SetCond(cond5).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
}
|
|
|
|
func TestLimit(t *testing.T) {
|
|
var posts []*Post
|
|
qs := dORM.QueryTable("post")
|
|
num, err := qs.Limit(1).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Limit(-1).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 4))
|
|
|
|
num, err = qs.Limit(-1, 2).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
num, err = qs.Limit(0, 2).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
}
|
|
|
|
func TestOffset(t *testing.T) {
|
|
var posts []*Post
|
|
qs := dORM.QueryTable("post")
|
|
num, err := qs.Limit(1).Offset(2).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Offset(2).All(&posts)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
}
|
|
|
|
func TestOrderBy(t *testing.T) {
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.OrderBy("status").Filter("user_name", "slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.OrderClauses(
|
|
order_clause.Clause(
|
|
order_clause.Column(`profile__age`),
|
|
order_clause.SortDescending(),
|
|
),
|
|
).Filter("user_name", "astaxie").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
if IsMysql {
|
|
num, err = qs.OrderClauses(
|
|
order_clause.Clause(
|
|
order_clause.Column(`rand()`),
|
|
order_clause.Raw(),
|
|
),
|
|
).Filter("user_name", "astaxie").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
}
|
|
|
|
func TestAll(t *testing.T) {
|
|
var users []*User
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.OrderBy("Id").All(&users)
|
|
throwFail(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
|
|
throwFail(t, AssertIs(users[0].UserName, "slene"))
|
|
throwFail(t, AssertIs(users[1].UserName, "astaxie"))
|
|
throwFail(t, AssertIs(users[2].UserName, "nobody"))
|
|
|
|
var users2 []User
|
|
qs = dORM.QueryTable("user")
|
|
num, err = qs.OrderBy("Id").All(&users2)
|
|
throwFail(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
|
|
throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
|
|
throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
|
|
|
|
qs = dORM.QueryTable("user")
|
|
num, err = qs.OrderBy("Id").RelatedSel().All(&users2, "UserName")
|
|
throwFail(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
throwFailNow(t, AssertIs(len(users2), 3))
|
|
throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
|
|
throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
|
|
throwFailNow(t, AssertIs(users2[0].ID, 0))
|
|
throwFailNow(t, AssertIs(users2[1].ID, 0))
|
|
throwFailNow(t, AssertIs(users2[2].ID, 0))
|
|
throwFailNow(t, AssertIs(users2[0].Profile == nil, false))
|
|
throwFailNow(t, AssertIs(users2[1].Profile == nil, false))
|
|
throwFailNow(t, AssertIs(users2[2].Profile == nil, true))
|
|
|
|
qs = dORM.QueryTable("user")
|
|
num, err = qs.Filter("user_name", "nothing").All(&users)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 0))
|
|
|
|
var users3 []*User
|
|
qs = dORM.QueryTable("user")
|
|
num, err = qs.Filter("user_name", "nothing").All(&users3)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 0))
|
|
throwFailNow(t, AssertIs(users3 == nil, false))
|
|
}
|
|
|
|
func TestOne(t *testing.T) {
|
|
var user User
|
|
qs := dORM.QueryTable("user")
|
|
err := qs.One(&user)
|
|
throwFail(t, err)
|
|
|
|
user = User{}
|
|
err = qs.OrderBy("Id").Limit(1).One(&user)
|
|
throwFailNow(t, err)
|
|
throwFail(t, AssertIs(user.UserName, "slene"))
|
|
throwFail(t, AssertNot(err, ErrMultiRows))
|
|
|
|
user = User{}
|
|
err = qs.OrderBy("-Id").Limit(100).One(&user)
|
|
throwFailNow(t, err)
|
|
throwFail(t, AssertIs(user.UserName, "nobody"))
|
|
throwFail(t, AssertNot(err, ErrMultiRows))
|
|
|
|
err = qs.Filter("user_name", "nothing").One(&user)
|
|
throwFail(t, AssertIs(err, ErrNoRows))
|
|
}
|
|
|
|
func TestValues(t *testing.T) {
|
|
var maps []Params
|
|
qs := dORM.QueryTable("user")
|
|
|
|
num, err := qs.OrderBy("Id").Values(&maps)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
|
|
throwFail(t, AssertIs(maps[2]["Profile"], nil))
|
|
}
|
|
|
|
num, err = qs.OrderClauses(
|
|
order_clause.Clause(
|
|
order_clause.Column("Id"),
|
|
order_clause.SortAscending(),
|
|
),
|
|
).Values(&maps)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
|
|
throwFail(t, AssertIs(maps[2]["Profile"], nil))
|
|
}
|
|
|
|
num, err = qs.OrderBy("Id").Values(&maps, "UserName", "Profile__Age")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
|
|
throwFail(t, AssertIs(maps[0]["Profile__Age"], 28))
|
|
throwFail(t, AssertIs(maps[2]["Profile__Age"], nil))
|
|
}
|
|
|
|
num, err = qs.Filter("UserName", "slene").Values(&maps)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestValuesList(t *testing.T) {
|
|
var list []ParamsList
|
|
qs := dORM.QueryTable("user")
|
|
|
|
num, err := qs.OrderBy("Id").ValuesList(&list)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(list[0][1], "slene")) // username
|
|
throwFail(t, AssertIs(list[2][10], nil)) // profile
|
|
}
|
|
|
|
num, err = qs.OrderBy("Id").ValuesList(&list, "UserName", "Profile__Age")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(list[0][0], "slene"))
|
|
throwFail(t, AssertIs(list[0][1], 28))
|
|
throwFail(t, AssertIs(list[2][1], nil))
|
|
}
|
|
}
|
|
|
|
func TestValuesFlat(t *testing.T) {
|
|
var list ParamsList
|
|
qs := dORM.QueryTable("user")
|
|
|
|
num, err := qs.OrderBy("id").ValuesFlat(&list, "UserName")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(list[0], "slene"))
|
|
throwFail(t, AssertIs(list[1], "astaxie"))
|
|
throwFail(t, AssertIs(list[2], "nobody"))
|
|
}
|
|
}
|
|
|
|
func TestRelatedSel(t *testing.T) {
|
|
if IsTidb {
|
|
// Skip it. TiDB does not support relation now.
|
|
return
|
|
}
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.Filter("profile__age", 28).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("profile__age__gt", 28).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("profile__user__profile__age__gt", 28).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
var user User
|
|
err = qs.Filter("user_name", "slene").RelatedSel("profile").One(&user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
throwFail(t, AssertNot(user.Profile, nil))
|
|
if user.Profile != nil {
|
|
throwFail(t, AssertIs(user.Profile.Age, 28))
|
|
}
|
|
|
|
err = qs.Filter("user_name", "slene").RelatedSel().One(&user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
throwFail(t, AssertNot(user.Profile, nil))
|
|
if user.Profile != nil {
|
|
throwFail(t, AssertIs(user.Profile.Age, 28))
|
|
}
|
|
|
|
err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
throwFail(t, AssertIs(user.Profile, nil))
|
|
|
|
qs = dORM.QueryTable("user_profile")
|
|
num, err = qs.Filter("user__username", "slene").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
var posts []*Post
|
|
qs = dORM.QueryTable("post")
|
|
num, err = qs.RelatedSel().All(&posts)
|
|
throwFail(t, err)
|
|
throwFailNow(t, AssertIs(num, 4))
|
|
|
|
throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
|
|
throwFailNow(t, AssertIs(posts[1].User.UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(posts[2].User.UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(posts[3].User.UserName, "nobody"))
|
|
}
|
|
|
|
func TestReverseQuery(t *testing.T) {
|
|
var profile Profile
|
|
err := dORM.QueryTable("user_profile").Filter("User", 3).One(&profile)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(profile.Age, 30))
|
|
|
|
profile = Profile{}
|
|
err = dORM.QueryTable("user_profile").Filter("User__UserName", "astaxie").One(&profile)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(profile.Age, 30))
|
|
|
|
var user User
|
|
err = dORM.QueryTable("user").Filter("Posts__Title", "Examples").One(&user)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(user.UserName, "astaxie"))
|
|
|
|
user = User{}
|
|
err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").Limit(1).One(&user)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(user.UserName, "astaxie"))
|
|
|
|
user = User{}
|
|
err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").RelatedSel().Limit(1).One(&user)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(user.UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(user.Profile == nil, false))
|
|
throwFailNow(t, AssertIs(user.Profile.Age, 30))
|
|
|
|
var posts []*Post
|
|
num, err := dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").All(&posts)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
|
|
|
|
posts = []*Post{}
|
|
num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").Filter("User__UserName", "slene").All(&posts)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))
|
|
|
|
posts = []*Post{}
|
|
num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").
|
|
Filter("User__UserName", "slene").RelatedSel().All(&posts)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(posts[0].User == nil, false))
|
|
throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
|
|
|
|
var tags []*Tag
|
|
num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").All(&tags)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(tags[0].Name, "golang"))
|
|
|
|
tags = []*Tag{}
|
|
num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
|
|
Filter("BestPost__User__UserName", "astaxie").All(&tags)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(tags[0].Name, "golang"))
|
|
|
|
tags = []*Tag{}
|
|
num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
|
|
Filter("BestPost__User__UserName", "astaxie").RelatedSel().All(&tags)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(tags[0].Name, "golang"))
|
|
throwFailNow(t, AssertIs(tags[0].BestPost == nil, false))
|
|
throwFailNow(t, AssertIs(tags[0].BestPost.Title, "Examples"))
|
|
throwFailNow(t, AssertIs(tags[0].BestPost.User == nil, false))
|
|
throwFailNow(t, AssertIs(tags[0].BestPost.User.UserName, "astaxie"))
|
|
}
|
|
|
|
func TestLoadRelated(t *testing.T) {
|
|
// load reverse foreign key
|
|
user := User{ID: 3}
|
|
|
|
err := dORM.Read(&user)
|
|
throwFailNow(t, err)
|
|
|
|
num, err := dORM.LoadRelated(&user, "Posts")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(user.Posts), 2))
|
|
throwFailNow(t, AssertIs(user.Posts[0].User.ID, 3))
|
|
|
|
num, err = dORM.LoadRelated(&user, "Posts", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(user.Posts), 2))
|
|
throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie"))
|
|
|
|
num, err = dORM.LoadRelated(&user, "Posts",
|
|
hints.DefaultRelDepth(),
|
|
hints.Limit(1))
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(len(user.Posts), 1))
|
|
|
|
num, err = dORM.LoadRelated(&user, "Posts",
|
|
hints.DefaultRelDepth(),
|
|
hints.OrderBy("-Id"))
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(user.Posts), 2))
|
|
throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
|
|
|
|
num, err = dORM.LoadRelated(&user, "Posts",
|
|
hints.DefaultRelDepth(),
|
|
hints.Limit(1),
|
|
hints.Offset(1),
|
|
hints.OrderBy("Id"))
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(len(user.Posts), 1))
|
|
throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))
|
|
|
|
// load reverse one to one
|
|
profile := Profile{ID: 3}
|
|
profile.BestPost = &Post{ID: 2}
|
|
num, err = dORM.Update(&profile, "BestPost")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
err = dORM.Read(&profile)
|
|
throwFailNow(t, err)
|
|
|
|
num, err = dORM.LoadRelated(&profile, "User")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(profile.User == nil, false))
|
|
throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
|
|
|
|
num, err = dORM.LoadRelated(&profile, "User", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(profile.User == nil, false))
|
|
throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(profile.User.Profile.Age, profile.Age))
|
|
|
|
// load rel one to one
|
|
err = dORM.Read(&user)
|
|
throwFailNow(t, err)
|
|
|
|
num, err = dORM.LoadRelated(&user, "Profile")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(user.Profile == nil, false))
|
|
throwFailNow(t, AssertIs(user.Profile.Age, 30))
|
|
|
|
num, err = dORM.LoadRelated(&user, "Profile", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(user.Profile == nil, false))
|
|
throwFailNow(t, AssertIs(user.Profile.Age, 30))
|
|
throwFailNow(t, AssertIs(user.Profile.BestPost == nil, false))
|
|
throwFailNow(t, AssertIs(user.Profile.BestPost.Title, "Examples"))
|
|
|
|
post := Post{ID: 2}
|
|
|
|
// load rel foreign key
|
|
err = dORM.Read(&post)
|
|
throwFailNow(t, err)
|
|
|
|
num, err = dORM.LoadRelated(&post, "User")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(post.User == nil, false))
|
|
throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
|
|
|
|
num, err = dORM.LoadRelated(&post, "User", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(post.User == nil, false))
|
|
throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(post.User.Profile == nil, false))
|
|
throwFailNow(t, AssertIs(post.User.Profile.Age, 30))
|
|
|
|
// load rel m2m
|
|
post = Post{ID: 2}
|
|
|
|
err = dORM.Read(&post)
|
|
throwFailNow(t, err)
|
|
|
|
num, err = dORM.LoadRelated(&post, "Tags")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(post.Tags), 2))
|
|
throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
|
|
|
|
num, err = dORM.LoadRelated(&post, "Tags", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(post.Tags), 2))
|
|
throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
|
|
throwFailNow(t, AssertIs(post.Tags[0].BestPost == nil, false))
|
|
throwFailNow(t, AssertIs(post.Tags[0].BestPost.User.UserName, "astaxie"))
|
|
|
|
// load reverse m2m
|
|
tag := Tag{ID: 1}
|
|
|
|
err = dORM.Read(&tag)
|
|
throwFailNow(t, err)
|
|
|
|
num, err = dORM.LoadRelated(&tag, "Posts")
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].User.Profile == nil, true))
|
|
|
|
num, err = dORM.LoadRelated(&tag, "Posts", hints.DefaultRelDepth())
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
|
|
throwFailNow(t, AssertIs(tag.Posts[0].User.UserName, "slene"))
|
|
}
|
|
|
|
func TestQueryM2M(t *testing.T) {
|
|
post := Post{ID: 4}
|
|
m2m := dORM.QueryM2M(&post, "Tags")
|
|
|
|
tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}}
|
|
tag2 := &Tag{Name: "TestTag3"}
|
|
tag3 := []interface{}{&Tag{Name: "TestTag4"}}
|
|
|
|
tags := []interface{}{tag1[0], tag1[1], tag2, tag3[0]}
|
|
|
|
for _, tag := range tags {
|
|
_, err := dORM.Insert(tag)
|
|
throwFailNow(t, err)
|
|
}
|
|
|
|
num, err := m2m.Add(tag1)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
|
|
num, err = m2m.Add(tag2)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Add(tag3)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 5))
|
|
|
|
num, err = m2m.Remove(tag3)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 4))
|
|
|
|
exist := m2m.Exist(tag2)
|
|
throwFailNow(t, AssertIs(exist, true))
|
|
|
|
num, err = m2m.Remove(tag2)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
exist = m2m.Exist(tag2)
|
|
throwFailNow(t, AssertIs(exist, false))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
|
|
num, err = m2m.Clear()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 0))
|
|
|
|
tag := Tag{Name: "test"}
|
|
_, err = dORM.Insert(&tag)
|
|
throwFailNow(t, err)
|
|
|
|
m2m = dORM.QueryM2M(&tag, "Posts")
|
|
|
|
post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}}
|
|
post2 := &Post{Title: "TestPost3"}
|
|
post3 := []interface{}{&Post{Title: "TestPost4"}}
|
|
|
|
posts := []interface{}{post1[0], post1[1], post2, post3[0]}
|
|
|
|
for _, post := range posts {
|
|
p := post.(*Post)
|
|
p.User = &User{ID: 1}
|
|
_, err := dORM.Insert(post)
|
|
throwFailNow(t, err)
|
|
}
|
|
|
|
num, err = m2m.Add(post1)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
|
|
num, err = m2m.Add(post2)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Add(post3)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 4))
|
|
|
|
num, err = m2m.Remove(post3)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
|
|
exist = m2m.Exist(post2)
|
|
throwFailNow(t, AssertIs(exist, true))
|
|
|
|
num, err = m2m.Remove(post2)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
exist = m2m.Exist(post2)
|
|
throwFailNow(t, AssertIs(exist, false))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
|
|
num, err = m2m.Clear()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
|
|
num, err = m2m.Count()
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 0))
|
|
|
|
num, err = dORM.Delete(&tag)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestQueryRelate(_ *testing.T) {
|
|
// post := &Post{Id: 2}
|
|
|
|
// qs := dORM.QueryRelate(post, "Tags")
|
|
// num, err := qs.Count()
|
|
// throwFailNow(t, err)
|
|
// throwFailNow(t, AssertIs(num, 2))
|
|
|
|
// var tags []*Tag
|
|
// num, err = qs.All(&tags)
|
|
// throwFailNow(t, err)
|
|
// throwFailNow(t, AssertIs(num, 2))
|
|
// throwFailNow(t, AssertIs(tags[0].Name, "golang"))
|
|
|
|
// num, err = dORM.QueryTable("Tag").Filter("Posts__Post", 2).Count()
|
|
// throwFailNow(t, err)
|
|
// throwFailNow(t, AssertIs(num, 2))
|
|
}
|
|
|
|
func TestPkManyRelated(t *testing.T) {
|
|
permission := &Permission{Name: "readPosts"}
|
|
err := dORM.Read(permission, "Name")
|
|
throwFailNow(t, err)
|
|
|
|
var groups []*Group
|
|
qs := dORM.QueryTable("Group")
|
|
num, err := qs.Filter("Permissions__Permission", permission.ID).All(&groups)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
}
|
|
|
|
func TestPrepareInsert(t *testing.T) {
|
|
qs := dORM.QueryTable("user")
|
|
i, err := qs.PrepareInsert()
|
|
throwFailNow(t, err)
|
|
|
|
var user User
|
|
user.UserName = "testing1"
|
|
num, err := i.Insert(&user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num > 0, true))
|
|
|
|
user.UserName = "testing2"
|
|
num, err = i.Insert(&user)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num > 0, true))
|
|
|
|
num, err = qs.Filter("user_name__in", "testing1", "testing2").Delete()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 2))
|
|
|
|
err = i.Close()
|
|
throwFail(t, err)
|
|
err = i.Close()
|
|
throwFail(t, AssertIs(err, ErrStmtClosed))
|
|
}
|
|
|
|
func TestRawExec(t *testing.T) {
|
|
Q := dDbBaser.TableQuote()
|
|
|
|
query := fmt.Sprintf("UPDATE %suser%s SET %suser_name%s = ? WHERE %suser_name%s = ?", Q, Q, Q, Q, Q, Q)
|
|
res, err := dORM.Raw(query, "testing", "slene").Exec()
|
|
throwFail(t, err)
|
|
num, err := res.RowsAffected()
|
|
throwFail(t, AssertIs(num, 1), err)
|
|
|
|
res, err = dORM.Raw(query, "slene", "testing").Exec()
|
|
throwFail(t, err)
|
|
num, err = res.RowsAffected()
|
|
throwFail(t, AssertIs(num, 1), err)
|
|
}
|
|
|
|
func TestRawQueryRow(t *testing.T) {
|
|
var (
|
|
Boolean bool
|
|
Char string
|
|
Text string
|
|
Time time.Time
|
|
Date time.Time
|
|
DateTime time.Time
|
|
Byte byte
|
|
Rune rune
|
|
Int int
|
|
Int8 int
|
|
Int16 int16
|
|
Int32 int32
|
|
Int64 int64
|
|
Uint uint
|
|
Uint8 uint8
|
|
Uint16 uint16
|
|
Uint32 uint32
|
|
Uint64 uint64
|
|
Float32 float32
|
|
Float64 float64
|
|
Decimal float64
|
|
)
|
|
|
|
dataValues := make(map[string]interface{}, len(DataValues))
|
|
|
|
for k, v := range DataValues {
|
|
dataValues[strings.ToLower(k)] = v
|
|
}
|
|
|
|
Q := dDbBaser.TableQuote()
|
|
|
|
cols := []string{
|
|
"id", "boolean", "char", "text", "time", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32",
|
|
"int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal",
|
|
}
|
|
sep := fmt.Sprintf("%s, %s", Q, Q)
|
|
query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q)
|
|
var id int
|
|
values := []interface{}{
|
|
&id, &Boolean, &Char, &Text, &Time, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32,
|
|
&Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal,
|
|
}
|
|
err := dORM.Raw(query, 1).QueryRow(values...)
|
|
throwFailNow(t, err)
|
|
for i, col := range cols {
|
|
vu := values[i]
|
|
v := reflect.ValueOf(vu).Elem().Interface()
|
|
switch col {
|
|
case "id":
|
|
throwFail(t, AssertIs(id, 1))
|
|
case "time", "datetime":
|
|
v = v.(time.Time).In(DefaultTimeLoc)
|
|
value := dataValues[col].(time.Time).In(DefaultTimeLoc)
|
|
assert.True(t, v.(time.Time).Sub(value) <= time.Second)
|
|
case "date":
|
|
default:
|
|
throwFail(t, AssertIs(v, dataValues[col]))
|
|
}
|
|
}
|
|
|
|
var (
|
|
uid int
|
|
status *int
|
|
pid *int
|
|
)
|
|
|
|
cols = []string{
|
|
"id", "Status", "profile_id",
|
|
}
|
|
query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
|
|
err = dORM.Raw(query, 4).QueryRow(&uid, &status, &pid)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(uid, 4))
|
|
throwFail(t, AssertIs(*status, 3))
|
|
throwFail(t, AssertIs(pid, nil))
|
|
|
|
type Embedded struct {
|
|
Email string
|
|
}
|
|
type queryRowNoModelTest struct {
|
|
Id int
|
|
EmbedField Embedded
|
|
}
|
|
|
|
cols = []string{
|
|
"id", "email",
|
|
}
|
|
var row queryRowNoModelTest
|
|
query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
|
|
err = dORM.Raw(query, 4).QueryRow(&row)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(row.Id, 4))
|
|
throwFail(t, AssertIs(row.EmbedField.Email, "nobody@gmail.com"))
|
|
|
|
// test for sql.Null* Fields
|
|
nData := &DataNull{
|
|
NullString: sql.NullString{String: "test sql.null", Valid: true},
|
|
NullBool: sql.NullBool{Bool: true, Valid: true},
|
|
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
|
|
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
|
|
}
|
|
newId, err := dORM.Insert(nData)
|
|
throwFailNow(t, err)
|
|
|
|
var nd *DataNull
|
|
query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
|
|
err = dORM.Raw(query, newId).QueryRow(&nd)
|
|
throwFailNow(t, err)
|
|
|
|
throwFailNow(t, AssertNot(nd, nil))
|
|
throwFail(t, AssertIs(nd.NullBool.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullBool.Bool, true))
|
|
throwFail(t, AssertIs(nd.NullString.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
|
|
throwFail(t, AssertIs(nd.NullInt64.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
|
|
throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
|
|
}
|
|
|
|
// user_profile table
|
|
type userProfile struct {
|
|
User
|
|
Age int
|
|
Money float64
|
|
}
|
|
|
|
func TestQueryRows(t *testing.T) {
|
|
Q := dDbBaser.TableQuote()
|
|
|
|
var datas []*Data
|
|
|
|
query := fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
|
|
num, err := dORM.Raw(query).QueryRows(&datas)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(len(datas), 1))
|
|
|
|
ind := reflect.Indirect(reflect.ValueOf(datas[0]))
|
|
|
|
for name, value := range DataValues {
|
|
e := ind.FieldByName(name)
|
|
vu := e.Interface()
|
|
switch name {
|
|
case "Time":
|
|
case "Date":
|
|
case "DateTime":
|
|
assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
|
|
default:
|
|
assert.Equal(t, value, vu)
|
|
}
|
|
}
|
|
|
|
var datas2 []Data
|
|
|
|
query = fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
|
|
num, err = dORM.Raw(query).QueryRows(&datas2)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(len(datas2), 1))
|
|
|
|
ind = reflect.Indirect(reflect.ValueOf(datas2[0]))
|
|
|
|
for name, value := range DataValues {
|
|
e := ind.FieldByName(name)
|
|
vu := e.Interface()
|
|
switch name {
|
|
case "Time":
|
|
case "Date":
|
|
case "DateTime":
|
|
assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
|
|
default:
|
|
assert.Equal(t, value, vu)
|
|
}
|
|
|
|
}
|
|
|
|
var ids []int
|
|
var usernames []string
|
|
query = fmt.Sprintf("SELECT %sid%s, %suser_name%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q, Q, Q)
|
|
num, err = dORM.Raw(query).QueryRows(&ids, &usernames)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 3))
|
|
throwFailNow(t, AssertIs(len(ids), 3))
|
|
throwFailNow(t, AssertIs(ids[0], 2))
|
|
throwFailNow(t, AssertIs(usernames[0], "slene"))
|
|
throwFailNow(t, AssertIs(ids[1], 3))
|
|
throwFailNow(t, AssertIs(usernames[1], "astaxie"))
|
|
throwFailNow(t, AssertIs(ids[2], 4))
|
|
throwFailNow(t, AssertIs(usernames[2], "nobody"))
|
|
|
|
// test query rows by nested struct
|
|
var l []userProfile
|
|
query = fmt.Sprintf("SELECT * FROM %suser_profile%s LEFT JOIN %suser%s ON %suser_profile%s.%sid%s = %suser%s.%sid%s", Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q)
|
|
num, err = dORM.Raw(query).QueryRows(&l)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 2))
|
|
throwFailNow(t, AssertIs(len(l), 2))
|
|
throwFailNow(t, AssertIs(l[0].UserName, "slene"))
|
|
throwFailNow(t, AssertIs(l[0].Age, 28))
|
|
throwFailNow(t, AssertIs(l[1].UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(l[1].Age, 30))
|
|
|
|
// test for sql.Null* Fields
|
|
nData := &DataNull{
|
|
NullString: sql.NullString{String: "test sql.null", Valid: true},
|
|
NullBool: sql.NullBool{Bool: true, Valid: true},
|
|
NullInt64: sql.NullInt64{Int64: 42, Valid: true},
|
|
NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
|
|
}
|
|
newId, err := dORM.Insert(nData)
|
|
throwFailNow(t, err)
|
|
|
|
var nDataList []*DataNull
|
|
query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
|
|
num, err = dORM.Raw(query, newId).QueryRows(&nDataList)
|
|
throwFailNow(t, err)
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
|
|
nd := nDataList[0]
|
|
throwFailNow(t, AssertNot(nd, nil))
|
|
throwFail(t, AssertIs(nd.NullBool.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullBool.Bool, true))
|
|
throwFail(t, AssertIs(nd.NullString.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
|
|
throwFail(t, AssertIs(nd.NullInt64.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
|
|
throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
|
|
throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
|
|
}
|
|
|
|
func TestRawValues(t *testing.T) {
|
|
Q := dDbBaser.TableQuote()
|
|
|
|
var maps []Params
|
|
query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sStatus%s = ?", Q, Q, Q, Q, Q, Q)
|
|
num, err := dORM.Raw(query, 1).Values(&maps)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
if num == 1 {
|
|
throwFail(t, AssertIs(maps[0]["user_name"], "slene"))
|
|
}
|
|
|
|
var lists []ParamsList
|
|
num, err = dORM.Raw(query, 1).ValuesList(&lists)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
if num == 1 {
|
|
throwFail(t, AssertIs(lists[0][0], "slene"))
|
|
}
|
|
|
|
query = fmt.Sprintf("SELECT %sprofile_id%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q)
|
|
var list ParamsList
|
|
num, err = dORM.Raw(query).ValuesFlat(&list)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
if num == 3 {
|
|
throwFail(t, AssertIs(list[0], "2"))
|
|
throwFail(t, AssertIs(list[1], "3"))
|
|
throwFail(t, AssertIs(list[2], nil))
|
|
}
|
|
}
|
|
|
|
func TestForIssue4709(t *testing.T) {
|
|
pre, err := dORM.Raw("INSERT into null_value (value) VALUES (?)").Prepare()
|
|
assert.Nil(t, err)
|
|
_, err = pre.Exec(nil)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestRawPrepare(t *testing.T) {
|
|
var (
|
|
result sql.Result
|
|
err error
|
|
pre RawPreparer
|
|
)
|
|
if IsMysql || IsSqlite {
|
|
pre, err = dORM.Raw("INSERT INTO tag (name) VALUES (?)").Prepare()
|
|
assert.Nil(t, err)
|
|
if pre != nil {
|
|
result, err = pre.Exec("name1")
|
|
assert.Nil(t, err)
|
|
|
|
tid, err := result.LastInsertId()
|
|
assert.Nil(t, err)
|
|
assert.True(t, tid > 0)
|
|
|
|
result, err = pre.Exec("name2")
|
|
assert.Nil(t, err)
|
|
|
|
id, err := result.LastInsertId()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, id, tid+1)
|
|
|
|
result, err = pre.Exec("name3")
|
|
assert.Nil(t, err)
|
|
|
|
id, err = result.LastInsertId()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, id, tid+2)
|
|
|
|
err = pre.Close()
|
|
assert.Nil(t, err)
|
|
|
|
res, err := dORM.Raw("DELETE FROM tag WHERE name IN (?, ?, ?)", []string{"name1", "name2", "name3"}).Exec()
|
|
assert.Nil(t, err)
|
|
|
|
num, err := res.RowsAffected()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, num, int64(3))
|
|
}
|
|
} else if IsPostgres {
|
|
pre, err = dORM.Raw(`INSERT INTO "tag" ("name") VALUES (?) RETURNING "id"`).Prepare()
|
|
assert.Nil(t, err)
|
|
if pre != nil {
|
|
_, err = pre.Exec("name1")
|
|
assert.Nil(t, err)
|
|
|
|
_, err = pre.Exec("name2")
|
|
assert.Nil(t, err)
|
|
|
|
_, err = pre.Exec("name3")
|
|
assert.Nil(t, err)
|
|
|
|
err = pre.Close()
|
|
assert.Nil(t, err)
|
|
|
|
res, err := dORM.Raw(`DELETE FROM "tag" WHERE "name" IN (?, ?, ?)`, []string{"name1", "name2", "name3"}).Exec()
|
|
assert.Nil(t, err)
|
|
|
|
if err == nil {
|
|
num, err := res.RowsAffected()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, num, int64(3))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
qs := dORM.QueryTable("user")
|
|
num, err := qs.Filter("user_name", "slene").Filter("is_staff", false).Update(Params{
|
|
"is_staff": true,
|
|
"is_active": true,
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
// with join
|
|
num, err = qs.Filter("user_name", "slene").Filter("profile__age", 28).Filter("is_staff", true).Update(Params{
|
|
"is_staff": false,
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name", "slene").Update(Params{
|
|
"Nums": ColValue(ColAdd, 100),
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name", "slene").Update(Params{
|
|
"Nums": ColValue(ColMinus, 50),
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name", "slene").Update(Params{
|
|
"Nums": ColValue(ColMultiply, 3),
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = qs.Filter("user_name", "slene").Update(Params{
|
|
"Nums": ColValue(ColExcept, 5),
|
|
})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
user := User{UserName: "slene"}
|
|
err = dORM.Read(&user, "UserName")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(user.Nums, 30))
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
qs := dORM.QueryTable("user_profile")
|
|
num, err := qs.Filter("user__user_name", "slene").Delete()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
qs = dORM.QueryTable("user")
|
|
num, err = qs.Filter("user_name", "slene").Filter("profile__isnull", true).Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
qs = dORM.QueryTable("comment")
|
|
num, err = qs.Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 6))
|
|
|
|
qs = dORM.QueryTable("post")
|
|
num, err = qs.Filter("Id", 3).Delete()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
qs = dORM.QueryTable("comment")
|
|
num, err = qs.Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 4))
|
|
|
|
qs = dORM.QueryTable("comment")
|
|
num, err = qs.Filter("Post__User", 3).Delete()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
qs = dORM.QueryTable("comment")
|
|
num, err = qs.Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestTransaction(t *testing.T) {
|
|
// this test worked when database support transaction
|
|
|
|
o := NewOrm()
|
|
to, err := o.Begin()
|
|
throwFail(t, err)
|
|
|
|
names := []string{"1", "2", "3"}
|
|
|
|
var tag Tag
|
|
tag.Name = names[0]
|
|
id, err := to.Insert(&tag)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
|
|
num, err := to.QueryTable("tag").Filter("name", "golang").Update(Params{"name": names[1]})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
if IsMysql || IsSqlite {
|
|
res, err := to.Raw("INSERT INTO tag (name) VALUES (?)", names[2]).Exec()
|
|
throwFail(t, err)
|
|
if err == nil {
|
|
id, err = res.LastInsertId()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
}
|
|
}
|
|
|
|
err = to.Rollback()
|
|
assert.Nil(t, err)
|
|
num, err = o.QueryTable("tag").Filter("name__in", names).Count()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, int64(0), num)
|
|
|
|
to, err = o.Begin()
|
|
assert.Nil(t, err)
|
|
|
|
tag.Name = "commit"
|
|
id, err = to.Insert(&tag)
|
|
assert.Nil(t, err)
|
|
assert.True(t, id > 0)
|
|
|
|
err = to.Commit()
|
|
assert.Nil(t, err)
|
|
|
|
num, err = o.QueryTable("tag").Filter("name", "commit").Delete()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, int64(1), num)
|
|
}
|
|
|
|
func TestTxOrmRollbackUnlessCommit(t *testing.T) {
|
|
o := NewOrm()
|
|
var tag Tag
|
|
|
|
// test not committed and call RollbackUnlessCommit
|
|
to, err := o.Begin()
|
|
assert.Nil(t, err)
|
|
tag.Name = "rollback unless commit"
|
|
rows, err := to.Insert(&tag)
|
|
assert.Nil(t, err)
|
|
assert.True(t, rows > 0)
|
|
err = to.RollbackUnlessCommit()
|
|
assert.Nil(t, err)
|
|
num, err := o.QueryTable("tag").Filter("name", tag.Name).Delete()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, int64(0), num)
|
|
|
|
// test commit and call RollbackUnlessCommit
|
|
|
|
to, err = o.Begin()
|
|
assert.Nil(t, err)
|
|
tag.Name = "rollback unless commit"
|
|
rows, err = to.Insert(&tag)
|
|
assert.Nil(t, err)
|
|
assert.True(t, rows > 0)
|
|
|
|
err = to.Commit()
|
|
assert.Nil(t, err)
|
|
|
|
err = to.RollbackUnlessCommit()
|
|
assert.Nil(t, err)
|
|
|
|
num, err = o.QueryTable("tag").Filter("name", tag.Name).Delete()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, int64(1), num)
|
|
}
|
|
|
|
func TestTransactionIsolationLevel(t *testing.T) {
|
|
// this test worked when database support transaction isolation level
|
|
if IsSqlite {
|
|
return
|
|
}
|
|
|
|
o1 := NewOrm()
|
|
o2 := NewOrm()
|
|
|
|
// start two transaction with isolation level repeatable read
|
|
to1, err := o1.BeginWithCtxAndOpts(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
|
|
throwFail(t, err)
|
|
to2, err := o2.BeginWithCtxAndOpts(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
|
|
throwFail(t, err)
|
|
|
|
// o1 insert tag
|
|
var tag Tag
|
|
tag.Name = "test-transaction"
|
|
id, err := to1.Insert(&tag)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
|
|
// o2 query tag table, no result
|
|
num, err := to2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 0))
|
|
|
|
// o1 commit
|
|
to1.Commit()
|
|
|
|
// o2 query tag table, still no result
|
|
num, err = to2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 0))
|
|
|
|
// o2 commit and query tag table, get the result
|
|
to2.Commit()
|
|
num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
|
|
num, err = o1.QueryTable("tag").Filter("name", "test-transaction").Delete()
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestBeginTxWithContextCanceled(t *testing.T) {
|
|
o := NewOrm()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
to, _ := o.BeginWithCtx(ctx)
|
|
id, err := to.Insert(&Tag{Name: "test-context"})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id > 0, true))
|
|
|
|
// cancel the context before commit to make it error
|
|
cancel()
|
|
err = to.Commit()
|
|
throwFail(t, AssertIs(err, context.Canceled))
|
|
}
|
|
|
|
func TestReadOrCreate(t *testing.T) {
|
|
u := &User{
|
|
UserName: "Kyle",
|
|
Email: "kylemcc@gmail.com",
|
|
Password: "other_pass",
|
|
Status: 7,
|
|
IsStaff: false,
|
|
IsActive: true,
|
|
}
|
|
|
|
created, pk, err := dORM.ReadOrCreate(u, "UserName")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, true))
|
|
throwFail(t, AssertIs(u.ID, pk))
|
|
throwFail(t, AssertIs(u.UserName, "Kyle"))
|
|
throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com"))
|
|
throwFail(t, AssertIs(u.Password, "other_pass"))
|
|
throwFail(t, AssertIs(u.Status, 7))
|
|
throwFail(t, AssertIs(u.IsStaff, false))
|
|
throwFail(t, AssertIs(u.IsActive, true))
|
|
throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), testDate))
|
|
throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), testDateTime))
|
|
|
|
nu := &User{UserName: u.UserName, Email: "someotheremail@gmail.com"}
|
|
created, pk, err = dORM.ReadOrCreate(nu, "UserName")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, false))
|
|
throwFail(t, AssertIs(nu.ID, u.ID))
|
|
throwFail(t, AssertIs(pk, u.ID))
|
|
throwFail(t, AssertIs(nu.UserName, u.UserName))
|
|
throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above
|
|
throwFail(t, AssertIs(nu.Password, u.Password))
|
|
throwFail(t, AssertIs(nu.Status, u.Status))
|
|
throwFail(t, AssertIs(nu.IsStaff, u.IsStaff))
|
|
throwFail(t, AssertIs(nu.IsActive, u.IsActive))
|
|
assert.True(t, u.ID > 0)
|
|
|
|
dORM.Delete(u)
|
|
assert.True(t, u.ID > 0)
|
|
}
|
|
|
|
func TestInLine(t *testing.T) {
|
|
name := "inline"
|
|
email := "hello@go.com"
|
|
inline := NewInLine()
|
|
inline.Name = name
|
|
inline.Email = email
|
|
|
|
id, err := dORM.Insert(inline)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 1))
|
|
|
|
il := NewInLine()
|
|
il.ID = 1
|
|
err = dORM.Read(il)
|
|
throwFail(t, err)
|
|
|
|
throwFail(t, AssertIs(il.Name, name))
|
|
throwFail(t, AssertIs(il.Email, email))
|
|
assert.True(t, il.Created.In(DefaultTimeLoc).Sub(inline.Created.In(DefaultTimeLoc)) <= time.Second)
|
|
assert.True(t, il.Updated.In(DefaultTimeLoc).Sub(inline.Updated.In(DefaultTimeLoc)) <= time.Second)
|
|
}
|
|
|
|
func TestInLineOneToOne(t *testing.T) {
|
|
name := "121"
|
|
email := "121@go.com"
|
|
inline := NewInLine()
|
|
inline.Name = name
|
|
inline.Email = email
|
|
|
|
id, err := dORM.Insert(inline)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(id, 2))
|
|
|
|
note := "one2one"
|
|
il121 := NewInLineOneToOne()
|
|
il121.Note = note
|
|
il121.InLine = inline
|
|
_, err = dORM.Insert(il121)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(il121.ID, 1))
|
|
|
|
il := NewInLineOneToOne()
|
|
err = dORM.QueryTable(il).Filter("Id", 1).RelatedSel().One(il)
|
|
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(il.Note, note))
|
|
throwFail(t, AssertIs(il.InLine.ID, id))
|
|
throwFail(t, AssertIs(il.InLine.Name, name))
|
|
throwFail(t, AssertIs(il.InLine.Email, email))
|
|
|
|
rinline := NewInLine()
|
|
err = dORM.QueryTable(rinline).Filter("InLineOneToOne__Id", 1).One(rinline)
|
|
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(rinline.ID, id))
|
|
throwFail(t, AssertIs(rinline.Name, name))
|
|
throwFail(t, AssertIs(rinline.Email, email))
|
|
}
|
|
|
|
func TestIntegerPk(t *testing.T) {
|
|
its := []IntegerPk{
|
|
{ID: math.MinInt64, Value: "-"},
|
|
{ID: 0, Value: "0"},
|
|
{ID: math.MaxInt64, Value: "+"},
|
|
}
|
|
|
|
num, err := dORM.InsertMulti(len(its), its)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, len(its)))
|
|
|
|
for _, intPk := range its {
|
|
out := IntegerPk{ID: intPk.ID}
|
|
err = dORM.Read(&out)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(out.Value, intPk.Value))
|
|
}
|
|
|
|
num, err = dORM.InsertMulti(1, []*IntegerPk{{
|
|
ID: 1, Value: "ok",
|
|
}})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestInsertAuto(t *testing.T) {
|
|
u := &User{
|
|
UserName: "autoPre",
|
|
Email: "autoPre@gmail.com",
|
|
}
|
|
|
|
id, err := dORM.Insert(u)
|
|
throwFail(t, err)
|
|
|
|
id += 100
|
|
su := &User{
|
|
ID: int(id),
|
|
UserName: "auto",
|
|
Email: "auto@gmail.com",
|
|
}
|
|
|
|
nid, err := dORM.Insert(su)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(nid, id))
|
|
|
|
users := []User{
|
|
{ID: int(id + 100), UserName: "auto_100"},
|
|
{ID: int(id + 110), UserName: "auto_110"},
|
|
{ID: int(id + 120), UserName: "auto_120"},
|
|
}
|
|
num, err := dORM.InsertMulti(100, users)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 3))
|
|
|
|
u = &User{
|
|
UserName: "auto_121",
|
|
}
|
|
|
|
nid, err = dORM.Insert(u)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(nid, id+120+1))
|
|
}
|
|
|
|
func TestUintPk(t *testing.T) {
|
|
name := "go"
|
|
u := &UintPk{
|
|
ID: 8,
|
|
Name: name,
|
|
}
|
|
|
|
created, _, err := dORM.ReadOrCreate(u, "ID")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, true))
|
|
throwFail(t, AssertIs(u.Name, name))
|
|
|
|
nu := &UintPk{ID: 8}
|
|
created, pk, err := dORM.ReadOrCreate(nu, "ID")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, false))
|
|
throwFail(t, AssertIs(nu.ID, u.ID))
|
|
throwFail(t, AssertIs(pk, u.ID))
|
|
throwFail(t, AssertIs(nu.Name, name))
|
|
|
|
dORM.Delete(u)
|
|
}
|
|
|
|
func TestPtrPk(t *testing.T) {
|
|
parent := &IntegerPk{ID: 10, Value: "10"}
|
|
|
|
id, _ := dORM.Insert(parent)
|
|
if !IsMysql {
|
|
// MySql does not support last_insert_id in this case: see #2382
|
|
throwFail(t, AssertIs(id, 10))
|
|
}
|
|
|
|
ptr := PtrPk{ID: parent, Positive: true}
|
|
num, err := dORM.InsertMulti(2, []PtrPk{ptr})
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
throwFail(t, AssertIs(ptr.ID, parent))
|
|
|
|
nptr := &PtrPk{ID: parent}
|
|
created, pk, err := dORM.ReadOrCreate(nptr, "ID")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, false))
|
|
throwFail(t, AssertIs(pk, 10))
|
|
throwFail(t, AssertIs(nptr.ID, parent))
|
|
throwFail(t, AssertIs(nptr.Positive, true))
|
|
|
|
nptr = &PtrPk{Positive: true}
|
|
created, pk, err = dORM.ReadOrCreate(nptr, "Positive")
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(created, false))
|
|
throwFail(t, AssertIs(pk, 10))
|
|
throwFail(t, AssertIs(nptr.ID, parent))
|
|
|
|
nptr.Positive = false
|
|
num, err = dORM.Update(nptr)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
throwFail(t, AssertIs(nptr.ID, parent))
|
|
throwFail(t, AssertIs(nptr.Positive, false))
|
|
|
|
num, err = dORM.Delete(nptr)
|
|
throwFail(t, err)
|
|
throwFail(t, AssertIs(num, 1))
|
|
}
|
|
|
|
func TestSnake(t *testing.T) {
|
|
cases := map[string]string{
|
|
"i": "i",
|
|
"I": "i",
|
|
"iD": "i_d",
|
|
"ID": "i_d",
|
|
"NO": "n_o",
|
|
"NOO": "n_o_o",
|
|
"NOOooOOoo": "n_o_ooo_o_ooo",
|
|
"OrderNO": "order_n_o",
|
|
"tagName": "tag_name",
|
|
"tag_Name": "tag__name",
|
|
"tag_name": "tag_name",
|
|
"_tag_name": "_tag_name",
|
|
"tag_666name": "tag_666name",
|
|
"tag_666Name": "tag_666_name",
|
|
}
|
|
for name, want := range cases {
|
|
got := models.SnakeString(name)
|
|
throwFail(t, AssertIs(got, want))
|
|
}
|
|
}
|
|
|
|
func TestIgnoreCaseTag(t *testing.T) {
|
|
type testTagModel struct {
|
|
ID int `orm:"pk"`
|
|
NOO string `orm:"column(n)"`
|
|
Name01 string `orm:"NULL"`
|
|
Name02 string `orm:"COLUMN(Name)"`
|
|
Name03 string `orm:"Column(name)"`
|
|
}
|
|
defaultModelCache.clean()
|
|
RegisterModel(&testTagModel{})
|
|
info, ok := defaultModelCache.get("test_tag_model")
|
|
throwFail(t, AssertIs(ok, true))
|
|
throwFail(t, AssertNot(info, nil))
|
|
if t == nil {
|
|
return
|
|
}
|
|
throwFail(t, AssertIs(info.Fields.GetByName("NOO").Column, "n"))
|
|
throwFail(t, AssertIs(info.Fields.GetByName("Name01").Null, true))
|
|
throwFail(t, AssertIs(info.Fields.GetByName("Name02").Column, "Name"))
|
|
throwFail(t, AssertIs(info.Fields.GetByName("Name03").Column, "name"))
|
|
}
|
|
|
|
func TestInsertOrUpdate(t *testing.T) {
|
|
RegisterModel(new(User))
|
|
userName := "unique_username133"
|
|
column := "user_name"
|
|
user := User{UserName: userName, Status: 1, Password: "o"}
|
|
user1 := User{UserName: userName, Status: 2, Password: "o"}
|
|
user2 := User{UserName: userName, Status: 3, Password: "oo"}
|
|
dORM.Insert(&user)
|
|
fmt.Println(dORM.Driver().Name())
|
|
if dORM.Driver().Name() == "sqlite3" {
|
|
fmt.Println("sqlite3 is nonsupport")
|
|
return
|
|
}
|
|
|
|
specs := []struct {
|
|
description string
|
|
user User
|
|
colConflitAndArgs []string
|
|
assertion func(expected User, actual User)
|
|
isPostgresCompatible bool
|
|
}{
|
|
{
|
|
description: "test1",
|
|
user: user1,
|
|
colConflitAndArgs: []string{column},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs(expected.Status, actual.Status))
|
|
},
|
|
isPostgresCompatible: true,
|
|
},
|
|
{
|
|
description: "test2",
|
|
user: user2,
|
|
colConflitAndArgs: []string{column},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs(expected.Status, actual.Status))
|
|
throwFailNow(t, AssertIs(expected.Password, strings.TrimSpace(actual.Password)))
|
|
},
|
|
isPostgresCompatible: true,
|
|
},
|
|
{
|
|
description: "test3 +",
|
|
user: user2,
|
|
colConflitAndArgs: []string{column, "status=status+1"},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs(expected.Status+1, actual.Status))
|
|
},
|
|
isPostgresCompatible: false,
|
|
},
|
|
{
|
|
description: "test4 -",
|
|
user: user2,
|
|
colConflitAndArgs: []string{column, "status=status-1"},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs((expected.Status+1)-1, actual.Status))
|
|
},
|
|
isPostgresCompatible: false,
|
|
},
|
|
{
|
|
description: "test5 *",
|
|
user: user2,
|
|
colConflitAndArgs: []string{column, "status=status*3"},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs(((expected.Status+1)-1)*3, actual.Status))
|
|
},
|
|
isPostgresCompatible: false,
|
|
},
|
|
{
|
|
description: "test6 /",
|
|
user: user2,
|
|
colConflitAndArgs: []string{column, "Status=Status/3"},
|
|
assertion: func(expected, actual User) {
|
|
throwFailNow(t, AssertIs((((expected.Status+1)-1)*3)/3, actual.Status))
|
|
},
|
|
isPostgresCompatible: false,
|
|
},
|
|
}
|
|
|
|
for _, spec := range specs {
|
|
// postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values
|
|
if IsPostgres && !spec.isPostgresCompatible {
|
|
continue
|
|
}
|
|
|
|
_, err := dORM.InsertOrUpdate(&spec.user, spec.colConflitAndArgs...)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
if !(err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego") {
|
|
throwFailNow(t, err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
test := User{UserName: userName}
|
|
err = dORM.Read(&test, column)
|
|
throwFailNow(t, AssertIs(err, nil))
|
|
spec.assertion(spec.user, test)
|
|
}
|
|
}
|
|
|
|
func TestStrPkInsert(t *testing.T) {
|
|
RegisterModel(new(StrPk))
|
|
pk := `1`
|
|
value := `StrPkValues(*56`
|
|
strPk := &StrPk{
|
|
Id: pk,
|
|
Value: value,
|
|
}
|
|
|
|
var err error
|
|
_, err = dORM.Insert(strPk)
|
|
if err != ErrLastInsertIdUnavailable {
|
|
throwFailNow(t, AssertIs(err, nil))
|
|
}
|
|
|
|
var vForTesting StrPk
|
|
err = dORM.QueryTable(new(StrPk)).Filter(`id`, pk).One(&vForTesting)
|
|
throwFailNow(t, AssertIs(err, nil))
|
|
throwFailNow(t, AssertIs(vForTesting.Value, value))
|
|
|
|
value2 := `s8s5da7as`
|
|
strPkForUpsert := &StrPk{
|
|
Id: pk,
|
|
Value: value2,
|
|
}
|
|
|
|
_, err = dORM.InsertOrUpdate(strPkForUpsert, `id`)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
|
|
return
|
|
} else if err == ErrLastInsertIdUnavailable {
|
|
return
|
|
} else {
|
|
throwFailNow(t, err)
|
|
}
|
|
} else {
|
|
var vForTesting2 StrPk
|
|
err = dORM.QueryTable(new(StrPk)).Filter(`id`, pk).One(&vForTesting2)
|
|
throwFailNow(t, AssertIs(err, nil))
|
|
throwFailNow(t, AssertIs(vForTesting2.Value, value2))
|
|
}
|
|
}
|
|
|
|
func TestPSQueryBuilder(t *testing.T) {
|
|
// only test postgres
|
|
if dORM.Driver().Type() != 4 {
|
|
return
|
|
}
|
|
|
|
var user User
|
|
var l []userProfile
|
|
o := NewOrm()
|
|
|
|
qb, err := NewQueryBuilder("postgres")
|
|
if err != nil {
|
|
throwFailNow(t, err)
|
|
}
|
|
qb.Select("user.id", "user.user_name").
|
|
From("user").Where("id = ?").OrderBy("user_name").
|
|
Desc().Limit(1).Offset(0)
|
|
sql := qb.String()
|
|
err = o.Raw(sql, 2).QueryRow(&user)
|
|
if err != nil {
|
|
throwFailNow(t, err)
|
|
}
|
|
throwFail(t, AssertIs(user.UserName, "slene"))
|
|
|
|
qb.Select("*").
|
|
From("user_profile").InnerJoin("user").
|
|
On("user_profile.id = user.id")
|
|
sql = qb.String()
|
|
num, err := o.Raw(sql).QueryRows(&l)
|
|
if err != nil {
|
|
throwFailNow(t, err)
|
|
}
|
|
throwFailNow(t, AssertIs(num, 1))
|
|
throwFailNow(t, AssertIs(l[0].UserName, "astaxie"))
|
|
throwFailNow(t, AssertIs(l[0].Age, 30))
|
|
}
|
|
|
|
func TestCondition(t *testing.T) {
|
|
// test Condition whether to include yourself
|
|
cond := NewCondition()
|
|
cond = cond.AndCond(cond.Or("ID", 1))
|
|
cond = cond.AndCond(cond.Or("ID", 2))
|
|
cond = cond.AndCond(cond.Or("ID", 3))
|
|
cond = cond.AndCond(cond.Or("ID", 4))
|
|
|
|
cycleFlag := false
|
|
var hasCycle func(*Condition)
|
|
hasCycle = func(c *Condition) {
|
|
if nil == c || cycleFlag {
|
|
return
|
|
}
|
|
condPointMap := make(map[string]bool)
|
|
condPointMap[fmt.Sprintf("%p", c)] = true
|
|
for _, p := range c.params {
|
|
if p.isCond {
|
|
adr := fmt.Sprintf("%p", p.cond)
|
|
if condPointMap[adr] {
|
|
// self as sub cond was cycle
|
|
cycleFlag = true
|
|
break
|
|
}
|
|
condPointMap[adr] = true
|
|
|
|
}
|
|
}
|
|
if cycleFlag {
|
|
return
|
|
}
|
|
for _, p := range c.params {
|
|
if p.isCond {
|
|
// check next cond
|
|
hasCycle(p.cond)
|
|
}
|
|
}
|
|
}
|
|
hasCycle(cond)
|
|
// cycleFlag was true,meaning use self as sub cond
|
|
throwFail(t, AssertIs(!cycleFlag, true))
|
|
}
|
|
|
|
func TestContextCanceled(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
user := User{UserName: "slene"}
|
|
|
|
err := dORM.ReadWithCtx(ctx, &user, "UserName")
|
|
throwFail(t, err)
|
|
|
|
cancel()
|
|
err = dORM.ReadWithCtx(ctx, &user, "UserName")
|
|
throwFail(t, AssertIs(err, context.Canceled))
|
|
|
|
ctx, cancel = context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
qs := dORM.QueryTable(user)
|
|
_, err = qs.Filter("UserName", "slene").CountWithCtx(ctx)
|
|
throwFail(t, AssertIs(err, context.Canceled))
|
|
}
|
|
|
|
func TestDebugLog(t *testing.T) {
|
|
txCommitFn := func() {
|
|
o := NewOrm()
|
|
o.DoTx(func(ctx context.Context, txOrm TxOrmer) (txerr error) {
|
|
_, txerr = txOrm.QueryTable(&User{}).Count()
|
|
return
|
|
})
|
|
}
|
|
|
|
txRollbackFn := func() {
|
|
o := NewOrm()
|
|
o.DoTx(func(ctx context.Context, txOrm TxOrmer) (txerr error) {
|
|
user := NewUser()
|
|
user.UserName = "slene"
|
|
user.Email = "vslene@gmail.com"
|
|
user.Password = "pass"
|
|
user.Status = 3
|
|
user.IsStaff = true
|
|
user.IsActive = true
|
|
|
|
txOrm.Insert(user)
|
|
txerr = fmt.Errorf("mock error")
|
|
return
|
|
})
|
|
}
|
|
|
|
Debug = true
|
|
output1 := captureDebugLogOutput(txCommitFn)
|
|
|
|
assert.Contains(t, output1, "START TRANSACTION")
|
|
assert.Contains(t, output1, "COMMIT")
|
|
|
|
output2 := captureDebugLogOutput(txRollbackFn)
|
|
|
|
assert.Contains(t, output2, "START TRANSACTION")
|
|
assert.Contains(t, output2, "ROLLBACK")
|
|
|
|
Debug = false
|
|
output1 = captureDebugLogOutput(txCommitFn)
|
|
assert.EqualValues(t, output1, "")
|
|
|
|
output2 = captureDebugLogOutput(txRollbackFn)
|
|
assert.EqualValues(t, output2, "")
|
|
}
|
|
|
|
func captureDebugLogOutput(f func()) string {
|
|
var buf bytes.Buffer
|
|
logs.DebugLog.SetOutput(&buf)
|
|
defer func() {
|
|
logs.DebugLog.SetOutput(os.Stderr)
|
|
}()
|
|
f()
|
|
return buf.String()
|
|
}
|