} // String returns the user id and email func (user User) String() string { return fmt.Sprintf("%d: %s", user.ID, user.Email) } // Users is the postgres schema for users var Users = postgres.Table("users", sol.Column("id", postgres.Serial()), sol.Column("email", types.Varchar().Limit(256).NotNull()), sol.Column("first_name", types.Varchar().Limit(64).NotNull()), sol.Column("last_name", types.Varchar().Limit(64).NotNull()), sol.Column("about", types.Varchar().Limit(512).NotNull()), sol.Column("photo", types.Varchar().Limit(512).NotNull()), sol.Column("is_active", types.Boolean().NotNull().Default(true)), sol.Column("is_superuser", types.Boolean().NotNull().Default(false)), sol.Column("password", types.Varchar().Limit(256).NotNull()), sol.Column("token", types.Varchar().Limit(256).NotNull()), sol.Column( "token_set_at", postgres.Timestamp().WithTimezone().NotNull().Default(postgres.Now), ), sol.Column( "created_at", postgres.Timestamp().WithTimezone().NotNull().Default(postgres.Now), ), sol.PrimaryKey("id"), sol.Unique("email"), )
// IntegrationTest runs a large, neutral dialect test func IntegrationTest(t *testing.T, conn *DB, ddlCommit bool) { // Perform all tests in a transaction // TODO What features should be tested outside of a transaction? // CREATE TABLE is performed outside of the transaction because any // change to the DDL in MySQL is a implicit commit // Other databases: http://stackoverflow.com/a/4736346 testusers := Table("testusers", Column("id", types.Integer()), Column("email", types.Varchar().Limit(255).NotNull()), Column("is_admin", types.Boolean().NotNull()), Column("created_at", types.Timestamp()), PrimaryKey("id"), Unique("email"), ) type testuser struct { ID int64 Email string IsAdmin bool CreatedAt time.Time } tx, err := conn.Begin() if err != nil { t.Fatalf("Creating a new transaction should not error: %s", err) } defer tx.Rollback() if ddlCommit { if err = conn.Query(testusers.Create().IfNotExists()); err != nil { t.Fatalf("CREATE TABLE should not error: %s", err) } } else { if err = tx.Query(testusers.Create().IfNotExists()); err != nil { t.Fatalf("CREATE TABLE should not error: %s", err) } } // INSERT by struct // Truncate the time.Time field to avoid significant digit errors admin := testuser{ ID: 1, Email: "*****@*****.**", IsAdmin: true, CreatedAt: time.Now().UTC().Truncate(time.Second), } if err = tx.Query(testusers.Insert().Values(admin)); err != nil { t.Fatalf("INSERT by struct should not fail %s", err) } // SELECT var selected testuser if err = tx.Query( testusers.Select().Where(testusers.C("id").Equals(admin.ID)), &selected, ); err != nil { t.Fatalf("SELECT should not fail: %s", err) } // TODO test with direct comparison: selected == admin // For now, test each field since DATETIME handling is terribly // inconsistent across databases if selected.ID != admin.ID { t.Errorf( "Unequal testusers id: have %d, want %d", selected.ID, admin.ID, ) } if selected.Email != admin.Email { t.Errorf( "Unequal testusers email: have %s, want %s", selected.Email, admin.Email, ) } if selected.IsAdmin != admin.IsAdmin { t.Errorf( "Unequal testusers is_admin: have %t, want %t", selected.IsAdmin, admin.IsAdmin, ) } if !selected.CreatedAt.Equal(admin.CreatedAt) { t.Errorf( "Unequal testusers created_at: have %v, want %v", selected.CreatedAt, admin.CreatedAt, ) } // UPDATE if err = tx.Query( testusers.Update().Values( Values{"is_admin": false}, ).Where(testusers.C("id").Equals(admin.ID)), ); err != nil { t.Fatalf("UPDATE should not fail: %s", err) } var updated testuser if err = tx.Query(testusers.Select().Limit(1), &updated); err != nil { t.Fatalf("SELECT should not fail: %s", err) } selected.IsAdmin = false if updated != selected { t.Errorf( "Unequal testusers: have %+v, want %+v", updated, selected, ) } // INSERT by values client := Values{ "id": 2, "email": "*****@*****.**", "is_admin": false, "created_at": time.Now().UTC().Truncate(time.Second), } if err = tx.Query(testusers.Insert().Values(client)); err != nil { t.Fatalf("INSERT by values should not fail %s", err) } var list []testuser if err = tx.Query( testusers.Select().OrderBy(testusers.C("id").Desc()), &list, ); err != nil { t.Fatalf("SELECT with ORDER BY should not fail: %s", err) } if len(list) != 2 { t.Fatalf("Unexpected length of list: want 2, have %d", len(list)) } // The client should be first if list[0].Email != "*****@*****.**" { t.Errorf( "Unexpected email: want [email protected], have %s", list[0].Email, ) } var count int64 if err = tx.Query(Select(Count(testusers.C("id"))), &count); err != nil { t.Fatalf("SELECT with COUNT should not fail: %s", err) } if count != 2 { t.Errorf("Unexpected COUNT: want 2, have %d", count) } // DELETE if err = tx.Query( testusers.Delete().Where(testusers.C("email").Equals(admin.Email)), ); err != nil { t.Fatalf("DELETE should not fail: %s", err) } // DROP TABLE // TODO Since this is a DDL, this will likely commit in MySQL if err = tx.Query(testusers.Drop()); err != nil { t.Fatalf("DROP TABLE should not fail %s", err) } // Test a recover func() { defer func() { if panicked := recover(); panicked == nil { t.Errorf("Connection failed to panic on error") } }() conn.Must().Query(testusers.Select(), list) }() }