package sol import ( "testing" "github.com/aodin/sol/types" ) var tableA = Table("a", Column("id", types.Integer()), Column("value", types.Varchar()), ) var tableB = Table("b", Column("id", types.Integer()), Column("value", types.Varchar()), ) var relations = Table("relations", Column("a_id", types.Integer()), Column("b_id", types.Integer()), Unique("a_id", "b_id"), ) func TestJoinClause(t *testing.T) { expect := NewTester(t, &defaultDialect{}) expect.SQL( Select(tableA).CrossJoin(relations), `SELECT a.id, a.value FROM a CROSS JOIN relations`, )
package sol import ( "time" "github.com/aodin/sol/types" ) // Valid schemas should not panic var users = Table("users", Column("id", types.Integer()), Column("email", types.Varchar().Limit(256).NotNull()), // TODO unique Column("name", types.Varchar().Limit(32).NotNull()), Column("password", types.Varchar()), Column("created_at", types.Timestamp()), PrimaryKey("id"), Unique("email"), ) var contacts = Table("contacts", Column("id", types.Integer()), Column("user_id", types.Integer()), // TODO FK Column("key", types.Varchar()), Column("value", types.Varchar()), PrimaryKey("id"), Unique("user_id", "key"), ) type user struct { ID uint64 `db:",omitempty"` Email string
// 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) }() }
CreatedAt time.Time `db:",omitempty"` } var itemsA = Table("items_a", sol.Column("id", Serial()), sol.Column("name", types.Varchar()), ) var itemsB = Table("items_b", sol.Column("id", Serial()), sol.Column("name", types.Varchar()), sol.PrimaryKey("id"), ) var itemsFK = Table("items_fk", sol.ForeignKey("id", itemsB, types.Integer().NotNull()), sol.Column("name", types.Varchar()), ) type item struct { ID uint64 `db:",omitempty"` Name string } func (i item) Exists() bool { return i.ID != 0 } var meetings = Table("meetings", sol.Column("uuid", UUID().NotNull().Unique().Default(GenerateV4)), sol.Column("time", TimestampRange()),
} return session.manager.Delete(session.Key) } // Exists returns true if the session exists func (session Session) Exists() bool { return session.Key != "" } // Sessions is the postgres schema for sessions var Sessions = postgres.Table("sessions", sol.Column("key", types.Varchar().NotNull()), sol.ForeignKey( "user_id", Users.C("id"), types.Integer().NotNull(), ).OnDelete(sol.Cascade).OnUpdate(sol.Cascade), sol.Column("expires", postgres.Timestamp().WithTimezone()), sol.PrimaryKey("key"), ) // SessionManager is the internal manager of sessions type SessionManager struct { conn sol.Conn cookie config.Cookie keyFunc KeyFunc nowFunc func() time.Time } // Create creates a new session using a key generated for the given User func (m *SessionManager) Create(user User) (session Session) {