summaryrefslogtreecommitdiff
path: root/tests/papod.go
diff options
context:
space:
mode:
Diffstat (limited to 'tests/papod.go')
-rw-r--r--tests/papod.go4530
1 files changed, 4081 insertions, 449 deletions
diff --git a/tests/papod.go b/tests/papod.go
index 42974f4..283017a 100644
--- a/tests/papod.go
+++ b/tests/papod.go
@@ -19,6 +19,261 @@ import (
+type userChangeT struct{
+ id int64
+ timestamp time.Time
+ user_id int64
+ attribute string
+ valueStr *string
+ valueBlob *guuid.UUID
+ valueBool *bool
+ op bool
+}
+
+type networkChangeT struct{
+ id int64
+ timestamp time.Time
+ network_id int64
+ attribute string
+ value string
+ op bool
+}
+
+type channelChangeT struct{
+ id int64
+ timestamp time.Time
+ channel_id int64
+ attribute string
+ value string
+ op bool
+}
+
+
+
+func userChangesSQL(prefix string) string {
+ const tmpl = `
+ SELECT
+ "%s_user_changes".id,
+ "%s_user_changes".timestamp,
+ "%s_user_changes".user_id,
+ "%s_user_changes".attribute,
+ "%s_user_changes".value_text,
+ "%s_user_changes".value_blob,
+ "%s_user_changes".value_bool,
+ "%s_user_changes".op
+ FROM "%s_user_changes"
+ JOIN "%s_users" ON
+ "%s_user_changes".user_id = "%s_users".id
+ WHERE "%s_users".user_uuid = ?
+ ORDER BY "%s_user_changes".id ASC;
+ `
+ return fmt.Sprintf(
+ tmpl,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ )
+}
+
+func makeUserChanges(db *sql.DB, prefix string) func(guuid.UUID) []userChangeT {
+ q := userChangesSQL(prefix)
+
+ return func(userID guuid.UUID) []userChangeT {
+ userChanges := []userChangeT{}
+ rows, err := db.Query(q, userID[:])
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ for rows.Next() {
+ userChange := userChangeT{}
+ var (
+ timestr string
+ value_bytes []byte
+ )
+ err := rows.Scan(
+ &userChange.id,
+ &timestr,
+ &userChange.user_id,
+ &userChange.attribute,
+ &userChange.valueStr,
+ &value_bytes,
+ &userChange.valueBool,
+ &userChange.op,
+ )
+ g.TErrorIf(err)
+
+ if value_bytes != nil {
+ valueBlob := guuid.UUID(value_bytes)
+ userChange.valueBlob = &valueBlob
+ }
+
+ userChange.timestamp, err = time.Parse(
+ time.RFC3339Nano,
+ timestr,
+ )
+ g.TErrorIf(err)
+
+ userChanges = append(userChanges, userChange)
+ }
+
+ g.TErrorIf(rows.Err())
+ return userChanges
+ }
+}
+
+func networkChangesSQL(prefix string) string {
+ const tmpl = `
+ SELECT
+ "%s_network_changes".id,
+ "%s_network_changes".timestamp,
+ "%s_network_changes".network_id,
+ "%s_network_changes".attribute,
+ "%s_network_changes".value,
+ "%s_network_changes".op
+ FROM "%s_network_changes"
+ JOIN "%s_networks" ON
+ "%s_network_changes".network_id = "%s_networks".id
+ WHERE "%s_networks".uuid = ?
+ ORDER BY "%s_network_changes".id ASC;
+ `
+ return fmt.Sprintf(
+ tmpl,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ prefix,
+ )
+}
+
+func makeNetworkChanges(
+ db *sql.DB,
+ prefix string,
+) func(guuid.UUID) []networkChangeT {
+ q := networkChangesSQL(prefix)
+
+ return func(networkID guuid.UUID) []networkChangeT {
+ networkChanges := []networkChangeT{}
+ rows, err := db.Query(q, networkID[:])
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ for rows.Next() {
+ networkChange := networkChangeT{}
+ var timestr string
+ err := rows.Scan(
+ &networkChange.id,
+ &timestr,
+ &networkChange.network_id,
+ &networkChange.attribute,
+ &networkChange.value,
+ &networkChange.op,
+ )
+ g.TErrorIf(err)
+
+ networkChange.timestamp, err = time.Parse(
+ time.RFC3339Nano,
+ timestr,
+ )
+ g.TErrorIf(err)
+
+ networkChanges = append(networkChanges, networkChange)
+ }
+
+ g.TErrorIf(rows.Err())
+ return networkChanges
+ }
+}
+
+func channelChangesSQL(prefix string) string {
+ const tmpl = `
+ SELECT
+ id,
+ timestamp,
+ channel_id,
+ attribute,
+ value_text,
+ value_bool,
+ op
+ FROM "%s_channel_changes"
+ WHERE channel_id = ?
+ ORDER BY id ASC;
+ `
+ return fmt.Sprintf(tmpl, prefix)
+}
+
+func makeChannelChanges(
+ db *sql.DB,
+ prefix string,
+) func(int64) []channelChangeT {
+ q := channelChangesSQL(prefix)
+
+ return func(id int64) []channelChangeT {
+ channelChanges := []channelChangeT{}
+ rows, err := db.Query(q, id)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ for rows.Next() {
+ channelChange := channelChangeT{}
+ var (
+ timestr string
+ valueString sql.NullString
+ valueBool sql.NullBool
+ )
+ err := rows.Scan(
+ &channelChange.id,
+ &timestr,
+ &channelChange.channel_id,
+ &channelChange.attribute,
+ &valueString,
+ &valueBool,
+ &channelChange.op,
+ )
+ g.TErrorIf(err)
+
+ if valueString.Valid {
+ channelChange.value = valueString.String
+ } else if valueBool.Valid {
+ if valueBool.Bool {
+ channelChange.value = "true"
+ } else {
+ channelChange.value = "false"
+ }
+ }
+
+ channelChange.timestamp, err = time.Parse(
+ time.RFC3339Nano,
+ timestr,
+ )
+ g.TErrorIf(err)
+
+ channelChanges = append(channelChanges, channelChange)
+ }
+
+ g.TErrorIf(rows.Err())
+ return channelChanges
+ }
+}
+
func mknstring(n int) string {
buffer := make([]byte, n)
_, err := io.ReadFull(rand.Reader, buffer)
@@ -39,14 +294,6 @@ func test_defaultPrefix() {
})
}
-func test_serialized() {
- // FIXME
-}
-
-func test_execSerialized() {
- // FIXME
-}
-
func test_tryRollback() {
g.TestStart("tryRollback()")
@@ -143,7 +390,7 @@ func test_createUserStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -159,6 +406,8 @@ func test_createUserStmt() {
db.Close,
)
+ userChanges := makeUserChanges(db, prefix)
+
g.Testing("userID's must be unique", func() {
newUser := newUserT{
@@ -223,6 +472,61 @@ func test_createUserStmt() {
g.TErrorIf(err2)
})
+ g.Testing("new user trigger inserts into *_user_changes", func() {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ username: mkstring(),
+ displayName: mkstring(),
+ }
+
+ _, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ changes := userChanges(newUser.uuid)
+ g.TAssertEqual(len(changes), 3)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ changes[2].attribute,
+ *changes[0].valueStr,
+ *changes[1].valueStr,
+ },
+ []string{
+ "username",
+ "display_name",
+ "deleted",
+ newUser.username,
+ newUser.displayName,
+ },
+ )
+ g.TAssertEqual(*changes[2].valueBool, false)
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[2].op,
+ changes[0].valueBlob == nil,
+ changes[0].valueBool == nil,
+ changes[1].valueBlob == nil,
+ changes[1].valueBool == nil,
+ changes[2].valueStr == nil,
+ changes[2].valueBlob == nil,
+ },
+ []bool{
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ },
+ )
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
createUserClose(),
@@ -240,7 +544,7 @@ func test_userByUUIDStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -326,7 +630,7 @@ func test_updateUserStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -366,10 +670,12 @@ func test_updateUserStmt() {
return user
}
+ userChanges := makeUserChanges(db, prefix)
+
g.Testing("a user needs to exist to be updated", func() {
virtualUser := userT{
- id: 1234,
+ id: 1234,
}
g.TAssertEqual(updateUser(virtualUser), sql.ErrNoRows)
@@ -452,7 +758,7 @@ func test_updateUserStmt() {
user2 := user1
user2.timestamp = user2.timestamp.Add(time.Minute * 1)
user2.uuid = guuid.New()
- err = updateUser(user2)
+ err := updateUser(user2)
g.TErrorIf(err)
user3, err := userByUUID(user1.uuid)
@@ -460,6 +766,154 @@ func test_updateUserStmt() {
g.TAssertEqual(user3, user1)
})
+ g.Testing("no extra writes to *_user_changes when not updated", func() {
+ user := create()
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+
+ err := updateUser(user)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+ })
+
+ g.Testing("new username end up in *_user_changes when updated", func() {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ username: "first username",
+ displayName: "display name",
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+
+ user.username = "second username"
+ g.TErrorIf(updateUser(user))
+
+ changes := userChanges(user.uuid)[3:]
+ g.TAssertEqual(len(changes), 2)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ *changes[0].valueStr,
+ *changes[1].valueStr,
+ },
+ []string{
+ "username",
+ "username",
+ "first username",
+ "second username",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[0].valueBlob == nil,
+ changes[0].valueBool == nil,
+ changes[1].valueBlob == nil,
+ changes[1].valueBool == nil,
+ },
+ []bool{
+ false,
+ true,
+ true,
+ true,
+ true,
+ true,
+ },
+ )
+ })
+
+ g.Testing("displayName end up in *_user_changes when updated", func() {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ username: "username",
+ displayName: "first display name",
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+
+ user.displayName = "second display name"
+ g.TErrorIf(updateUser(user))
+ changes := userChanges(user.uuid)[3:]
+ g.TAssertEqual(len(changes), 2)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ *changes[0].valueStr,
+ *changes[1].valueStr,
+ },
+ []string{
+ "display_name",
+ "display_name",
+ "first display name",
+ "second display name",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[0].valueBlob == nil,
+ changes[0].valueBool == nil,
+ changes[1].valueBlob == nil,
+ changes[1].valueBool == nil,
+ },
+ []bool{
+ false,
+ true,
+ true,
+ true,
+ true,
+ true,
+ },
+ )
+ })
+
+ g.Testing("pictureID end up in *_user_changes when updated", func() {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ username: "username",
+ displayName: "first display name",
+ }
+ pictureID := guuid.New()
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+
+ user.pictureID = &pictureID
+ g.TErrorIf(updateUser(user))
+ changes := userChanges(user.uuid)[3:]
+ g.TAssertEqual(len(changes), 1)
+ g.TAssertEqual(changes[0].attribute, "picture_uuid")
+ g.TAssertEqual(*changes[0].valueBlob, pictureID)
+ g.TAssertEqual(changes[0].op, true)
+
+ user.pictureID = nil
+ g.TErrorIf(updateUser(user))
+ changes = userChanges(user.uuid)[4:]
+ g.TAssertEqual(len(changes), 1)
+ g.TAssertEqual(changes[0].attribute, "picture_uuid")
+ g.TAssertEqual(*changes[0].valueBlob, pictureID)
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[0].valueStr == nil,
+ changes[0].valueBool == nil,
+ },
+ []bool{
+ false,
+ true,
+ true,
+ },
+ )
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
updateUserClose(),
@@ -477,7 +931,7 @@ func test_deleteUserStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -495,8 +949,11 @@ func test_deleteUserStmt() {
defer g.SomeFnError(
createUserClose,
deleteUserClose,
+ db.Close,
)
+ userChanges := makeUserChanges(db, prefix)
+
g.Testing("a user needs to exist to be deleted", func() {
err := deleteUser(guuid.New())
@@ -517,6 +974,49 @@ func test_deleteUserStmt() {
g.TAssertEqual(err2, sql.ErrNoRows)
})
+ g.Testing("deletion triggers insertion into *_user_changes", func() {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(userChanges(user.uuid)), 3)
+
+ g.TErrorIf(deleteUser(user.uuid))
+ changes := userChanges(user.uuid)[3:]
+ g.TAssertEqual(len(changes), 2)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ },
+ []string{ "deleted", "deleted" },
+ )
+ g.TAssertEqual(
+ []bool{
+ *changes[0].valueBool,
+ changes[0].op,
+ changes[0].valueStr == nil,
+ changes[0].valueBlob == nil,
+ *changes[1].valueBool,
+ changes[1].op,
+ changes[1].valueStr == nil,
+ changes[1].valueBlob == nil,
+ },
+ []bool{
+ false,
+ false,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ },
+ )
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
deleteUserClose(),
@@ -527,7 +1027,6 @@ func test_deleteUserStmt() {
}
func test_addNetworkStmt() {
- return // FIXME
g.TestStart("addNetworkStmt()")
const (
@@ -535,7 +1034,7 @@ func test_addNetworkStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -547,15 +1046,22 @@ func test_addNetworkStmt() {
createUser, createUserClose, createUserErr := createUserStmt(cfg)
deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ members, membersClose, membersErr := membersStmt(cfg)
g.TErrorIf(g.SomeError(
createUserErr,
deleteUserErr,
addNetworkErr,
+ membershipErr,
+ membersErr,
))
defer g.SomeFnError(
createUserClose,
deleteUserClose,
addNetworkClose,
+ membershipClose,
+ membersClose,
+ db.Close,
)
create := func() userT {
@@ -569,8 +1075,25 @@ func test_addNetworkStmt() {
return user
}
+ allMembers := func(actor memberT, networkID guuid.UUID) []memberT {
+ rows, err := members(actor)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ var members []memberT
+ err = memberEach(rows, func(member memberT) error {
+ members = append(members, member)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ return members
+ }
+
+ networkChanges := makeNetworkChanges(db, prefix)
- g.Testing("a user can create a newtwork", func() {
+
+ g.Testing("a user can create a network", func() {
creator := create()
newNetwork := newNetworkT{
@@ -580,13 +1103,12 @@ func test_addNetworkStmt() {
type_: NetworkType_Unlisted,
}
- network, err := addNetwork(creator, newNetwork)
+ network, err := addNetwork(creator, newNetwork, guuid.New())
g.TErrorIf(err)
g.TAssertEqual(network.id == 0, false)
g.TAssertEqual(network.timestamp == time.Time{}, false)
g.TAssertEqual(network.uuid, newNetwork.uuid)
- g.TAssertEqual(network.createdBy, creator.uuid)
g.TAssertEqual(network.name, "the network name")
g.TAssertEqual(network.description, "the network description")
g.TAssertEqual(network.type_, NetworkType_Unlisted)
@@ -594,14 +1116,15 @@ func test_addNetworkStmt() {
g.Testing("the creator needs to exist", func() {
newNetwork := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
}
virtualUser := userT{
- uuid: guuid.New(),
+ id: 1234,
}
- _, err := addNetwork(virtualUser, newNetwork)
+ _, err := addNetwork(virtualUser, newNetwork, guuid.New())
g.TAssertEqual(
err.(golite.Error).ExtendedCode,
golite.ErrConstraintNotNull,
@@ -612,12 +1135,13 @@ func test_addNetworkStmt() {
creator := create()
newNetwork := newNetworkT{
- uuid: guuid.New(),
- name: mkstring(),
+ uuid: guuid.New(),
+ name: mkstring(),
+ type_: NetworkType_Unlisted,
}
- _, err1 := addNetwork(creator, newNetwork)
- _, err2 := addNetwork(creator, newNetwork)
+ _, err1 := addNetwork(creator, newNetwork, guuid.New())
+ _, err2 := addNetwork(creator, newNetwork, guuid.New())
g.TErrorIf(err1)
g.TAssertEqual(
err2.(golite.Error).ExtendedCode,
@@ -629,43 +1153,130 @@ func test_addNetworkStmt() {
creator := create()
newNetwork1 := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
}
newNetwork2 := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
}
- network1, err1 := addNetwork(creator, newNetwork1)
- network2, err2 := addNetwork(creator, newNetwork2)
+ _, err1 := addNetwork(creator, newNetwork1, guuid.New())
+ _, err2 := addNetwork(creator, newNetwork2, guuid.New())
g.TErrorIf(err1)
g.TErrorIf(err2)
-
- g.TAssertEqual(network1.createdBy, creator.uuid)
- g.TAssertEqual(network2.createdBy, creator.uuid)
})
g.Testing("a deleted user can't create a network", func() {
creator := create()
newNetwork1 := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
}
newNetwork2 := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
}
- _, err := addNetwork(creator, newNetwork1)
+ _, err := addNetwork(creator, newNetwork1, guuid.New())
g.TErrorIf(err)
err = deleteUser(creator.uuid)
g.TErrorIf(err)
- _, err = addNetwork(creator, newNetwork2)
+ _, err = addNetwork(creator, newNetwork2, guuid.New())
g.TAssertEqual(
err.(golite.Error).ExtendedCode,
golite.ErrConstraintNotNull,
)
})
+ g.Testing("new network triggers inserts to the changes table", func() {
+ creator := create()
+
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ name: "the network name",
+ description: "the network description",
+ type_: NetworkType_Unlisted,
+ }
+
+ _, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ changes := networkChanges(newNetwork.uuid)
+ g.TAssertEqual(len(changes), 4)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ changes[2].attribute,
+ changes[3].attribute,
+ changes[0].value,
+ changes[1].value,
+ changes[2].value,
+ changes[3].value,
+ },
+ []string{
+ "name",
+ "description",
+ "type",
+ "deleted",
+ "the network name",
+ "the network description",
+ "unlisted",
+ "0",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[2].op,
+ },
+ []bool{ true, true, true },
+ )
+ })
+
+ g.Testing("the creator is automatically a member", func() {
+ creator := create()
+ memberID := guuid.New()
+
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
+ }
+
+ network, err := addNetwork(creator, newNetwork, memberID)
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ members := allMembers(member, network.uuid)
+ g.TAssertEqual(len(members), 1)
+ g.TAssertEqual(members[0].uuid, memberID)
+ g.TAssertEqual(members[0].status, MemberStatus_Active)
+ })
+
+ g.Testing(`the creator has "creator" and "admin" roles`, func() {
+ creator := create()
+ memberID := guuid.New()
+
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Unlisted,
+ }
+
+ network, err := addNetwork(creator, newNetwork, memberID)
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(member.roles, []string{"admin", "creator"})
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
addNetworkClose(),
@@ -676,7 +1287,6 @@ func test_addNetworkStmt() {
}
func test_getNetworkStmt() {
- return // FIXME
g.TestStart("getNetworkStmt()")
const (
@@ -684,7 +1294,7 @@ func test_getNetworkStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -697,6 +1307,7 @@ func test_getNetworkStmt() {
deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
getNetwork, getNetworkClose, getNetworkErr := getNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
dropMember, dropMemberClose, dropMemberErr := dropMemberStmt(cfg)
g.TErrorIf(g.SomeError(
@@ -704,6 +1315,7 @@ func test_getNetworkStmt() {
deleteUserErr,
addNetworkErr,
getNetworkErr,
+ membershipErr,
addMemberErr,
dropMemberErr,
))
@@ -712,8 +1324,10 @@ func test_getNetworkStmt() {
deleteUserClose,
addNetworkClose,
getNetworkClose,
+ membershipClose,
addMemberClose,
dropMemberClose,
+ db.Close,
)
create := func() userT {
@@ -727,22 +1341,25 @@ func test_getNetworkStmt() {
return user
}
- add := func(user userT, type_ NetworkType) networkT {
+ add := func(user userT, type_ NetworkType) (networkT, memberT) {
newNetwork := newNetworkT{
uuid: guuid.New(),
type_: type_,
}
- network, err := addNetwork(user, newNetwork)
+ network, err := addNetwork(user, newNetwork, guuid.New())
g.TErrorIf(err)
- return network
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
}
g.Testing("what we get is the same that was created", func() {
creator := create()
- network1 := add(creator, NetworkType_Public)
+ network1, _ := add(creator, NetworkType_Public)
network2, err := getNetwork(creator, network1.uuid)
g.TErrorIf(err)
@@ -754,9 +1371,9 @@ func test_getNetworkStmt() {
g.TAssertEqual(err, sql.ErrNoRows)
})
- g.Testing("the probing user needs to exist", func() {
+ g.Testing("the probing member needs to exist", func() {
creator := create()
- network := add(creator, NetworkType_Public)
+ network, _ := add(creator, NetworkType_Public)
virtualUser := userT{
id: 1234,
@@ -772,7 +1389,7 @@ func test_getNetworkStmt() {
g.Testing("the probing user can see any public network", func() {
creator := create()
user := create()
- network := add(creator, NetworkType_Public)
+ network, _ := add(creator, NetworkType_Public)
network1, err1 := getNetwork(creator, network.uuid)
network2, err2 := getNetwork(user, network.uuid)
@@ -785,7 +1402,7 @@ func test_getNetworkStmt() {
g.Testing("the probing user sees the given unlisted network", func() {
creator := create()
user := create()
- network := add(creator, NetworkType_Unlisted)
+ network, _ := add(creator, NetworkType_Unlisted)
network1, err1 := getNetwork(creator, network.uuid)
network2, err2 := getNetwork(user, network.uuid)
@@ -798,7 +1415,7 @@ func test_getNetworkStmt() {
g.Testing("the probing user can't see a private network", func() {
creator := create()
user := create()
- network := add(creator, NetworkType_Private)
+ network, _ := add(creator, NetworkType_Private)
_, err1 := getNetwork(creator, network.uuid)
_, err2 := getNetwork(user, network.uuid)
@@ -808,17 +1425,19 @@ func test_getNetworkStmt() {
g.Testing("the probing user must be a member to see it", func() {
creator := create()
- member := create()
- network := add(creator, NetworkType_Private)
+ user := create()
+ network, member := add(creator, NetworkType_Private)
newMember := newMemberT{
- userID: member.uuid,
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
}
- _, err := addMember(creator, network, newMember)
+ _, err := addMember(member, newMember)
g.TErrorIf(err)
network1, err1 := getNetwork(creator, network.uuid)
- network2, err2 := getNetwork(member, network.uuid)
+ network2, err2 := getNetwork(user, network.uuid)
g.TErrorIf(err1)
g.TErrorIf(err2)
g.TAssertEqual(network1, network)
@@ -827,13 +1446,15 @@ func test_getNetworkStmt() {
g.Testing("we can get the network if the creator was deleted", func() {
creator := create()
- member := create()
- network := add(creator, NetworkType_Public)
+ user := create()
+ network, member := add(creator, NetworkType_Public)
newMember := newMemberT{
- userID: member.uuid,
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
}
- _, err := addMember(creator, network, newMember)
+ _, err := addMember(member, newMember)
g.TErrorIf(err)
network1, err := getNetwork(creator, network.uuid)
@@ -842,14 +1463,14 @@ func test_getNetworkStmt() {
err = deleteUser(creator.uuid)
g.TErrorIf(err)
- network2, err := getNetwork(member, network.uuid)
+ network2, err := getNetwork(user, network.uuid)
g.TErrorIf(err)
g.TAssertEqual(network2, network1)
})
g.Testing("a deleted creator can't get a network", func() {
creator := create()
- network := add(creator, NetworkType_Public)
+ network, _ := add(creator, NetworkType_Public)
_, err := getNetwork(creator, network.uuid)
g.TErrorIf(err)
@@ -863,7 +1484,7 @@ func test_getNetworkStmt() {
g.Testing("a deleted user can't get a public network", func() {
user := create()
- network := add(create(), NetworkType_Public)
+ network, _ := add(create(), NetworkType_Public)
_, err := getNetwork(user, network.uuid)
g.TErrorIf(err)
@@ -876,41 +1497,51 @@ func test_getNetworkStmt() {
})
g.Testing("a deleted member can't get a private network", func() {
- creator := create()
- member := create()
- network := add(creator, NetworkType_Private)
+ creator := create()
+ user := create()
+ network, member := add(creator, NetworkType_Private)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
- _, err := getNetwork(member, network.uuid)
+ _, err := addMember(member, newMember)
g.TErrorIf(err)
- err = deleteUser(member.uuid)
+ _, err = getNetwork(user, network.uuid)
g.TErrorIf(err)
- _, err = getNetwork(member, network.uuid)
+ err = deleteUser(user.uuid)
+ g.TErrorIf(err)
+
+ _, err = getNetwork(user, network.uuid)
g.TAssertEqual(err, sql.ErrNoRows)
})
g.Testing("a removed member can't get a private network", func() {
creator := create()
- member := create()
- network := add(creator, NetworkType_Private)
+ user := create()
+ network, member := add(creator, NetworkType_Private)
newMember := newMemberT{
- userID: member.uuid,
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
}
- _, err := getNetwork(member, network.uuid)
+ _, err := getNetwork(user, network.uuid)
g.TAssertEqual(err, sql.ErrNoRows)
- _, err = addMember(creator, network, newMember)
+ _, err = addMember(member, newMember)
g.TErrorIf(err)
- _, err = getNetwork(member, network.uuid)
+ _, err = getNetwork(user, network.uuid)
g.TErrorIf(err)
- err = dropMember(creator, member.uuid)
+ err = dropMember(member, newMember.memberID)
g.TErrorIf(err)
- _, err = getNetwork(member, network.uuid)
+ _, err = getNetwork(user, network.uuid)
g.TAssertEqual(err, sql.ErrNoRows)
})
@@ -924,12 +1555,181 @@ func test_getNetworkStmt() {
}
func test_networkEach() {
- // FIXME
+ g.TestStart("networkEach()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ networks, networksClose, networksErr := networksStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ networksErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ networksClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) guuid.UUID {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ _, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ return newNetwork.uuid
+ }
+
+
+ g.Testing("callback is not called on empty set", func() {
+ rows, err := networks(create())
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ networkEach(rows, func(networkT) error {
+ g.Unreachable()
+ return nil
+ })
+ })
+
+ g.Testing("the callback is called once for each entry", func() {
+ creator := create()
+ networkIDs := []guuid.UUID{
+ add(creator),
+ add(creator),
+ add(creator),
+ }
+
+ rows, err := networks(creator)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ var collectedIDs[]guuid.UUID
+ err = networkEach(rows, func(network networkT) error {
+ collectedIDs = append(collectedIDs, network.uuid)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ g.TAssertEqual(collectedIDs, networkIDs)
+ })
+
+ g.Testing("we halt if the timestamp is ill-formatted", func() {
+ creator := create()
+
+ add(creator)
+ add(creator)
+ networkID := add(creator)
+ add(creator)
+
+ const tmpl = `
+ UPDATE "%s_networks"
+ SET timestamp = %s
+ WHERE uuid = ?;
+ `
+ q1 := fmt.Sprintf(tmpl, prefix, "'01/01/1970'")
+ _, err := db.Exec(q1, networkID[:])
+ g.TErrorIf(err)
+
+ rows, err := networks(creator)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ n := 0
+ err = networkEach(rows, func(networkT) error {
+ n++
+ return nil
+ })
+
+ g.TAssertEqual(
+ err,
+ &time.ParseError{
+ Layout: time.RFC3339Nano,
+ Value: "01/01/1970",
+ LayoutElem: "2006",
+ ValueElem: "01/01/1970",
+ Message: "",
+ },
+ )
+ g.TAssertEqual(n, 5)
+
+ q2 := fmt.Sprintf(tmpl, prefix, g.SQLiteNow)
+ _, err = db.Exec(q2, networkID[:])
+ g.TErrorIf(err)
+ })
+
+ g.Testing("we halt if the callback returns an error", func() {
+ creator := create()
+ myErr := errors.New("callback error early return")
+
+ rows1, err := networks(creator)
+ g.TErrorIf(err)
+ defer rows1.Close()
+
+ n1 := 0
+ err1 := networkEach(rows1, func(networkT) error {
+ n1++
+ if n1 == 3 {
+ return myErr
+ }
+ return nil
+ })
+
+ rows2, err := networks(creator)
+ g.TErrorIf(err)
+ defer rows2.Close()
+
+ n2 := 0
+ err2 := networkEach(rows2, func(networkT) error {
+ n2++
+ return nil
+ })
+
+ g.TAssertEqual(err1, myErr)
+ g.TErrorIf(err2)
+ g.TAssertEqual(n1, 3)
+ g.TAssertEqual(n2, 7)
+ })
+
+ g.Testing("noop when given nil for *sql.Rows", func() {
+ err := networkEach(nil, func(networkT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
}
func test_networksStmt() {
- /*
- FIXME
g.TestStart("networksStmt()")
const (
@@ -937,55 +1737,153 @@ func test_networksStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
networks, networksClose, networksErr := networksStmt(cfg)
g.TErrorIf(g.SomeError(
+ createUserErr,
+ deleteUserErr,
addNetworkErr,
networksErr,
))
defer g.SomeFnError(
+ createUserClose,
+ deleteUserClose,
addNetworkClose,
networksClose,
db.Close,
)
- nets := func(user userT) []networkT {
- rows, err := networks(user)
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT, type_ NetworkType) networkT {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: type_,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
g.TErrorIf(err)
+ return network
+ }
+
+ allNetworks := func(user userT) ([]networkT, error) {
+ rows, err := networks(user)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
networkList := []networkT{}
err = networkEach(rows, func(network networkT) error {
networkList = append(networkList, network)
return nil
})
- g.TErrorIf(err)
+ if err != nil {
+ return nil, err
+ }
- return networkList
+ return networkList, nil
}
- g.Testing("when there are no networks, we get none", func() {
- // FIXME
+ g.Testing("when there are no networks, we get 0", func() {
+ networks, err := allNetworks(create())
+ g.TErrorIf(err)
+ g.TAssertEqual(len(networks), 0)
})
g.Testing("if we have only private networks, we also get none", func() {
- // FIXME
+ creator := create()
+ add(creator, NetworkType_Private)
+ add(creator, NetworkType_Private)
+ add(creator, NetworkType_Private)
+
+ networks, err := allNetworks(create())
+ g.TErrorIf(err)
+ g.TAssertEqual(len(networks), 0)
+ })
+
+ g.Testing("when only unlisted networks, we also get none", func() {
+ creator := create()
+ add(creator, NetworkType_Unlisted)
+ add(creator, NetworkType_Unlisted)
+ add(creator, NetworkType_Unlisted)
+
+ networks, err := allNetworks(create())
+ g.TErrorIf(err)
+ g.TAssertEqual(len(networks), 0)
})
g.Testing("we can get a list of public networks", func() {
- // FIXME
+ creator := create()
+ expected := []networkT{
+ add(creator, NetworkType_Public),
+ add(creator, NetworkType_Public),
+ add(creator, NetworkType_Public),
+ }
+
+ networks, err := allNetworks(create())
+ g.TErrorIf(err)
+ g.TAssertEqual(networks, expected)
})
g.Testing("a member user can see their's private networks", func() {
- // FIXME
+ creator1 := create()
+ creator2 := create()
+ add(creator1, NetworkType_Private)
+ add(creator1, NetworkType_Private)
+ add(creator1, NetworkType_Private)
+ add(creator2, NetworkType_Private)
+ add(creator2, NetworkType_Private)
+ add(creator2, NetworkType_Private)
+
+ networks, err := allNetworks(creator2)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(networks), 6)
})
- g.Testing("unlisted networks aren't shown", func() {
- // FIXME
+ g.Testing("a member user can see their's unlisted networks", func() {
+ creator1 := create()
+ creator2 := create()
+ add(creator1, NetworkType_Unlisted)
+ add(creator1, NetworkType_Unlisted)
+ add(creator1, NetworkType_Unlisted)
+ add(creator2, NetworkType_Unlisted)
+ add(creator2, NetworkType_Unlisted)
+ add(creator2, NetworkType_Unlisted)
+
+ networks, err := allNetworks(creator2)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(networks), 6)
+ })
+
+ g.Testing("a deleted user can't list anything", func() {
+ creator := create()
+ g.TErrorIf(deleteUser(creator.uuid))
+
+ _, err := allNetworks(creator)
+ g.TAssertEqual(err, sql.ErrNoRows)
})
g.Testing("no error if closed more than once", func() {
@@ -995,11 +1893,9 @@ func test_networksStmt() {
networksClose(),
))
})
- */
}
func test_setNetworkStmt() {
- return // FIXME
g.TestStart("setNetworkStmt()")
const (
@@ -1007,7 +1903,7 @@ func test_setNetworkStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -1020,17 +1916,35 @@ func test_setNetworkStmt() {
addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
getNetwork, getNetworkClose, getNetworkErr := getNetworkStmt(cfg)
setNetwork, setNetworkClose, setNetworkErr := setNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addRole, addRoleClose, addRoleErr := addRoleStmt(cfg)
+ dropRole, dropRoleClose, dropRoleErr := dropRoleStmt(cfg)
+ editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
+ dropMember, dropMemberClose, dropMemberErr := dropMemberStmt(cfg)
g.TErrorIf(g.SomeError(
createUserErr,
addNetworkErr,
getNetworkErr,
setNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addRoleErr,
+ dropRoleErr,
+ editMemberErr,
+ dropMemberErr,
))
defer g.SomeFnError(
createUserClose,
addNetworkClose,
getNetworkClose,
setNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addRoleClose,
+ dropRoleClose,
+ editMemberClose,
+ dropMemberClose,
db.Close,
)
@@ -1045,64 +1959,179 @@ func test_setNetworkStmt() {
return user
}
- add := func(user userT) networkT {
+ add := func(user userT) (networkT, memberT) {
newNetwork := newNetworkT{
- uuid: guuid.New(),
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
}
+ memberID := guuid.New()
- network, err := addNetwork(user, newNetwork)
+ network, err := addNetwork(user, newNetwork, memberID)
g.TErrorIf(err)
- return network
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
}
+ networkChanges := makeNetworkChanges(db, prefix)
- g.Testing("a network needs to exist to be updated", func() {
+
+ g.Testing("creator can change the network", func() {
creator := create()
- virtualNetwork := networkT{
- id: 1234,
- }
+ network1, member := add(creator)
- err := setNetwork(creator, virtualNetwork)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
+ name := network1.name + "name suffix"
+ network1.name = name
+ err := setNetwork(member, network1)
+ g.TErrorIf(err)
- g.Testing("creator can change the network", func() {
- // FIXME
+ network2, err := getNetwork(creator, network1.uuid)
+ g.TErrorIf(err)
+ g.TAssertEqual(network2.name, name)
})
- g.Testing(`"network-settings-admin" can change the network`, func() {
- // FIXME
+ g.Testing(`"network-settings-update" can change the network`, func() {
+ creator := create()
+ admin := create()
+ network, creatorMember := add(creator)
+ newMember := newMemberT{
+ userID: admin.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ adminMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = addRole(
+ creatorMember,
+ "network-settings-update",
+ adminMember,
+ )
+ g.TErrorIf(err)
+
+ name := network.name + "name suffix"
+ network.name = name
+ err = setNetwork(adminMember, network)
+ g.TErrorIf(err)
})
- g.Testing("ex-admin creator looses ability to change it", func() {
- // FIXME
+ g.Testing(`"admin" can change the network`, func() {
+ creator := create()
+ admin := create()
+ network, creatorMember := add(creator)
+ newMember := newMemberT{
+ userID: admin.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ adminMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = addRole(creatorMember, "admin", adminMember)
+ g.TErrorIf(err)
+
+ name := network.name + "name suffix"
+ network.name = name
+ err = setNetwork(adminMember, network)
+ g.TErrorIf(err)
})
g.Testing("ex-member creator looses ability to change it", func() {
- // FIXME
+ creator := create()
+ network, member := add(creator)
+
+ err := dropMember(member, member.uuid)
+ g.TErrorIf(err)
+
+ network.name = network.name + "name suffix"
+ err = setNetwork(member, network)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("ex-admin member looses ability to change it", func() {
+ creator := create()
+ admin := create()
+ network, creatorMember := add(creator)
+ newMember := newMemberT{
+ userID: admin.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ adminMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = addRole(creatorMember, "admin", adminMember)
+ g.TErrorIf(err)
+
+ name := network.name + "name suffix"
+ network.name = name
+ err = setNetwork(adminMember, network)
+ g.TErrorIf(err)
+
+ err = dropRole(creatorMember, "admin", adminMember)
+ g.TErrorIf(err)
+
+ err = setNetwork(adminMember, network)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
})
g.Testing("unauthorized users can't change the network", func() {
creator := create()
- member := create()
- network := add(creator)
+ user := create()
+ network, creatorMember := add(creator)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ userMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
network.name = "member can't set the name"
- err := setNetwork(member, network)
- g.TAssertEqual(err, "403")
+ err = setNetwork(userMember, network)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("non members can't change the network", func() {
+ creator := create()
+ network, member := add(creator)
+ _, otherMember := add(creator)
+
+ network.name = "non member can't set the name xablauzinho"
+ err1 := setNetwork(otherMember, network)
+ err2 := setNetwork(member, network)
+ g.TAssertEqual(
+ err1.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ g.TErrorIf(err2)
})
g.Testing("after setting, getting gives us the newer data", func() {
- creator := create()
- network1 := add(creator)
+ creator := create()
+ network1, member := add(creator)
network2 := network1
network2.name = "first network name"
network2.description = "first network description"
network2.type_ = NetworkType_Private
- err := setNetwork(creator, network2)
+ err := setNetwork(member, network2)
g.TErrorIf(err)
network3, err := getNetwork(creator, network1.uuid)
@@ -1112,19 +2141,136 @@ func test_setNetworkStmt() {
g.Testing("the uuid, timestamp or creator never changes", func() {
creator := create()
- network1 := add(creator)
+ network1, member := add(creator)
network2 := network1
network2.uuid = guuid.New()
network2.timestamp = time.Time{}
- network2.createdBy = guuid.New()
- err := setNetwork(creator, network2)
+ err := setNetwork(member, network2)
g.TErrorIf(err)
network3, err := getNetwork(creator, network1.uuid)
g.TErrorIf(err)
g.TAssertEqual(network3, network1)
+ g.TAssertEqual(reflect.DeepEqual(network3, network2), false)
+ })
+
+ g.Testing("inactive member can't set the network", func() {
+ network, member := add(create())
+
+ member.status = "inactive"
+ err := editMember(member, member)
+ g.TErrorIf(err)
+
+ err = setNetwork(member, network)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("removed member can't set the network", func() {
+ network, member := add(create())
+
+ member.status = "removed"
+ err := editMember(member, member)
+ g.TErrorIf(err)
+
+ err = setNetwork(member, network)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("no extra writes to changes table when no updates", func() {
+ network, member := add(create())
+
+ lenBefore := len(networkChanges(network.uuid))
+
+ err := setNetwork(member, network)
+ g.TErrorIf(err)
+
+ lenAfter := len(networkChanges(network.uuid))
+
+ g.TAssertEqual(lenBefore, lenAfter)
+ })
+
+ g.Testing("updates do cause writes to changes table", func() {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ name: "first name",
+ description: "first description",
+ type_: NetworkType_Public,
+ }
+ memberID := guuid.New()
+
+ network, err := addNetwork(creator, newNetwork, memberID)
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ lenBefore := len(networkChanges(network.uuid))
+
+ network.name = "second name"
+ network.description = "second description"
+ network.type_ = NetworkType_Unlisted
+ err = setNetwork(member, network)
+ g.TErrorIf(err)
+
+ changes := networkChanges(network.uuid)[lenBefore:]
+ g.TAssertEqual(len(changes), 6)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ changes[2].attribute,
+ changes[3].attribute,
+ changes[4].attribute,
+ changes[5].attribute,
+ changes[0].value,
+ changes[1].value,
+ changes[2].value,
+ changes[3].value,
+ changes[4].value,
+ changes[5].value,
+ },
+ []string{
+ "type",
+ "type",
+ "description",
+ "description",
+ "name",
+ "name",
+ "public",
+ "unlisted",
+ "first description",
+ "second description",
+ "first name",
+ "second name",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[2].op,
+ changes[3].op,
+ changes[4].op,
+ changes[5].op,
+ },
+ []bool{
+ false,
+ true,
+ false,
+ true,
+ false,
+ true,
+ },
+ )
})
g.Testing("no error if closed more than once", func() {
@@ -1137,40 +2283,693 @@ func test_setNetworkStmt() {
}
func test_nipNetworkStmt() {
- // FIXME
+ g.TestStart("nipNetworkStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ getNetwork, getNetworkClose, getNetworkErr := getNetworkStmt(cfg)
+ networks, networksClose, networksErr := networksStmt(cfg)
+ setNetwork, setNetworkClose, setNetworkErr := setNetworkStmt(cfg)
+ nipNetwork, nipNetworkClose, nipNetworkErr := nipNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addRole, addRoleClose, addRoleErr := addRoleStmt(cfg)
+ editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ getNetworkErr,
+ networksErr,
+ setNetworkErr,
+ nipNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addRoleErr,
+ editMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ getNetworkClose,
+ networksClose,
+ setNetworkClose,
+ nipNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addRoleClose,
+ editMemberClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) (networkT, memberT) {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
+ }
+
+ allNetworks := func(user userT) []networkT {
+ rows, err := networks(user)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ networkList := []networkT{}
+ err = networkEach(rows, func(network networkT) error {
+ networkList = append(networkList, network)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ return networkList
+ }
+
+ networkChanges := makeNetworkChanges(db, prefix)
+
+
+ g.Testing("can't `get` a deleted network", func() {
+ creator := create()
+ network, member := add(creator)
+ err := nipNetwork(member)
+ g.TErrorIf(err)
+
+ _, err = getNetwork(creator, network.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("after deletion it vanishes from `networks()`", func() {
+ creator := create()
+ _, member := add(creator)
+
+ g.TAssertEqual(len(allNetworks(creator)), 1)
+
+ err := nipNetwork(member)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(len(allNetworks(creator)), 0)
+ })
+
+ g.Testing("can't `set` a deleted network", func() {
+ network, member := add(create())
+ err := nipNetwork(member)
+ g.TErrorIf(err)
+
+ err = setNetwork(member, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't delete a network more than once", func() {
+ _, member := add(create())
+
+ err1 := nipNetwork(member)
+ err2 := nipNetwork(member)
+ g.TErrorIf(err1)
+ g.TAssertEqual(err2, sql.ErrNoRows)
+ })
+
+ g.Testing("can't get membership of a delete network", func() {
+ creator := create()
+ network, member := add(creator)
+
+ _, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ err = nipNetwork(member)
+ g.TErrorIf(err)
+
+ _, err = membership(creator, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("an admin can delete a network", func() {
+ admin := create()
+ _, creatorMember := add(create())
+ newMember := newMemberT{
+ userID: admin.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ adminMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = addRole(creatorMember, "admin", adminMember)
+ g.TErrorIf(err)
+
+ err = nipNetwork(adminMember)
+ g.TErrorIf(err)
+ })
+
+ g.Testing("a member can't delete", func() {
+ user := create()
+ _, creatorMember := add(create())
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ userMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = nipNetwork(userMember)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("an inactive admin member also can't", func() {
+ user := create()
+ _, member := add(create())
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member.status = "inactive"
+ err := editMember(member, member)
+ g.TErrorIf(err)
+
+ _, err = addMember(member, newMember)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintNotNull,
+ )
+ })
+
+ g.Testing("deletion triggers writes to the changes table", func() {
+ network, member := add(create())
+
+ changes1Len := len(networkChanges(network.uuid))
+
+ err := nipNetwork(member)
+ g.TErrorIf(err)
+
+ changes := networkChanges(network.uuid)
+ g.TAssertEqual(len(changes), changes1Len + 2)
+
+ changes = changes[changes1Len:]
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ changes[0].value,
+ changes[1].value,
+ },
+ []string{
+ "deleted",
+ "deleted",
+ "0",
+ "1",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ },
+ []bool{
+ false,
+ true,
+ },
+ )
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ nipNetworkClose(),
+ nipNetworkClose(),
+ nipNetworkClose(),
+ ))
+ })
+}
+
+func test_membershipStmt() {
+ g.TestStart("membershipStmt")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ nipNetwork, nipNetworkClose, nipNetworkErr := nipNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ dropMember, dropMemberClose, dropMemberErr := dropMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ deleteUserErr,
+ addNetworkErr,
+ nipNetworkErr,
+ membershipErr,
+ addMemberErr,
+ dropMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ deleteUserClose,
+ addNetworkClose,
+ nipNetworkClose,
+ membershipClose,
+ addMemberClose,
+ dropMemberClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) networkT {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Private,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ return network
+ }
+
+
+ g.Testing("user needs to exist", func() {
+ virtualUser := userT{
+ id: 1234,
+ }
+ network := add(create())
+
+ _, err := membership(virtualUser, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("network needs to exist", func() {
+ virtualNetwork := networkT{
+ id: 1234,
+ }
+
+ _, err := membership(create(), virtualNetwork)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("the member contains all of its roles", func() {
+ creator := create()
+ network := add(creator)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+ g.TAssertEqual(member.roles, []string{ "admin", "creator" })
+ })
+
+ g.Testing("a deleted user can't get their membership", func() {
+ creator := create()
+ network := add(creator)
+
+ err := deleteUser(creator.uuid)
+ g.TErrorIf(err)
+
+ _, err = membership(creator, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't get member from a deleted network", func() {
+ creator := create()
+ network := add(creator)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ err = nipNetwork(member)
+ g.TErrorIf(err)
+
+ _, err = membership(creator, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("we get the same data as `addMember()`", func() {
+ creator := create()
+ user := create()
+ network := add(creator)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: "a username",
+ }
+
+ creatorMember, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ member1, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ member2, err := membership(user, network)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(member2, member1)
+ })
+
+ g.Testing("can't get membership of ex-member", func() {
+ creator := create()
+ admin := create()
+ network := add(creator)
+ newMember := newMemberT{
+ userID: admin.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ creatorMember, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ adminMember, err := addMember(creatorMember, newMember)
+ g.TErrorIf(err)
+
+ err = dropMember(adminMember, adminMember.uuid)
+ g.TErrorIf(err)
+
+ _, err = membership(admin, network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("a non-member gets an error", func() {
+ network := add(create())
+
+ _, err := membership(create(), network)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("no error if closed more than once", func() {
+ g.TErrorIf(g.SomeError(
+ membershipClose(),
+ membershipClose(),
+ membershipClose(),
))
})
}
func test_addMemberStmt() {
- /*
- FIXME
- g.TestStart("addMember()")
+ g.TestStart("addMemberStmt()")
const (
dbpath = golite.InMemory
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ nipNetwork, nipNetworkClose, nipNetworkErr := nipNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addRole, addRoleClose, addRoleErr := addRoleStmt(cfg)
g.TErrorIf(g.SomeError(
+ createUserErr,
+ deleteUserErr,
addNetworkErr,
+ nipNetworkErr,
+ membershipErr,
addMemberErr,
+ addRoleErr,
))
defer g.SomeFnError(
+ createUserClose,
+ deleteUserClose,
addNetworkClose,
+ nipNetworkClose,
+ membershipClose,
addMemberClose,
+ addRoleClose,
+ db.Close,
)
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) (networkT, memberT) {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
+ }
+
+
+ g.Testing("the user needs to exist", func() {
+ _, member := add(create())
+ newMember := newMemberT{
+ userID: guuid.New(),
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ _, err := addMember(member, newMember)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintNotNull,
+ )
+ })
+
+ g.Testing(`the member with role "add-member" is allowed`, func() {
+ user1 := create()
+ user2 := create()
+ _, member0 := add(create())
+ newMember1 := newMemberT{
+ userID: user1.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+ newMember2 := newMemberT{
+ userID: user2.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member1, err := addMember(member0, newMember1)
+ g.TErrorIf(err)
+
+ err = addRole(member0, "add-member", member1)
+ g.TErrorIf(err)
+
+ _, err = addMember(member1, newMember2)
+ g.TErrorIf(err)
+ })
+
+ g.Testing(`the member with role "admin" is allowed`, func() {
+ user1 := create()
+ user2 := create()
+ _, member0 := add(create())
+ newMember1 := newMemberT{
+ userID: user1.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+ newMember2 := newMemberT{
+ userID: user2.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member1, err := addMember(member0, newMember1)
+ g.TErrorIf(err)
+
+ err = addRole(member0, "admin", member1)
+ g.TErrorIf(err)
+
+ _, err = addMember(member1, newMember2)
+ g.TErrorIf(err)
+ })
+
+ g.Testing(`member without role "add-member" is forbidden`, func() {
+ user1 := create()
+ user2 := create()
+ _, member0 := add(create())
+ newMember1 := newMemberT{
+ userID: user1.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+ newMember2 := newMemberT{
+ userID: user2.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member1, err := addMember(member0, newMember1)
+ g.TErrorIf(err)
+
+ _, err = addMember(member1, newMember2)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintTrigger,
+ )
+ })
+
+ g.Testing("can't add the same user twice", func() {
+ creator := create()
+ user := create()
+ _, member := add(creator)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ _, err1 := addMember(member, newMember)
+ _, err2 := addMember(member, newMember)
+ g.TErrorIf(err1)
+ g.TAssertEqual(
+ err2.(golite.Error).ExtendedCode,
+ golite.ErrConstraintUnique,
+ )
+ })
+
+ g.Testing("the memberID must be unique", func() {
+ creator := create()
+ user1 := create()
+ user2 := create()
+ _, member := add(creator)
+ memberID := guuid.New()
+ newMember1 := newMemberT{
+ userID: user1.uuid,
+ memberID: memberID,
+ username: mkstring(),
+ }
+ newMember2 := newMemberT{
+ userID: user2.uuid,
+ memberID: memberID,
+ username: mkstring(),
+ }
+
+ _, err1 := addMember(member, newMember1)
+ _, err2 := addMember(member, newMember2)
+ g.TErrorIf(err1)
+ g.TAssertEqual(
+ err2.(golite.Error).ExtendedCode,
+ golite.ErrConstraintUnique,
+ )
+ })
+
+ g.Testing("the new member can't be a deleted user", func() {
+ creator := create()
+ user := create()
+ _, member := add(creator)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ err := deleteUser(user.uuid)
+ g.TErrorIf(err)
+
+ _, err = addMember(member, newMember)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintNotNull,
+ )
+ })
+
+ g.Testing("can't add to a deleted network", func() {
+ creator := create()
+ user := create()
+ _, member := add(creator)
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ err := nipNetwork(member)
+ g.TErrorIf(err)
+
+ _, err = addMember(member, newMember)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintNotNull,
+ )
+ })
+
+ g.Testing("the same user can be a member of distinct networks", func() {
+ // FIXME
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
addMemberClose(),
@@ -1178,39 +2977,620 @@ func test_addMemberStmt() {
addMemberClose(),
))
})
- */
}
-func test_showMemberStmt() {
+func test_addRoleStmt() {
+ g.TestStart("addRoleStmt")
+
// FIXME
+}
+
+func test_dropRoleStmt() {
+ g.TestStart("dropRoleStmt")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ dropRole, dropRoleClose, dropRoleErr := dropRoleStmt(cfg)
+ showMember, showMemberClose, showMemberErr := showMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ dropRoleErr,
+ showMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ dropRoleClose,
+ showMemberClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) (networkT, memberT) {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
+ }
+
+
+ g.Testing("acting member must exist", func() {
+ _, member := add(create())
+ virtualMember := memberT{}
+
+ err := dropRole(virtualMember, "a-role", member)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("target member must also exist", func() {
+ _, member := add(create())
+ virtualMember := memberT{}
+
+ err := dropRole(member, "a-role", virtualMember)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("noop if member doesn't have the role", func() {
+ return // FIXME
+ _, member := add(create())
+ g.TAssertEqual(member.roles, []string{ "admin", "creator" })
+
+ err := dropRole(member, "does-not-exist", member)
+ g.TErrorIf(err)
+
+ member, err = showMember(member, member.uuid)
+ g.TErrorIf(err)
+ g.TAssertEqual(member.roles, []string{ "admin", "creator" })
+ })
+
+ g.Testing("role is removed when exists", func() {
+ _, member := add(create())
+ g.TAssertEqual(member.roles, []string{ "admin", "creator" })
+
+ err := dropRole(member, "admin", member)
+ g.TErrorIf(err)
+
+ member, err = showMember(member, member.uuid)
+ g.TErrorIf(err)
+ g.TAssertEqual(member.roles, []string{ "creator" })
+ })
+
+ g.Testing("member can remove all roles from themselves", func() {
+ // FIXME
+ })
+
+ g.Testing(`member with "role-write" can drop others roles`, func() {
+ // FIXME
+ })
+
+ g.Testing(`member without "role-write" can't`, func() {
+ // FIXME
+ })
+
+ g.Testing("does not affect other members from other networks", func() {
+ // FIXME
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ dropRoleClose(),
+ dropRoleClose(),
+ dropRoleClose(),
+ ))
+ })
+}
+
+func test_showMemberStmt() {
+ g.TestStart("showMemberStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ showMember, showMemberClose, showMemberErr := showMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ showMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ showMemberClose,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func(user userT) (networkT, memberT) {
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return network, member
+ }
+
+
+ g.Testing("target member must exist", func() {
+ _, member := add(create())
+ _, err := showMember(member, guuid.New())
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("acting member must exist", func() {
+ virtualMember := memberT{
+ id: 1234,
+ }
+
+ _, member := add(create())
+ _, err := showMember(virtualMember, member.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("target user must belong to the same network", func() {
+ _, member1 := add(create())
+ _, member2 := add(create())
+
+ _, err := showMember(member1, member2.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("we get the full member", func() {
+ _, member1 := add(create())
+ member2, err := showMember(member1, member1.uuid)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(member2, member1)
+ })
+
+ g.Testing("no error if closed more than once", func() {
+ g.TErrorIf(g.SomeError(
+ showMemberClose(),
+ showMemberClose(),
+ showMemberClose(),
))
})
}
func test_memberEach() {
- // FIXME
+ g.TestStart("memberEach()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ members, membersClose, membersErr := membersStmt(cfg)
+ editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addMemberErr,
+ membersErr,
+ editMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addMemberClose,
+ membersClose,
+ editMemberClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addM := func(actor memberT, status MemberStatus) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member, err := addMember(actor, newMember)
+ g.TErrorIf(err)
+
+ member.status = status
+ err = editMember(actor, member)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+
+ g.Testing("callback is called once for new network", func() {
+ member := add()
+
+ rows, err := members(member)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ memberIDs := []guuid.UUID{}
+ err = memberEach(rows, func(member memberT) error {
+ memberIDs = append(memberIDs, member.uuid)
+ return nil
+ })
+
+ g.TAssertEqual(len(memberIDs), 1)
+ g.TAssertEqual(memberIDs[0], member.uuid)
+ })
+
+ g.Testing("we halt if the callback returns an error", func() {
+ myErr := errors.New("callback custom error")
+ member := add()
+ expectedIDs := []guuid.UUID{
+ member.uuid,
+ addM(member, MemberStatus_Active).uuid,
+ addM(member, MemberStatus_Active).uuid,
+ addM(member, MemberStatus_Active).uuid,
+ addM(member, MemberStatus_Active).uuid,
+ addM(member, MemberStatus_Active).uuid,
+ }
+
+ rows, err := members(member)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ memberIDs := []guuid.UUID{}
+ err = memberEach(rows, func(member memberT) error {
+ if len(memberIDs) == 3 {
+ return myErr
+ }
+
+ memberIDs = append(memberIDs, member.uuid)
+ return nil
+ })
+ g.TAssertEqual(err, myErr)
+ g.TAssertEqual(len(memberIDs), 3)
+ g.TAssertEqual(memberIDs, expectedIDs[0:3])
+ })
+
+ g.Testing("noop when given nil for *sql.Rows", func() {
+ err := memberEach(nil, func(memberT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
}
func test_membersStmt() {
- // FIXME
+ g.TestStart("membersStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ nipNetwork, nipNetworkClose, nipNetworkErr := nipNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ members, membersClose, membersErr := membersStmt(cfg)
+ editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ nipNetworkErr,
+ membershipErr,
+ addMemberErr,
+ membersErr,
+ editMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ nipNetworkClose,
+ membershipClose,
+ addMemberClose,
+ membersClose,
+ editMemberClose,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ member.roles = nil
+
+ return member
+ }
+
+ addM := func(actor memberT, status MemberStatus) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member, err := addMember(actor, newMember)
+ g.TErrorIf(err)
+
+ member.status = status
+ err = editMember(actor, member)
+ g.TErrorIf(err)
+
+ member.roles = nil
+
+ return member
+ }
+
+ allMembers := func(member memberT) []memberT {
+ rows, err := members(member)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ memberList := []memberT{}
+ err = memberEach(rows, func(member memberT) error {
+ memberList = append(memberList, member)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ return memberList
+ }
+
+
+ // FIXME: members from other networks do not show up
+ g.Testing("inactive and removed members aren't listed", func() {
+ member := add()
+ expected := []memberT{
+ member,
+ addM(member, MemberStatus_Active),
+ addM(member, MemberStatus_Inactive),
+ addM(member, MemberStatus_Inactive),
+ addM(member, MemberStatus_Removed),
+ addM(member, MemberStatus_Removed),
+ }
+
+ given := allMembers(member)
+
+ g.TAssertEqual(len(given), 2)
+ g.TAssertEqual(given[0], expected[0])
+ g.TAssertEqual(given, expected[0:2])
+ })
+
+ g.Testing("a deleted network has 0 members", func() {
+ member := add()
+
+ g.TAssertEqual(len(allMembers(member)), 1)
+
+ err = nipNetwork(member)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(len(allMembers(member)), 0)
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ membersClose(),
+ membersClose(),
+ membersClose(),
))
})
}
func test_editMemberStmt() {
+ g.TestStart("editMemberStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ nipNetwork, nipNetworkClose, nipNetworkErr := nipNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ showMember, showMemberClose, showMemberErr := showMemberStmt(cfg)
+ editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ nipNetworkErr,
+ membershipErr,
+ showMemberErr,
+ editMemberErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ nipNetworkClose,
+ membershipClose,
+ showMemberClose,
+ editMemberClose,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ user := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ // FIXME
+ // memberChanges := makeMemberChanges(db, prefix)
+
// FIXME
+ if nipNetwork != nil && showMember != nil && editMember != nil {}
+ if add != nil {}
+
+ g.Testing("edit triggers writes to changes table", func() {
+ // FIXME
+ })
+
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ editMemberClose(),
+ editMemberClose(),
+ editMemberClose(),
))
})
}
@@ -1226,8 +3606,6 @@ func test_dropMemberStmt() {
}
func test_addChannelStmt() {
- // FIXME
- return
g.TestStart("addChannelStmt()")
const (
@@ -1235,7 +3613,7 @@ func test_addChannelStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -1244,65 +3622,649 @@ func test_addChannelStmt() {
dbpath: dbpath,
prefix: prefix,
}
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
- channels, channelsClose, channelsErr := channelsStmt(cfg)
g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
addChannelErr,
- channelsErr,
))
defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
addChannelClose,
- channelsClose,
db.Close,
)
- collect := func(workspaceID guuid.UUID) []channelT {
- rows, err := channels(workspaceID)
- g.TErrorIf(err)
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
- collected := []channelT{}
- err = channelEach(rows, func(channel channelT) error {
- collected = append(collected, channel)
- return nil
- })
+ user, err := createUser(newUser)
g.TErrorIf(err)
- return collected
+
+ return user
}
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
- if true {
- g.TAssertEqual(addChannel, collect)
+ return member
}
- // private channels one is not a part of doesn't show up
- // channels only from the same workspace
+
+ channelChanges := makeChannelChanges(db, prefix)
+
+
+ g.Testing("the new channel has the data it was given", func() {
+ member := add()
+ publicName := "a-name"
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: "a-label",
+ description: "the description",
+ virtual: false,
+ }
+
+ channel, err := addChannel(member, newChannel)
+ g.TErrorIf(err)
+
+ g.TAssertEqual(channel.id == 0, false)
+ g.TAssertEqual(channel.timestamp == time.Time{}, false)
+ g.TAssertEqual(channel.uuid, newChannel.uuid)
+ g.TAssertEqual(*channel.publicName, "a-name")
+ g.TAssertEqual(channel.label, "a-label")
+ g.TAssertEqual(channel.description, "the description")
+ g.TAssertEqual(channel.virtual, false)
+ })
+
+ g.Testing("new channel causes inserts to the changes table", func() {
+ member := add()
+ publicName := "another name"
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: "the label",
+ description: "the description",
+ virtual: false,
+ }
+
+ channel, err := addChannel(member, newChannel)
+ g.TErrorIf(err)
+
+ changes := channelChanges(channel.id)
+ g.TAssertEqual(len(changes), 4)
+ g.TAssertEqual(
+ []string{
+ changes[0].attribute,
+ changes[1].attribute,
+ changes[2].attribute,
+ changes[3].attribute,
+ changes[0].value,
+ changes[1].value,
+ changes[2].value,
+ changes[3].value,
+ },
+ []string{
+ "public_name",
+ "label",
+ "description",
+ "virtual",
+ "another name",
+ "the label",
+ "the description",
+ "false",
+ },
+ )
+ g.TAssertEqual(
+ []bool{
+ changes[0].op,
+ changes[1].op,
+ changes[2].op,
+ changes[3].op,
+ },
+ []bool{
+ true,
+ true,
+ true,
+ true,
+ },
+ )
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ addChannelClose(),
+ addChannelClose(),
+ addChannelClose(),
))
})
}
func test_channelEach() {
- // FIXME
+ g.TestStart("channelEach()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ channels, channelsClose, channelsErr := channelsStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addChannelErr,
+ channelsErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addChannelClose,
+ channelsClose,
+ db.Close,
+ )
+
+ add := func() memberT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(member memberT) channelT {
+ publicName := mkstring()
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: true,
+ }
+
+ channel, err := addChannel(member, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+
+ g.Testing("callback is not called on empty set", func() {
+ rows, err := channels(add())
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ err = channelEach(rows, func(channelT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
+
+ g.Testing("the callback is called once for each entry", func() {
+ member := add()
+ expected := []channelT{
+ addC(member),
+ addC(member),
+ addC(member),
+ }
+
+ rows, err := channels(member)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ channels := []channelT{}
+ err = channelEach(rows, func(channel channelT) error {
+ channels = append(channels, channel)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ g.TAssertEqual(channels, expected)
+ })
+
+ g.Testing("we halt if the callback returns an error", func() {
+ member := add()
+ myErr := errors.New("callback error early return")
+ addC(member)
+ addC(member)
+ addC(member)
+ addC(member)
+ addC(member)
+
+ rows1, err1 := channels(member)
+ rows2, err2 := channels(member)
+ g.TErrorIf(err1)
+ g.TErrorIf(err2)
+ defer rows1.Close()
+ defer rows2.Close()
+
+ n1 := 0
+ n2 := 0
+
+ err1 = channelEach(rows1, func(channelT) error {
+ n1++
+ if n1 == 3 {
+ return myErr
+ }
+ return nil
+ })
+
+ err2 = channelEach(rows2, func(channelT) error {
+ n2++
+ return nil
+ })
+
+ g.TAssertEqual(err1, myErr)
+ g.TErrorIf(err2)
+ g.TAssertEqual(n1, 3)
+ g.TAssertEqual(n2, 5)
+ })
+
+ g.Testing("noop when given nil for *sql.Rows", func() {
+ err := channelEach(nil, func(channelT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
}
func test_channelsStmt() {
- // FIXME
+ g.TestStart("channelsStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ channels, channelsClose, channelsErr := channelsStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addChannelErr,
+ channelsErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addChannelClose,
+ channelsClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ user := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(user, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(user, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addM := func(member memberT) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ addedMember, err := addMember(member, newMember)
+ g.TErrorIf(err)
+
+ return addedMember
+ }
+
+ addC := func(
+ member memberT,
+ publicName *string,
+ virtual bool,
+ ) channelT {
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: virtual,
+ }
+
+ channel, err := addChannel(member, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+ allChannels := func(member memberT) ([]channelT, error) {
+ rows, err := channels(member)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ channelList := []channelT{}
+ err = channelEach(rows, func(channel channelT) error {
+ channelList = append(channelList, channel)
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return channelList, nil
+ }
+
+
+ g.Testing("when there are no channels, we get 0", func() {
+ channels, err := allChannels(add())
+ g.TErrorIf(err)
+ g.TAssertEqual(len(channels), 0)
+ })
+
+ g.Testing("when only private channels, owner gets all", func() {
+ member := add()
+ addC(member, nil, true)
+ addC(member, nil, true)
+ addC(member, nil, false)
+ addC(member, nil, false)
+
+ channels, err := allChannels(member)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(channels), 4)
+ })
+
+ g.Testing("when only private channels, others get none", func() {
+ member1 := add()
+ member2 := addM(member1)
+ addC(member1, nil, true)
+ addC(member1, nil, true)
+ addC(member1, nil, false)
+ addC(member1, nil, false)
+
+ channels1, err1 := allChannels(member1)
+ channels2, err2 := allChannels(member2)
+ g.TErrorIf(err1)
+ g.TErrorIf(err2)
+ g.TAssertEqual(len(channels1), 4)
+ g.TAssertEqual(len(channels2), 0)
+ })
+
+ g.Testing("private channels we are a member of show up", func() {
+ member1 := add()
+ member2 := addM(member1)
+ name1 := "channel-name-1"
+ name2 := "channel-name-2"
+ addC(member1, nil, true)
+ addC(member2, nil, true)
+ addC(member1, nil, false)
+ addC(member2, nil, false)
+ addC(member1, &name1, true)
+ addC(member2, &name2, false)
+
+ channels, err := allChannels(member1)
+ g.TErrorIf(err)
+ g.TAssertEqual(len(channels), 4)
+ })
+
+ g.Testing("we never list channels from other networks", func() {
+ member := add()
+ name1 := "a name 1"
+ name2 := "a name 2"
+ addC(member, nil, true)
+ addC(member, nil, false)
+ addC(member, &name1, true)
+ addC(member, &name2, false)
+
+ channels, err := allChannels(add())
+ g.TErrorIf(err)
+ g.TAssertEqual(len(channels), 0)
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ channelsClose(),
+ channelsClose(),
+ channelsClose(),
))
})
}
-func test_topicStmt() {
- // FIXME
+func test_setChannelStmt() {
+ g.TestStart("setChannelStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ setChannel, setChannelClose, setChannelErr := setChannelStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addChannelErr,
+ setChannelErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addChannelClose,
+ setChannelClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addM := func(actor memberT) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member, err := addMember(actor, newMember)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(actor memberT, description string) channelT {
+ publicName := mkstring()
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: description,
+ virtual: false,
+ }
+
+ channel, err := addChannel(actor, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+
+ g.Testing("acting member must exist", func() {
+ channel := addC(add(), "description")
+ virtualMember := memberT{
+ id: 1234,
+ }
+
+ err := setChannel(virtualMember, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("target channel must exist", func() {
+ virtualChannel := channelT{
+ id: 1234,
+ }
+
+ err := setChannel(add(), virtualChannel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("member can't set topic of other network", func() {
+ member := add()
+ otherMember := add()
+ channel := addC(member, "desc")
+
+ err := setChannel(otherMember, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("actor must participate in the channel to set topic", func() {
+ member1 := add()
+ member2 := addM(member1)
+ channel := addC(member1, "desc")
+
+ err := setChannel(member2, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("participant can edit", func() {
+ member := add()
+ channel := addC(member, "first description")
+
+ channel.description = "second description"
+ err := setChannel(member, channel)
+ g.TErrorIf(err)
+ })
+
+ // we can make a private channel public
+ // we can make a public channel private
+
+ g.Testing("update adds entries to *_changes table", func() {
+ // FIXME
+ })
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ setChannelClose(),
+ setChannelClose(),
+ setChannelClose(),
))
})
}
@@ -1318,21 +4280,371 @@ func test_endChannelStmt() {
}
func test_joinStmt() {
+ g.TestStart("joinStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ setChannel, setChannelClose, setChannelErr := setChannelStmt(cfg)
+ join, joinClose, joinErr := joinStmt(cfg)
+ part, partClose, partErr := partStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addChannelErr,
+ setChannelErr,
+ joinErr,
+ partErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addChannelClose,
+ setChannelClose,
+ joinClose,
+ partClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addM := func(actor memberT) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member, err := addMember(actor, newMember)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(actor memberT, publicName *string, virtual bool) channelT {
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: virtual,
+ }
+
+ channel, err := addChannel(actor, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+
+ g.Testing("acting member must exist", func() {
+ name := "name"
+ channel := addC(add(), &name, false)
+ virtualMember := memberT{
+ id: 1234,
+ }
+
+ err := join(virtualMember, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("target channel must exist", func() {
+ err := join(add(), guuid.New())
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't join a private channel", func() {
+ creator := add()
+ member := addM(creator)
+ channel := addC(creator, nil, false)
+
+ err := join(member, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't join a virtual channel", func() {
+ creator := add()
+ member := addM(creator)
+ channel := addC(creator, nil, true)
+
+ err := join(member, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't join channel in different network", func() {
+ name := "name"
+ member1 := add()
+ member2 := add()
+ channel := addC(member1, &name, false)
+
+ err := join(member2, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("creator can't rejoin after leaving private channel", func() {
+ creator := add()
+ channel := addC(creator, nil, false)
+
+ err := part(creator, channel)
+ g.TErrorIf(err)
+
+ err = join(creator, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't join public channel one already participates", func() {
+ name := "name"
+ creator := add()
+ channel := addC(creator, &name, false)
+
+ err := join(creator, channel.uuid)
+ g.TAssertEqual(
+ err.(golite.Error).ExtendedCode,
+ golite.ErrConstraintUnique,
+ )
+ })
+
+ g.Testing("neither a private channel", func() {
+ creator := add()
+ channel := addC(creator, nil, false)
+
+ err := join(creator, channel.uuid)
+ return // FIXME
+ g.TAssertEqual(
+ err,
+ golite.ErrConstraintUnique,
+ )
+ })
+
+ g.Testing("after made public, one can join a channel", func() {
+ name := "name"
+ creator := add()
+ member := addM(creator)
+ channel := addC(creator, nil, false)
+
+ err := join(member, channel.uuid)
+ g.TAssertEqual(err, sql.ErrNoRows)
+
+ channel.publicName = &name
+ err = setChannel(creator, channel)
+ g.TErrorIf(err)
+
+ err = join(member, channel.uuid)
+ g.TErrorIf(err)
+ })
+
// FIXME
+ // creates "user-join" event in feed
+ // joining adds rows to *_changes table
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ joinClose(),
+ joinClose(),
+ joinClose(),
))
})
}
func test_partStmt() {
+ g.TestStart("partStmt()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ part, partClose, partErr := partStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addMemberErr,
+ addChannelErr,
+ partErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addMemberClose,
+ addChannelClose,
+ partClose,
+ db.Close,
+ )
+
+ create := func() userT{
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addM := func(actor memberT) memberT {
+ user := create()
+ newMember := newMemberT{
+ userID: user.uuid,
+ memberID: guuid.New(),
+ username: mkstring(),
+ }
+
+ member, err := addMember(actor, newMember)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(actor memberT, virtual bool) channelT {
+ publicName := "public name"
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: virtual,
+ }
+
+ channel, err := addChannel(actor, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+
+ g.Testing("acting member must exist", func() {
+ channel := addC(add(), false)
+ virtualMember := memberT{
+ id: 1234,
+ }
+
+ err := part(virtualMember, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("target channel must exist", func() {
+ virtualChannel := channelT{
+ id: 1234,
+ }
+
+ err := part(add(), virtualChannel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("must be a member to part", func() {
+ creator := add()
+ member := addM(creator)
+ channel := addC(creator, false)
+
+ err := part(member, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can't part from a virtual channel", func() {
+ member := add()
+ channel := addC(member, true)
+
+ err := part(member, channel)
+ g.TAssertEqual(err, sql.ErrNoRows)
+ })
+
+ g.Testing("can part from non-virtual channel", func() {
+ member := add()
+ channel := addC(member, false)
+
+ err := part(member, channel)
+ g.TErrorIf(err)
+ })
+
+
// FIXME
+ // parting adds rows to *_changes table
+ // after parting, vanishes from member channel list
g.Testing("no error if closed more than once", func() {
g.TErrorIf(g.SomeError(
- // FIXME
+ partClose(),
+ partClose(),
+ partClose(),
))
})
}
@@ -1352,7 +4664,6 @@ func test_namesStmt() {
}
func test_addEventStmt() {
- return // FIXME
g.TestStart("addEventStmt()")
const (
@@ -1360,7 +4671,7 @@ func test_addEventStmt() {
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -1369,43 +4680,83 @@ func test_addEventStmt() {
dbpath: dbpath,
prefix: prefix,
}
- addEvent, addEventClose, addEventErr := addEventStmt(cfg)
- g.TErrorIf(addEventErr)
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ addEvent, addEventClose, addEventErr := addEventStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addChannelErr,
+ addEventErr,
+ ))
defer g.SomeFnError(
- addEventClose,
- db.Close,
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addChannelClose,
+ addEventClose,
+ db.Close,
)
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
- g.Testing("we can create new events", func() {
- newEvent := newEventT{
- eventID: guuid.New(),
- channelID: guuid.New(),
- connectionID: guuid.New(),
- type_: "user-message",
- payload: "xablau",
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
}
- _, err := addEvent(newEvent)
+ network, err := addNetwork(creator, newNetwork, guuid.New())
g.TErrorIf(err)
- })
- g.Testing("eventID's must be unique", func() {
- // FIXME
- })
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
- g.Testing("the database fills the generated values", func() {
- const (
- type_ = "user-message"
- payload = "the payload"
- )
- eventID := guuid.New()
+ return member
+ }
+
+ addC := func(actor memberT) channelT {
+ publicName := mkstring()
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: false,
+ }
+
+ channel, err := addChannel(actor, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+
+ g.Testing("we can create new events", func() {
+ creator := add()
+ channel := addC(creator)
newEvent := newEventT{
- eventID: eventID,
- channelID: guuid.New(),
- connectionID: guuid.New(),
- type_: type_,
- payload: payload,
+ eventID: guuid.New(),
+ channelID: channel.uuid,
+ source: sourceT{
+ uuid: creator.uuid,
+ type_: SourceType_Logon,
+ },
+ type_: EventType_UserMessage,
+ payload: "the-payload",
}
event, err := addEvent(newEvent)
@@ -1413,19 +4764,91 @@ func test_addEventStmt() {
g.TAssertEqual(event.id == 0, false)
g.TAssertEqual(event.timestamp == time.Time{}, false)
- g.TAssertEqual(event.channelID == guuid.UUID{}, false)
- g.TAssertEqual(event.connectionID == guuid.UUID{}, false)
- g.TAssertEqual(event.uuid, eventID)
- g.TAssertEqual(event.type_, type_)
- g.TAssertEqual(event.payload, payload)
+ g.TAssertEqual(event.uuid, newEvent.eventID)
+ g.TAssertEqual(event.channelID, newEvent.channelID)
+ g.TAssertEqual(event.source, newEvent.source)
+ g.TAssertEqual(event.type_, EventType_UserMessage)
+ g.TAssertEqual(event.payload, "the-payload")
+ g.TAssertEqual(event.metadata == nil, true)
})
- g.Testing("multiple messages can have the same connectionID", func() {
- // FIXME
+ g.Testing("eventID's must be unique", func() {
+ creator := add()
+ channel := addC(creator)
+ newEvent := newEventT{
+ eventID: guuid.New(),
+ channelID: channel.uuid,
+ source: sourceT{
+ uuid: creator.uuid,
+ type_: SourceType_Logon,
+ },
+ type_: EventType_UserMessage,
+ payload: "the payload",
+ }
+
+ _, err1 := addEvent(newEvent)
+ _, err2 := addEvent(newEvent)
+ g.TErrorIf(err1)
+ g.TAssertEqual(
+ err2.(golite.Error).ExtendedCode,
+ golite.ErrConstraintUnique,
+ )
})
- g.Testing("messages can be dupicated: same type and payload", func() {
- // FIXME
+ g.Testing("multiple messages can have the same source", func() {
+ source := sourceT{
+ uuid: guuid.New(),
+ type_: SourceType_Logon,
+ }
+
+ newEvent1 := newEventT{
+ eventID: guuid.New(),
+ channelID: addC(add()).uuid,
+ source: source,
+ type_: EventType_UserMessage,
+ }
+ newEvent2 := newEventT{
+ eventID: guuid.New(),
+ channelID: addC(add()).uuid,
+ source: source,
+ type_: EventType_UserMessage,
+ }
+
+ _, err1 := addEvent(newEvent1)
+ _, err2 := addEvent(newEvent2)
+ g.TErrorIf(err1)
+ g.TErrorIf(err2)
+ })
+
+ g.Testing("messages can be duplicated: same type and payload", func() {
+ type_ := EventType_UserMessage
+ payload := "a-payload"
+
+ newEvent1 := newEventT{
+ eventID: guuid.New(),
+ channelID: addC(add()).uuid,
+ source: sourceT{
+ uuid: guuid.New(),
+ type_: SourceType_Logon,
+ },
+ type_: type_,
+ payload: payload,
+ }
+ newEvent2 := newEventT{
+ eventID: guuid.New(),
+ channelID: addC(add()).uuid,
+ source: sourceT{
+ uuid: guuid.New(),
+ type_: SourceType_Logon,
+ },
+ type_: type_,
+ payload: payload,
+ }
+
+ _, err1 := addEvent(newEvent1)
+ _, err2 := addEvent(newEvent2)
+ g.TErrorIf(err1)
+ g.TErrorIf(err2)
})
g.Testing("no error if closed more than once", func() {
@@ -1438,18 +4861,210 @@ func test_addEventStmt() {
}
func test_eventEach() {
- // FIXME
+ g.TestStart("eventEach()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ db, err := sql.Open(golite.DriverName, dbpath)
+ g.TErrorIf(err)
+ g.TErrorIf(createTables(db, prefix))
+
+ cfg := dbconfigT{
+ shared: db,
+ dbpath: dbpath,
+ prefix: prefix,
+ }
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
+ addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
+ addEvent, addEventClose, addEventErr := addEventStmt(cfg)
+ allAfter, allAfterClose, allAfterErr := allAfterStmt(cfg)
+ g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
+ addChannelErr,
+ addEventErr,
+ allAfterErr,
+ ))
+ defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
+ addChannelClose,
+ addEventClose,
+ allAfterClose,
+ db.Close,
+ )
+
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(actor memberT) channelT {
+ publicName := mkstring()
+ newChannel := newChannelT{
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: false,
+ }
+
+ channel, err := addChannel(actor, newChannel)
+ g.TErrorIf(err)
+
+ return channel
+ }
+
+ eventCount := 0
+ addE := func(channelID guuid.UUID) eventT {
+ eventCount++
+ newEvent := newEventT{
+ // FIXME: missing eventID?
+ channelID: channelID,
+ source: sourceT{
+ uuid: guuid.New(),
+ type_: SourceType_Logon,
+ },
+ type_: EventType_UserMessage,
+ payload: fmt.Sprintf("event %s", eventCount),
+ }
+
+ event, err := addEvent(newEvent)
+ g.TErrorIf(err)
+
+ return event
+ }
+
+
+ g.Testing("callback is not called when there is no message", func() {
+ eventID := guuid.New()
+ member := add()
+ rows, err := allAfter(member, eventID)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ err = eventEach(rows, func(eventT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
+
+ g.Testing("the callback is called once for each entry", func() {
+ return // FIXME
+
+ eventID := guuid.New()
+ member := add()
+ channel := addC(member)
+ expected := []eventT{
+ addE(channel.uuid),
+ addE(channel.uuid),
+ addE(channel.uuid),
+ }
+
+ rows, err := allAfter(member, eventID)
+ g.TErrorIf(err)
+ defer rows.Close()
+
+ events := []eventT{}
+ err = eventEach(rows, func(event eventT) error {
+ events = append(events, event)
+ return nil
+ })
+ g.TErrorIf(err)
+
+ g.TAssertEqual(events, expected)
+ })
+
+ g.Testing("it halts if a callback returns an error", func() {
+ return // FIXME
+
+ eventID := guuid.New()
+ member := add()
+ channel := addC(member)
+ myErr := errors.New("callback error early return")
+ addE(channel.uuid)
+ addE(channel.uuid)
+ addE(channel.uuid)
+ addE(channel.uuid)
+ addE(channel.uuid)
+
+ rows1, err1 := allAfter(member, eventID)
+ rows2, err2 := allAfter(member, eventID)
+ g.TErrorIf(err1)
+ g.TErrorIf(err2)
+ defer rows1.Close()
+ defer rows2.Close()
+
+ n1 := 0
+ n2 := 0
+
+ err1 = eventEach(rows1, func(eventT) error {
+ n1++
+ if n1 == 3 {
+ return myErr
+ }
+ return nil
+ })
+
+ err2 = eventEach(rows2, func(eventT) error {
+ n2++
+ return nil
+ })
+
+ g.TAssertEqual(n1, myErr)
+ g.TErrorIf(err2)
+ g.TAssertEqual(n1, 3)
+ g.TAssertEqual(n2, 5)
+ })
+
+ g.Testing("noop when given a nil for *sql.Rows", func() {
+ err := eventEach(nil, func(eventT) error {
+ g.Unreachable()
+ return nil
+ })
+ g.TErrorIf(err)
+ })
}
func test_allAfterStmt() {
- g.TestStart("allAfter()")
+ g.TestStart("allAfterStmt()")
const (
dbpath = golite.InMemory
prefix = defaultPrefix
)
- db, err := sql.Open(golite.DriverName, golite.InMemory)
+ db, err := sql.Open(golite.DriverName, dbpath)
g.TErrorIf(err)
g.TErrorIf(createTables(db, prefix))
@@ -1458,41 +5073,85 @@ func test_allAfterStmt() {
dbpath: dbpath,
prefix: prefix,
}
+ createUser, createUserClose, createUserErr := createUserStmt(cfg)
+ addNetwork, addNetworkClose, addNetworkErr := addNetworkStmt(cfg)
+ membership, membershipClose, membershipErr := membershipStmt(cfg)
addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
addEvent, addEventClose, addEventErr := addEventStmt(cfg)
allAfter, allAfterClose, allAfterErr := allAfterStmt(cfg)
g.TErrorIf(g.SomeError(
+ createUserErr,
+ addNetworkErr,
+ membershipErr,
addChannelErr,
addEventErr,
allAfterErr,
))
defer g.SomeFnError(
+ createUserClose,
+ addNetworkClose,
+ membershipClose,
addChannelClose,
addEventClose,
allAfterClose,
db.Close,
)
- channel := func(publicName string) channelT {
- networkID := guuid.New()
+ create := func() userT {
+ newUser := newUserT{
+ uuid: guuid.New(),
+ }
+
+ user, err := createUser(newUser)
+ g.TErrorIf(err)
+
+ return user
+ }
+
+ add := func() memberT {
+ creator := create()
+ newNetwork := newNetworkT{
+ uuid: guuid.New(),
+ type_: NetworkType_Public,
+ }
+
+ network, err := addNetwork(creator, newNetwork, guuid.New())
+ g.TErrorIf(err)
+
+ member, err := membership(creator, network)
+ g.TErrorIf(err)
+
+ return member
+ }
+
+ addC := func(actor memberT) channelT {
+ publicName := mkstring()
newChannel := newChannelT{
- uuid: guuid.New(),
- publicName: publicName,
+ uuid: guuid.New(),
+ publicName: &publicName,
+ label: mkstring(),
+ description: mkstring(),
+ virtual: false,
}
- channel, err := addChannel(networkID, newChannel)
+ channel, err := addChannel(actor, newChannel)
g.TErrorIf(err)
return channel
}
- add := func(channelID guuid.UUID, type_ string, payload string) eventT {
+ eventCount := 0
+ addE := func(channelID guuid.UUID) eventT {
+ eventCount++
newEvent := newEventT{
- eventID: guuid.New(),
- channelID: channelID,
- connectionID: guuid.New(),
- type_: type_,
- payload: payload,
+ eventID: guuid.New(),
+ channelID: channelID,
+ source: sourceT{
+ uuid: guuid.New(),
+ type_: SourceType_Logon,
+ },
+ type_: EventType_UserMessage,
+ payload: fmt.Sprintf("event %s", eventCount),
}
event, err := addEvent(newEvent)
@@ -1501,9 +5160,11 @@ func test_allAfterStmt() {
return event
}
- all := func(eventID guuid.UUID) []eventT {
- rows, err := allAfter(eventID)
+ // FIXME
+ allEvents := func(eventID guuid.UUID) []eventT {
+ rows, err := allAfter(memberT{}, eventID)
g.TErrorIf(err)
+ defer rows.Close()
events := []eventT{}
err = eventEach(rows, func(event eventT) error {
@@ -1517,24 +5178,65 @@ func test_allAfterStmt() {
g.Testing("after joining the channel, there are no events", func() {
- ch := channel("#ch")
- join := add(ch.uuid, "user-join", "fulano")
+ return // FIXME
+
+ eventID := guuid.New()
+ member := add()
+ channel := addC(member)
expected := []eventT{
- add(ch.uuid, "user-join", "ciclano"),
- add(ch.uuid, "user-join", "beltrano"),
- add(ch.uuid, "user-message", "hi there"),
+ // FIXME: missing "user-join" event
+ addE(channel.uuid),
+ addE(channel.uuid),
+ addE(channel.uuid),
}
- given := all(join.uuid)
-
+ given := allEvents(eventID)
g.TAssertEqual(given, expected)
})
g.Testing("we don't get events from other channels", func() {
+ return // FIXME
+
+ eventID := guuid.New()
+ member := add()
+ channel1 := addC(member)
+ channel2 := addC(member)
+
+ events := []eventT{
+ addE(channel1.uuid),
+ addE(channel1.uuid),
+ addE(channel2.uuid),
+ addE(channel2.uuid),
+ addE(channel1.uuid),
+ addE(channel1.uuid),
+ }
+
+ given := allEvents(eventID)
+ g.TAssertEqual(given, events[2:4])
})
g.Testing("as we change the reference point, the list changes", func() {
+ return // FIXME
+
+ eventID := guuid.New()
+ member := add()
+ channel := addC(member)
+
+ events := []eventT{
+ addE(channel.uuid),
+ addE(channel.uuid),
+ addE(channel.uuid),
+ addE(channel.uuid),
+ addE(channel.uuid),
+ }
+
+ g.TAssertEqual(len(allEvents(eventID)), 5)
+ g.TAssertEqual(len(allEvents(events[0].uuid)), 4)
+ g.TAssertEqual(len(allEvents(events[1].uuid)), 3)
+ g.TAssertEqual(len(allEvents(events[2].uuid)), 2)
+ g.TAssertEqual(len(allEvents(events[3].uuid)), 1)
+ g.TAssertEqual(len(allEvents(events[4].uuid)), 0)
})
g.Testing("no error if closed more than once", func() {
@@ -1544,7 +5246,6 @@ func test_allAfterStmt() {
allAfterClose(),
))
})
- // FIXME
}
func test_logMessageStmt() {
@@ -1560,11 +5261,43 @@ func test_logMessageStmt() {
}
func test_initDB() {
- // FIXME
+ g.TestStart("initDB()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ queries, err := initDB(dbpath, prefix)
+ g.TErrorIf(err)
+ defer queries.close()
+
+
+ g.Testing("we can perform all wrapped operations", func() {
+ // FIXME
+ })
}
func test_queriesTclose() {
- // FIXME
+ g.TestStart("queriesT.close()")
+
+ const (
+ dbpath = golite.InMemory
+ prefix = defaultPrefix
+ )
+
+ queries, err := initDB(dbpath, prefix)
+ g.TErrorIf(err)
+ defer queries.close()
+
+
+ g.Testing("closing more than once does not error", func() {
+ g.TErrorIf(g.SomeError(
+ queries.close(),
+ queries.close(),
+ queries.close(),
+ ))
+ })
}
func test_splitOnCRLF() {
@@ -1681,184 +5414,34 @@ func test_parseMessageParams() {
g.Testing("we can parse the string params", func() {
type tableT struct{
input string
- expected messageParamsT
+ expected []string
}
table := []tableT{
- {
- "",
- messageParamsT{
- middle: []string { },
- trailing: "",
- },
- },
- {
- " ",
- messageParamsT{
- middle: []string { },
- trailing: "",
- },
- },
- {
- " :",
- messageParamsT{
- middle: []string { },
- trailing: "",
- },
- },
- {
- " : ",
- messageParamsT{
- middle: []string { },
- trailing: " ",
- },
- },
- {
- ": ",
- messageParamsT{
- middle: []string { ":" },
- trailing: "",
- },
- },
- {
- ": ",
- messageParamsT{
- middle: []string { ":" },
- trailing: "",
- },
- },
- {
- " : ",
- messageParamsT{
- middle: []string { },
- trailing: " ",
- },
- },
- {
- " :",
- messageParamsT{
- middle: []string { },
- trailing: "",
- },
- },
- {
- " :",
- messageParamsT{
- middle: []string { },
- trailing: "",
- },
- },
- {
- "a",
- messageParamsT{
- middle: []string { "a" },
- trailing: "",
- },
- },
- {
- "ab",
- messageParamsT{
- middle: []string { "ab" },
- trailing: "",
- },
- },
- {
- "a b",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: "",
- },
- },
- {
- "a b c",
- messageParamsT{
- middle: []string { "a", "b", "c" },
- trailing: "",
- },
- },
- {
- "a b:c",
- messageParamsT{
- middle: []string { "a", "b:c" },
- trailing: "",
- },
- },
- {
- "a b:c:",
- messageParamsT{
- middle: []string { "a", "b:c:" },
- trailing: "",
- },
- },
- {
- "a b :c",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: "c",
- },
- },
- {
- "a b :c:",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: "c:",
- },
- },
- {
- "a b :c ",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: "c ",
- },
- },
- {
- "a b : c",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c",
- },
- },
- {
- "a b : c ",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c ",
- },
- },
- {
- "a b : c :",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c :",
- },
- },
- {
- "a b : c : ",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c : ",
- },
- },
- {
- "a b : c :d",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c :d",
- },
- },
- {
- "a b : c :d ",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c :d ",
- },
- },
- {
- "a b : c : d ",
- messageParamsT{
- middle: []string { "a", "b" },
- trailing: " c : d ",
- },
- },
+ { "", []string{} },
+ { " ", []string{} },
+ { " :", []string{ "" } },
+ { " : ", []string{ " " } },
+ { ": ", []string{ ":" } },
+ { ": ", []string{ ":" } },
+ { " : ", []string{ " " } },
+ { " :", []string{ "" } },
+ { " :", []string{ "" } },
+ { "a", []string{ "a" } },
+ { "ab", []string{ "ab" } },
+ { "a b", []string{ "a", "b" } },
+ { "a b c", []string{ "a", "b", "c" } },
+ { "a b:c", []string{ "a", "b:c" } },
+ { "a b:c:", []string{ "a", "b:c:" } },
+ { "a b :c", []string{ "a", "b", "c" } },
+ { "a b :c:", []string{ "a", "b", "c:" } },
+ { "a b :c ", []string{ "a", "b", "c " } },
+ { "a b : c", []string{ "a", "b", " c" } },
+ { "a b : c ", []string{ "a", "b", " c " } },
+ { "a b : c :", []string{ "a", "b", " c :" } },
+ { "a b : c : ", []string{ "a", "b", " c : " } },
+ { "a b : c :d", []string{ "a", "b", " c :d" } },
+ { "a b : c :d ", []string{ "a", "b", " c :d " } },
+ { "a b : c : d ", []string{ "a", "b", " c : d " } },
}
for _, entry := range table {
@@ -1881,10 +5464,7 @@ func test_parseMessage() {
messageT{
prefix: "",
command: "NICK",
- params: messageParamsT{
- middle: []string { "joebloe" },
- trailing: "",
- },
+ params: []string{ "joebloe" },
raw: "NICK joebloe ",
},
}, {
@@ -1892,11 +5472,11 @@ func test_parseMessage() {
messageT{
prefix: "",
command: "USER",
- params: messageParamsT{
- middle: []string {
- "joebloe", "0.0.0.0", "joe",
- },
- trailing: "Joe Bloe",
+ params: []string{
+ "joebloe",
+ "0.0.0.0",
+ "joe",
+ "Joe Bloe",
},
raw: "USER joebloe 0.0.0.0 joe :Joe Bloe",
},
@@ -1905,11 +5485,11 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string {
- "joebloe", "0.0.0.0", "joe",
- },
- trailing: "Joe Bloe",
+ params: []string{
+ "joebloe",
+ "0.0.0.0",
+ "joe",
+ "Joe Bloe",
},
raw: ":pre USER joebloe 0.0.0.0 joe :Joe Bloe",
},
@@ -1918,11 +5498,11 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string {
- "joebloe", "0.0.0.0", "joe",
- },
- trailing: " Joe Bloe ",
+ params: []string{
+ "joebloe",
+ "0.0.0.0",
+ "joe",
+ " Joe Bloe ",
},
raw: ":pre USER joebloe 0.0.0.0 " +
"joe : Joe Bloe ",
@@ -1932,11 +5512,11 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string {
- "jbloe:", "0:0:0:1", "joe::a:",
- },
- trailing: " Joe Bloe ",
+ params: []string{
+ "jbloe:",
+ "0:0:0:1",
+ "joe::a:",
+ " Joe Bloe ",
},
raw: ":pre USER jbloe: 0:0:0:1 " +
"joe::a: : Joe Bloe ",
@@ -1946,10 +5526,7 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string { },
- trailing: "Joe Bloe",
- },
+ params: []string{ "Joe Bloe" },
raw: ":pre USER :Joe Bloe",
},
}, {
@@ -1957,10 +5534,7 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string { },
- trailing: " Joe Bloe",
- },
+ params: []string{ " Joe Bloe" },
raw: ":pre USER : Joe Bloe",
},
}, {
@@ -1968,10 +5542,7 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string { },
- trailing: " Joe Bloe",
- },
+ params: []string{ " Joe Bloe" },
raw: ":pre USER : Joe Bloe",
},
}, {
@@ -1979,10 +5550,7 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string { },
- trailing: " ",
- },
+ params: []string{ " " },
raw: ":pre USER : ",
},
}, {
@@ -1990,10 +5558,7 @@ func test_parseMessage() {
messageT{
prefix: "pre",
command: "USER",
- params: messageParamsT{
- middle: []string { },
- trailing: "",
- },
+ params: []string{},
raw: ":pre USER :",
},
}}
@@ -2034,10 +5599,72 @@ func test_parseMessage() {
})
}
+func test_addTrailingSeparator() {
+ g.TestStart("addTrailingSeparator()")
+
+ g.Testing("noop on empty slice", func() {
+ input := []string{}
+ addTrailingSeparator(input)
+ g.TAssertEqual(input, []string{})
+ })
+
+ g.Testing("noop if last doesn't have a space", func() {
+ type tableT struct{
+ input []string
+ expected []string
+ }
+
+ entries := []tableT{
+ { []string{ "" }, []string{ "" } },
+ { []string{ "", "" }, []string{ "", "" } },
+ { []string{ "a", "b" }, []string{ "a", "b" } },
+ {
+ []string{ "a", "b", "c-d" },
+ []string{ "a", "b", "c-d" },
+ },
+ {
+ []string{ "a ", "b", "cd" },
+ []string{ "a ", "b", "cd" },
+ },
+ }
+
+ for i, entry := range entries {
+ addTrailingSeparator(entry.input)
+ g.TAssertEqual(entry.input, entries[i].expected)
+ }
+ })
+
+ g.Testing("add ':' to the last otherwise", func() {
+ type tableT struct{
+ input []string
+ expected []string
+ }
+
+ entries := []tableT{
+ { []string{ " " }, []string{ ": " } },
+ { []string{ "a " }, []string{ ":a " } },
+ { []string{ " a" }, []string{ ": a" } },
+ { []string{ "a ", " b" }, []string{ "a ", ": b" } },
+ { []string{ "a", " b" }, []string{ "a", ": b" } },
+ {
+ []string{ "a", "b", "c d" },
+ []string{ "a", "b", ":c d" },
+ },
+ }
+
+ for i, entry := range entries {
+ addTrailingSeparator(entry.input)
+ addTrailingSeparator(entry.input)
+ g.TAssertEqual(entry.input, entries[i].expected)
+ }
+ })
+}
+
func dumpQueries() {
queries := []struct{name string; fn func(string) queryT}{
{ "createTables", createTablesSQL },
+ { "memberRoles", memberRolesSQL },
{ "createUser", createUserSQL },
{ "userByUUID", userByUUIDSQL },
{ "updateUser", updateUserSQL },
@@ -2047,14 +5674,17 @@ func dumpQueries() {
{ "networks", networksSQL },
{ "setNetwork", setNetworkSQL },
{ "nipNetwork", nipNetworkSQL },
+ { "membership", membershipSQL },
{ "addMember", addMemberSQL },
+ { "addRole", addRoleSQL },
+ { "dropRole", dropRoleSQL },
{ "showMember", showMemberSQL },
{ "members", membersSQL },
{ "editMember", editMemberSQL },
{ "dropMember", dropMemberSQL },
{ "addChannel", addChannelSQL },
{ "channels", channelsSQL },
- { "topic", topicSQL },
+ { "setChannel", setChannelSQL },
{ "endChannel", endChannelSQL },
{ "join", joinSQL },
{ "part", partSQL },
@@ -2081,8 +5711,6 @@ func MainTest() {
g.Init()
test_defaultPrefix()
- test_serialized()
- test_execSerialized()
test_tryRollback()
test_inTx()
test_createTables()
@@ -2096,7 +5724,10 @@ func MainTest() {
test_networksStmt()
test_setNetworkStmt()
test_nipNetworkStmt()
+ test_membershipStmt()
test_addMemberStmt()
+ test_addRoleStmt()
+ test_dropRoleStmt()
test_showMemberStmt()
test_memberEach()
test_membersStmt()
@@ -2105,7 +5736,7 @@ func MainTest() {
test_addChannelStmt()
test_channelEach()
test_channelsStmt()
- test_topicStmt()
+ test_setChannelStmt()
test_endChannelStmt()
test_joinStmt()
test_partStmt()
@@ -2121,4 +5752,5 @@ func MainTest() {
test_splitOnRawMessage()
test_parseMessageParams()
test_parseMessage()
+ test_addTrailingSeparator()
}