From 3fa042167eb9f2c0113f341bde57903265d25686 Mon Sep 17 00:00:00 2001 From: Eggbertx Date: Sun, 28 Aug 2022 19:26:03 -0700 Subject: [PATCH] Add sqlmock driver and schema initialization --- go.mod | 1 + go.sum | 2 + initdb_master.sql | 2 +- initdb_mysql.sql | 2 +- initdb_postgres.sql | 2 +- pkg/config/config.go | 15 +- pkg/gcsql/connect.go | 4 +- pkg/gcsql/initdb_test.go | 610 +++++++++++++++++++++++++++++++++++++++ pkg/gcsql/posts_test.go | 12 + pkg/gcsql/util.go | 11 + 10 files changed, 653 insertions(+), 8 deletions(-) create mode 100644 pkg/gcsql/initdb_test.go create mode 100644 pkg/gcsql/posts_test.go diff --git a/go.mod b/go.mod index e435df12..676bc7a9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gochan-org/gochan go 1.13 require ( + github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/aquilax/tripcode v1.0.0 github.com/disintegration/imaging v1.6.2 github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 diff --git a/go.sum b/go.sum index e7d3ad1f..abc9b0d4 100755 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/aquilax/tripcode v1.0.0 h1:uPW1T2brVth0t6YiDPlouncHXFGneflsAvkh4zEBN58= github.com/aquilax/tripcode v1.0.0/go.mod h1:Tucn/H6BM/DEmxzj/tnmR7Vs/NV/bgCKo8Wi0yXrtzQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/initdb_master.sql b/initdb_master.sql index 8059a2ca..4f8a3063 100644 --- a/initdb_master.sql +++ b/initdb_master.sql @@ -126,7 +126,7 @@ CREATE TABLE DBPREFIXboard_staff( staff_id {fk to serial} NOT NULL, CONSTRAINT board_staff_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, CONSTRAINT board_staff_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE, - CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) + CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) ); CREATE TABLE DBPREFIXannouncements( diff --git a/initdb_mysql.sql b/initdb_mysql.sql index 4585fe48..eea17249 100644 --- a/initdb_mysql.sql +++ b/initdb_mysql.sql @@ -126,7 +126,7 @@ CREATE TABLE DBPREFIXboard_staff( staff_id BIGINT NOT NULL, CONSTRAINT board_staff_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, CONSTRAINT board_staff_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE, - CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) + CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) ); CREATE TABLE DBPREFIXannouncements( diff --git a/initdb_postgres.sql b/initdb_postgres.sql index 6b685174..5cd05194 100644 --- a/initdb_postgres.sql +++ b/initdb_postgres.sql @@ -126,7 +126,7 @@ CREATE TABLE DBPREFIXboard_staff( staff_id BIGINT NOT NULL, CONSTRAINT board_staff_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, CONSTRAINT board_staff_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE, - CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) + CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) ); CREATE TABLE DBPREFIXannouncements( diff --git a/pkg/config/config.go b/pkg/config/config.go index 633419f3..cc5c6056 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -2,6 +2,7 @@ package config import ( "encoding/json" + "flag" "io/ioutil" "net" "reflect" @@ -261,9 +262,9 @@ func (gcfg *GochanConfig) Write() error { } /* - SystemCriticalConfig contains configuration options that are extremely important, and fucking with them while - the server is running could have site breaking consequences. It should only be changed by modifying the configuration - file and restarting the server. +SystemCriticalConfig contains configuration options that are extremely important, and fucking with them while +the server is running could have site breaking consequences. It should only be changed by modifying the configuration +file and restarting the server. */ type SystemCriticalConfig struct { ListenIP string `critical:"true"` @@ -408,6 +409,14 @@ func GetBoardConfig(board string) *BoardConfig { return &bc } +func GetDebugMode() bool { + if flag.Lookup("test.v") != nil { + // running with go test + return true + } + return cfg.SystemCriticalConfig.DebugMode +} + func GetVersion() *GochanVersion { return cfg.Version } diff --git a/pkg/gcsql/connect.go b/pkg/gcsql/connect.go index 2a246ab3..5a5fb4f0 100644 --- a/pkg/gcsql/connect.go +++ b/pkg/gcsql/connect.go @@ -39,7 +39,7 @@ func initDB(initFile string) error { return RunSQLFile(filePath) } -//RunSQLFile cuts a given sql file into individual statements and runs it. +// RunSQLFile cuts a given sql file into individual statements and runs it. func RunSQLFile(path string) error { sqlBytes, err := ioutil.ReadFile(path) if err != nil { @@ -49,7 +49,7 @@ func RunSQLFile(path string) error { sqlStr := regexp.MustCompile("--.*\n?").ReplaceAllString(string(sqlBytes), " ") sqlArr := strings.Split(gcdb.replacer.Replace(sqlStr), ";") - debugMode := config.GetSystemCriticalConfig().DebugMode + debugMode := config.GetDebugMode() for _, statement := range sqlArr { statement = strings.Trim(statement, " \n\r\t") if len(statement) > 0 { diff --git a/pkg/gcsql/initdb_test.go b/pkg/gcsql/initdb_test.go new file mode 100644 index 00000000..5a985c16 --- /dev/null +++ b/pkg/gcsql/initdb_test.go @@ -0,0 +1,610 @@ +package gcsql + +import ( + "log" + "os" + "regexp" + "strings" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +var ( + sqm sqlmock.Sqlmock +) + +func connectTest() error { + gcdb = &GCDB{ + driver: "sqlmock", + replacer: strings.NewReplacer( + "DBNAME", "gochan", + "DBPREFIX", "gc_", + "\n", " "), + } + var err error + gcdb.db, sqm, err = sqlmock.New() + if err != nil { + return err + } + return err +} + +func prepTestQueryString(str string) string { + return regexp.QuoteMeta(strings.Replace(str, "\n", " ", -1)) +} + +func createTablesTest() error { + if gcdb == nil { + return ErrNotConnected + } + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_database_version( + component VARCHAR(40) NOT NULL PRIMARY KEY, + version INT NOT NULL +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(1, 1)) + + sqm.ExpectPrepare(regexp.QuoteMeta(`CREATE TABLE gc_sections( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + name TEXT NOT NULL, + abbreviation TEXT NOT NULL, + position SMALLINT NOT NULL, + hidden BOOL NOT NULL +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(2, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_boards( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + section_id BIGINT NOT NULL, + uri VARCHAR(45) NOT NULL, + dir VARCHAR(45) NOT NULL, + navbar_position SMALLINT NOT NULL, + title VARCHAR(45) NOT NULL, + subtitle VARCHAR(64) NOT NULL, + description VARCHAR(64) NOT NULL, + max_file_size INT NOT NULL, + max_threads SMALLINT NOT NULL, + default_style VARCHAR(45) NOT NULL, + locked BOOL NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + anonymous_name VARCHAR(45) NOT NULL DEFAULT 'Anonymous', + force_anonymous BOOL NOT NULL, + autosage_after SMALLINT NOT NULL, + no_images_after SMALLINT NOT NULL, + max_message_length SMALLINT NOT NULL, + min_message_length SMALLINT NOT NULL, + allow_embeds BOOL NOT NULL, + redirect_to_thread BOOL NOT NULL, + require_file BOOL NOT NULL, + enable_catalog BOOL NOT NULL, + CONSTRAINT boards_section_id_fk FOREIGN KEY(section_id) REFERENCES gc_sections(id), + CONSTRAINT boards_dir_unique UNIQUE(dir), + CONSTRAINT boards_uri_unique UNIQUE(uri) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(3, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_threads( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT NOT NULL, + locked BOOL NOT NULL DEFAULT FALSE, + stickied BOOL NOT NULL DEFAULT FALSE, + anchored BOOL NOT NULL DEFAULT FALSE, + cyclical BOOL NOT NULL DEFAULT FALSE, + last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_deleted BOOL NOT NULL DEFAULT FALSE, + CONSTRAINT threads_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(4, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE INDEX thread_deleted_index ON gc_threads(is_deleted);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(5, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_posts( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + thread_id BIGINT NOT NULL, + is_top_post BOOL NOT NULL DEFAULT FALSE, + ip VARCHAR(45) NOT NULL, + created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + name VARCHAR(50) NOT NULL DEFAULT '', + tripcode VARCHAR(10) NOT NULL DEFAULT '', + is_role_signature BOOL NOT NULL DEFAULT FALSE, + email VARCHAR(50) NOT NULL DEFAULT '', + subject VARCHAR(100) NOT NULL DEFAULT '', + message TEXT NOT NULL, + message_raw TEXT NOT NULL, + password TEXT NOT NULL, + deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_deleted BOOL NOT NULL DEFAULT FALSE, + banned_message TEXT, + CONSTRAINT posts_thread_id_fk FOREIGN KEY(thread_id) REFERENCES gc_threads(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(5, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_files( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + post_id BIGINT NOT NULL, + file_order INT NOT NULL, + original_filename VARCHAR(255) NOT NULL, + filename VARCHAR(45) NOT NULL, + checksum TEXT NOT NULL, + file_size INT NOT NULL, + is_spoilered BOOL NOT NULL, + thumbnail_width INT NOT NULL, + thumbnail_height INT NOT NULL, + width INT NOT NULL, + height INT NOT NULL, + CONSTRAINT files_post_id_fk FOREIGN KEY(post_id) REFERENCES gc_posts(id) ON DELETE CASCADE, + CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(6, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_staff( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + username VARCHAR(45) NOT NULL, + password_checksum VARCHAR(120) NOT NULL, + global_rank INT, + added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_active BOOL NOT NULL DEFAULT TRUE, + CONSTRAINT staff_username_unique UNIQUE(username) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(7, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_sessions( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + expires TIMESTAMP NOT NULL, + data VARCHAR(45) NOT NULL, + CONSTRAINT sessions_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_board_staff( + board_id BIGINT NOT NULL, + staff_id BIGINT NOT NULL, + CONSTRAINT board_staff_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE, + CONSTRAINT board_staff_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) ON DELETE CASCADE, + CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_announcements( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + subject VARCHAR(45) NOT NULL, + message TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_ip_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + board_id BIGINT, + banned_for_post_id BIGINT, + copy_post_text TEXT NOT NULL, + is_thread_ban BOOL NOT NULL, + is_active BOOL NOT NULL, + ip VARCHAR(45) NOT NULL, + issued_at TIMESTAMP NOT NULL, + appeal_at TIMESTAMP NOT NULL, + expires_at TIMESTAMP NOT NULL, + permanent BOOL NOT NULL, + staff_note VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + can_appeal BOOL NOT NULL, + CONSTRAINT ip_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE, + CONSTRAINT ip_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id), + CONSTRAINT ip_ban_banned_for_post_id_fk FOREIGN KEY(banned_for_post_id) REFERENCES gc_posts(id) ON DELETE SET NULL +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_ip_ban_audit( + ip_ban_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + staff_id BIGINT NOT NULL, + is_active BOOL NOT NULL, + is_thread_ban BOOL NOT NULL, + expires_at TIMESTAMP NOT NULL, + appeal_at TIMESTAMP NOT NULL, + permanent BOOL NOT NULL, + staff_note VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + can_appeal BOOL NOT NULL, + PRIMARY KEY(ip_ban_id, timestamp), + CONSTRAINT ip_ban_audit_ip_ban_id_fk FOREIGN KEY(ip_ban_id) REFERENCES gc_ip_ban(id) ON DELETE CASCADE, + CONSTRAINT ip_ban_audit_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_ip_ban_appeals( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT, + ip_ban_id BIGINT NOT NULL, + appeal_text TEXT NOT NULL, + staff_response TEXT, + is_denied BOOL NOT NULL, + CONSTRAINT ip_ban_appeals_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id), + CONSTRAINT ip_ban_appeals_ip_ban_id_fk FOREIGN KEY(ip_ban_id) REFERENCES gc_ip_ban(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_ip_ban_appeals_audit( + appeal_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + staff_id BIGINT, + appeal_text TEXT NOT NULL, + staff_response TEXT, + is_denied BOOL NOT NULL, + PRIMARY KEY(appeal_id, timestamp), + CONSTRAINT ip_ban_appeals_audit_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id), + CONSTRAINT ip_ban_appeals_audit_appeal_id_fk FOREIGN KEY(appeal_id) REFERENCES gc_ip_ban_appeals(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_reports( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + handled_by_staff_id BIGINT, + post_id BIGINT NOT NULL, + ip VARCHAR(45) NOT NULL, + reason TEXT NOT NULL, + is_cleared BOOL NOT NULL, + CONSTRAINT reports_handled_by_staff_id_fk FOREIGN KEY(handled_by_staff_id) REFERENCES gc_staff(id), + CONSTRAINT reports_post_id_fk FOREIGN KEY(post_id) REFERENCES gc_posts(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_reports_audit( + report_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + handled_by_staff_id BIGINT, + is_cleared BOOL NOT NULL, + CONSTRAINT reports_audit_handled_by_staff_id_fk FOREIGN KEY(handled_by_staff_id) REFERENCES gc_staff(id), + CONSTRAINT reports_audit_report_id_fk FOREIGN KEY(report_id) REFERENCES gc_reports(id) ON DELETE CASCADE +);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_filename_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + filename VARCHAR(255) NOT NULL, + is_regex BOOL NOT NULL, + CONSTRAINT filename_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE, + CONSTRAINT filename_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) +)`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_username_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + username VARCHAR(255) NOT NULL, + is_regex BOOL NOT NULL, + CONSTRAINT username_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE, + CONSTRAINT username_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) +)`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_file_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + checksum TEXT NOT NULL, + CONSTRAINT file_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES gc_boards(id) ON DELETE CASCADE, + CONSTRAINT file_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id) +)`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`CREATE TABLE gc_wordfilters( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_dirs VARCHAR(255) DEFAULT '*', + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + search VARCHAR(75) NOT NULL, + is_regex BOOL NOT NULL, + change_to VARCHAR(75) NOT NULL, + CONSTRAINT wordfilters_staff_id_fk FOREIGN KEY(staff_id) REFERENCES gc_staff(id), + CONSTRAINT wordfilters_search_check CHECK (search <> '') +)`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + sqm.ExpectPrepare(prepTestQueryString(`INSERT INTO gc_database_version(component, version) +VALUES('gochan', 1);`)).ExpectExec().WillReturnResult(sqlmock.NewResult(8, 1)) + + // start fulfilling the expected execs + + var err error + if _, err = ExecSQL(`CREATE TABLE DBPREFIXdatabase_version( + component VARCHAR(40) NOT NULL PRIMARY KEY, + version INT NOT NULL +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXsections( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + name TEXT NOT NULL, + abbreviation TEXT NOT NULL, + position SMALLINT NOT NULL, + hidden BOOL NOT NULL +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXboards( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + section_id BIGINT NOT NULL, + uri VARCHAR(45) NOT NULL, + dir VARCHAR(45) NOT NULL, + navbar_position SMALLINT NOT NULL, + title VARCHAR(45) NOT NULL, + subtitle VARCHAR(64) NOT NULL, + description VARCHAR(64) NOT NULL, + max_file_size INT NOT NULL, + max_threads SMALLINT NOT NULL, + default_style VARCHAR(45) NOT NULL, + locked BOOL NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + anonymous_name VARCHAR(45) NOT NULL DEFAULT 'Anonymous', + force_anonymous BOOL NOT NULL, + autosage_after SMALLINT NOT NULL, + no_images_after SMALLINT NOT NULL, + max_message_length SMALLINT NOT NULL, + min_message_length SMALLINT NOT NULL, + allow_embeds BOOL NOT NULL, + redirect_to_thread BOOL NOT NULL, + require_file BOOL NOT NULL, + enable_catalog BOOL NOT NULL, + CONSTRAINT boards_section_id_fk FOREIGN KEY(section_id) REFERENCES DBPREFIXsections(id), + CONSTRAINT boards_dir_unique UNIQUE(dir), + CONSTRAINT boards_uri_unique UNIQUE(uri) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXthreads( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT NOT NULL, + locked BOOL NOT NULL DEFAULT FALSE, + stickied BOOL NOT NULL DEFAULT FALSE, + anchored BOOL NOT NULL DEFAULT FALSE, + cyclical BOOL NOT NULL DEFAULT FALSE, + last_bump TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_deleted BOOL NOT NULL DEFAULT FALSE, + CONSTRAINT threads_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE INDEX thread_deleted_index ON DBPREFIXthreads(is_deleted);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXposts( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + thread_id BIGINT NOT NULL, + is_top_post BOOL NOT NULL DEFAULT FALSE, + ip VARCHAR(45) NOT NULL, + created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + name VARCHAR(50) NOT NULL DEFAULT '', + tripcode VARCHAR(10) NOT NULL DEFAULT '', + is_role_signature BOOL NOT NULL DEFAULT FALSE, + email VARCHAR(50) NOT NULL DEFAULT '', + subject VARCHAR(100) NOT NULL DEFAULT '', + message TEXT NOT NULL, + message_raw TEXT NOT NULL, + password TEXT NOT NULL, + deleted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_deleted BOOL NOT NULL DEFAULT FALSE, + banned_message TEXT, + CONSTRAINT posts_thread_id_fk FOREIGN KEY(thread_id) REFERENCES DBPREFIXthreads(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXfiles( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + post_id BIGINT NOT NULL, + file_order INT NOT NULL, + original_filename VARCHAR(255) NOT NULL, + filename VARCHAR(45) NOT NULL, + checksum TEXT NOT NULL, + file_size INT NOT NULL, + is_spoilered BOOL NOT NULL, + thumbnail_width INT NOT NULL, + thumbnail_height INT NOT NULL, + width INT NOT NULL, + height INT NOT NULL, + CONSTRAINT files_post_id_fk FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE, + CONSTRAINT files_post_id_file_order_unique UNIQUE(post_id, file_order) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXstaff( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + username VARCHAR(45) NOT NULL, + password_checksum VARCHAR(120) NOT NULL, + global_rank INT, + added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_active BOOL NOT NULL DEFAULT TRUE, + CONSTRAINT staff_username_unique UNIQUE(username) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXsessions( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + expires TIMESTAMP NOT NULL, + data VARCHAR(45) NOT NULL, + CONSTRAINT sessions_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXboard_staff( + board_id BIGINT NOT NULL, + staff_id BIGINT NOT NULL, + CONSTRAINT board_staff_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, + CONSTRAINT board_staff_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) ON DELETE CASCADE, + CONSTRAINT board_staff_pk PRIMARY KEY (board_id,staff_id) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXannouncements( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + subject VARCHAR(45) NOT NULL, + message TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT announcements_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXip_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT NOT NULL, + board_id BIGINT, + banned_for_post_id BIGINT, + copy_post_text TEXT NOT NULL, + is_thread_ban BOOL NOT NULL, + is_active BOOL NOT NULL, + ip VARCHAR(45) NOT NULL, + issued_at TIMESTAMP NOT NULL, + appeal_at TIMESTAMP NOT NULL, + expires_at TIMESTAMP NOT NULL, + permanent BOOL NOT NULL, + staff_note VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + can_appeal BOOL NOT NULL, + CONSTRAINT ip_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, + CONSTRAINT ip_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT ip_ban_banned_for_post_id_fk FOREIGN KEY(banned_for_post_id) REFERENCES DBPREFIXposts(id) ON DELETE SET NULL +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXip_ban_audit( + ip_ban_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + staff_id BIGINT NOT NULL, + is_active BOOL NOT NULL, + is_thread_ban BOOL NOT NULL, + expires_at TIMESTAMP NOT NULL, + appeal_at TIMESTAMP NOT NULL, + permanent BOOL NOT NULL, + staff_note VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + can_appeal BOOL NOT NULL, + PRIMARY KEY(ip_ban_id, timestamp), + CONSTRAINT ip_ban_audit_ip_ban_id_fk FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE, + CONSTRAINT ip_ban_audit_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXip_ban_appeals( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + staff_id BIGINT, + ip_ban_id BIGINT NOT NULL, + appeal_text TEXT NOT NULL, + staff_response TEXT, + is_denied BOOL NOT NULL, + CONSTRAINT ip_ban_appeals_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT ip_ban_appeals_ip_ban_id_fk FOREIGN KEY(ip_ban_id) REFERENCES DBPREFIXip_ban(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXip_ban_appeals_audit( + appeal_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + staff_id BIGINT, + appeal_text TEXT NOT NULL, + staff_response TEXT, + is_denied BOOL NOT NULL, + PRIMARY KEY(appeal_id, timestamp), + CONSTRAINT ip_ban_appeals_audit_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT ip_ban_appeals_audit_appeal_id_fk FOREIGN KEY(appeal_id) REFERENCES DBPREFIXip_ban_appeals(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXreports( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + handled_by_staff_id BIGINT, + post_id BIGINT NOT NULL, + ip VARCHAR(45) NOT NULL, + reason TEXT NOT NULL, + is_cleared BOOL NOT NULL, + CONSTRAINT reports_handled_by_staff_id_fk FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT reports_post_id_fk FOREIGN KEY(post_id) REFERENCES DBPREFIXposts(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXreports_audit( + report_id BIGINT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + handled_by_staff_id BIGINT, + is_cleared BOOL NOT NULL, + CONSTRAINT reports_audit_handled_by_staff_id_fk FOREIGN KEY(handled_by_staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT reports_audit_report_id_fk FOREIGN KEY(report_id) REFERENCES DBPREFIXreports(id) ON DELETE CASCADE +);`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXfilename_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + filename VARCHAR(255) NOT NULL, + is_regex BOOL NOT NULL, + CONSTRAINT filename_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, + CONSTRAINT filename_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) + );`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXusername_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + username VARCHAR(255) NOT NULL, + is_regex BOOL NOT NULL, + CONSTRAINT username_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, + CONSTRAINT username_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) + );`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXfile_ban( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_id BIGINT, + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + checksum TEXT NOT NULL, + CONSTRAINT file_ban_board_id_fk FOREIGN KEY(board_id) REFERENCES DBPREFIXboards(id) ON DELETE CASCADE, + CONSTRAINT file_ban_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id) + );`); err != nil { + return err + } + if _, err = ExecSQL(`CREATE TABLE DBPREFIXwordfilters( + id BIGINT NOT NULL AUTO_INCREMENT UNIQUE PRIMARY KEY, + board_dirs VARCHAR(255) DEFAULT '*', + staff_id BIGINT NOT NULL, + staff_note VARCHAR(255) NOT NULL, + issued_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + search VARCHAR(75) NOT NULL, + is_regex BOOL NOT NULL, + change_to VARCHAR(75) NOT NULL, + CONSTRAINT wordfilters_staff_id_fk FOREIGN KEY(staff_id) REFERENCES DBPREFIXstaff(id), + CONSTRAINT wordfilters_search_check CHECK (search <> '') + );`); err != nil { + return err + } + if _, err = ExecSQL(`INSERT INTO DBPREFIXdatabase_version(component, version) +VALUES('gochan', 1);`); err != nil { + return err + } + + return sqm.ExpectationsWereMet() +} + +func TestMain(m *testing.M) { + log.SetFlags(0) + + err := connectTest() + if err != nil { + log.Fatalln("Failed setting up sqlmock db:", err.Error()) + } + defer gcdb.Close() + + if err = createTablesTest(); err != nil { + log.Fatalln("Failed setting up sqlmock db tables:", err.Error()) + } + exitCode := m.Run() + + os.Exit(exitCode) +} diff --git a/pkg/gcsql/posts_test.go b/pkg/gcsql/posts_test.go new file mode 100644 index 00000000..6a20fe2e --- /dev/null +++ b/pkg/gcsql/posts_test.go @@ -0,0 +1,12 @@ +package gcsql + +import ( + "log" + "testing" + + _ "github.com/DATA-DOG/go-sqlmock" +) + +func TestInsertPost(t *testing.T) { + log.Println("Inserting post") +} diff --git a/pkg/gcsql/util.go b/pkg/gcsql/util.go index db5d7689..bf95f798 100644 --- a/pkg/gcsql/util.go +++ b/pkg/gcsql/util.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "strings" + + "github.com/gochan-org/gochan/pkg/config" ) const ( @@ -45,6 +47,12 @@ func SetupSQLString(query string, dbConn *GCDB) (string, error) { arr[i] += fmt.Sprintf("$%d", i+1) } prepared = strings.Join(arr, "") + case "sqlmock": + if config.GetDebugMode() { + prepared = query + break + } + fallthrough default: return "", ErrUnsupportedDB } @@ -63,6 +71,7 @@ func Close() error { /* ExecSQL automatically escapes the given values and caches the statement Example: + var intVal int var stringVal string result, err := gcsql.ExecSQL(db, "mysql", @@ -79,6 +88,7 @@ func ExecSQL(query string, values ...interface{}) (sql.Result, error) { QueryRowSQL gets a row from the db with the values in values[] and fills the respective pointers in out[] Automatically escapes the given values and caches the query Example: + id := 32 var intVal int var stringVal string @@ -97,6 +107,7 @@ func QueryRowSQL(query string, values, out []interface{}) error { QuerySQL gets all rows from the db with the values in values[] and fills the respective pointers in out[] Automatically escapes the given values and caches the query Example: + rows, err := sqlutil.QuerySQL("SELECT * FROM table") if err == nil { for rows.Next() {