summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2025-09-20 15:45:46 -0300
committerEuAndreh <eu@euandre.org>2025-09-20 15:50:05 -0300
commitaee466b543b9d87a0206a59b1ade4686aa1f459e (patch)
tree386b25e72df3e44acc052bfc2806523d190f8de3
parentmeta.capim: Incorporate previous long-description file (diff)
downloadpapod-aee466b543b9d87a0206a59b1ade4686aa1f459e.tar.gz
papod-aee466b543b9d87a0206a59b1ade4686aa1f459e.tar.xz
Remove SQLite code
-rw-r--r--Makefile7
-rw-r--r--src/papod.go3759
-rw-r--r--tests/papod.go5374
3 files changed, 143 insertions, 8997 deletions
diff --git a/Makefile b/Makefile
index 466e823..1c8873b 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ MANDIR = $(SHAREDIR)/man
EXEC = ./
## Where to store the installation. Empty by default.
DESTDIR =
-LDLIBS = --static -lscrypt-kdf -lsqlite3 -lm
+LDLIBS = --static -lscrypt-kdf
GOCFLAGS = -I $(GOLIBDIR)
GOLDFLAGS = -L $(GOLIBDIR)
N = `nproc`
@@ -108,10 +108,6 @@ $(manpages.XX.N.adoc): po/doc/po4a.cfg
po4a --no-update --translate-only $@ po/doc/po4a.cfg
-.PRECIOUS: tests/queries.sql
-tests/queries.sql: tests/main.bin ALWAYS
- env TESTING_DUMP_SQL_QUERIES=1 $(EXEC)tests/main.bin | diff -U10 $@ -
-
tests.bin-check = \
tests/main.bin-check \
$(functional/main.go:.go=.bin-check) \
@@ -120,7 +116,6 @@ $(tests.bin-check):
$(EXEC)$*.bin
check-unit: $(tests.bin-check)
-check-unit: tests/queries.sql
integration-tests = \
diff --git a/src/papod.go b/src/papod.go
index b6544da..5bf2467 100644
--- a/src/papod.go
+++ b/src/papod.go
@@ -3,8 +3,8 @@ package papod
import (
"bufio"
"bytes"
- "database/sql"
"errors"
+ "flag"
"fmt"
"io"
"log/slog"
@@ -13,11 +13,9 @@ import (
"regexp"
"strings"
"sync"
- "time"
+ "acude"
"cracha"
- "fiinha"
- "golite"
"uuid"
"pds"
"stm"
@@ -27,27 +25,11 @@ import (
-const (
- defaultPrefix = "papod"
- rollbackErrorFmt = "rollback error: %w; while executing: %w"
-
- NEW_CHANNEL = "new-channel"
-
- pingFrequency = time.Duration(30) * time.Second
- pongMaxLatency = time.Duration(5) * time.Second
-)
-
-
-
-type dbconfigT struct{
- shared *sql.DB
- dbpath string
- prefix string
+type newUserT struct{
}
-type queryT struct{
- write string
- read string
+type userT struct{
+ username string
}
type queriesT struct{
@@ -55,156 +37,7 @@ type queriesT struct{
userByUUID func(uuid.UUID) (userT, error)
updateUser func(userT) error
deleteUser func(uuid.UUID) error
- addNetwork func(userT, newNetworkT, uuid.UUID) (networkT, error)
- getNetwork func(userT, uuid.UUID) (networkT, error)
- networks func(userT, func(networkT) error) error
- setNetwork func(memberT, networkT) error
- nipNetwork func(memberT) error
- membership func(userT, networkT) (memberT, error)
- addMember func(memberT, newMemberT) (memberT, error)
- addRole func(memberT, string, memberT) error
- dropRole func(memberT, string, memberT) error
- showMember func(memberT, uuid.UUID) (memberT, error)
- members func(memberT, func(memberT) error) error
- editMember func(memberT, memberT) error
- dropMember func(memberT, uuid.UUID) error
- addChannel func(memberT, newChannelT) (channelT, error)
- chanByName func(memberT, string) (channelT, error)
- channels func(memberT, func(channelT) error) error
- setChannel func(memberT, channelT) error
- endChannel func(memberT, uuid.UUID) error
- join func(memberT, uuid.UUID) error
- part func(memberT, channelT) error
- names func(memberT, uuid.UUID, func(memberT) error) error
- addEvent func(newEventT) (eventT, error)
- allAfter func(memberT, uuid.UUID, func(eventT) error) error
- logMessage func(userT, messageT) error
- close func() error
-}
-
-type newUserT struct{
- uuid uuid.UUID
- username string
- displayName string
-}
-
-type userT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- username string
- displayName string
- pictureID *uuid.UUID
-}
-
-type NetworkType string
-const (
- NetworkType_Public NetworkType = "public"
- NetworkType_Private NetworkType = "private"
- NetworkType_Unlisted NetworkType = "unlisted"
-)
-
-type MemberStatus string
-const (
- MemberStatus_Active MemberStatus = "active"
- MemberStatus_Inactive MemberStatus = "inactive"
- MemberStatus_Removed MemberStatus = "removed"
-)
-
-type newNetworkT struct{
- uuid uuid.UUID
- name string
- description string
- type_ NetworkType
-}
-
-type networkT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- name string
- description string
- type_ NetworkType
-}
-
-type newMemberT struct{
- userID uuid.UUID
- memberID uuid.UUID
- username string
-}
-
-type memberT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- username string
- displayName string
- pictureID *uuid.UUID
- status MemberStatus
- roles []string
-}
-
-type newChannelT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- publicName *string
- label string
- description string
- virtual bool
-}
-
-type channelT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- publicName *string
- label string
- description string
- virtual bool
-}
-
-type SourceType string
-const (
- SourceType_Logon SourceType = "logon"
-)
-
-type sourceT struct{
- uuid uuid.UUID
- type_ SourceType
- metadata *map[string]interface{}
-}
-
-type EventType string
-const (
- EventType_UserJoin EventType = "user-join"
- EventType_UserMessage EventType = "user-message"
-)
-
-type newEventT struct{
- eventID uuid.UUID
- channelID uuid.UUID
- source sourceT
- type_ EventType
- payload string
- metadata *map[string]interface{}
-}
-
-type eventT struct{
- id int64
- timestamp time.Time
- uuid uuid.UUID
- channelID uuid.UUID
- source sourceT
- type_ EventType
- payload string
- metadata *map[string]interface{}
-}
-
-type eventEntryT struct{
- event eventT
- previous *eventT
- isFirst bool
+ createNetwork func(userT, newNetworkT, uuid.UUID) (networkT, error)
}
type messageT struct{
@@ -219,19 +52,22 @@ type replyT struct{
params []string
}
+type taskT struct{
+}
+
+type actionT struct{
+ replies []replyT
+ shouldClose bool
+ err error
+ task []taskT
+}
+
type listenersT struct{
daemon net.Listener
commander net.Listener
close func() error
}
-type consumerT struct{
- topic string
- name string
- // FIXME: use generic to avoid circular reference?
- handlerFn func(papodT) func(fiinha.Message) error
-}
-
type netConnI interface{
Write(p []byte) (n int, err error)
Close() error
@@ -252,3264 +88,48 @@ type metricsT struct{
sentReply func(...any)
}
-type stateMutableT struct{
- // disconnect func(*connectionT)
- subscribe func(string, []string)
-}
-
type stateT struct{
members *pds.Map[string, []string]
users *pds.Map[string, []uuid.UUID]
connections *pds.Map[uuid.UUID, connectionT]
}
-// TODO: key for members should be the channelID, not its name
-type stateMutableDataT struct{
- connections map[uuid.UUID]*connectionT
- users map[string][]uuid.UUID
- members map[string]map[string][]uuid.UUID
-}
-
type papodT struct{
- auth cracha.IAuth
- queue fiinha.IQueue
- queries queriesT
+ acude acude.AcudeI
+ cracha cracha.CrachaI
listeners listenersT
- consumers []consumerT
- stateMutable stateMutableT
- state *stm.AtomT[*stateT]
+ state *stm.AtomT[stateT]
metrics metricsT
- // logger g.Logger
}
-type IPapod interface{
+type PapoI interface{
Start() error
Close() error
}
-
-
-func serialized[A any, B any](callback func(...A) B) (func(...A) B, func()) {
- in := make(chan []A)
- out := make(chan B)
-
- closed := false
- var (
- closeWg sync.WaitGroup
- closeMutex sync.Mutex
- )
- closeWg.Add(1)
-
- go func() {
- for input := range in {
- out <- callback(input...)
- }
- close(out)
- closeWg.Done()
- }()
-
- fn := func(input ...A) B {
- in <- input
- return (<- out)
- }
-
- closeFn := func() {
- closeMutex.Lock()
- defer closeMutex.Unlock()
- if closed {
- return
- }
- close(in)
- closed = true
- closeWg.Wait()
- }
-
- return fn, closeFn
-}
-
-func execSerialized(query string, db *sql.DB) (func(...any) error, func()) {
- return serialized(func(args ...any) error {
- return inTx(db, func(tx *sql.Tx) error {
- _, err := tx.Exec(query, args...)
- return err
- })
- })
-}
-
-func tryRollback(tx *sql.Tx, err error) error {
- rollbackErr := tx.Rollback()
- if rollbackErr != nil {
- return fmt.Errorf(
- rollbackErrorFmt,
- rollbackErr,
- err,
- )
- }
-
- return err
+type argsT struct{
+ allArgs []string
+ subArgs []string
+ baseDir string
+ tag string
}
-func inTx(db *sql.DB, fn func(*sql.Tx) error) error {
- tx, err := db.Begin()
- if err != nil {
- return err
- }
-
- err = fn(tx)
- if err != nil {
- return tryRollback(tx, err)
- }
-
- err = tx.Commit()
- if err != nil {
- return tryRollback(tx, err)
- }
-
- return nil
+type envT struct{
+ args argsT
+ in io.Reader
+ out io.Writer
+ err io.Writer
}
-/// "papod_users".uuid is the same as cracha_users.uuid. Not a foreign key to
-/// allow them to live in different physical locations. Adding it here feels
-/// less like an optimization related decision, and more of a coupling one. The
-/// way that New() works now uses the same databasePath for the fiinha.IQueue
-/// *and* cracha.IAuth, but cracha in no way exposes where it stores the user
-/// UUID or how the it is handled. This has similarities to how events here
-/// don't reference the fiinha.Message.ID via foreign keys either. They're
-/// treated only as opaque IDs.
-func createTablesSQL(prefix string) queryT {
- const tmpl_write = `
- -- TODO: unconfirmed premise: statements within a trigger are
- -- part of the transaction that caused it, and so are
- -- atomic.
- -- See also:
- -- https://stackoverflow.com/questions/77441888/
- -- https://stackoverflow.com/questions/30511116/
-
- CREATE TABLE IF NOT EXISTS "%s_users" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- -- provided by cracha
- user_uuid BLOB NOT NULL UNIQUE,
- username TEXT NOT NULL,
- display_name TEXT NOT NULL,
- picture_uuid BLOB UNIQUE,
- deleted INT NOT NULL CHECK(deleted IN (0, 1))
- ) STRICT;
- CREATE TABLE IF NOT EXISTS "%s_user_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- user_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'username',
- 'display_name',
- 'picture_uuid',
- 'deleted'
- )
- ),
- value_text TEXT,
- value_blob BLOB,
- value_bool INT CHECK(value_bool IN (0, 1)),
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
- CREATE TRIGGER IF NOT EXISTS "%s_user_new"
- AFTER INSERT ON "%s_users"
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'username', NEW.username, true),
- (NEW.id, 'display_name', NEW.display_name, true)
- ;
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_bool, op
- ) VALUES
- (NEW.id, 'deleted', NEW.deleted, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_new_picture_uuid"
- AFTER INSERT ON "%s_users"
- WHEN NEW.picture_uuid IS NOT NULL
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_update_username"
- AFTER UPDATE ON "%s_users"
- WHEN OLD.username != NEW.username
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'username', OLD.username, false),
- (NEW.id, 'username', NEW.username, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_update_display_name"
- AFTER UPDATE ON "%s_users"
- WHEN OLD.display_name != NEW.display_name
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'display_name', OLD.display_name, false),
- (NEW.id, 'display_name', NEW.display_name, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_add_picture_uuid"
- AFTER UPDATE ON "%s_users"
- WHEN (
- OLD.picture_uuid IS NULL AND
- NEW.picture_uuid IS NOT NULL
- )
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_remove_picture_uuid"
- AFTER UPDATE ON "%s_users"
- WHEN (
- OLD.picture_uuid IS NOT NULL AND
- NEW.picture_uuid IS NULL
- )
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', OLD.picture_uuid, false)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_update_picture_uuid"
- AFTER UPDATE ON "%s_users"
- WHEN (
- OLD.picture_uuid IS NOT NULL AND
- NEW.picture_uuid IS NOT NULL AND
- OLD.picture_uuid != NEW.picture_uuid
- )
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', OLD.picture_uuid, false),
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_user_update_deleted"
- AFTER UPDATE ON "%s_users"
- WHEN OLD.deleted != NEW.deleted
- BEGIN
- INSERT INTO "%s_user_changes" (
- user_id, attribute, value_bool, op
- ) VALUES
- (NEW.id, 'deleted', OLD.deleted, false),
- (NEW.id, 'deleted', NEW.deleted, true)
- ;
- END;
-
- CREATE TABLE IF NOT EXISTS "%s_sessions" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- -- provided by cracha
- session_uuid BLOB NOT NULL UNIQUE,
- user_id INTEGER NOT NULL
- REFERENCES "%s_users"(id),
- finished_at TEXT
- );
- CREATE TABLE IF NOT EXISTS "%s_connections" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- uuid BLOB NOT NULL UNIQUE,
- finished_at TEXT
- );
- CREATE TABLE IF NOT EXISTS "%s_logons" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- session_id INTEGER NOT NULL
- REFERENCES "%s_sessions"(id),
- connection_id INTEGER NOT NULL
- REFERENCES "%s_connections"(id),
- UNIQUE (session_id, connection_id)
- ) STRICT;
-
- CREATE TABLE IF NOT EXISTS "%s_networks" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- uuid BLOB NOT NULL UNIQUE,
- name TEXT NOT NULL,
- description TEXT NOT NULL,
- type TEXT NOT NULL CHECK(
- type IN ('public', 'private', 'unlisted')
- ),
- deleted INT NOT NULL CHECK(deleted IN (0, 1))
- ) STRICT;
- CREATE INDEX IF NOT EXISTS "%s_networks_type"
- ON "%s_networks"(type);
- CREATE TABLE IF NOT EXISTS "%s_network_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- network_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'name',
- 'description',
- 'type',
- 'deleted',
- 'logon_id' -- FIXME
- )
- ),
- value TEXT NOT NULL,
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
- CREATE TRIGGER IF NOT EXISTS "%s_network_new"
- AFTER INSERT ON "%s_networks"
- BEGIN
- INSERT INTO "%s_network_changes" (
- network_id, attribute, value, op
- ) VALUES
- (NEW.id, 'name', NEW.name, true),
- (NEW.id, 'description', NEW.description, true),
- (NEW.id, 'type', NEW.type, true),
- (NEW.id, 'deleted', NEW.deleted, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_network_update_name"
- AFTER UPDATE ON "%s_networks"
- WHEN OLD.name != NEW.name
- BEGIN
- INSERT INTO "%s_network_changes" (
- network_id, attribute, value, op
- ) VALUES
- (NEW.id, 'name', OLD.name, false),
- (NEW.id, 'name', NEW.name, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_network_update_description"
- AFTER UPDATE ON "%s_networks"
- WHEN OLD.description != NEW.description
- BEGIN
- INSERT INTO "%s_network_changes" (
- network_id, attribute, value, op
- ) VALUES
- (NEW.id, 'description', OLD.description, false),
- (NEW.id, 'description', NEW.description, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_network_update_type"
- AFTER UPDATE ON "%s_networks"
- WHEN OLD.description != NEW.description
- BEGIN
- INSERT INTO "%s_network_changes" (
- network_id, attribute, value, op
- ) VALUES
- (NEW.id, 'type', OLD.type, false),
- (NEW.id, 'type', NEW.type, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_network_update_deleted"
- AFTER UPDATE ON "%s_networks"
- WHEN OLD.deleted != NEW.deleted
- BEGIN
- INSERT INTO "%s_network_changes" (
- network_id, attribute, value, op
- ) VALUES
- (NEW.id, 'deleted', OLD.deleted, false),
- (NEW.id, 'deleted', NEW.deleted, true)
- ;
- END;
-
- CREATE TABLE IF NOT EXISTS "%s_members" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- uuid BLOB NOT NULL UNIQUE,
- network_id INTEGER NOT NULL
- REFERENCES "%s_networks"(id),
- user_id INTEGER NOT NULL,
- username TEXT NOT NULL,
- display_name TEXT NOT NULL,
- picture_uuid BLOB UNIQUE,
- status TEXT NOT NULL CHECK(
- status IN ('active', 'inactive', 'removed')
- ),
- active_uniq TEXT CHECK(
- active_uniq IN ('active', NULL)
- ),
- UNIQUE (network_id, username, active_uniq),
- UNIQUE (network_id, user_id)
- ) STRICT;
- CREATE TABLE IF NOT EXISTS "%s_member_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- member_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'username',
- 'display_name',
- 'picture_uuid',
- 'status',
- 'logon_id' -- FIXME
- )
- ),
- value_text TEXT,
- value_blob BLOB,
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
- CREATE TRIGGER IF NOT EXISTS "%s_member_new"
- AFTER INSERT ON "%s_members"
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'username', NEW.username, true),
- (NEW.id, 'display_name', NEW.display_name, true),
- (NEW.id, 'status', NEW.status, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_new_picture_uuid"
- AFTER INSERT ON "%s_members"
- WHEN NEW.picture_uuid IS NOT NULL
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_update_username"
- AFTER UPDATE ON "%s_members"
- WHEN OLD.username != NEW.username
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'username', OLD.username, false),
- (NEW.id, 'username', NEW.username, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_update_display_name"
- AFTER UPDATE ON "%s_members"
- WHEN OLD.display_name != NEW.display_name
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'display_name', OLD.display_name, false),
- (NEW.id, 'display_name', NEW.display_name, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_update_status"
- AFTER UPDATE ON "%s_members"
- WHEN OLD.status != NEW.status
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'status', OLD.status, false),
- (NEW.id, 'status', NEW.status, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_add_picture_uuid"
- AFTER UPDATE ON "%s_members"
- WHEN (
- OLD.picture_uuid IS NULL AND
- NEW.picture_uuid IS NOT NULL
- )
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_remove_picture_uuid"
- AFTER UPDATE ON "%s_members"
- WHEN (
- OLD.picture_uuid IS NOT NULL AND
- NEW.picture_uuid IS NULL
- )
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', OLD.picture_uuid, false)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_update_picture_uuid"
- AFTER UPDATE ON "%s_members"
- WHEN (
- OLD.picture_uuid IS NOT NULL AND
- NEW.picture_uuid IS NOT NULL AND
- OLD.picture_uuid != NEW.picture_uuid
- )
- BEGIN
- INSERT INTO "%s_member_changes" (
- member_id, attribute, value_blob, op
- ) VALUES
- (NEW.id, 'picture_uuid', OLD.picture_uuid, false),
- (NEW.id, 'picture_uuid', NEW.picture_uuid, true)
- ;
- END;
-
- CREATE TABLE IF NOT EXISTS "%s_member_roles" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- member_id INTEGER NOT NULL
- REFERENCES "%s_members"(id),
- role TEXT NOT NULL,
- UNIQUE (member_id, role)
- ) STRICT;
- CREATE TABLE IF NOT EXISTS "%s_member_role_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- role_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'role',
- 'logon_id' -- FIXME
- )
- ),
- value TEXT NOT NULL,
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
- CREATE TRIGGER IF NOT EXISTS "%s_member_role_add"
- AFTER INSERT ON "%s_member_roles"
- BEGIN
- INSERT INTO "%s_member_role_changes" (
- role_id, attribute, value, op
- ) VALUES
- (NEW.id, 'role', NEW.role, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_member_role_remove"
- AFTER DELETE ON "%s_member_roles"
- BEGIN
- INSERT INTO "%s_member_role_changes" (
- role_id, attribute, value, op
- ) VALUES
- (OLD.id, 'role', OLD.role, false)
- ;
- END;
- CREATE TABLE IF NOT EXISTS "%s_channels" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- uuid BLOB NOT NULL UNIQUE,
- network_id INTEGER NOT NULL
- REFERENCES "%s_networks"(id),
- public_name TEXT,
- label TEXT NOT NULL,
- description TEXT NOT NULL,
- virtual INT NOT NULL CHECK(virtual IN (0, 1)),
- UNIQUE (network_id, public_name)
- ) STRICT;
- CREATE TABLE IF NOT EXISTS "%s_channel_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- channel_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'public_name',
- 'label',
- 'description',
- 'virtual',
- 'logon_id' -- FIXME
- )
- ),
- value_text TEXT,
- value_bool INT CHECK(value_bool IN (0, 1)),
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_new"
- AFTER INSERT ON "%s_channels"
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'label', NEW.label, true),
- (NEW.id, 'description', NEW.description, true)
- ;
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_bool, op
- ) VALUES
- (NEW.id, 'virtual', NEW.virtual, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_new_public_name"
- AFTER INSERT ON "%s_channels"
- WHEN NEW.public_name IS NOT NULL
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'public_name', NEW.public_name, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_update_label"
- AFTER UPDATE ON "%s_channels"
- WHEN OLD.label != NEW.label
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'label', OLD.label, false),
- (NEW.id, 'label', NEW.label, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_update_description"
- AFTER UPDATE ON "%s_channels"
- WHEN OLD.description != NEW.description
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'description', OLD.description, false),
- (NEW.id, 'description', NEW.description, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_update_virtual"
- AFTER UPDATE ON "%s_channels"
- WHEN OLD.virtual != NEW.virtual
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_bool, op
- ) VALUES
- (NEW.id, 'virtual', OLD.virtual, false),
- (NEW.id, 'virtual', NEW.virtual, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_add_public_name"
- AFTER UPDATE ON "%s_channels"
- WHEN (
- OLD.public_name IS NULL AND
- NEW.public_name IS NOT NULL
- )
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (NEW.id, 'public_name', NEW.public_name, true)
- ;
- END;
- CREATE TRIGGER IF NOT EXISTS "%s_channel_remove_public_name"
- AFTER UPDATE ON "%s_channels"
- WHEN (
- OLD.public_name IS NOT NULL AND
- NEW.public_name IS NULL
- )
- BEGIN
- INSERT INTO "%s_channel_changes" (
- channel_id, attribute, value_text, op
- ) VALUES
- (OLD.id, 'public_name', OLD.public_name, false)
- ;
- END;
-
- CREATE TABLE IF NOT EXISTS "%s_participants" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- channel_id INTEGER NOT NULL
- REFERENCES "%s_channels"(id),
- member_id INTEGER NOT NULL
- REFERENCES "%s_members"(id),
- UNIQUE (channel_id, member_id)
- ) STRICT;
- CREATE TABLE IF NOT EXISTS "%s_participant_changes" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- participant_id INTEGER NOT NULL,
- attribute TEXT NOT NULL CHECK(
- attribute IN (
- 'connection_id'
- )
- ),
- value TEXT NOT NULL,
- op INT NOT NULL CHECK(op IN (0, 1))
- ) STRICT;
-
- CREATE TABLE IF NOT EXISTS "%s_channel_events" (
- id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
- timestamp TEXT NOT NULL DEFAULT (
- %s
- ),
- uuid BLOB NOT NULL UNIQUE,
- channel_id INTEGER NOT NULL
- REFERENCES "%s_channels"(id),
- source_uuid BLOB NOT NULL,
- source_type TEXT NOT NULL CHECK(
- source_type IN (
- 'logon'
- )
- ),
- source_metadata TEXT,
- type TEXT NOT NULL CHECK(
- type IN (
- 'user-join',
- 'user-message'
- )
- ),
- payload TEXT NOT NULL,
- metadata TEXT
- ) STRICT;
-
- `
- return queryT{
- write: fmt.Sprintf(
- tmpl_write,
- prefix,
- g.SQLiteNow,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- g.SQLiteNow,
- prefix,
- g.SQLiteNow,
- prefix,
- ),
- }
-}
-
-func createTables(db *sql.DB, prefix string) error {
- q := createTablesSQL(prefix)
-
- return inTx(db, func(tx *sql.Tx) error {
- _, err := tx.Exec(q.write)
- return err
- })
-}
-
-func memberRolesSQL(prefix string) queryT {
- const tmpl_read = `
- SELECT role FROM "%s_member_roles"
- JOIN "%s_members" ON
- "%s_member_roles".member_id = "%s_members".id
- WHERE "%s_members".uuid = ?
- ORDER BY "%s_member_roles".id;
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func collectRoles(rows *sql.Rows) ([]string, error) {
- roles := []string{}
-
- for rows.Next() {
- var role string
- err := rows.Scan(&role)
- if err != nil {
- rows.Close()
- return nil, err
- }
-
- roles = append(roles, role)
- }
-
- return roles, g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func createUserSQL(prefix string) queryT {
- const tmpl_write = `
- INSERT INTO "%s_users" (
- user_uuid, username, display_name, picture_uuid, deleted
- ) VALUES (
- ?, ?, ?, NULL, false
- ) RETURNING id, timestamp;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func createUserStmt(
- cfg dbconfigT,
-) (func(newUserT) (userT, error), func() error, error) {
- q := createUserSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(newUser newUserT) (userT, error) {
- user := userT{
- uuid: newUser.uuid,
- username: newUser.username,
- displayName: newUser.displayName,
- }
-
- var timestr string
- err := writeStmt.QueryRow(
- newUser.uuid[:],
- newUser.username,
- newUser.displayName,
- ).Scan(&user.id, &timestr)
- if err != nil {
- return userT{}, err
- }
-
- user.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return userT{}, err
- }
-
- return user, nil
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func userByUUIDSQL(prefix string) queryT {
- const tmpl_read = `
- SELECT
- id,
- timestamp,
- username,
- display_name,
- picture_uuid
- FROM "%s_users"
- WHERE
- user_uuid = ? AND
- deleted = false;
- `
- return queryT{
- read: fmt.Sprintf(tmpl_read, prefix),
- }
-}
-
-func userByUUIDStmt(
- cfg dbconfigT,
-) (func(uuid.UUID) (userT, error), func() error, error) {
- q := userByUUIDSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(userID uuid.UUID) (userT, error) {
- user := userT{
- uuid: userID,
- }
-
- var (
- timestr string
- picture_id_bytes []byte
- )
- err := readStmt.QueryRow(userID[:]).Scan(
- &user.id,
- &timestr,
- &user.username,
- &user.displayName,
- &picture_id_bytes,
- )
- if err != nil {
- return userT{}, err
- }
- if picture_id_bytes != nil {
- pictureID := uuid.UUID(picture_id_bytes)
- user.pictureID = &pictureID
- }
-
- user.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return userT{}, err
- }
-
- return user, err
- }
-
- return fn, readStmt.Close, nil
-}
-
-func updateUserSQL(prefix string) queryT {
- const tmpl_write = `
- UPDATE "%s_users"
- SET
- username = ?,
- display_name = ?,
- picture_uuid = ?
- WHERE
- id = ? AND
- deleted = false
- RETURNING id;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func updateUserStmt(
- cfg dbconfigT,
-) (func(userT) error, func() error, error) {
- q := updateUserSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(user userT) error {
- var picture_id_bytes []byte = nil
- if user.pictureID != nil {
- picture_id_bytes = user.pictureID[:]
- }
- var _id int64
- return writeStmt.QueryRow(
- user.username,
- user.displayName,
- picture_id_bytes,
- user.id,
- ).Scan(&_id)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func deleteUserSQL(prefix string) queryT {
- const tmpl_write = `
- UPDATE "%s_users"
- SET deleted = true
- WHERE
- user_uuid = ? AND
- deleted = false
- RETURNING id;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func deleteUserStmt(
- cfg dbconfigT,
-) (func(uuid.UUID) error, func() error, error) {
- q := deleteUserSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(userID uuid.UUID) error {
- var _id int64
- return writeStmt.QueryRow(userID[:]).Scan(&_id)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func addNetworkSQL(prefix string) queryT {
- const tmpl_write = `
- INSERT INTO "%s_networks" (
- uuid, name, description, type, deleted
- )
- VALUES (
- ?,
- ?,
- ?,
- ?,
- false
- ) RETURNING id;
-
- WITH creator AS (
- SELECT username, display_name, picture_uuid
- FROM "%s_users"
- WHERE id = ? AND deleted = false
- ), new_network AS (
- SELECT id FROM "%s_networks" WHERE uuid = ?
- )
- INSERT INTO "%s_members" (
- uuid, network_id, user_id, username, display_name,
- picture_uuid, status, active_uniq
- ) VALUES (
- ?,
- (SELECT id FROM new_network),
- ?,
- (SELECT username FROM creator),
- (SELECT display_name FROM creator),
- (SELECT picture_uuid FROM creator),
- 'active',
- 'active'
- ) RETURNING id;
-
- WITH new_member AS (
- SELECT id FROM "%s_members" WHERE uuid = ?
- )
- INSERT INTO "%s_member_roles" (member_id, role)
- VALUES (
- (SELECT id FROM new_member),
- 'admin'
- ),
- (
- (SELECT id FROM new_member),
- 'creator'
- )
- RETURNING id;
- `
- const tmpl_read = `
- SELECT id, timestamp FROM "%s_networks"
- WHERE uuid = ? AND deleted = false;
- `
- return queryT{
- write: fmt.Sprintf(
- tmpl_write,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- read: fmt.Sprintf(tmpl_read, prefix),
- }
-}
-
-func addNetworkStmt(
- cfg dbconfigT,
-) (
- func(userT, newNetworkT, uuid.UUID) (networkT, error),
- func() error,
- error,
-) {
- q := addNetworkSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- privateDB, err := sql.Open(golite.DriverName, cfg.dbpath)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- writeFn, writeFnClose := execSerialized(q.write, privateDB)
-
- fn := func(
- user userT,
- newNetwork newNetworkT,
- memberID uuid.UUID,
- ) (networkT, error) {
- network := networkT{
- uuid: newNetwork.uuid,
- name: newNetwork.name,
- description: newNetwork.description,
- type_: newNetwork.type_,
- }
-
- err := writeFn(
- newNetwork.uuid[:],
- newNetwork.name,
- newNetwork.description,
- newNetwork.type_,
- user.id,
- newNetwork.uuid[:],
- memberID[:],
- user.id,
- memberID[:],
- )
- if err != nil {
- return networkT{}, err
- }
-
- var timestr string
- err = readStmt.QueryRow(network.uuid[:]).Scan(
- &network.id,
- &timestr,
- )
- if err != nil {
- return networkT{}, err
- }
-
- network.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return networkT{}, err
- }
-
- return network, nil
- }
-
- closeFn := func() error {
- writeFnClose()
- return g.SomeError(privateDB.Close(), readStmt.Close())
- }
-
- return fn, closeFn, nil
-}
-
-func getNetworkSQL(prefix string) queryT {
- const tmpl_read = `
- WITH probing_user AS (
- SELECT id FROM "%s_users"
- WHERE id = ? AND deleted = false
- ), target_network AS (
- SELECT id FROM "%s_networks"
- WHERE uuid = ? AND deleted = false
- )
- SELECT
- id,
- timestamp,
- name,
- description,
- type
- FROM "%s_networks"
- WHERE
- uuid = ? AND
- deleted = false AND
- ? IN probing_user AND
- (
- type IN ('public', 'unlisted') OR
- ? IN (
- SELECT user_id FROM "%s_members"
- WHERE
- user_id = ? AND
- network_id IN target_network AND
- status != 'removed'
- )
- );
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func getNetworkStmt(
- cfg dbconfigT,
-) (func(userT, uuid.UUID) (networkT, error), func() error, error) {
- q := getNetworkSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(user userT, networkID uuid.UUID) (networkT, error) {
- network := networkT{
- uuid: networkID,
- }
-
- var timestr string
- err := readStmt.QueryRow(
- user.id,
- networkID[:],
- networkID[:],
- user.id,
- user.id,
- user.id,
- ).Scan(
- &network.id,
- &timestr,
- &network.name,
- &network.description,
- &network.type_,
- )
- if err != nil {
- return networkT{}, err
- }
-
- network.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return networkT{}, err
- }
-
- return network, nil
- }
-
- return fn, readStmt.Close, nil
-}
-
-func networkEach(rows *sql.Rows, callback func(networkT) error) error {
- if rows == nil {
- return nil
- }
-
- for rows.Next() {
- var (
- network networkT
- timestr string
- network_id_bytes []byte
- deleted bool
- )
- err := rows.Scan(
- &network.id,
- &timestr,
- &network_id_bytes,
- &network.name,
- &network.description,
- &network.type_,
- &deleted,
- )
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- network.uuid = uuid.UUID(network_id_bytes)
-
- if deleted {
- return sql.ErrNoRows
- }
-
- network.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
-
- err = callback(network)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- }
-
- return g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func networksSQL(prefix string) queryT {
- const tmpl_read = `
- WITH current_user AS (
- SELECT id, deleted FROM "%s_users" WHERE id = ?
- )
- SELECT
- "%s_networks".id,
- "%s_networks".timestamp,
- "%s_networks".uuid,
- "%s_networks".name,
- "%s_networks".description,
- "%s_networks".type,
- (SELECT deleted FROM current_user)
- FROM "%s_networks"
- JOIN "%s_members" ON
- "%s_networks".id = "%s_members".network_id
- WHERE (
- "%s_networks".type = 'public' OR
- "%s_networks".id IN (
- SELECT network_id FROM "%s_members"
- WHERE user_id IN (SELECT id FROM current_user)
- )
- ) AND "%s_networks".deleted = false
- ORDER BY "%s_networks".id;
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func networksStmt(
- cfg dbconfigT,
-) (func(userT) (*sql.Rows, error), func() error, error) {
- q := networksSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(user userT) (*sql.Rows, error) {
- return readStmt.Query(user.id)
- }
-
- return fn, readStmt.Close, nil
-}
-
-func setNetworkSQL(prefix string) queryT {
- const tmpl_write = `
- UPDATE "%s_networks"
- SET
- name = ?,
- description = ?,
- type = ?
- WHERE id = ? AND deleted = false
- RETURNING (
- SELECT CASE WHEN EXISTS (
- SELECT role from "%s_member_roles"
- WHERE
- member_id = ? AND
- role IN (
- 'admin',
- 'network-settings-update'
- ) AND ? IN (
- SELECT network_id
- FROM "%s_members"
- WHERE
- id = ? AND
- status = 'active'
- )
- ) THEN true ELSE RAISE(
- ABORT,
- 'member not allowed to update network data'
- ) END
- );
-
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix, prefix),
- }
-}
-
-func setNetworkStmt(
- cfg dbconfigT,
-) (func(memberT, networkT) error, func() error, error) {
- q := setNetworkSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, network networkT) error {
- var _allowed bool
- return writeStmt.QueryRow(
- network.name,
- network.description,
- network.type_,
- network.id,
- actor.id,
- network.id,
- actor.id,
- ).Scan(&_allowed)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func nipNetworkSQL(prefix string) queryT {
- const tmpl_write = `
- WITH target_network AS (
- SELECT network_id AS id
- FROM "%s_members"
- WHERE
- id = ? AND
- status = 'active'
- )
- UPDATE "%s_networks"
- SET deleted = true
- WHERE id IN target_network AND deleted = false
- RETURNING (
- SELECT CASE WHEN EXISTS (
- SELECT role FROM "%s_member_roles"
- WHERE
- member_id = ? AND
- role IN (
- 'admin'
- )
- ) THEN true ELSE RAISE(
- ABORT,
- 'member not allowed to delete network'
- ) END
- );
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix, prefix),
- }
-}
-
-func nipNetworkStmt(
- cfg dbconfigT,
-) (func(memberT) error, func() error, error) {
- q := nipNetworkSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT) error {
- var _allowed bool
- return writeStmt.QueryRow(actor.id, actor.id).Scan(&_allowed)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func membershipSQL(prefix string) queryT {
- const tmpl_read = `
- SELECT
- "%s_members".id,
- "%s_members".timestamp,
- "%s_members".uuid,
- "%s_members".username,
- "%s_members".display_name,
- "%s_members".picture_uuid,
- "%s_members".status
- FROM "%s_members"
- JOIN "%s_users" ON
- "%s_users".id = "%s_members".user_id
- JOIN "%s_networks" ON
- "%s_networks".id = "%s_members".network_id
- WHERE
- "%s_members".user_id = ? AND
- "%s_members".network_id = ? AND
- "%s_members".status = 'active' AND
- "%s_users".deleted = false AND
- "%s_networks".deleted = false;
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func membershipStmt(
- cfg dbconfigT,
-) (func(userT, networkT) (memberT, error), func() error, error) {
- q := membershipSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- rolesStmt, err := cfg.shared.Prepare(memberRolesSQL(cfg.prefix).read)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- fn := func(actor userT, network networkT) (memberT, error) {
- member := memberT{}
-
- var (
- timestr string
- member_id_bytes []byte
- picture_id_bytes []byte
- )
- err := readStmt.QueryRow(actor.id, network.id).Scan(
- &member.id,
- &timestr,
- &member_id_bytes,
- &member.username,
- &member.displayName,
- &picture_id_bytes,
- &member.status,
- )
- if err != nil {
- return memberT{}, err
- }
- member.uuid = uuid.UUID(member_id_bytes)
-
- member.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return memberT{}, err
- }
-
- rows, err := rolesStmt.Query(member_id_bytes)
- if err != nil {
- return memberT{}, err
- }
-
- member.roles, err = collectRoles(rows)
- if err != nil {
- return memberT{}, err
- }
-
- return member, nil
- }
-
- closeFn := func() error {
- return g.SomeError(
- readStmt.Close(),
- rolesStmt.Close(),
- )
- }
-
- return fn, closeFn, nil
-}
-
-func addMemberSQL(prefix string) queryT {
- const tmpl_write = `
- WITH target_user AS (
- SELECT id, username, display_name, picture_uuid
- FROM "%s_users"
- WHERE user_uuid = ? AND deleted = false
- ), target_network AS (
- SELECT "%s_members".network_id AS id
- FROM "%s_members"
- JOIN "%s_networks" ON
- "%s_members".network_id = "%s_networks".id
- WHERE
- "%s_members".id = ? AND
- "%s_members".status = 'active' AND
- "%s_networks".deleted = false
- )
- INSERT INTO "%s_members" (
- uuid, network_id, user_id, username, display_name,
- picture_uuid, status, active_uniq
- ) VALUES (
- ?,
- (SELECT id FROM target_network),
- (SELECT id FROM target_user),
- ?,
- (SELECT display_name FROM target_user),
- (SELECT picture_uuid FROM target_user),
- 'active',
- 'active'
- ) RETURNING id, timestamp, display_name, picture_uuid, status, (
- SELECT CASE WHEN EXISTS (
- SELECT role from "%s_member_roles"
- WHERE
- member_id = ? AND
- role IN (
- 'admin',
- 'add-member'
- )
- ) THEN true ELSE RAISE(
- ABORT,
- 'member not allowed to add another member'
- ) END
- );
- `
- return queryT{
- write: fmt.Sprintf(
- tmpl_write,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func addMemberStmt(
- cfg dbconfigT,
-) (func(memberT, newMemberT) (memberT, error), func() error, error) {
- q := addMemberSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- rolesStmt, err := cfg.shared.Prepare(memberRolesSQL(cfg.prefix).read)
- if err != nil {
- writeStmt.Close()
- return nil, nil, err
- }
-
- fn := func(actor memberT, newMember newMemberT) (memberT, error) {
- member := memberT{
- uuid: newMember.memberID,
- username: newMember.username,
- }
-
- var (
- timestr string
- picture_id_bytes []byte
- _allowed bool
- )
- err := writeStmt.QueryRow(
- newMember.userID[:],
- actor.id,
- newMember.memberID[:],
- newMember.username,
- actor.id,
- ).Scan(
- &member.id,
- &timestr,
- &member.displayName,
- &picture_id_bytes,
- &member.status,
- &_allowed,
- )
- if err != nil {
- return memberT{}, err
- }
- if picture_id_bytes != nil {
- pictureID := uuid.UUID(picture_id_bytes)
- member.pictureID = &pictureID
- }
-
- member.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return memberT{}, err
- }
-
- rows, err := rolesStmt.Query(member.uuid[:])
- if err != nil {
- return memberT{}, err
- }
-
- member.roles, err = collectRoles(rows)
- if err != nil {
- return memberT{}, err
- }
-
- return member, nil
- }
-
- closeFn := func() error {
- return g.SomeError(
- writeStmt.Close(),
- rolesStmt.Close(),
- )
- }
-
- return fn, closeFn, nil
-}
-
-func addRoleSQL(prefix string) queryT {
- const tmpl_write = `
- INSERT INTO "%s_member_roles" (member_id, role)
- VALUES (?, ?);
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func addRoleStmt(
- cfg dbconfigT,
-) (func(memberT, string, memberT) error, func() error, error) {
- q := addRoleSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, role string, member memberT) error {
- // FIXME: do authorization
- _, err := writeStmt.Exec(member.id, role)
- return err
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func dropRoleSQL(prefix string) queryT {
- const tmpl_write = `
- DELETE FROM "%s_member_roles"
- WHERE
- member_id = ? AND
- role = ?
- RETURNING 1;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func dropRoleStmt(
- cfg dbconfigT,
-) (func(memberT, string, memberT) error, func() error, error) {
- q := dropRoleSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, role string, member memberT) error {
- // FIXME: do authorization
- // _, err := writeStmt.Exec(member.id, role)
- // return err
- var _id int64
- return writeStmt.QueryRow(member.id, role).Scan(&_id)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-
-func showMemberSQL(prefix string) queryT {
- const tmpl_read = `
- WITH current_network AS (
- SELECT network_id
- FROM "%s_members"
- WHERE id = ?
- )
- SELECT
- id,
- timestamp,
- username,
- display_name,
- picture_uuid,
- status
- FROM "%s_members"
- WHERE
- uuid = ? AND
- network_id IN current_network;
- `
- return queryT{
- read: fmt.Sprintf(tmpl_read, prefix, prefix),
- }
-}
-
-func showMemberStmt(
- cfg dbconfigT,
-) (func(memberT, uuid.UUID) (memberT, error), func() error, error) {
- q := showMemberSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- rolesStmt, err := cfg.shared.Prepare(memberRolesSQL(cfg.prefix).read)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- fn := func(actor memberT, memberID uuid.UUID) (memberT, error) {
- member := memberT{
- uuid: memberID,
- }
-
- var (
- timestr string
- picture_id_bytes []byte
- )
- err := readStmt.QueryRow(actor.id, memberID[:]).Scan(
- &member.id,
- &timestr,
- &member.username,
- &member.displayName,
- &picture_id_bytes,
- &member.status,
- )
- if err != nil {
- return memberT{}, err
- }
- if picture_id_bytes != nil {
- pictureID := uuid.UUID(picture_id_bytes)
- // FIXME: test this
- member.pictureID = &pictureID
- }
-
- member.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return memberT{}, err
- }
-
- rows, err := rolesStmt.Query(memberID[:])
- if err != nil {
- return memberT{}, err
- }
-
- member.roles, err = collectRoles(rows)
- if err != nil {
- return memberT{}, err
- }
-
- return member, nil
- }
-
- closeFn := func() error {
- return g.SomeError(
- readStmt.Close(),
- rolesStmt.Close(),
- )
- }
-
- return fn, closeFn, nil
-}
-
-func memberEach(rows *sql.Rows, callback func(memberT) error) error {
- if rows == nil {
- return nil
- }
-
- for rows.Next() {
- var (
- member memberT
- timestr string
- member_id_bytes []byte
- picture_id_bytes []byte
- )
- err := rows.Scan(
- &member.id,
- &timestr,
- &member_id_bytes,
- &member.username,
- &member.displayName,
- &picture_id_bytes,
- &member.status,
- )
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- member.uuid = uuid.UUID(member_id_bytes)
- if picture_id_bytes != nil {
- pictureID := uuid.UUID(picture_id_bytes)
- member.pictureID = &pictureID
- }
-
- member.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
-
- err = callback(member)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- }
-
- return g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func membersSQL(prefix string) queryT {
- const tmpl_read = `
- WITH target_network AS (
- SELECT "%s_members".network_id
- FROM "%s_members"
- JOIN "%s_networks" ON
- "%s_members".network_id = "%s_networks".id
- WHERE
- "%s_members".id = ? AND
- "%s_networks".deleted = false
- )
- SELECT
- id,
- timestamp,
- uuid,
- username,
- display_name,
- picture_uuid,
- status
- FROM "%s_members"
- WHERE
- network_id IN target_network AND
- status = 'active';
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func membersStmt(
- cfg dbconfigT,
-) (func(memberT) (*sql.Rows, error), func() error, error) {
- q := membersSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT) (*sql.Rows, error) {
- return readStmt.Query(actor.id)
- }
-
- return fn, readStmt.Close, nil
-}
-
-func editMemberSQL(prefix string) queryT {
- const tmpl_write = `
- UPDATE "%s_members"
- SET
- status = ?
- WHERE id = ?
- RETURNING id;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func editMemberStmt(
- cfg dbconfigT,
-) (func(memberT, memberT) error, func() error, error) {
- q := editMemberSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, member memberT) error {
- var _id int64
- return writeStmt.QueryRow(
- member.status,
- member.id,
- ).Scan(&_id)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func dropMemberSQL(prefix string) queryT {
- const tmpl_write = `
- UPDATE "%s_members" SET status = 'removed'
- WHERE uuid = ? RETURNING id;
-
- DELETE FROM "%s_member_roles"
- WHERE
- role != 'creator' AND
- member_id IN (
- SELECT id FROM "%s_members"
- WHERE uuid = ?
- )
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix, prefix),
- }
-}
-
-func dropMemberStmt(
- cfg dbconfigT,
-) (func(memberT, uuid.UUID) error, func() error, error) {
- q := dropMemberSQL(cfg.prefix)
-
- privateDB, err := sql.Open(golite.DriverName, cfg.dbpath)
- if err != nil {
- return nil, nil, err
- }
-
- writeFn, writeFnClose := execSerialized(q.write, privateDB)
-
- fn := func(actor memberT, memberID uuid.UUID) error {
- err := writeFn(memberID[:], memberID[:])
- if err != nil {
- return err
- }
-
- // if res == 0 { // FIXME }
- return nil
- }
-
- closeFn := func() error {
- writeFnClose()
- return privateDB.Close()
- }
-
- return fn, closeFn, nil
-}
-
-func addChannelSQL(prefix string) queryT {
- const tmpl_write = `
- WITH target_network AS (
- SELECT network_id AS id
- FROM "%s_members"
- WHERE id = ?
- )
- INSERT INTO "%s_channels" (
- uuid,
- network_id,
- public_name,
- label,
- description,
- virtual
- ) VALUES (
- ?,
- (SELECT id FROM target_network),
- ?,
- ?,
- ?,
- ?
- ) RETURNING id, timestamp;
-
- WITH new_channel AS (
- SELECT id FROM "%s_channels" WHERE uuid = ?
- )
- INSERT INTO "%s_participants" (channel_id, member_id)
- VALUES (
- (SELECT id FROM new_channel),
- ?
- );
- `
- const tmpl_read = `
- SELECT id, timestamp FROM "%s_channels"
- WHERE uuid = ?;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix, prefix, prefix),
- read: fmt.Sprintf(tmpl_read, prefix),
- }
-}
-
-func addChannelStmt(
- cfg dbconfigT,
-) (func (memberT, newChannelT) (channelT, error), func() error, error) {
- q := addChannelSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- privateDB, err := sql.Open(golite.DriverName, cfg.dbpath)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- writeFn, writeFnClose := execSerialized(q.write, privateDB)
-
- fn := func(actor memberT, newChannel newChannelT) (channelT, error) {
- channel := channelT{
- uuid: newChannel.uuid,
- publicName: newChannel.publicName,
- label: newChannel.label,
- description: newChannel.description,
- virtual: newChannel.virtual,
- }
-
- var timestr string
- err := writeFn(
- actor.id,
- newChannel.uuid[:],
- newChannel.publicName,
- newChannel.label,
- newChannel.description,
- newChannel.virtual,
- newChannel.uuid[:],
- actor.id,
- )
- if err != nil {
- return channelT{}, err
- }
-
- err = readStmt.QueryRow(newChannel.uuid[:]).Scan(
- &channel.id,
- &timestr,
- )
- if err != nil {
- return channelT{}, err
- }
-
- channel.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return channelT{}, err
- }
-
- return channel, nil
- }
-
- closeFn := func() error {
- writeFnClose()
- return readStmt.Close()
- }
-
- return fn, closeFn, nil
-}
-
-/*
-func chanByName(prefix string) queryT {
- const tmpl_read = `
- SELECT
- id,
- timestamp,
- uuid,
- public,
-
- `
- return queryT{
- read: fmt.Sprintf(tmpl_read, prefix),
- }
-}
-
-func chanByStmt(
- cfg dbconfigT,
-) (
-FIXME
-*/
-
-func channelEach(rows *sql.Rows, callback func(channelT) error) error {
- if rows == nil {
- return nil
- }
-
- for rows.Next() {
- var (
- channel channelT
- timestr string
- channel_id_bytes []byte
- publicName sql.NullString
- )
- err := rows.Scan(
- &channel.id,
- &timestr,
- &channel_id_bytes,
- &publicName,
- &channel.label,
- &channel.description,
- &channel.virtual,
- )
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- channel.uuid = uuid.UUID(channel_id_bytes)
- if publicName.Valid {
- channel.publicName = &publicName.String
- }
-
- channel.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
-
- err = callback(channel)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- }
-
- return g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func channelsSQL(prefix string) queryT {
- const tmpl_read = `
- WITH current_network AS (
- SELECT network_id AS id
- FROM "%s_members"
- WHERE id = ?
- ), member_private_channels AS (
- SELECT channel_id AS id
- FROM "%s_participants"
- WHERE member_id = ?
- )
- SELECT
- id,
- timestamp,
- uuid,
- public_name,
- label,
- description,
- virtual
- FROM "%s_channels"
- WHERE
- network_id IN current_network AND
- (
- public_name IS NOT NULL OR
- id IN member_private_channels
- )
- ORDER BY id;
- `
- return queryT{
- read: fmt.Sprintf(tmpl_read, prefix, prefix, prefix),
- }
-}
-
-func channelsStmt(
- cfg dbconfigT,
-) (func(memberT) (*sql.Rows, error), func() error, error) {
- q := channelsSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT) (*sql.Rows, error) {
- return readStmt.Query(actor.id, actor.id)
- }
-
- return fn, readStmt.Close, nil
-}
-
-func setChannelSQL(prefix string) queryT {
- const tmpl_write = `
- WITH participant_channel AS (
- SELECT channel_id AS id
- FROM "%s_participants"
- WHERE
- member_id = ? AND
- channel_id = ?
- )
- UPDATE "%s_channels"
- SET
- description = ?,
- public_name = ?
- WHERE id IN participant_channel
- RETURNING id;
- `
- const tmpl_read = `
- SELECT (
- SELECT network_id AS id
- FROM "%s_channels"
- WHERE id = ?
- ) AS channel_network_id, (
- SELECT network_id AS id
- FROM "%s_members"
- WHERE id = ?
- ) AS member_network_id;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix),
- read: fmt.Sprintf(tmpl_read, prefix, prefix),
- }
-}
-
-func setChannelStmt(
- cfg dbconfigT,
-) (func(memberT, channelT) error, func() error, error) {
- q := setChannelSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- fn := func(actor memberT, channel channelT) error {
- var (
- netid1 sql.NullInt64
- netid2 sql.NullInt64
- )
- err := readStmt.QueryRow(channel.id, actor.id).Scan(
- &netid1,
- &netid2,
- )
- if err != nil {
- return err
- }
- if !netid1.Valid || !netid2.Valid ||
- netid1.Int64 != netid2.Int64 {
- return sql.ErrNoRows
- }
-
- var _id int64
- return writeStmt.QueryRow(
- actor.id,
- channel.id,
- channel.description,
- channel.publicName,
- ).Scan(&_id)
- }
-
- closeFn := func() error {
- return g.SomeError(
- readStmt.Close(),
- writeStmt.Close(),
- )
- }
-
- return fn, closeFn, nil
-}
-
-func endChannelSQL(prefix string) queryT {
- const tmpl_write = `
- -- FIXME %s
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func endChannelStmt(
- cfg dbconfigT,
-) (func(memberT, uuid.UUID) error, func() error, error) {
- q := endChannelSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, channelID uuid.UUID) error {
- _, err := writeStmt.Exec(channelID[:])
- return err
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func joinSQL(prefix string) queryT {
- const tmpl_write = `
- WITH target_channel AS (
- SELECT id
- FROM "%s_channels"
- WHERE
- uuid = ? AND
- public_name IS NOT NULL
- )
- INSERT INTO "%s_participants" (channel_id, member_id)
- VALUES (
- (SELECT id FROM target_channel),
- ?
- ) RETURNING id;
- `
- const tmpl_read = `
- SELECT (
- SELECT network_id AS id
- FROM "%s_channels"
- WHERE
- uuid = ? AND
- public_name IS NOT NULL
- ) AS channel_network_id, (
- SELECT network_id AS id
- FROM "%s_members" WHERE id = ?
- ) AS member_network_id;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix),
- read: fmt.Sprintf(tmpl_read, prefix, prefix),
- }
-}
-
-func joinStmt(
- cfg dbconfigT,
-) (func(memberT, uuid.UUID) error, func() error, error) {
- q := joinSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- readStmt.Close()
- return nil, nil, err
- }
-
- fn := func(actor memberT, channelID uuid.UUID) error {
- var (
- netid1 sql.NullInt64
- netid2 sql.NullInt64
- )
- err := readStmt.QueryRow(channelID[:], actor.id).Scan(
- &netid1,
- &netid2,
- )
- if err != nil {
- return err
- }
-
- if !netid1.Valid || !netid2.Valid ||
- netid1.Int64 != netid2.Int64 {
- return sql.ErrNoRows
- }
-
- var _id int64
- return writeStmt.QueryRow(channelID[:], actor.id).Scan(&_id)
- }
-
- closeFn := func() error {
- return g.SomeError(
- readStmt.Close(),
- writeStmt.Close(),
- )
- }
-
- return fn, closeFn, nil
-}
-
-func partSQL(prefix string) queryT {
- const tmpl_write = `
- WITH target_channel AS (
- SELECT id
- FROM "%s_channels"
- WHERE
- id = ? AND
- virtual = false
- )
- DELETE FROM "%s_participants"
- WHERE
- member_id = ? AND
- channel_id IN target_channel
- RETURNING 1;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix),
- }
-}
-
-func partStmt(
- cfg dbconfigT,
-) (func(memberT, channelT) error, func() error, error) {
- q := partSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, channel channelT) error {
- var _id int64
- return writeStmt.QueryRow(channel.id, actor.id).Scan(&_id)
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func nameEach(rows *sql.Rows, callback func(memberT) error) error {
- if rows == nil {
- return nil
- }
-
- for rows.Next() {
- var (
- member memberT
- timestr string
- member_id_bytes []byte
- )
- err := rows.Scan(
- &member.id,
- &timestr,
- &member_id_bytes,
- )
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- member.uuid = uuid.UUID(member_id_bytes)
-
- member.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
-
- err = callback(member)
- if err != nil {
- return g.WrapErrors(rows.Close(), err)
- }
- }
-
- return g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func namesSQL(prefix string) queryT {
- const tmpl_read = `
- -- FIXME %s
- `
- return queryT{
- read: fmt.Sprintf(tmpl_read, prefix),
- }
-}
-
-func namesStmt(
- cfg dbconfigT,
-) (func(memberT, uuid.UUID) (*sql.Rows, error), func() error, error) {
- q := namesSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, channelID uuid.UUID) (*sql.Rows, error) {
- return readStmt.Query(channelID[:])
- }
-
- return fn, readStmt.Close, nil
-}
-
-func addEventSQL(prefix string) queryT {
- const tmpl_write = `
- INSERT INTO "%s_channel_events" (
- uuid, channel_id, source_uuid, source_type,
- source_metadata, type, payload, metadata
- ) VALUES (
- ?,
- (SELECT id FROM "%s_channels" WHERE uuid = ?),
- ?,
- ?,
- ?,
- ?,
- ?,
- ?
- ) RETURNING id, timestamp;
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix, prefix),
- }
-}
-
-func addEventStmt(
- cfg dbconfigT,
-) (func (newEventT) (eventT, error), func() error, error) {
- q := addEventSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(newEvent newEventT) (eventT, error) {
- event := eventT{
- uuid: newEvent.eventID,
- channelID: newEvent.channelID,
- source: newEvent.source,
- type_: newEvent.type_,
- payload: newEvent.payload,
- metadata: newEvent.metadata,
- }
-
- var timestr string
- err := writeStmt.QueryRow(
- newEvent.eventID[:],
- newEvent.channelID[:],
- newEvent.source.uuid[:],
- newEvent.source.type_,
- newEvent.source.metadata,
- newEvent.type_,
- newEvent.payload,
- newEvent.metadata,
- ).Scan(&event.id, &timestr)
- if err != nil {
- return eventT{}, err
- }
-
- event.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- return eventT{}, err
- }
-
- return event, nil
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func eventEach(rows *sql.Rows, callback func(eventT) error) error {
- if rows == nil {
- return nil
- }
-
- for rows.Next() {
- var (
- event eventT
- timestr string
- event_id_bytes []byte
- channel_id_bytes []byte
- )
- err := rows.Scan(
- &event.id,
- &timestr,
- &event_id_bytes,
- &channel_id_bytes,
- &event.type_,
- &event.payload,
- )
- if err != nil {
- rows.Close()
- return err
- }
- event.uuid = uuid.UUID(event_id_bytes)
- event.channelID = uuid.UUID(channel_id_bytes)
-
- event.timestamp, err = time.Parse(time.RFC3339Nano, timestr)
- if err != nil {
- rows.Close()
- return err
- }
-
- err = callback(event)
- if err != nil {
- rows.Close()
- return err
- }
- }
-
- return g.WrapErrors(rows.Err(), rows.Close())
-}
-
-func allAfterSQL(prefix string) queryT {
- const tmpl_read = `
- WITH landmark_event AS (
- SELECT id, channel_id
- FROM "%s_channel_events"
- WHERE uuid = ?
- )
- SELECT
- "%s_channel_events".id,
- "%s_channel_events".timestamp,
- "%s_channel_events".uuid,
- "%s_channels".uuid,
- -- "%s_channel_events".connection_uuid,
- "%s_channel_events".type,
- "%s_channel_events".payload
- FROM "%s_channel_events"
- JOIN "%s_channels" ON
- "%s_channel_events".channel_id = "%s_channels".id
- WHERE
- "%s_channel_events".id > (
- SELECT id FROM landmark_event
- ) AND channel_id = (
- SELECT channel_id FROM landmark_event
- );
- `
- return queryT{
- read: fmt.Sprintf(
- tmpl_read,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- prefix,
- ),
- }
-}
-
-func allAfterStmt(
- cfg dbconfigT,
-) (func (memberT, uuid.UUID) (*sql.Rows, error), func() error, error) {
- q := allAfterSQL(cfg.prefix)
-
- readStmt, err := cfg.shared.Prepare(q.read)
- if err != nil {
- return nil, nil, err
- }
-
- fn := func(actor memberT, eventID uuid.UUID) (*sql.Rows, error) {
- return readStmt.Query(eventID[:])
- }
-
- return fn, readStmt.Close, nil
-}
-
-func logMessageSQL(prefix string) queryT{
- const tmpl_write = `
- -- FIXME %s
- `
- return queryT{
- write: fmt.Sprintf(tmpl_write, prefix),
- }
-}
-
-func logMessageStmt(
- cfg dbconfigT,
-) (func(userT, messageT) error, func() error, error) {
- q := logMessageSQL(cfg.prefix)
-
- writeStmt, err := cfg.shared.Prepare(q.write)
- if err != nil {
- return nil, nil, err
- }
-
- // FIXME: actor?
- fn := func(user userT, message messageT) error {
- return nil // FIXME
- _, err := writeStmt.Exec(user, message)
- return err
- }
-
- return fn, writeStmt.Close, nil
-}
-
-func initDB(
- dbpath string,
- prefix string,
-) (queriesT, error) {
- err := g.ValidateSQLTablePrefix(prefix)
- if err != nil {
- return queriesT{}, err
- }
-
- shared, err := sql.Open(golite.DriverName, dbpath)
- if err != nil {
- return queriesT{}, err
- }
-
- cfg := dbconfigT{
- shared: shared,
- dbpath: dbpath,
- prefix: prefix,
- }
-
- createTablesErr := createTables(shared, prefix)
- createUser, createUserClose, createUserErr := createUserStmt(cfg)
- userByUUID, userByUUIDClose, userByUUIDErr := userByUUIDStmt(cfg)
- updateUser, updateUserClose, updateUserErr := updateUserStmt(cfg)
- deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(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)
- dropRole, dropRoleClose, dropRoleErr := dropRoleStmt(cfg)
- showMember, showMemberClose, showMemberErr := showMemberStmt(cfg)
- members, membersClose, membersErr := membersStmt(cfg)
- editMember, editMemberClose, editMemberErr := editMemberStmt(cfg)
- dropMember, dropMemberClose, dropMemberErr := dropMemberStmt(cfg)
- addChannel, addChannelClose, addChannelErr := addChannelStmt(cfg)
- channels, channelsClose, channelsErr := channelsStmt(cfg)
- setChannel, setChannelClose, setChannelErr := setChannelStmt(cfg)
- endChannel, endChannelClose, endChannelErr := endChannelStmt(cfg)
- join, joinClose, joinErr := joinStmt(cfg)
- part, partClose, partErr := partStmt(cfg)
- names, namesClose, namesErr := namesStmt(cfg)
- addEvent, addEventClose, addEventErr := addEventStmt(cfg)
- allAfter, allAfterClose, allAfterErr := allAfterStmt(cfg)
- logMessage, logMessageClose, logMessageErr := logMessageStmt(cfg)
-
- closeFn := func() error {
- return g.SomeFnError(
- createUserClose,
- userByUUIDClose,
- updateUserClose,
- deleteUserClose,
- addNetworkClose,
- getNetworkClose,
- networksClose,
- setNetworkClose,
- nipNetworkClose,
- membershipClose,
- addMemberClose,
- addRoleClose,
- dropRoleClose,
- showMemberClose,
- membersClose,
- editMemberClose,
- dropMemberClose,
- addChannelClose,
- channelsClose,
- setChannelClose,
- endChannelClose,
- joinClose,
- partClose,
- namesClose,
- addEventClose,
- allAfterClose,
- logMessageClose,
- )
- }
-
- err = g.SomeError(
- createTablesErr,
- createUserErr,
- userByUUIDErr,
- updateUserErr,
- deleteUserErr,
- addNetworkErr,
- getNetworkErr,
- networksErr,
- setNetworkErr,
- nipNetworkErr,
- membershipErr,
- addMemberErr,
- addRoleErr,
- dropRoleErr,
- showMemberErr,
- membersErr,
- editMemberErr,
- dropMemberErr,
- addChannelErr,
- channelsErr,
- setChannelErr,
- endChannelErr,
- joinErr,
- partErr,
- namesErr,
- addEventErr,
- allAfterErr,
- logMessageErr,
- )
- if err != nil {
- closeFn()
- return queriesT{}, err
- }
-
- var connMutex sync.RWMutex
- return queriesT{
- createUser: func(a newUserT) (userT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return createUser(a)
- },
- userByUUID: func(a uuid.UUID) (userT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return userByUUID(a)
- },
- updateUser: func(a userT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return updateUser(a)
- },
- deleteUser: func(a uuid.UUID) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return deleteUser(a)
- },
- addNetwork: func(
- a userT,
- b newNetworkT,
- c uuid.UUID,
- ) (networkT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return addNetwork(a, b, c)
- },
- getNetwork: func(a userT, b uuid.UUID) (networkT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return getNetwork(a, b)
- },
- networks: func(
- a userT,
- callback func(networkT) error,
- ) error {
- var (
- err error
- rows *sql.Rows
- )
- {
- connMutex.RLock()
- defer connMutex.RUnlock()
- rows, err = networks(a)
- }
- if err != nil {
- return err
- }
-
- return networkEach(rows, callback)
- },
- setNetwork: func(a memberT, b networkT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return setNetwork(a, b)
- },
- nipNetwork: func(a memberT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return nipNetwork(a)
- },
- membership: func(a userT, b networkT) (memberT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return membership(a, b)
- },
- addMember: func(a memberT, b newMemberT) (memberT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return addMember(a, b)
- },
- addRole: func(a memberT, b string, c memberT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return addRole(a, b, c)
- },
- dropRole: func(a memberT, b string, c memberT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return dropRole(a, b, c)
- },
- showMember: func(a memberT, b uuid.UUID) (memberT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return showMember(a, b)
- },
- members: func(a memberT, callback func(memberT) error) error {
- var (
- err error
- rows *sql.Rows
- )
- {
- connMutex.RLock()
- defer connMutex.RUnlock()
- rows, err = members(a)
- }
- if err != nil {
- return err
- }
-
- return memberEach(rows, callback)
- },
- editMember: func(a memberT, b memberT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return editMember(a, b)
- },
- dropMember: func(a memberT, b uuid.UUID) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return dropMember(a, b)
- },
- addChannel: func(
- a memberT, b newChannelT,
- ) (channelT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return addChannel(a, b)
- },
- channels: func(
- a memberT,
- callback func(channelT) error,
- ) error {
- var (
- err error
- rows *sql.Rows
- )
- {
- connMutex.RLock()
- defer connMutex.RUnlock()
- rows, err = channels(a)
- }
- if err != nil {
- return err
- }
-
- return channelEach(rows, callback)
- },
- setChannel: func(a memberT, b channelT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return setChannel(a, b)
- },
- endChannel: func(a memberT, b uuid.UUID) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return endChannel(a, b)
- },
- join: func(a memberT, b uuid.UUID) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return join(a, b)
- },
- part: func(a memberT, b channelT) error {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return part(a, b)
- },
- names: func(
- a memberT,
- b uuid.UUID,
- callback func(memberT) error,
- ) error {
- var (
- err error
- rows *sql.Rows
- )
- {
- connMutex.RLock()
- defer connMutex.RUnlock()
- rows, err = names(a, b)
- }
- if err != nil {
- return err
- }
-
- return nameEach(rows, callback)
- },
- addEvent: func(a newEventT) (eventT, error) {
- connMutex.RLock()
- defer connMutex.RUnlock()
- return addEvent(a)
- },
- allAfter: func(
- a memberT,
- b uuid.UUID,
- callback func(eventT) error,
- ) error {
- var (
- err error
- rows *sql.Rows
- )
- {
- connMutex.RLock()
- defer connMutex.RUnlock()
- rows, err = allAfter(a, b)
- }
- if err != nil {
- return err
- }
-
- return eventEach(rows, callback)
- },
- logMessage: func(a userT, b messageT) error {
- connMutex.Lock()
- defer connMutex.Unlock()
- return logMessage(a, b)
- },
- close: func() error {
- connMutex.Lock()
- defer connMutex.Unlock()
- return closeFn()
- },
- }, nil
-}
-
-func newChannelHandler(papod papodT) func(fiinha.Message) error {
- return func(message fiinha.Message) error {
- return nil
- }
-}
-
-func buildConsumers(prefix string) []consumerT {
- return []consumerT{
- consumerT{
- topic: NEW_CHANNEL,
- name: prefix + NEW_CHANNEL,
- handlerFn: newChannelHandler,
- },
- }
-}
-
-func unregisterConsumers(
- unsubscribeFn func(string, string),
- consumers []consumerT,
-) {
- for _, c := range consumers {
- unsubscribeFn(c.topic, c.name)
- }
-}
-
-func registerConsumers(
- subscribeFn func(string, string, func(fiinha.Message) error) error,
- unsubscribeFn func(string, string),
- papod papodT,
- consumers []consumerT,
-) error {
- for _, c := range consumers {
- err := subscribeFn(c.topic, c.name, c.handlerFn(papod))
- if err != nil {
- unregisterConsumers(unsubscribeFn, consumers)
- return err
- }
- }
- return nil
-}
func initListeners(
daemonSocketPath string,
commanderSocketPath string,
) (listenersT, error) {
+ _ = os.Remove(daemonSocketPath)
+ _ = os.Remove(commanderSocketPath)
+
daemon, err := net.Listen("unix", daemonSocketPath)
if err != nil {
return listenersT{}, err
@@ -3525,202 +145,89 @@ func initListeners(
daemon: daemon,
commander: commander,
close: func() error {
- return g.SomeError(
- daemon.Close(),
- commander.Close(),
+ return g.SomeFnError(
+ daemon.Close,
+ commander.Close,
)
},
}, nil
}
-// TODO: lock is global, should be by network
-func newStateMutable() stateMutableT {
- var rwmutex sync.RWMutex
- state := stateMutableDataT{
- connections: map[uuid.UUID]*connectionT{},
- users: map[string][]uuid.UUID{},
- members: map[string]map[string][]uuid.UUID{},
- }
- return stateMutableT{
- /*
- connected: func(connection *connectionT) {
- rwmutex.Lock()
- defer rwmutex.Unlock()
- state.connections[connection.uuid] = connection
- },
- disconnect: func(connection *connectionT) {
- {
- rwmutex.Lock()
- defer rwmutex.Unlock()
- delete(state.connections, connection.uuid)
- delete(state.users, connection.user.username)
- delete(state.members, connection.user.username)
- }
- err := connection.conn.Close()
- if err != nil {
- g.Warning(
- "Failed to close the connection",
- "close-error",
- "from", "daemon",
- "err", err,
- )
- }
- },
- authenticated: func(connection *connectionT) {
- username := connection.user.username
- rwmutex.Lock()
- defer rwmutex.Unlock()
- if state.users[username] == nil {
- state.users[username] = []uuid.UUID{}
- }
- state.users[username] = append(
- state.users[username],
- connection.uuid,
- )
- },
- */
- subscribe: func(
- username string,
- channelNames []string,
- ) {
- rwmutex.Lock()
- defer rwmutex.Unlock()
- for _, channelName := range channelNames {
- if state.members[channelName] == nil {
- state.members[channelName] =
- map[string][]uuid.UUID{}
- }
- state.members[channelName][username] =
- state.users[username]
- }
- },
- /*
- members: func(channelName string) []string {
- rwmutex.RLock()
- defer rwmutex.RUnlock()
- usernames := make(
- []string,
- len(state.members[channelName]),
- )
- i := 0
- for username, _ := range state.members[channelName] {
- usernames[i] = username
- i++
- }
- return usernames
- },
- connections: func(username string) []uuid.UUID {
- rwmutex.RLock()
- defer rwmutex.RUnlock()
- connections := make(
- []uuid.UUID,
- len(state.users[username]),
- )
- copy(connections, state.users[username])
- return connections
- },
- connection: func(connectionID uuid.UUID) *connectionT {
- rwmutex.RLock()
- defer rwmutex.RUnlock()
- return state.connections[connectionID]
- },
- */
- }
-}
-
-func newState() *stateT {
- return &stateT{
+func newState() stateT {
+ return stateT{
members: pds.NewMap[string, []string](nil),
users: pds.NewMap[string, []uuid.UUID](nil),
connections: pds.NewMap[uuid.UUID, connectionT](nil),
}
}
-func buildMetrics(prefix string) metricsT {
+func buildMetrics(tag string) metricsT {
return metricsT{
activeConnections: g.MakeGauge(
"active-connection",
- "prefix", prefix,
+ "tag", tag,
),
nicksInChannel: g.MakeGauge(
"nicks-in-channel",
- "prefix", prefix,
+ "tag", tag,
),
sendToClientError: g.MakeCounter(
"send-to-client-error",
- "prefix", prefix,
+ "tag", tag,
),
receivedMessage: g.MakeCounter(
"received-message",
- "prefix", prefix,
+ "tag", tag,
),
sentReply: g.MakeCounter(
"sent-reply",
- "prefix", prefix,
+ "tag", tag,
),
}
}
-func NewWithPrefix(
- databasePath string,
- prefix string,
- daemonSocketPath string,
- commanderSocketPath string,
-) (IPapod, error) {
- queue, err := fiinha.New(databasePath)
- if err != nil {
- return papodT{}, err
- }
+func initDB(path string) (acude.AcudeI, error) {
+ return nil, nil
+}
- auth, err := cracha.New(databasePath)
+func newPapod(baseDir string, tag string) (papodT, error) {
+ databasePath := baseDir + "/papod.dedo"
+ daemonSocketPath := baseDir + "/papod.daemon.socket"
+ commanderSocketPath := baseDir + "/papod.commander.socket"
+
+ cracha, err := cracha.New(databasePath)
if err != nil {
- queue.Close()
return papodT{}, err
}
listeners, err := initListeners(daemonSocketPath, commanderSocketPath)
if err != nil {
- queue.Close()
- auth.Close()
+ cracha.Close()
return papodT{}, err
}
- queries, err := initDB(databasePath, prefix)
+ acude, err := initDB(databasePath)
if err != nil {
- queue.Close()
- auth.Close()
+ cracha.Close()
listeners.close()
return papodT{}, err
}
- consumers := buildConsumers(prefix)
- stateMutable := newStateMutable()
- state := newState()
- // receivers := makeReceivers()
- metrics := buildMetrics(prefix)
- // logger := g.NewLogger("prefix", prefix, "program", "papod")
-
return papodT{
- queue: queue,
- auth: auth,
- queries: queries,
+ acude: acude,
+ cracha: cracha,
listeners: listeners,
- consumers: consumers,
- stateMutable: stateMutable,
- state: stm.Atom(state),
- // receivers: receivers,
- metrics: metrics,
- // logger: logger,
+ state: stm.Atom(newState()),
+ metrics: buildMetrics(tag),
}, nil
}
-func New(basePath string) (IPapod, error) {
- return NewWithPrefix(
- basePath + "/papod.db",
- defaultPrefix,
- basePath + "/papod.daemon.sock",
- basePath + "/papod.commander.sock",
- )
+func NewWith(baseDir string, tag string) (PapoI, error) {
+ return newPapod(baseDir, tag)
+}
+
+func New() (PapoI, error) {
+ return NewWith(".", "papod-default")
}
func splitOnCRLF(data []byte, _atEOF bool) (int, []byte, error) {
@@ -3796,24 +303,8 @@ func parseMessage(rawMessage string) (messageT, error) {
return msg, nil
}
-func asNewEvent(msg messageT) newEventT {
- return newEventT{}
-}
-
-func joinEvent(member memberT) eventT {
- return eventT{}
-}
-
-func asMessage(event eventT) messageT {
- return messageT{}
-}
-
-func asReply(event eventT) replyT {
- return replyT{}
-}
-
-func removeConnection(state *stateT, connection *connectionT) *stateT {
- return state
+func removeConnection(state stateT, connection *connectionT) stateT {
+ return state // FIXME
}
/// Is this death by a thousand goroutines? Is the runtime able to handle the
@@ -3824,7 +315,7 @@ func removeConnection(state *stateT, connection *connectionT) *stateT {
func broadcastMessage(
message messageT,
channelName string,
- state *stateT,
+ state stateT,
) {
usernames, _ := state.members.Get(channelName)
for _, username := range usernames {
@@ -4147,8 +638,8 @@ func handleJOIN(
msg messageT,
) ([]replyT, bool, error) {
// FIXME: add to database
- channels := strings.FieldsFunc(msg.params[0], splitCommas)
- papod.stateMutable.subscribe(connection.user.username, channels)
+ // channels := strings.FieldsFunc(msg.params[0], splitCommas)
+ // papod.stateMutable.subscribe(connection.user.username, channels)
return []replyT{
_RPL_NOTOPIC (connection, msg),
@@ -4189,7 +680,6 @@ func handleAWAY(
msg messageT,
) ([]replyT, bool, error) {
replyFn := _RPL_NOWAWAY
-
if len(msg.params) == 0 {
replyFn = _RPL_UNAWAY
}
@@ -4304,6 +794,7 @@ func handleUnknown(
msg messageT,
) ([]replyT, bool, error) {
// FIXME: user doesn't exist when unauthenticated
+ /*
err := papod.queries.logMessage(userT{ }, msg)
if err != nil {
g.Warning(
@@ -4314,6 +805,7 @@ func handleUnknown(
"err", err,
)
}
+ */
return []replyT{
_ERR_UNKNOWNCOMMAND(connection, msg),
@@ -4422,7 +914,7 @@ func processMessage(
)
}
- stm.Swap(papod.state, func(state *stateT) *stateT {
+ stm.Swap(papod.state, func(state stateT) stateT {
return removeConnection(state, connection)
})
err := connection.conn.Close()
@@ -4439,10 +931,13 @@ func processMessage(
}
if shouldClose {
+ // FIXME
// papod.stateMutable.disconnect(connection)
}
}
+func processTasks() // FIXME
+
func handleConnection(papod papodT, conn net.Conn) {
connection := connectionT{
uuid: uuid.New(),
@@ -4509,27 +1004,17 @@ func mkbgrun() (func(func()), func()) {
}
func (papod papodT) Start() error {
- err := registerConsumers(
- papod.queue.Subscribe,
- papod.queue.Unsubscribe,
- papod,
- papod.consumers,
- )
- if err != nil {
- return err
- }
-
- // FIXME: papod.logger.Info
g.Info("Starting service", "lifecycle-event",
"event", "starting-server",
slog.Group(
"versions",
- "gobang", g.Version,
"cracha", cracha.Version,
- "fiinha", fiinha.Version,
- "golite", golite.Version,
- "uuid", uuid.Version,
+ "uuid", uuid.Version,
+ "pds", pds.Version,
+ "stm", stm.Version,
"papod", Version,
+ "gobang", g.Version,
+ "gotext", gt.Version,
),
)
@@ -4543,7 +1028,7 @@ func (papod papodT) Start() error {
func (papod papodT) Close() error {
// FIXME: does this wait for current handlers to wait? Well, it should.
- unregisterConsumers(papod.queue.Unsubscribe, papod.consumers)
+ /*
return g.WrapErrors(
papod.listeners.close(),
// papod.connCloser.closeAll(),
@@ -4551,36 +1036,76 @@ func (papod papodT) Close() error {
papod.queue.Close(),
papod.queries.close(),
)
+ */
+ return nil
}
-func basePathFrom(args []string) (string, error) {
- if len(args) < 2 {
- return os.Getwd()
- } else {
- return args[1], nil
- }
+func usage(argv0 string, w io.Writer) {
+ fmt.Fprintf(
+ w,
+ gt.Gettext("Usage: %s [-t TAG] [BASEDIR]"),
+ argv0,
+ )
}
+func getopt(allArgs []string, w io.Writer) (argsT, int) {
+ argv0 := allArgs[0]
+ argv := allArgs[1:]
+ fs := flag.NewFlagSet("", flag.ContinueOnError)
+ fs.Usage = func() {}
+ fs.SetOutput(w)
+ tag := fs.String(
+ "t",
+ "",
+ "The specific instance tag for inclusion in the log",
+ )
+ if fs.Parse(argv) != nil {
+ usage(argv0, w)
+ return argsT{}, 2
+ }
-func Main() {
- g.Init("program", "papod")
-
- basePath, err := basePathFrom(os.Args)
- if err != nil {
- fmt.Printf("%v\n", err)
- os.Exit(1)
+ subArgs := fs.Args()
+ baseDir := "."
+ if len(subArgs) != 0 {
+ baseDir = subArgs[0]
}
- ipapod, err := New(basePath)
+ return argsT{
+ allArgs: allArgs,
+ subArgs: subArgs,
+ baseDir: baseDir,
+ tag: *tag,
+ }, 0
+}
+
+func run(env envT) int {
+ papod, err := newPapod(env.args.baseDir, env.args.tag)
if err != nil {
- fmt.Printf("Failed to create papod: %v\n", err)
- os.Exit(1)
+ fmt.Fprintln(env.err, err)
+ return 1
}
- err = ipapod.Start()
+ err = papod.Start()
if err != nil {
- fmt.Printf(gt.Gettext("Failed to start: %v\n"), err)
- os.Exit(1)
+ fmt.Fprintln(env.err, err)
+ return 1
}
+
+ return 0
+}
+
+
+
+func Main() {
+ g.Init()
+ gt.Init(Name, LOCALEDIR)
+ args, rc := getopt(os.Args, os.Stderr)
+ g.ExitIf(rc)
+ os.Exit(run(envT{
+ args: args,
+ in: os.Stdin,
+ out: os.Stdout,
+ err: os.Stderr,
+ }))
}
diff --git a/tests/papod.go b/tests/papod.go
index 3e82d82..3f0e557 100644
--- a/tests/papod.go
+++ b/tests/papod.go
@@ -2,5304 +2,14 @@ package papod
import (
"bufio"
- "crypto/rand"
- "database/sql"
"errors"
- "fmt"
- "io"
- "os"
- "reflect"
"strings"
- "time"
- "golite"
- "uuid"
g "gobang"
)
-type userChangeT struct{
- id int64
- timestamp time.Time
- user_id int64
- attribute string
- valueStr *string
- valueBlob *uuid.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(uuid.UUID) []userChangeT {
- q := userChangesSQL(prefix)
-
- return func(userID uuid.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 := uuid.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(uuid.UUID) []networkChangeT {
- q := networkChangesSQL(prefix)
-
- return func(networkID uuid.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)
- g.TErrorIf(err)
- return string(buffer)
-}
-
-func mkstring() string {
- return mknstring(32)
-}
-
-
-func test_defaultPrefix() {
- g.TestStart("defaultPrefix")
-
- g.Testing("the defaultPrefix is valid", func() {
- g.TErrorIf(g.ValidateSQLTablePrefix(defaultPrefix))
- })
-}
-
-func test_tryRollback() {
- g.TestStart("tryRollback()")
-
- myErr := errors.New("bottom error")
-
- db, err := sql.Open(golite.DriverName, golite.InMemory)
- g.TErrorIf(err)
- defer db.Close()
-
-
- g.Testing("the error is propagated if rollback doesn't fail", func() {
- tx, err := db.Begin()
- g.TErrorIf(err)
-
- err = tryRollback(tx, myErr)
- g.TAssertEqual(err, myErr)
- })
-
- g.Testing("a wrapped error when rollback fails", func() {
- tx, err := db.Begin()
- g.TErrorIf(err)
-
- err = tx.Commit()
- g.TErrorIf(err)
-
- err = tryRollback(tx, myErr)
- g.TAssertEqual(reflect.DeepEqual(err, myErr), false)
- g.TAssertEqual(errors.Is(err, myErr), true)
- })
-}
-
-func test_inTx() {
- g.TestStart("inTx()")
-
- db, err := sql.Open(golite.DriverName, golite.InMemory)
- g.TErrorIf(err)
- defer db.Close()
-
-
- g.Testing("when fn() errors, we propagate it", func() {
- myErr := errors.New("to be propagated")
- err := inTx(db, func(tx *sql.Tx) error {
- return myErr
- })
- g.TAssertEqual(err, myErr)
- })
-
- g.Testing("on nil error we get nil", func() {
- err := inTx(db, func(tx *sql.Tx) error {
- return nil
- })
- g.TErrorIf(err)
- })
-}
-
-func test_createTables() {
- g.TestStart("createTables()")
-
- db, err := sql.Open(golite.DriverName, golite.InMemory)
- g.TErrorIf(err)
- defer db.Close()
-
-
- g.Testing("tables exist afterwards", func() {
- const tmpl_read = `
- SELECT id FROM "%s_channel_events" LIMIT 1;
- `
- qRead := fmt.Sprintf(tmpl_read, defaultPrefix)
-
- _, err := db.Exec(qRead)
- g.TErrorNil(err)
-
- err = createTables(db, defaultPrefix)
- g.TErrorIf(err)
-
- _, err = db.Exec(qRead)
- g.TErrorIf(err)
- })
-
- g.Testing("we can do it multiple times", func() {
- g.TErrorIf(g.SomeError(
- createTables(db, defaultPrefix),
- createTables(db, defaultPrefix),
- createTables(db, defaultPrefix),
- ))
- })
-}
-
-func test_createUserStmt() {
- g.TestStart("createUserStmt()")
-
- 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)
- g.TErrorIf(createUserErr)
- defer g.SomeFnError(
- createUserClose,
- db.Close,
- )
-
- userChanges := makeUserChanges(db, prefix)
-
-
- g.Testing("userID's must be unique", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- _, err1 := createUser(newUser)
- _, err2 := createUser(newUser)
- g.TErrorIf(err1)
- g.TAssertEqual(
- err2.(golite.Error).ExtendedCode,
- golite.ErrConstraintUnique,
- )
- })
-
- g.Testing("a new user starts without a pictureID", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
- g.TAssertEqual(user.pictureID == nil, true)
- })
-
- g.Testing("the database fills default and generated values", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- username: "the username",
- displayName: "the display name",
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- g.TAssertEqual(user.id == 0, false)
- g.TAssertEqual(user.timestamp == time.Time{}, false)
- g.TAssertEqual(user.uuid, newUser.uuid)
- g.TAssertEqual(user.username, "the username")
- g.TAssertEqual(user.displayName, "the display name")
- })
-
- g.Testing("users can have duplicate names and usernames", func() {
- username := mkstring()
- displayName := mkstring()
-
- newUser1 := newUserT{
- uuid: uuid.New(),
- username: username,
- displayName: displayName,
- }
-
- newUser2 := newUserT{
- uuid: uuid.New(),
- username: username,
- displayName: displayName,
- }
-
- _, err1 := createUser(newUser1)
- _, err2 := createUser(newUser2)
- g.TErrorIf(err1)
- g.TErrorIf(err2)
- })
-
- g.Testing("new user trigger inserts into *_user_changes", func() {
- newUser := newUserT{
- uuid: uuid.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(),
- createUserClose(),
- createUserClose(),
- ))
- })
-}
-
-func test_userByUUIDStmt() {
- g.TestStart("userByUUIDStmt()")
-
- 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)
- userByUUID, userByUUIDClose, userByUUIDErr := userByUUIDStmt(cfg)
- deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
- g.TErrorIf(g.SomeError(
- createUserErr,
- deleteUserErr,
- userByUUIDErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- deleteUserClose,
- userByUUIDClose,
- db.Close,
- )
-
-
- g.Testing("when a user doesn't exist, we get sql.ErrNoRows", func() {
- _, err := userByUUID(uuid.New())
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("after creating, we can retrieve the user", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- username: mkstring(),
- displayName: mkstring(),
- }
-
- user1, err := createUser(newUser)
- g.TErrorIf(err)
-
- user2, err := userByUUID(newUser.uuid)
- g.TErrorIf(err)
-
- g.TAssertEqual(user2.uuid, newUser.uuid)
- g.TAssertEqual(user2.username, newUser.username)
- g.TAssertEqual(user2.displayName, newUser.displayName)
- g.TAssertEqual(user2.pictureID == nil, true)
-
- g.TAssertEqual(user2, user1)
- })
-
- g.Testing("we can't retrieve a user once deleted",func() {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- _, err := createUser(newUser)
- g.TErrorIf(err)
-
- _, err = userByUUID(newUser.uuid)
- g.TErrorIf(err)
-
- err = deleteUser(newUser.uuid)
- g.TErrorIf(err)
-
- _, err = userByUUID(newUser.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- userByUUIDClose(),
- userByUUIDClose(),
- userByUUIDClose(),
- ))
- })
-}
-
-func test_updateUserStmt() {
- g.TestStart("updateUserStmt()")
-
- 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)
- userByUUID, userByUUIDClose, userByUUIDErr := userByUUIDStmt(cfg)
- updateUser, updateUserClose, updateUserErr := updateUserStmt(cfg)
- deleteUser, deleteUserClose, deleteUserErr := deleteUserStmt(cfg)
- g.TErrorIf(g.SomeError(
- createUserErr,
- userByUUIDErr,
- updateUserErr,
- deleteUserErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- userByUUIDClose,
- updateUserClose,
- deleteUserClose,
- db.Close,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- username: mkstring(),
- displayName: mkstring(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- userChanges := makeUserChanges(db, prefix)
-
-
- g.Testing("a user needs to exist to be updated", func() {
- virtualUser := userT{
- id: 1234,
- }
-
- g.TAssertEqual(updateUser(virtualUser), sql.ErrNoRows)
- })
-
- g.Testing("after updating, fetching gives us the newer data", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- username: "first username",
- displayName: "first display name",
- }
-
- user1, err := createUser(newUser)
- g.TErrorIf(err)
-
- g.TAssertEqual(user1.username, newUser.username)
- g.TAssertEqual(user1.displayName, newUser.displayName)
-
-
- user2 := user1
- user2.username = "second username"
- user2.displayName = "second display name"
-
- err = updateUser(user2)
- g.TErrorIf(err)
-
- g.TAssertEqual(user2.id, user1.id)
- g.TAssertEqual(user2.timestamp, user1.timestamp)
- g.TAssertEqual(user2.uuid, user1.uuid)
-
-
- user3, err := userByUUID(user2.uuid)
- g.TErrorIf(err)
-
- g.TAssertEqual(user3, user2)
- g.TAssertEqual(user3.username, "second username")
- g.TAssertEqual(user3.displayName, "second display name")
- })
-
- g.Testing("can't update a deleted user", func() {
- user := create()
-
- err = deleteUser(user.uuid)
- g.TErrorIf(err)
-
- err = updateUser(user)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("we can add (and remove) a picture to the user", func() {
- user1 := create()
- g.TAssertEqual(user1.pictureID == nil, true)
-
- pictureID := uuid.New()
- user2 := user1
- user2.pictureID = &pictureID
- err = updateUser(user2)
- g.TErrorIf(err)
- g.TAssertEqual(user2.pictureID == nil, false)
-
- user3, err := userByUUID(user1.uuid)
- g.TErrorIf(err)
- g.TAssertEqual(user3.pictureID == nil, false)
- g.TAssertEqual(user3, user2)
-
- user4 := user3
- user4.pictureID = nil
- err = updateUser(user4)
- g.TErrorIf(err)
-
- user5, err := userByUUID(user1.uuid)
- g.TErrorIf(err)
- g.TAssertEqual(user5.pictureID == nil, true)
- g.TAssertEqual(user5, user4)
- })
-
- g.Testing("we can't update the timestamp or uuid", func() {
- user1 := create()
-
- user2 := user1
- user2.timestamp = user2.timestamp.Add(time.Minute * 1)
- user2.uuid = uuid.New()
- err := updateUser(user2)
- g.TErrorIf(err)
-
- user3, err := userByUUID(user1.uuid)
- g.TErrorIf(err)
- 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: uuid.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: uuid.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: uuid.New(),
- username: "username",
- displayName: "first display name",
- }
- pictureID := uuid.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(),
- updateUserClose(),
- updateUserClose(),
- ))
- })
-}
-
-func test_deleteUserStmt() {
- g.TestStart("deleteUserStmt()")
-
- 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)
- g.TErrorIf(g.SomeError(
- createUserErr,
- deleteUserErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- deleteUserClose,
- db.Close,
- )
-
- userChanges := makeUserChanges(db, prefix)
-
-
- g.Testing("a user needs to exist to be deleted", func() {
- err := deleteUser(uuid.New())
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("error if deleted more than once", func() {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- _, err := createUser(newUser)
- g.TErrorIf(err)
-
- err1 := deleteUser(newUser.uuid)
- err2 := deleteUser(newUser.uuid)
- g.TErrorIf(err1)
- g.TAssertEqual(err2, sql.ErrNoRows)
- })
-
- g.Testing("deletion triggers insertion into *_user_changes", func() {
- newUser := newUserT{
- uuid: uuid.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(),
- deleteUserClose(),
- deleteUserClose(),
- ))
- })
-}
-
-func test_addNetworkStmt() {
- g.TestStart("addNetworkStmt()")
-
- 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)
- 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 {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- allMembers := func(actor memberT, networkID uuid.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 network", func() {
- creator := create()
-
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- name: "the network name",
- description: "the network description",
- type_: NetworkType_Unlisted,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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.name, "the network name")
- g.TAssertEqual(network.description, "the network description")
- g.TAssertEqual(network.type_, NetworkType_Unlisted)
- })
-
- g.Testing("the creator needs to exist", func() {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Unlisted,
- }
-
- virtualUser := userT{
- id: 1234,
- }
-
- _, err := addNetwork(virtualUser, newNetwork, uuid.New())
- g.TAssertEqual(
- err.(golite.Error).ExtendedCode,
- golite.ErrConstraintNotNull,
- )
- })
-
- g.Testing("we can't add the same network twice", func() {
- creator := create()
-
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- name: mkstring(),
- type_: NetworkType_Unlisted,
- }
-
- _, err1 := addNetwork(creator, newNetwork, uuid.New())
- _, err2 := addNetwork(creator, newNetwork, uuid.New())
- g.TErrorIf(err1)
- g.TAssertEqual(
- err2.(golite.Error).ExtendedCode,
- golite.ErrConstraintUnique,
- )
- })
-
- g.Testing("a user can create multiple networks", func() {
- creator := create()
-
- newNetwork1 := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Unlisted,
- }
- newNetwork2 := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Unlisted,
- }
-
- _, err1 := addNetwork(creator, newNetwork1, uuid.New())
- _, err2 := addNetwork(creator, newNetwork2, uuid.New())
- g.TErrorIf(err1)
- g.TErrorIf(err2)
- })
-
- g.Testing("a deleted user can't create a network", func() {
- creator := create()
-
- newNetwork1 := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Unlisted,
- }
- newNetwork2 := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Unlisted,
- }
-
- _, err := addNetwork(creator, newNetwork1, uuid.New())
- g.TErrorIf(err)
-
- err = deleteUser(creator.uuid)
- g.TErrorIf(err)
- _, err = addNetwork(creator, newNetwork2, uuid.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: uuid.New(),
- name: "the network name",
- description: "the network description",
- type_: NetworkType_Unlisted,
- }
-
- _, err := addNetwork(creator, newNetwork, uuid.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 := uuid.New()
-
- newNetwork := newNetworkT{
- uuid: uuid.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 := uuid.New()
-
- newNetwork := newNetworkT{
- uuid: uuid.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(),
- addNetworkClose(),
- addNetworkClose(),
- ))
- })
-}
-
-func test_getNetworkStmt() {
- g.TestStart("getNetworkStmt()")
-
- 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)
- getNetwork, getNetworkClose, getNetworkErr := getNetworkStmt(cfg)
- membership, membershipClose, membershipErr := membershipStmt(cfg)
- addMember, addMemberClose, addMemberErr := addMemberStmt(cfg)
- dropMember, dropMemberClose, dropMemberErr := dropMemberStmt(cfg)
- g.TErrorIf(g.SomeError(
- createUserErr,
- deleteUserErr,
- addNetworkErr,
- getNetworkErr,
- membershipErr,
- addMemberErr,
- dropMemberErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- deleteUserClose,
- addNetworkClose,
- getNetworkClose,
- membershipClose,
- addMemberClose,
- dropMemberClose,
- db.Close,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT, type_ NetworkType) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: type_,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- 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)
-
- network2, err := getNetwork(creator, network1.uuid)
- g.TErrorIf(err)
- g.TAssertEqual(network2, network1)
- })
-
- g.Testing("a network needs to exist", func() {
- _, err := getNetwork(create(), uuid.New())
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("the probing member needs to exist", func() {
- creator := create()
- network, _ := add(creator, NetworkType_Public)
-
- virtualUser := userT{
- id: 1234,
- }
-
- _, err := getNetwork(creator, network.uuid)
- g.TErrorIf(err)
-
- _, err = getNetwork(virtualUser, network.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("the probing user can see any public network", func() {
- creator := create()
- user := create()
- network, _ := add(creator, NetworkType_Public)
-
- network1, err1 := getNetwork(creator, network.uuid)
- network2, err2 := getNetwork(user, network.uuid)
- g.TErrorIf(err1)
- g.TErrorIf(err2)
- g.TAssertEqual(network1, network)
- g.TAssertEqual(network2, network)
- })
-
- g.Testing("the probing user sees the given unlisted network", func() {
- creator := create()
- user := create()
- network, _ := add(creator, NetworkType_Unlisted)
-
- network1, err1 := getNetwork(creator, network.uuid)
- network2, err2 := getNetwork(user, network.uuid)
- g.TErrorIf(err1)
- g.TErrorIf(err2)
- g.TAssertEqual(network1, network)
- g.TAssertEqual(network2, network)
- })
-
- g.Testing("the probing user can't see a private network", func() {
- creator := create()
- user := create()
- network, _ := add(creator, NetworkType_Private)
-
- _, err1 := getNetwork(creator, network.uuid)
- _, err2 := getNetwork(user, network.uuid)
- g.TErrorIf(err1)
- g.TAssertEqual(err2, sql.ErrNoRows)
- })
-
- g.Testing("the probing user must be a member to see it", func() {
- creator := create()
- user := create()
- network, member := add(creator, NetworkType_Private)
- newMember := newMemberT{
- userID: user.uuid,
- memberID: uuid.New(),
- username: mkstring(),
- }
-
- _, err := addMember(member, newMember)
- g.TErrorIf(err)
-
- network1, err1 := getNetwork(creator, network.uuid)
- network2, err2 := getNetwork(user, network.uuid)
- g.TErrorIf(err1)
- g.TErrorIf(err2)
- g.TAssertEqual(network1, network)
- g.TAssertEqual(network2, network)
- })
-
- g.Testing("we can get the network if the creator was deleted", func() {
- creator := create()
- user := create()
- network, member := add(creator, NetworkType_Public)
- newMember := newMemberT{
- userID: user.uuid,
- memberID: uuid.New(),
- username: mkstring(),
- }
-
- _, err := addMember(member, newMember)
- g.TErrorIf(err)
-
- network1, err := getNetwork(creator, network.uuid)
- g.TErrorIf(err)
-
- err = deleteUser(creator.uuid)
- g.TErrorIf(err)
-
- 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)
-
- _, err := getNetwork(creator, network.uuid)
- g.TErrorIf(err)
-
- err = deleteUser(creator.uuid)
- g.TErrorIf(err)
-
- _, err = getNetwork(creator, network.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("a deleted user can't get a public network", func() {
- user := create()
- network, _ := add(create(), NetworkType_Public)
-
- _, err := getNetwork(user, network.uuid)
- g.TErrorIf(err)
-
- err = deleteUser(user.uuid)
- g.TErrorIf(err)
-
- _, err = getNetwork(user, network.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("a deleted member can't get a private network", func() {
- creator := create()
- user := create()
- network, member := add(creator, NetworkType_Private)
- newMember := newMemberT{
- userID: user.uuid,
- memberID: uuid.New(),
- username: mkstring(),
- }
-
- _, err := addMember(member, newMember)
- g.TErrorIf(err)
-
- _, err = getNetwork(user, network.uuid)
- g.TErrorIf(err)
-
- 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()
- user := create()
- network, member := add(creator, NetworkType_Private)
- newMember := newMemberT{
- userID: user.uuid,
- memberID: uuid.New(),
- username: mkstring(),
- }
-
- _, err := getNetwork(user, network.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
-
- _, err = addMember(member, newMember)
- g.TErrorIf(err)
-
- _, err = getNetwork(user, network.uuid)
- g.TErrorIf(err)
-
- err = dropMember(member, newMember.memberID)
- g.TErrorIf(err)
-
- _, err = getNetwork(user, network.uuid)
- g.TAssertEqual(err, sql.ErrNoRows)
- })
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- getNetworkClose(),
- getNetworkClose(),
- getNetworkClose(),
- ))
- })
-}
-
-func test_networkEach() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) uuid.UUID {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- _, err := addNetwork(user, newNetwork, uuid.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 := []uuid.UUID{
- add(creator),
- add(creator),
- add(creator),
- }
-
- rows, err := networks(creator)
- g.TErrorIf(err)
- defer rows.Close()
-
- var collectedIDs[]uuid.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() {
- g.TestStart("networksStmt()")
-
- 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)
- networks, networksClose, networksErr := networksStmt(cfg)
- g.TErrorIf(g.SomeError(
- createUserErr,
- deleteUserErr,
- addNetworkErr,
- networksErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- deleteUserClose,
- addNetworkClose,
- networksClose,
- db.Close,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT, type_ NetworkType) networkT {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: type_,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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
- })
- if err != nil {
- return nil, err
- }
-
- return networkList, nil
- }
-
-
- 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() {
- 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() {
- 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() {
- 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("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() {
- g.TErrorIf(g.SomeError(
- networksClose(),
- networksClose(),
- networksClose(),
- ))
- })
-}
-
-func test_setNetworkStmt() {
- g.TestStart("setNetworkStmt()")
-
- 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)
- 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,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
- memberID := uuid.New()
-
- network, err := addNetwork(user, newNetwork, memberID)
- g.TErrorIf(err)
-
- member, err := membership(user, network)
- g.TErrorIf(err)
-
- return network, member
- }
-
- networkChanges := makeNetworkChanges(db, prefix)
-
-
- g.Testing("creator can change the network", func() {
- creator := create()
- network1, member := add(creator)
-
- name := network1.name + "name suffix"
- network1.name = name
- err := setNetwork(member, network1)
- g.TErrorIf(err)
-
- network2, err := getNetwork(creator, network1.uuid)
- g.TErrorIf(err)
- g.TAssertEqual(network2.name, name)
- })
-
- g.Testing(`"network-settings-update" can change the network`, func() {
- creator := create()
- admin := create()
- network, creatorMember := add(creator)
- newMember := newMemberT{
- userID: admin.uuid,
- memberID: uuid.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(`"admin" can change the network`, func() {
- creator := create()
- admin := create()
- network, creatorMember := add(creator)
- newMember := newMemberT{
- userID: admin.uuid,
- memberID: uuid.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() {
- 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: uuid.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()
- user := create()
- network, creatorMember := add(creator)
- newMember := newMemberT{
- userID: user.uuid,
- memberID: uuid.New(),
- username: mkstring(),
- }
-
- userMember, err := addMember(creatorMember, newMember)
- g.TErrorIf(err)
-
- network.name = "member can't set the name"
- 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, member := add(creator)
-
- network2 := network1
- network2.name = "first network name"
- network2.description = "first network description"
- network2.type_ = NetworkType_Private
-
- err := setNetwork(member, network2)
- g.TErrorIf(err)
-
- network3, err := getNetwork(creator, network1.uuid)
- g.TErrorIf(err)
- g.TAssertEqual(network3, network2)
- })
-
- g.Testing("the uuid, timestamp or creator never changes", func() {
- creator := create()
- network1, member := add(creator)
-
- network2 := network1
- network2.uuid = uuid.New()
- network2.timestamp = time.Time{}
-
- 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: uuid.New(),
- name: "first name",
- description: "first description",
- type_: NetworkType_Public,
- }
- memberID := uuid.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() {
- g.TErrorIf(g.SomeError(
- setNetworkClose(),
- setNetworkClose(),
- setNetworkClose(),
- ))
- })
-}
-
-func test_nipNetworkStmt() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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: uuid.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: uuid.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: uuid.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(
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) networkT {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Private,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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: uuid.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: uuid.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() {
- g.TestStart("addMemberStmt()")
-
- 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)
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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: uuid.New(),
- memberID: uuid.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: uuid.New(),
- username: mkstring(),
- }
- newMember2 := newMemberT{
- userID: user2.uuid,
- memberID: uuid.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: uuid.New(),
- username: mkstring(),
- }
- newMember2 := newMemberT{
- userID: user2.uuid,
- memberID: uuid.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: uuid.New(),
- username: mkstring(),
- }
- newMember2 := newMemberT{
- userID: user2.uuid,
- memberID: uuid.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: uuid.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 := uuid.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: uuid.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: uuid.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(),
- addMemberClose(),
- addMemberClose(),
- ))
- })
-}
-
-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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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(
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func(user userT) (networkT, memberT) {
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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, uuid.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() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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: uuid.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 := []uuid.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 := []uuid.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 := []uuid.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() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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: uuid.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(
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- user := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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(
- editMemberClose(),
- editMemberClose(),
- editMemberClose(),
- ))
- })
-}
-
-func test_dropMemberStmt() {
- // FIXME
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- // FIXME
- ))
- })
-}
-
-func test_addChannelStmt() {
- g.TestStart("addChannelStmt()")
-
- 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)
- g.TErrorIf(g.SomeError(
- createUserErr,
- addNetworkErr,
- membershipErr,
- addChannelErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- addNetworkClose,
- membershipClose,
- addChannelClose,
- db.Close,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- member, err := membership(creator, network)
- g.TErrorIf(err)
-
- return member
- }
-
- channelChanges := makeChannelChanges(db, prefix)
-
-
- g.Testing("the new channel has the data it was given", func() {
- member := add()
- publicName := "a-name"
- newChannel := newChannelT{
- uuid: uuid.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: uuid.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(
- addChannelClose(),
- addChannelClose(),
- addChannelClose(),
- ))
- })
-}
-
-func test_channelEach() {
- 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: uuid.New(),
- }
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- network, err := addNetwork(user, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- member, err := membership(user, network)
- g.TErrorIf(err)
-
- return member
- }
-
- addC := func(member memberT) channelT {
- publicName := mkstring()
- newChannel := newChannelT{
- uuid: uuid.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() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- user := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(user, newNetwork, uuid.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: uuid.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: uuid.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(
- channelsClose(),
- channelsClose(),
- channelsClose(),
- ))
- })
-}
-
-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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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: uuid.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: uuid.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(
- setChannelClose(),
- setChannelClose(),
- setChannelClose(),
- ))
- })
-}
-
-func test_endChannelStmt() {
- // FIXME
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- // FIXME
- ))
- })
-}
-
-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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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: uuid.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: uuid.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(), uuid.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(
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.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: uuid.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: uuid.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(
- partClose(),
- partClose(),
- partClose(),
- ))
- })
-}
-
-func test_nameEach() {
- // FIXME
-}
-
-func test_namesStmt() {
- // FIXME
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- // FIXME
- ))
- })
-}
-
-func test_addEventStmt() {
- g.TestStart("addEventStmt()")
-
- 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)
- g.TErrorIf(g.SomeError(
- createUserErr,
- addNetworkErr,
- membershipErr,
- addChannelErr,
- addEventErr,
- ))
- defer g.SomeFnError(
- createUserClose,
- addNetworkClose,
- membershipClose,
- addChannelClose,
- addEventClose,
- db.Close,
- )
-
- create := func() userT {
- newUser := newUserT{
- uuid: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- member, err := membership(creator, network)
- g.TErrorIf(err)
-
- return member
- }
-
- addC := func(actor memberT) channelT {
- publicName := mkstring()
- newChannel := newChannelT{
- uuid: uuid.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: uuid.New(),
- channelID: channel.uuid,
- source: sourceT{
- uuid: creator.uuid,
- type_: SourceType_Logon,
- },
- type_: EventType_UserMessage,
- payload: "the-payload",
- }
-
- event, err := addEvent(newEvent)
- g.TErrorIf(err)
-
- g.TAssertEqual(event.id == 0, false)
- g.TAssertEqual(event.timestamp == time.Time{}, false)
- 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("eventID's must be unique", func() {
- creator := add()
- channel := addC(creator)
- newEvent := newEventT{
- eventID: uuid.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("multiple messages can have the same source", func() {
- source := sourceT{
- uuid: uuid.New(),
- type_: SourceType_Logon,
- }
-
- newEvent1 := newEventT{
- eventID: uuid.New(),
- channelID: addC(add()).uuid,
- source: source,
- type_: EventType_UserMessage,
- }
- newEvent2 := newEventT{
- eventID: uuid.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: uuid.New(),
- channelID: addC(add()).uuid,
- source: sourceT{
- uuid: uuid.New(),
- type_: SourceType_Logon,
- },
- type_: type_,
- payload: payload,
- }
- newEvent2 := newEventT{
- eventID: uuid.New(),
- channelID: addC(add()).uuid,
- source: sourceT{
- uuid: uuid.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() {
- g.TErrorIf(g.SomeError(
- addEventClose(),
- addEventClose(),
- addEventClose(),
- ))
- })
-}
-
-func test_eventEach() {
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- member, err := membership(creator, network)
- g.TErrorIf(err)
-
- return member
- }
-
- addC := func(actor memberT) channelT {
- publicName := mkstring()
- newChannel := newChannelT{
- uuid: uuid.New(),
- publicName: &publicName,
- label: mkstring(),
- description: mkstring(),
- virtual: false,
- }
-
- channel, err := addChannel(actor, newChannel)
- g.TErrorIf(err)
-
- return channel
- }
-
- eventCount := 0
- addE := func(channelID uuid.UUID) eventT {
- eventCount++
- newEvent := newEventT{
- // FIXME: missing eventID?
- channelID: channelID,
- source: sourceT{
- uuid: uuid.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 := uuid.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 := uuid.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 := uuid.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("allAfterStmt()")
-
- 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: uuid.New(),
- }
-
- user, err := createUser(newUser)
- g.TErrorIf(err)
-
- return user
- }
-
- add := func() memberT {
- creator := create()
- newNetwork := newNetworkT{
- uuid: uuid.New(),
- type_: NetworkType_Public,
- }
-
- network, err := addNetwork(creator, newNetwork, uuid.New())
- g.TErrorIf(err)
-
- member, err := membership(creator, network)
- g.TErrorIf(err)
-
- return member
- }
-
- addC := func(actor memberT) channelT {
- publicName := mkstring()
- newChannel := newChannelT{
- uuid: uuid.New(),
- publicName: &publicName,
- label: mkstring(),
- description: mkstring(),
- virtual: false,
- }
-
- channel, err := addChannel(actor, newChannel)
- g.TErrorIf(err)
-
- return channel
- }
-
- eventCount := 0
- addE := func(channelID uuid.UUID) eventT {
- eventCount++
- newEvent := newEventT{
- eventID: uuid.New(),
- channelID: channelID,
- source: sourceT{
- uuid: uuid.New(),
- type_: SourceType_Logon,
- },
- type_: EventType_UserMessage,
- payload: fmt.Sprintf("event %s", eventCount),
- }
-
- event, err := addEvent(newEvent)
- g.TErrorIf(err)
-
- return event
- }
-
- // FIXME
- allEvents := func(eventID uuid.UUID) []eventT {
- rows, err := allAfter(memberT{}, 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)
-
- return events
- }
-
-
- g.Testing("after joining the channel, there are no events", func() {
- return // FIXME
-
- eventID := uuid.New()
- member := add()
- channel := addC(member)
-
- expected := []eventT{
- // FIXME: missing "user-join" event
- addE(channel.uuid),
- addE(channel.uuid),
- addE(channel.uuid),
- }
-
- given := allEvents(eventID)
- g.TAssertEqual(given, expected)
- })
-
- g.Testing("we don't get events from other channels", func() {
- return // FIXME
-
- eventID := uuid.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 := uuid.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() {
- g.TErrorIf(g.SomeError(
- allAfterClose(),
- allAfterClose(),
- allAfterClose(),
- ))
- })
-}
-
-func test_logMessageStmt() {
- g.TestStart("logMessageStmt()")
-
- // FIXME
-
- g.Testing("no error if closed more than once", func() {
- g.TErrorIf(g.SomeError(
- // FIXME
- ))
- })
-}
-
-func test_initDB() {
- 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() {
- 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() {
g.TestStart("splitOnCRLF()")
@@ -5661,93 +371,9 @@ func test_addTrailingSeparator() {
}
-func dumpQueries() {
- queries := []struct{name string; fn func(string) queryT}{
- { "createTables", createTablesSQL },
- { "memberRoles", memberRolesSQL },
- { "createUser", createUserSQL },
- { "userByUUID", userByUUIDSQL },
- { "updateUser", updateUserSQL },
- { "deleteUser", deleteUserSQL },
- { "addNetwork", addNetworkSQL },
- { "getNetwork", getNetworkSQL },
- { "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 },
- { "setChannel", setChannelSQL },
- { "endChannel", endChannelSQL },
- { "join", joinSQL },
- { "part", partSQL },
- { "names", namesSQL },
- { "addEvent", addEventSQL },
- { "allAfter", allAfterSQL },
- { "logMessage", logMessageSQL },
- }
- for _, query := range queries {
- q := query.fn(defaultPrefix)
- fmt.Printf("\n-- %s.sql:", query.name)
- fmt.Printf("\n-- write:%s\n", q.write)
- fmt.Printf("\n-- read:%s\n", q.read)
- }
-}
-
-
func MainTest() {
- if os.Getenv("TESTING_DUMP_SQL_QUERIES") != "" {
- dumpQueries()
- return
- }
-
g.Init()
- test_defaultPrefix()
- test_tryRollback()
- test_inTx()
- test_createTables()
- test_createUserStmt()
- test_userByUUIDStmt()
- test_updateUserStmt()
- test_deleteUserStmt()
- test_addNetworkStmt()
- test_getNetworkStmt()
- test_networkEach()
- test_networksStmt()
- test_setNetworkStmt()
- test_nipNetworkStmt()
- test_membershipStmt()
- test_addMemberStmt()
- test_addRoleStmt()
- test_dropRoleStmt()
- test_showMemberStmt()
- test_memberEach()
- test_membersStmt()
- test_editMemberStmt()
- test_dropMemberStmt()
- test_addChannelStmt()
- test_channelEach()
- test_channelsStmt()
- test_setChannelStmt()
- test_endChannelStmt()
- test_joinStmt()
- test_partStmt()
- test_nameEach()
- test_namesStmt()
- test_addEventStmt()
- test_eventEach()
- test_allAfterStmt()
- test_logMessageStmt()
- test_initDB()
- test_queriesTclose()
test_splitOnCRLF()
test_splitOnRawMessage()
test_parseMessageParams()