From 89265ee38374f29bad104d0c0d7978fa6d4e3f58 Mon Sep 17 00:00:00 2001 From: a Date: Mon, 21 Feb 2022 03:16:15 +0000 Subject: [PATCH] remove more useless files --- .vscode/settings.json | 2 +- CONTRIBUTING.md | 53 - build-nowebsockets.bat | 63 - build.bat | 63 - build_templates.bat | 11 - cmd/common_hook_gen/hookgen.go | 166 +- cmd/elasticsearch/setup.go | 428 +- cmd/hook_gen/main.go | 110 +- cmd/hook_stub_gen/main.go | 64 +- cmd/install/install.go | 506 +- cmd/query_gen/build.bat | 4 +- cmd/query_gen/main.go | 580 +- cmd/query_gen/run.bat | 4 +- cmd/query_gen/spitter.go | 50 +- cmd/query_gen/tables.go | 1626 ++--- dev-update.bat | 16 +- gen_mysql.go | 140 +- gen_pgsql.go | 76 +- gosora_example.service | 21 - install-docker | 7 - install-linux | 8 - install.bat | 29 - install/install.go | 58 +- install/mssql.go | 216 +- install/mysql.go | 306 +- install/pgsql.go | 98 +- install/utils.go | 22 +- last_version.txt | 1 - main.go | 1314 ++-- misc_test.go | 5498 ++++++++--------- mysql.go | 110 +- public/Sortable-1.4.0/.jshintrc | 44 +- public/Sortable-1.4.0/README.md | 378 +- public/Sortable-1.4.0/Sortable.js | 2396 +++---- public/Sortable-1.4.0/bower.json | 12 +- public/Sortable-1.4.0/jquery.binding.js | 90 +- public/account.js | 6 +- public/analytics.js | 292 +- public/chartist/chartist.css.map | 16 +- public/convo.js | 20 +- public/global.js | 1960 +++--- public/jquery-emojiarea/jquery.emojiarea.css | 126 +- public/jquery-emojiarea/jquery.emojiarea.js | 930 +-- public/member.js | 686 +- public/panel_forum_edit.js | 6 +- public/panel_forums.js | 82 +- public/panel_menu_items.js | 82 +- public/profile_member.js | 32 +- public/register.js | 24 +- public/widgets.js | 110 +- quick-update-linux | 8 - router.go | 494 +- run-nowebsockets.bat | 96 - run.bat | 98 - run_mssql.bat | 96 - run_tests.bat | 79 - run_tests_mssql.bat | 79 - schema/mssql/query_activity_stream.sql | 18 +- .../mssql/query_activity_stream_matches.sql | 6 +- schema/mssql/query_activity_subscriptions.sql | 8 +- schema/mssql/query_administration_logs.sql | 14 +- schema/mssql/query_attachments.sql | 18 +- schema/mssql/query_conversations.sql | 12 +- .../query_conversations_participants.sql | 4 +- schema/mssql/query_conversations_posts.sql | 12 +- schema/mssql/query_emails.sql | 8 +- schema/mssql/query_forums.sql | 26 +- schema/mssql/query_forums_actions.sql | 16 +- schema/mssql/query_forums_permissions.sql | 10 +- schema/mssql/query_likes.sql | 12 +- schema/mssql/query_login_logs.sql | 12 +- schema/mssql/query_memchunks.sql | 8 +- schema/mssql/query_menu_items.sql | 32 +- schema/mssql/query_menus.sql | 4 +- schema/mssql/query_meta.sql | 4 +- schema/mssql/query_moderation_logs.sql | 14 +- schema/mssql/query_pages.sql | 14 +- schema/mssql/query_password_resets.sql | 10 +- schema/mssql/query_perfchunks.sql | 8 +- schema/mssql/query_plugins.sql | 8 +- schema/mssql/query_polls.sql | 14 +- schema/mssql/query_polls_options.sql | 6 +- schema/mssql/query_polls_voters.sql | 6 +- schema/mssql/query_polls_votes.sql | 10 +- schema/mssql/query_postchunks.sql | 4 +- schema/mssql/query_registration_logs.sql | 16 +- schema/mssql/query_replies.sql | 34 +- schema/mssql/query_revisions.sql | 12 +- schema/mssql/query_settings.sql | 10 +- schema/mssql/query_sync.sql | 2 +- schema/mssql/query_themes.sql | 6 +- schema/mssql/query_topicchunks.sql | 4 +- schema/mssql/query_topics.sql | 52 +- schema/mssql/query_updates.sql | 2 +- schema/mssql/query_users.sql | 58 +- schema/mssql/query_users_2fa_keys.sql | 24 +- schema/mssql/query_users_avatar_queue.sql | 4 +- schema/mssql/query_users_blocks.sql | 4 +- schema/mssql/query_users_groups.sql | 20 +- .../mssql/query_users_groups_promotions.sql | 18 +- schema/mssql/query_users_groups_scheduler.sql | 14 +- schema/mssql/query_users_replies.sql | 20 +- schema/mssql/query_viewchunks.sql | 8 +- schema/mssql/query_viewchunks_agents.sql | 6 +- schema/mssql/query_viewchunks_forums.sql | 6 +- schema/mssql/query_viewchunks_langs.sql | 6 +- schema/mssql/query_viewchunks_referrers.sql | 6 +- schema/mssql/query_viewchunks_systems.sql | 6 +- schema/mssql/query_widgets.sql | 16 +- schema/mssql/query_word_filters.sql | 8 +- schema/mysql/query_activity_stream.sql | 18 +- .../mysql/query_activity_stream_matches.sql | 6 +- schema/mysql/query_activity_subscriptions.sql | 8 +- schema/mysql/query_administration_logs.sql | 14 +- schema/mysql/query_attachments.sql | 18 +- schema/mysql/query_conversations.sql | 12 +- .../query_conversations_participants.sql | 4 +- schema/mysql/query_conversations_posts.sql | 12 +- schema/mysql/query_emails.sql | 8 +- schema/mysql/query_forums.sql | 26 +- schema/mysql/query_forums_actions.sql | 16 +- schema/mysql/query_forums_permissions.sql | 10 +- schema/mysql/query_likes.sql | 12 +- schema/mysql/query_login_logs.sql | 12 +- schema/mysql/query_memchunks.sql | 8 +- schema/mysql/query_menu_items.sql | 32 +- schema/mysql/query_menus.sql | 4 +- schema/mysql/query_meta.sql | 4 +- schema/mysql/query_moderation_logs.sql | 14 +- schema/mysql/query_pages.sql | 14 +- schema/mysql/query_password_resets.sql | 10 +- schema/mysql/query_perfchunks.sql | 8 +- schema/mysql/query_plugins.sql | 8 +- schema/mysql/query_polls.sql | 14 +- schema/mysql/query_polls_options.sql | 6 +- schema/mysql/query_polls_voters.sql | 6 +- schema/mysql/query_polls_votes.sql | 10 +- schema/mysql/query_postchunks.sql | 4 +- schema/mysql/query_registration_logs.sql | 16 +- schema/mysql/query_replies.sql | 34 +- schema/mysql/query_revisions.sql | 12 +- schema/mysql/query_settings.sql | 10 +- schema/mysql/query_sync.sql | 2 +- schema/mysql/query_themes.sql | 6 +- schema/mysql/query_topicchunks.sql | 4 +- schema/mysql/query_topics.sql | 52 +- schema/mysql/query_updates.sql | 2 +- schema/mysql/query_users.sql | 62 +- schema/mysql/query_users_2fa_keys.sql | 24 +- schema/mysql/query_users_avatar_queue.sql | 4 +- schema/mysql/query_users_blocks.sql | 4 +- schema/mysql/query_users_groups.sql | 20 +- .../mysql/query_users_groups_promotions.sql | 18 +- schema/mysql/query_users_groups_scheduler.sql | 14 +- schema/mysql/query_users_replies.sql | 20 +- schema/mysql/query_viewchunks.sql | 8 +- schema/mysql/query_viewchunks_agents.sql | 6 +- schema/mysql/query_viewchunks_forums.sql | 6 +- schema/mysql/query_viewchunks_langs.sql | 6 +- schema/mysql/query_viewchunks_referrers.sql | 6 +- schema/mysql/query_viewchunks_systems.sql | 6 +- schema/mysql/query_widgets.sql | 16 +- schema/mysql/query_word_filters.sql | 8 +- schema/pgsql/query_activity_stream.sql | 18 +- .../pgsql/query_activity_stream_matches.sql | 6 +- schema/pgsql/query_activity_subscriptions.sql | 8 +- schema/pgsql/query_administration_logs.sql | 14 +- schema/pgsql/query_attachments.sql | 18 +- schema/pgsql/query_conversations.sql | 12 +- .../query_conversations_participants.sql | 4 +- schema/pgsql/query_conversations_posts.sql | 12 +- schema/pgsql/query_emails.sql | 8 +- schema/pgsql/query_forums.sql | 26 +- schema/pgsql/query_forums_actions.sql | 16 +- schema/pgsql/query_forums_permissions.sql | 10 +- schema/pgsql/query_likes.sql | 12 +- schema/pgsql/query_login_logs.sql | 12 +- schema/pgsql/query_memchunks.sql | 8 +- schema/pgsql/query_menu_items.sql | 32 +- schema/pgsql/query_menus.sql | 4 +- schema/pgsql/query_meta.sql | 4 +- schema/pgsql/query_moderation_logs.sql | 14 +- schema/pgsql/query_pages.sql | 14 +- schema/pgsql/query_password_resets.sql | 10 +- schema/pgsql/query_perfchunks.sql | 8 +- schema/pgsql/query_plugins.sql | 8 +- schema/pgsql/query_polls.sql | 14 +- schema/pgsql/query_polls_options.sql | 6 +- schema/pgsql/query_polls_votes.sql | 10 +- schema/pgsql/query_postchunks.sql | 4 +- schema/pgsql/query_registration_logs.sql | 16 +- schema/pgsql/query_replies.sql | 34 +- schema/pgsql/query_revisions.sql | 12 +- schema/pgsql/query_settings.sql | 10 +- schema/pgsql/query_sync.sql | 2 +- schema/pgsql/query_themes.sql | 6 +- schema/pgsql/query_topicchunks.sql | 4 +- schema/pgsql/query_topics.sql | 52 +- schema/pgsql/query_updates.sql | 2 +- schema/pgsql/query_users.sql | 62 +- schema/pgsql/query_users_2fa_keys.sql | 24 +- schema/pgsql/query_users_avatar_queue.sql | 4 +- schema/pgsql/query_users_blocks.sql | 4 +- schema/pgsql/query_users_groups.sql | 20 +- .../pgsql/query_users_groups_promotions.sql | 18 +- schema/pgsql/query_users_groups_scheduler.sql | 14 +- schema/pgsql/query_users_replies.sql | 20 +- schema/pgsql/query_viewchunks.sql | 8 +- schema/pgsql/query_viewchunks_agents.sql | 6 +- schema/pgsql/query_viewchunks_forums.sql | 6 +- schema/pgsql/query_viewchunks_langs.sql | 6 +- schema/pgsql/query_viewchunks_referrers.sql | 6 +- schema/pgsql/query_viewchunks_systems.sql | 6 +- schema/pgsql/query_widgets.sql | 16 +- schema/pgsql/query_word_filters.sql | 8 +- schema/schema.json | 8 +- tickloop.go | 494 +- tmpl_client/stub.go | 18 +- update-deps.bat | 23 - uutils/utils.go | 34 +- 220 files changed, 10888 insertions(+), 11623 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 build-nowebsockets.bat delete mode 100644 build.bat delete mode 100644 build_templates.bat delete mode 100644 gosora_example.service delete mode 100644 install-docker delete mode 100644 install-linux delete mode 100644 install.bat delete mode 100644 last_version.txt delete mode 100644 quick-update-linux delete mode 100644 run-nowebsockets.bat delete mode 100644 run.bat delete mode 100644 run_mssql.bat delete mode 100644 run_tests.bat delete mode 100644 run_tests_mssql.bat delete mode 100644 update-deps.bat diff --git a/.vscode/settings.json b/.vscode/settings.json index 0231b2a8..765a941b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ // Place your settings in this file to overwrite default and user settings. { - "editor.insertSpaces": false + "editor.insertSpaces": true } \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index eb8eb1ec..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,53 +0,0 @@ -# Contributing - -First and foremost, if you want to add a contribution, you'll have to open a pull request and to sign the CLA (contributor level agreement). - -It's mainly there to deal with any legal issues which may come our way and to switch licenses without having to track down every contributor who has ever contributed. - -Some things we could do is commercial licensing for companies which are not authorised to use open source licenses or moving to a more permissive license, although I'm not too experianced in these matters, if anyone has any ideas, then feel free to put them forward. - -Try to prefix commits which introduce a lot of bugs or otherwise has a large impact on the usability of Gosora with UNSTABLE. - -If something seems to be strange, then feel free to bring up an alternative for it, although I'd rather not get hung up on the little details, if it's something which is purely a matter of opinion. - -# Coding Standards - -All code must be unit tested where ever possible with the exception of JavaScript which is untestable with our current technologies, tread with caution there. - -Use tabs not spaces for indentation. - -# Golang - -Use the standard linter and listen to what it tells you to do. - -The route assignments in main.go are *legacy code*, add new routes to `router_gen/routes.go` instead. - -Try to use the single responsibility principle where ever possible, with the exception for if doing so will cause a large performance drop. In other words, don't give your interfaces / structs too many responsibilities, keep them simple. - -Avoid hand-rolling queries. Use the builders, a ready built statement or a datastore structure instead. Preferably a datastore. - -Commits which require the patcher / update script to be run should be prefixed with "Database Changes: " - -More coming up. - -# JavaScript - -Use semicolons at the end of statements. If you don't, you might wind up breaking a minifier or two. - -Always use strict mode. - -Don't worry about ES5, we're targetting modern browsers. If we decide to backport code to older browsers, then we'll transpile the files. - -Please don't use await. It incurs too much of a cognitive overhead as to where and when you can use it. We can't use it everywhere quite yet, which means that we really should be using it nowhere. - -Please don't abuse `const` just to shave off a few nanoseconds. Even in the Go server where I care about performance the most, I don't use const everywhere, only in about five spots in thirty thousand lines and I don't use it for performance at all there. - -To keep consistency with Go code, variables must be camelCase. - -# JSON - -To keep consistency with Go code, map keys must be camelCase. - -# Phrases - -Try to keep the name of the phrase close to the actual phrase in english to make it easier for localisers to reason about which phrase is which. diff --git a/build-nowebsockets.bat b/build-nowebsockets.bat deleted file mode 100644 index 0a0f9f31..00000000 --- a/build-nowebsockets.bat +++ /dev/null @@ -1,63 +0,0 @@ -@echo off -rem TODO: Make these deletes a little less noisy -del "template_*.go" -del "tmpl_*.go" -del "gen_*.go" -del ".\tmpl_client\template_*" -del ".\tmpl_client\tmpl_*" -del ".\common\gen_extend.go" -del "gosora.exe" - -echo Generating the dynamic code -go generate -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Generating the JSON handlers -easyjson -pkg common - -echo Building the executable -go build -ldflags="-s -w" -o gosora.exe -tags no_ws -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the installer -go build -ldflags="-s -w" "./cmd/install" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the router generator -go build -ldflags="-s -w" ./router_gen -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the hook stub generator -go build -ldflags="-s -w" "./cmd/hook_stub_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the hook generator -go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the query generator -go build -ldflags="-s -w" "./cmd/query_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) -echo Gosora was successfully built -pause \ No newline at end of file diff --git a/build.bat b/build.bat deleted file mode 100644 index d3aa36d6..00000000 --- a/build.bat +++ /dev/null @@ -1,63 +0,0 @@ -@echo off -rem TODO: Make these deletes a little less noisy -del "template_*.go" -del "tmpl_*.go" -del "gen_*.go" -del ".\tmpl_client\template_*" -del ".\tmpl_client\tmpl_*" -del ".\common\gen_extend.go" -del "gosora.exe" - -echo Generating the dynamic code -go generate -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Generating the JSON handlers -easyjson -pkg common - -echo Building the executable -go build -ldflags="-s -w" -o gosora.exe -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the installer -go build -ldflags="-s -w" "./cmd/install" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the router generator -go build -ldflags="-s -w" ./router_gen -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the hook stub generator -go build -ldflags="-s -w" "./cmd/hook_stub_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the hook generator -go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the query generator -go build -ldflags="-s -w" "./cmd/query_gen" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) -echo Gosora was successfully built -pause \ No newline at end of file diff --git a/build_templates.bat b/build_templates.bat deleted file mode 100644 index d67fa836..00000000 --- a/build_templates.bat +++ /dev/null @@ -1,11 +0,0 @@ -echo Building the templates -gosora.exe -build-templates - -echo Rebuilding the executable -go build -ldflags="-s -w" -o gosora.exe -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -pause \ No newline at end of file diff --git a/cmd/common_hook_gen/hookgen.go b/cmd/common_hook_gen/hookgen.go index 092bc247..8592391d 100644 --- a/cmd/common_hook_gen/hookgen.go +++ b/cmd/common_hook_gen/hookgen.go @@ -1,115 +1,115 @@ package hookgen import ( - "bytes" - "log" - "os" - "text/template" + "bytes" + "log" + "os" + "text/template" ) type HookVars struct { - Imports []string - Hooks []Hook + Imports []string + Hooks []Hook } type Hook struct { - Name string - Params string - Params2 string - Ret string - Type string - Any bool - MultiHook bool - Skip bool - DefaultRet string - Pure string + Name string + Params string + Params2 string + Ret string + Type string + Any bool + MultiHook bool + Skip bool + DefaultRet string + Pure string } func AddHooks(add func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string)) { - vhookskip := func(name, params string) { - add(name, params, "(bool,RouteError)", "VhookSkippable_", false, true, "false,nil", "") - } - vhookskip("simple_forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *HeaderLite") - vhookskip("forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *Header") - vhookskip("router_after_filters", "w http.ResponseWriter,r *http.Request,prefix string") - vhookskip("router_pre_route", "w http.ResponseWriter,r *http.Request,u *User,prefix string") - vhookskip("route_forum_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header") - vhookskip("route_topic_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header") - vhookskip("route_attach_start", "w http.ResponseWriter,r *http.Request,u *User,fname string") - vhookskip("route_attach_post_get", "w http.ResponseWriter,r *http.Request,u *User,a *Attachment") + vhookskip := func(name, params string) { + add(name, params, "(bool,RouteError)", "VhookSkippable_", false, true, "false,nil", "") + } + vhookskip("simple_forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *HeaderLite") + vhookskip("forum_check_pre_perms", "w http.ResponseWriter,r *http.Request,u *User,fid *int,h *Header") + vhookskip("router_after_filters", "w http.ResponseWriter,r *http.Request,prefix string") + vhookskip("router_pre_route", "w http.ResponseWriter,r *http.Request,u *User,prefix string") + vhookskip("route_forum_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header") + vhookskip("route_topic_list_start", "w http.ResponseWriter,r *http.Request,u *User,h *Header") + vhookskip("route_attach_start", "w http.ResponseWriter,r *http.Request,u *User,fname string") + vhookskip("route_attach_post_get", "w http.ResponseWriter,r *http.Request,u *User,a *Attachment") - vhooknoret := func(name, params string) { - add(name, params, "", "Vhooks", false, false, "false,nil", "") - } - vhooknoret("router_end", "w http.ResponseWriter,r *http.Request,u *User,prefix string,extraData string") - vhooknoret("topic_reply_row_assign", "r *ReplyUser") - vhooknoret("counters_perf_tick_row", "low int64,high int64,avg int64") - //forums_frow_assign - //Hook(name string, data interface{}) interface{} - /*hook := func(name, params, ret, pure string) { - add(name,params,ret,"Hooks",true,false,ret,pure) - }*/ + vhooknoret := func(name, params string) { + add(name, params, "", "Vhooks", false, false, "false,nil", "") + } + vhooknoret("router_end", "w http.ResponseWriter,r *http.Request,u *User,prefix string,extraData string") + vhooknoret("topic_reply_row_assign", "r *ReplyUser") + vhooknoret("counters_perf_tick_row", "low int64,high int64,avg int64") + //forums_frow_assign + //Hook(name string, data interface{}) interface{} + /*hook := func(name, params, ret, pure string) { + add(name,params,ret,"Hooks",true,false,ret,pure) + }*/ - hooknoret := func(name, params string) { - add(name, params, "", "HooksNoRet", true, false, "", "") - } - hooknoret("forums_frow_assign", "f *Forum") + hooknoret := func(name, params string) { + add(name, params, "", "HooksNoRet", true, false, "", "") + } + hooknoret("forums_frow_assign", "f *Forum") - hookskip := func(name, params string) { - add(name, params, "(skip bool)", "HooksSkip", true, true, "", "") - } - //hookskip("forums_frow_assign","f *Forum") - hookskip("topic_create_frow_assign", "f *Forum") + hookskip := func(name, params string) { + add(name, params, "(skip bool)", "HooksSkip", true, true, "", "") + } + //hookskip("forums_frow_assign","f *Forum") + hookskip("topic_create_frow_assign", "f *Forum") - hookss := func(name string) { - add(name, "d string", "string", "Sshooks", true, false, "", "d") - } - hookss("topic_ogdesc_assign") + hookss := func(name string) { + add(name, "d string", "string", "Sshooks", true, false, "", "d") + } + hookss("topic_ogdesc_assign") } func Write(hookVars HookVars) { - fileData := `// Code generated by Gosora's Hook Generator. DO NOT EDIT. + fileData := `// Code generated by Gosora's Hook Generator. DO NOT EDIT. /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package common import ({{range .Imports}} - "{{.}}"{{end}} + "{{.}}"{{end}} ) {{range .Hooks}} func H_{{.Name}}_hook(t *HookTable,{{.Params}}) {{.Ret}} { {{if .Any}} - {{if .MultiHook}}for _, hook := range t.{{.Type}}["{{.Name}}"] { - {{if .Skip}}if skip = hook({{.Params2}}); skip { - break - }{{else}}{{if .Pure}}{{.Pure}} = {{else if .Ret}}return {{end}}hook({{.Params2}}){{end}} - }{{else}}hook := t.{{.Type}}["{{.Name}}"] - if hook != nil { - {{if .Ret}}return {{end}}hook({{.Params2}}) - } {{end}}{{end}}{{if .Pure}} - return {{.Pure}}{{else if .Ret}} - return {{.DefaultRet}}{{end}} + {{if .MultiHook}}for _, hook := range t.{{.Type}}["{{.Name}}"] { + {{if .Skip}}if skip = hook({{.Params2}}); skip { + break + }{{else}}{{if .Pure}}{{.Pure}} = {{else if .Ret}}return {{end}}hook({{.Params2}}){{end}} + }{{else}}hook := t.{{.Type}}["{{.Name}}"] + if hook != nil { + {{if .Ret}}return {{end}}hook({{.Params2}}) + } {{end}}{{end}}{{if .Pure}} + return {{.Pure}}{{else if .Ret}} + return {{.DefaultRet}}{{end}} }{{end}} ` - tmpl := template.Must(template.New("hooks").Parse(fileData)) - var b bytes.Buffer - if e := tmpl.Execute(&b, hookVars); e != nil { - log.Fatal(e) - } + tmpl := template.Must(template.New("hooks").Parse(fileData)) + var b bytes.Buffer + if e := tmpl.Execute(&b, hookVars); e != nil { + log.Fatal(e) + } - err := writeFile("./common/gen_extend.go", b.String()) - if err != nil { - log.Fatal(err) - } + err := writeFile("./common/gen_extend.go", b.String()) + if err != nil { + log.Fatal(err) + } } func writeFile(name, body string) error { - f, e := os.Create(name) - if e != nil { - return e - } - if _, e = f.WriteString(body); e != nil { - return e - } - if e = f.Sync(); e != nil { - return e - } - return f.Close() + f, e := os.Create(name) + if e != nil { + return e + } + if _, e = f.WriteString(body); e != nil { + return e + } + if e = f.Sync(); e != nil { + return e + } + return f.Close() } diff --git a/cmd/elasticsearch/setup.go b/cmd/elasticsearch/setup.go index 8c219721..b0930f57 100644 --- a/cmd/elasticsearch/setup.go +++ b/cmd/elasticsearch/setup.go @@ -2,265 +2,265 @@ package main import ( - "context" - "database/sql" - "encoding/json" - "errors" - "log" - "os" - "strconv" + "context" + "database/sql" + "encoding/json" + "errors" + "log" + "os" + "strconv" - c "github.com/Azareal/Gosora/common" - "github.com/Azareal/Gosora/query_gen" - "gopkg.in/olivere/elastic.v6" + c "github.com/Azareal/Gosora/common" + "github.com/Azareal/Gosora/query_gen" + "gopkg.in/olivere/elastic.v6" ) func main() { - log.Print("Loading the configuration data") - err := c.LoadConfig() - if err != nil { - log.Fatal(err) - } + log.Print("Loading the configuration data") + err := c.LoadConfig() + if err != nil { + log.Fatal(err) + } - log.Print("Processing configuration data") - err = c.ProcessConfig() - if err != nil { - log.Fatal(err) - } + log.Print("Processing configuration data") + err = c.ProcessConfig() + if err != nil { + log.Fatal(err) + } - if c.DbConfig.Adapter != "mysql" && c.DbConfig.Adapter != "" { - log.Fatal("Only MySQL is supported for upgrades right now, please wait for a newer build of the patcher") - } + if c.DbConfig.Adapter != "mysql" && c.DbConfig.Adapter != "" { + log.Fatal("Only MySQL is supported for upgrades right now, please wait for a newer build of the patcher") + } - err = prepMySQL() - if err != nil { - log.Fatal(err) - } + err = prepMySQL() + if err != nil { + log.Fatal(err) + } - client, err := elastic.NewClient(elastic.SetErrorLog(log.New(os.Stdout, "ES ", log.LstdFlags))) - if err != nil { - log.Fatal(err) - } - _, _, err = client.Ping("http://127.0.0.1:9200").Do(context.Background()) - if err != nil { - log.Fatal(err) - } + client, err := elastic.NewClient(elastic.SetErrorLog(log.New(os.Stdout, "ES ", log.LstdFlags))) + if err != nil { + log.Fatal(err) + } + _, _, err = client.Ping("http://127.0.0.1:9200").Do(context.Background()) + if err != nil { + log.Fatal(err) + } - err = setupIndices(client) - if err != nil { - log.Fatal(err) - } + err = setupIndices(client) + if err != nil { + log.Fatal(err) + } - err = setupData(client) - if err != nil { - log.Fatal(err) - } + err = setupData(client) + if err != nil { + log.Fatal(err) + } } func prepMySQL() error { - return qgen.Builder.Init("mysql", map[string]string{ - "host": c.DbConfig.Host, - "port": c.DbConfig.Port, - "name": c.DbConfig.Dbname, - "username": c.DbConfig.Username, - "password": c.DbConfig.Password, - "collation": "utf8mb4_general_ci", - }) + return qgen.Builder.Init("mysql", map[string]string{ + "host": c.DbConfig.Host, + "port": c.DbConfig.Port, + "name": c.DbConfig.Dbname, + "username": c.DbConfig.Username, + "password": c.DbConfig.Password, + "collation": "utf8mb4_general_ci", + }) } type ESIndexBase struct { - Mappings ESIndexMappings `json:"mappings"` + Mappings ESIndexMappings `json:"mappings"` } type ESIndexMappings struct { - Doc ESIndexDoc `json:"_doc"` + Doc ESIndexDoc `json:"_doc"` } type ESIndexDoc struct { - Properties map[string]map[string]string `json:"properties"` + Properties map[string]map[string]string `json:"properties"` } type ESDocMap map[string]map[string]string func (d ESDocMap) Add(column string, cType string) { - d["column"] = map[string]string{"type": cType} + d["column"] = map[string]string{"type": cType} } func setupIndices(client *elastic.Client) error { - exists, err := client.IndexExists("topics").Do(context.Background()) - if err != nil { - return err - } - if exists { - deleteIndex, err := client.DeleteIndex("topics").Do(context.Background()) - if err != nil { - return err - } - if !deleteIndex.Acknowledged { - return errors.New("delete not acknowledged") - } - } + exists, err := client.IndexExists("topics").Do(context.Background()) + if err != nil { + return err + } + if exists { + deleteIndex, err := client.DeleteIndex("topics").Do(context.Background()) + if err != nil { + return err + } + if !deleteIndex.Acknowledged { + return errors.New("delete not acknowledged") + } + } - docMap := make(ESDocMap) - docMap.Add("tid", "integer") - docMap.Add("title", "text") - docMap.Add("content", "text") - docMap.Add("createdBy", "integer") - docMap.Add("ip", "ip") - docMap.Add("suggest", "completion") - indexBase := ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}} - oBytes, err := json.Marshal(indexBase) - if err != nil { - return err - } - createIndex, err := client.CreateIndex("topics").Body(string(oBytes)).Do(context.Background()) - if err != nil { - return err - } - if !createIndex.Acknowledged { - return errors.New("not acknowledged") - } + docMap := make(ESDocMap) + docMap.Add("tid", "integer") + docMap.Add("title", "text") + docMap.Add("content", "text") + docMap.Add("createdBy", "integer") + docMap.Add("ip", "ip") + docMap.Add("suggest", "completion") + indexBase := ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}} + oBytes, err := json.Marshal(indexBase) + if err != nil { + return err + } + createIndex, err := client.CreateIndex("topics").Body(string(oBytes)).Do(context.Background()) + if err != nil { + return err + } + if !createIndex.Acknowledged { + return errors.New("not acknowledged") + } - exists, err = client.IndexExists("replies").Do(context.Background()) - if err != nil { - return err - } - if exists { - deleteIndex, err := client.DeleteIndex("replies").Do(context.Background()) - if err != nil { - return err - } - if !deleteIndex.Acknowledged { - return errors.New("delete not acknowledged") - } - } + exists, err = client.IndexExists("replies").Do(context.Background()) + if err != nil { + return err + } + if exists { + deleteIndex, err := client.DeleteIndex("replies").Do(context.Background()) + if err != nil { + return err + } + if !deleteIndex.Acknowledged { + return errors.New("delete not acknowledged") + } + } - docMap = make(ESDocMap) - docMap.Add("rid", "integer") - docMap.Add("tid", "integer") - docMap.Add("content", "text") - docMap.Add("createdBy", "integer") - docMap.Add("ip", "ip") - docMap.Add("suggest", "completion") - indexBase = ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}} - oBytes, err = json.Marshal(indexBase) - if err != nil { - return err - } - createIndex, err = client.CreateIndex("replies").Body(string(oBytes)).Do(context.Background()) - if err != nil { - return err - } - if !createIndex.Acknowledged { - return errors.New("not acknowledged") - } + docMap = make(ESDocMap) + docMap.Add("rid", "integer") + docMap.Add("tid", "integer") + docMap.Add("content", "text") + docMap.Add("createdBy", "integer") + docMap.Add("ip", "ip") + docMap.Add("suggest", "completion") + indexBase = ESIndexBase{ESIndexMappings{ESIndexDoc{docMap}}} + oBytes, err = json.Marshal(indexBase) + if err != nil { + return err + } + createIndex, err = client.CreateIndex("replies").Body(string(oBytes)).Do(context.Background()) + if err != nil { + return err + } + if !createIndex.Acknowledged { + return errors.New("not acknowledged") + } - return nil + return nil } type ESTopic struct { - ID int `json:"tid"` - Title string `json:"title"` - Content string `json:"content"` - CreatedBy int `json:"createdBy"` - IP string `json:"ip"` + ID int `json:"tid"` + Title string `json:"title"` + Content string `json:"content"` + CreatedBy int `json:"createdBy"` + IP string `json:"ip"` } type ESReply struct { - ID int `json:"rid"` - TID int `json:"tid"` - Content string `json:"content"` - CreatedBy int `json:"createdBy"` - IP string `json:"ip"` + ID int `json:"rid"` + TID int `json:"tid"` + Content string `json:"content"` + CreatedBy int `json:"createdBy"` + IP string `json:"ip"` } func setupData(client *elastic.Client) error { - tcount := 4 - errs := make(chan error) + tcount := 4 + errs := make(chan error) - go func() { - tin := make([]chan ESTopic, tcount) - tf := func(tin chan ESTopic) { - for { - topic, more := <-tin - if !more { - break - } - _, err := client.Index().Index("topics").Type("_doc").Id(strconv.Itoa(topic.ID)).BodyJson(topic).Do(context.Background()) - if err != nil { - errs <- err - } - } - } - for i := 0; i < 4; i++ { - go tf(tin[i]) - } + go func() { + tin := make([]chan ESTopic, tcount) + tf := func(tin chan ESTopic) { + for { + topic, more := <-tin + if !more { + break + } + _, err := client.Index().Index("topics").Type("_doc").Id(strconv.Itoa(topic.ID)).BodyJson(topic).Do(context.Background()) + if err != nil { + errs <- err + } + } + } + for i := 0; i < 4; i++ { + go tf(tin[i]) + } - oi := 0 - err := qgen.NewAcc().Select("topics").Cols("tid,title,content,createdBy,ip").Each(func(rows *sql.Rows) error { - t := ESTopic{} - err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IP) - if err != nil { - return err - } - tin[oi] <- t - if oi < 3 { - oi++ - } - return nil - }) - for i := 0; i < 4; i++ { - close(tin[i]) - } - errs <- err - }() + oi := 0 + err := qgen.NewAcc().Select("topics").Cols("tid,title,content,createdBy,ip").Each(func(rows *sql.Rows) error { + t := ESTopic{} + err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IP) + if err != nil { + return err + } + tin[oi] <- t + if oi < 3 { + oi++ + } + return nil + }) + for i := 0; i < 4; i++ { + close(tin[i]) + } + errs <- err + }() - go func() { - rin := make([]chan ESReply, tcount) - rf := func(rin chan ESReply) { - for { - reply, more := <-rin - if !more { - break - } - _, err := client.Index().Index("replies").Type("_doc").Id(strconv.Itoa(reply.ID)).BodyJson(reply).Do(context.Background()) - if err != nil { - errs <- err - } - } - } - for i := 0; i < 4; i++ { - rf(rin[i]) - } - oi := 0 - err := qgen.NewAcc().Select("replies").Cols("rid,tid,content,createdBy,ip").Each(func(rows *sql.Rows) error { - r := ESReply{} - err := rows.Scan(&r.ID, &r.TID, &r.Content, &r.CreatedBy, &r.IP) - if err != nil { - return err - } - rin[oi] <- r - if oi < 3 { - oi++ - } - return nil - }) - for i := 0; i < 4; i++ { - close(rin[i]) - } - errs <- err - }() + go func() { + rin := make([]chan ESReply, tcount) + rf := func(rin chan ESReply) { + for { + reply, more := <-rin + if !more { + break + } + _, err := client.Index().Index("replies").Type("_doc").Id(strconv.Itoa(reply.ID)).BodyJson(reply).Do(context.Background()) + if err != nil { + errs <- err + } + } + } + for i := 0; i < 4; i++ { + rf(rin[i]) + } + oi := 0 + err := qgen.NewAcc().Select("replies").Cols("rid,tid,content,createdBy,ip").Each(func(rows *sql.Rows) error { + r := ESReply{} + err := rows.Scan(&r.ID, &r.TID, &r.Content, &r.CreatedBy, &r.IP) + if err != nil { + return err + } + rin[oi] <- r + if oi < 3 { + oi++ + } + return nil + }) + for i := 0; i < 4; i++ { + close(rin[i]) + } + errs <- err + }() - fin := 0 - for { - err := <-errs - if err == nil { - fin++ - if fin == 2 { - return nil - } - } else { - return err - } - } + fin := 0 + for { + err := <-errs + if err == nil { + fin++ + if fin == 2 { + return nil + } + } else { + return err + } + } } diff --git a/cmd/hook_gen/main.go b/cmd/hook_gen/main.go index ecc3c65a..423a62ac 100644 --- a/cmd/hook_gen/main.go +++ b/cmd/hook_gen/main.go @@ -3,67 +3,67 @@ package main // import "github.com/Azareal/Gosora/hook_gen" import ( - "fmt" - "log" - "runtime/debug" - "strings" + "fmt" + "log" + "runtime/debug" + "strings" - h "github.com/Azareal/Gosora/cmd/common_hook_gen" - c "github.com/Azareal/Gosora/common" - _ "github.com/Azareal/Gosora/extend" + h "github.com/Azareal/Gosora/cmd/common_hook_gen" + c "github.com/Azareal/Gosora/common" + _ "github.com/Azareal/Gosora/extend" ) // TODO: Make sure all the errors in this file propagate upwards properly func main() { - // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows - defer func() { - if r := recover(); r != nil { - fmt.Println(r) - debug.PrintStack() - } - }() + // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows + defer func() { + if r := recover(); r != nil { + fmt.Println(r) + debug.PrintStack() + } + }() - hooks := make(map[string]int) - for _, pl := range c.Plugins { - if len(pl.Meta.Hooks) > 0 { - for _, hook := range pl.Meta.Hooks { - hooks[hook]++ - } - continue - } - if pl.Init != nil { - if e := pl.Init(pl); e != nil { - log.Print("early plugin init err: ", e) - return - } - } - if pl.Hooks != nil { - log.Print("Hooks not nil for ", pl.UName) - for hook, _ := range pl.Hooks { - hooks[hook] += 1 - } - } - } - log.Printf("hooks: %+v\n", hooks) + hooks := make(map[string]int) + for _, pl := range c.Plugins { + if len(pl.Meta.Hooks) > 0 { + for _, hook := range pl.Meta.Hooks { + hooks[hook]++ + } + continue + } + if pl.Init != nil { + if e := pl.Init(pl); e != nil { + log.Print("early plugin init err: ", e) + return + } + } + if pl.Hooks != nil { + log.Print("Hooks not nil for ", pl.UName) + for hook, _ := range pl.Hooks { + hooks[hook] += 1 + } + } + } + log.Printf("hooks: %+v\n", hooks) - imports := []string{"net/http"} - hookVars := h.HookVars{imports, nil} - var params2sb strings.Builder - add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) { - first := true - for _, param := range strings.Split(params, ",") { - if !first { - params2sb.WriteRune(',') - } - pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ") - params2sb.WriteString(pspl[0]) - first = false - } - hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2sb.String(), ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure}) - params2sb.Reset() - } + imports := []string{"net/http"} + hookVars := h.HookVars{imports, nil} + var params2sb strings.Builder + add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) { + first := true + for _, param := range strings.Split(params, ",") { + if !first { + params2sb.WriteRune(',') + } + pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ") + params2sb.WriteString(pspl[0]) + first = false + } + hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2sb.String(), ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure}) + params2sb.Reset() + } - h.AddHooks(add) - h.Write(hookVars) - log.Println("Successfully generated the hooks") + h.AddHooks(add) + h.Write(hookVars) + log.Println("Successfully generated the hooks") } diff --git a/cmd/hook_stub_gen/main.go b/cmd/hook_stub_gen/main.go index d8837a7e..b9a31fd6 100644 --- a/cmd/hook_stub_gen/main.go +++ b/cmd/hook_stub_gen/main.go @@ -1,41 +1,41 @@ package main // import "github.com/Azareal/Gosora/hook_stub_gen" import ( - "fmt" - "log" - "strings" - "runtime/debug" - - h "github.com/Azareal/Gosora/cmd/common_hook_gen" + "fmt" + "log" + "strings" + "runtime/debug" + + h "github.com/Azareal/Gosora/cmd/common_hook_gen" ) // TODO: Make sure all the errors in this file propagate upwards properly func main() { - // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows - defer func() { - if r := recover(); r != nil { - fmt.Println(r) - debug.PrintStack() - } - }() - - imports := []string{"net/http"} - hookVars := h.HookVars{imports,nil} - add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) { - var params2 string - first := true - for _, param := range strings.Split(params,",") { - if !first { - params2 += "," - } - pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param)," "," ")," ") - params2 += pspl[0] - first = false - } - hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, true, multiHook, skip, defaultRet,pure}) - } + // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows + defer func() { + if r := recover(); r != nil { + fmt.Println(r) + debug.PrintStack() + } + }() + + imports := []string{"net/http"} + hookVars := h.HookVars{imports,nil} + add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) { + var params2 string + first := true + for _, param := range strings.Split(params,",") { + if !first { + params2 += "," + } + pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param)," "," ")," ") + params2 += pspl[0] + first = false + } + hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, true, multiHook, skip, defaultRet,pure}) + } - h.AddHooks(add) - h.Write(hookVars) - log.Println("Successfully generated the hooks") + h.AddHooks(add) + h.Write(hookVars) + log.Println("Successfully generated the hooks") } \ No newline at end of file diff --git a/cmd/install/install.go b/cmd/install/install.go index 46130338..4569df1a 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -7,15 +7,15 @@ package main import ( - "bufio" - "errors" - "fmt" - "os" - "runtime/debug" - "strconv" - "strings" + "bufio" + "errors" + "fmt" + "os" + "runtime/debug" + "strconv" + "strings" - "github.com/Azareal/Gosora/install" + "github.com/Azareal/Gosora/install" ) var scanner *bufio.Scanner @@ -35,287 +35,287 @@ var defaultsiteURL = "localhost" var defaultServerPort = "80" // 8080's a good one, if you're testing and don't want it to clash with port 80 func main() { - // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows - defer func() { - if r := recover(); r != nil { - fmt.Println(r) - debug.PrintStack() - pressAnyKey() - return - } - }() + // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows + defer func() { + if r := recover(); r != nil { + fmt.Println(r) + debug.PrintStack() + pressAnyKey() + return + } + }() - scanner = bufio.NewScanner(os.Stdin) - fmt.Println("Welcome to Gosora's Installer") - fmt.Println("We're going to take you through a few steps to help you get started :)") - adap, ok := handleDatabaseDetails() - if !ok { - err := scanner.Err() - if err != nil { - fmt.Println(err) - } else { - err = errors.New("Something went wrong!") - } - abortError(err) - return - } + scanner = bufio.NewScanner(os.Stdin) + fmt.Println("Welcome to Gosora's Installer") + fmt.Println("We're going to take you through a few steps to help you get started :)") + adap, ok := handleDatabaseDetails() + if !ok { + err := scanner.Err() + if err != nil { + fmt.Println(err) + } else { + err = errors.New("Something went wrong!") + } + abortError(err) + return + } - if !getSiteDetails() { - err := scanner.Err() - if err != nil { - fmt.Println(err) - } else { - err = errors.New("Something went wrong!") - } - abortError(err) - return - } + if !getSiteDetails() { + err := scanner.Err() + if err != nil { + fmt.Println(err) + } else { + err = errors.New("Something went wrong!") + } + abortError(err) + return + } - err := adap.InitDatabase() - if err != nil { - abortError(err) - return - } + err := adap.InitDatabase() + if err != nil { + abortError(err) + return + } - err = adap.TableDefs() - if err != nil { - abortError(err) - return - } + err = adap.TableDefs() + if err != nil { + abortError(err) + return + } - err = adap.CreateAdmin() - if err != nil { - abortError(err) - return - } + err = adap.CreateAdmin() + if err != nil { + abortError(err) + return + } - err = adap.InitialData() - if err != nil { - abortError(err) - return - } + err = adap.InitialData() + if err != nil { + abortError(err) + return + } - configContents := []byte(`{ - "Site": { - "ShortName":"` + siteShortName + `", - "Name":"` + siteName + `", - "URL":"` + siteURL + `", - "Port":"` + serverPort + `", - "EnableSsl":false, - "EnableEmails":false, - "HasProxy":false, - "Language": "english" - }, - "Config": { - "SslPrivkey": "", - "SslFullchain": "", - "SMTPServer": "", - "SMTPUsername": "", - "SMTPPassword": "", - "SMTPPort": "25", + configContents := []byte(`{ + "Site": { + "ShortName":"` + siteShortName + `", + "Name":"` + siteName + `", + "URL":"` + siteURL + `", + "Port":"` + serverPort + `", + "EnableSsl":false, + "EnableEmails":false, + "HasProxy":false, + "Language": "english" + }, + "Config": { + "SslPrivkey": "", + "SslFullchain": "", + "SMTPServer": "", + "SMTPUsername": "", + "SMTPPassword": "", + "SMTPPort": "25", - "MaxRequestSizeStr":"5MB", - "UserCache":"static", - "TopicCache":"static", - "ReplyCache":"static", - "UserCacheCapacity":180, - "TopicCacheCapacity":400, - "ReplyCacheCapacity":20, - "DefaultPath":"/topics/", - "DefaultGroup":3, - "ActivationGroup":5, - "StaffCSS":"staff_post", - "DefaultForum":2, - "MinifyTemplates":true, - "BuildSlugs":true, - "ServerCount":1, - "Noavatar":"https://api.adorable.io/avatars/{width}/{id}.png", - "ItemsPerPage":25 - }, - "Database": { - "Adapter": "` + adap.Name() + `", - "Host": "` + adap.DBHost() + `", - "Username": "` + adap.DBUsername() + `", - "Password": "` + adap.DBPassword() + `", - "Dbname": "` + adap.DBName() + `", - "Port": "` + adap.DBPort() + `", + "MaxRequestSizeStr":"5MB", + "UserCache":"static", + "TopicCache":"static", + "ReplyCache":"static", + "UserCacheCapacity":180, + "TopicCacheCapacity":400, + "ReplyCacheCapacity":20, + "DefaultPath":"/topics/", + "DefaultGroup":3, + "ActivationGroup":5, + "StaffCSS":"staff_post", + "DefaultForum":2, + "MinifyTemplates":true, + "BuildSlugs":true, + "ServerCount":1, + "Noavatar":"https://api.adorable.io/avatars/{width}/{id}.png", + "ItemsPerPage":25 + }, + "Database": { + "Adapter": "` + adap.Name() + `", + "Host": "` + adap.DBHost() + `", + "Username": "` + adap.DBUsername() + `", + "Password": "` + adap.DBPassword() + `", + "Dbname": "` + adap.DBName() + `", + "Port": "` + adap.DBPort() + `", - "TestAdapter": "` + adap.Name() + `", - "TestHost": "", - "TestUsername": "", - "TestPassword": "", - "TestDbname": "", - "TestPort": "" - }, - "Dev": { - "DebugMode":true, - "SuperDebug":false - } + "TestAdapter": "` + adap.Name() + `", + "TestHost": "", + "TestUsername": "", + "TestPassword": "", + "TestDbname": "", + "TestPort": "" + }, + "Dev": { + "DebugMode":true, + "SuperDebug":false + } }`) - fmt.Println("Opening the configuration file") - configFile, err := os.Create("./config/config.json") - if err != nil { - abortError(err) - return - } + fmt.Println("Opening the configuration file") + configFile, err := os.Create("./config/config.json") + if err != nil { + abortError(err) + return + } - fmt.Println("Writing to the configuration file...") - _, err = configFile.Write(configContents) - if err != nil { - abortError(err) - return - } + fmt.Println("Writing to the configuration file...") + _, err = configFile.Write(configContents) + if err != nil { + abortError(err) + return + } - configFile.Sync() - configFile.Close() - fmt.Println("Finished writing to the configuration file") + configFile.Sync() + configFile.Close() + fmt.Println("Finished writing to the configuration file") - fmt.Println("Yay, you have successfully installed Gosora!") - fmt.Println("Your name is Admin and you can login with the password 'password'. Don't forget to change it! Seriously. It's really insecure.") - pressAnyKey() + fmt.Println("Yay, you have successfully installed Gosora!") + fmt.Println("Your name is Admin and you can login with the password 'password'. Don't forget to change it! Seriously. It's really insecure.") + pressAnyKey() } func abortError(err error) { - fmt.Println(err) - fmt.Println("Aborting installation...") - pressAnyKey() + fmt.Println(err) + fmt.Println("Aborting installation...") + pressAnyKey() } func handleDatabaseDetails() (adap install.InstallAdapter, ok bool) { - var dbAdapter string - var dbHost string - var dbUsername string - var dbPassword string - var dbName string - // TODO: Let the admin set the database port? - //var dbPort string + var dbAdapter string + var dbHost string + var dbUsername string + var dbPassword string + var dbName string + // TODO: Let the admin set the database port? + //var dbPort string - for { - fmt.Println("Which database adapter do you wish to use? mysql or mssql? Default: mysql") - if !scanner.Scan() { - return nil, false - } - dbAdapter := strings.TrimSpace(scanner.Text()) - if dbAdapter == "" { - dbAdapter = defaultAdapter - } - adap, ok = install.Lookup(dbAdapter) - if ok { - break - } - fmt.Println("That adapter doesn't exist") - } - fmt.Println("Set database adapter to " + dbAdapter) + for { + fmt.Println("Which database adapter do you wish to use? mysql or mssql? Default: mysql") + if !scanner.Scan() { + return nil, false + } + dbAdapter := strings.TrimSpace(scanner.Text()) + if dbAdapter == "" { + dbAdapter = defaultAdapter + } + adap, ok = install.Lookup(dbAdapter) + if ok { + break + } + fmt.Println("That adapter doesn't exist") + } + fmt.Println("Set database adapter to " + dbAdapter) - fmt.Println("Database Host? Default: " + defaultHost) - if !scanner.Scan() { - return nil, false - } - dbHost = scanner.Text() - if dbHost == "" { - dbHost = defaultHost - } - fmt.Println("Set database host to " + dbHost) + fmt.Println("Database Host? Default: " + defaultHost) + if !scanner.Scan() { + return nil, false + } + dbHost = scanner.Text() + if dbHost == "" { + dbHost = defaultHost + } + fmt.Println("Set database host to " + dbHost) - fmt.Println("Database Username? Default: " + defaultUsername) - if !scanner.Scan() { - return nil, false - } - dbUsername = scanner.Text() - if dbUsername == "" { - dbUsername = defaultUsername - } - fmt.Println("Set database username to " + dbUsername) + fmt.Println("Database Username? Default: " + defaultUsername) + if !scanner.Scan() { + return nil, false + } + dbUsername = scanner.Text() + if dbUsername == "" { + dbUsername = defaultUsername + } + fmt.Println("Set database username to " + dbUsername) - fmt.Println("Database Password? Default: ''") - if !scanner.Scan() { - return nil, false - } - dbPassword = scanner.Text() - if len(dbPassword) == 0 { - fmt.Println("You didn't set a password for this user. This won't block the installation process, but it might create security issues in the future.") - fmt.Println("") - } else { - fmt.Println("Set password to " + obfuscatePassword(dbPassword)) - } + fmt.Println("Database Password? Default: ''") + if !scanner.Scan() { + return nil, false + } + dbPassword = scanner.Text() + if len(dbPassword) == 0 { + fmt.Println("You didn't set a password for this user. This won't block the installation process, but it might create security issues in the future.") + fmt.Println("") + } else { + fmt.Println("Set password to " + obfuscatePassword(dbPassword)) + } - fmt.Println("Database Name? Pick a name you like or one provided to you. Default: " + defaultDbname) - if !scanner.Scan() { - return nil, false - } - dbName = scanner.Text() - if dbName == "" { - dbName = defaultDbname - } - fmt.Println("Set database name to " + dbName) + fmt.Println("Database Name? Pick a name you like or one provided to you. Default: " + defaultDbname) + if !scanner.Scan() { + return nil, false + } + dbName = scanner.Text() + if dbName == "" { + dbName = defaultDbname + } + fmt.Println("Set database name to " + dbName) - adap.SetConfig(dbHost, dbUsername, dbPassword, dbName, adap.DefaultPort()) - return adap, true + adap.SetConfig(dbHost, dbUsername, dbPassword, dbName, adap.DefaultPort()) + return adap, true } func getSiteDetails() bool { - fmt.Println("Okay. We also need to know some actual information about your site!") - fmt.Println("What's your site's name? Default: " + defaultSiteName) - if !scanner.Scan() { - return false - } - siteName = scanner.Text() - if siteName == "" { - siteName = defaultSiteName - } - fmt.Println("Set the site name to " + siteName) + fmt.Println("Okay. We also need to know some actual information about your site!") + fmt.Println("What's your site's name? Default: " + defaultSiteName) + if !scanner.Scan() { + return false + } + siteName = scanner.Text() + if siteName == "" { + siteName = defaultSiteName + } + fmt.Println("Set the site name to " + siteName) - // ? - We could compute this based on the first letter of each word in the site's name, if it's name spans multiple words. I'm not sure how to do this for single word names. - fmt.Println("Can we have a short abbreviation for your site? Default: " + defaultSiteShortName) - if !scanner.Scan() { - return false - } - siteShortName = scanner.Text() - if siteShortName == "" { - siteShortName = defaultSiteShortName - } - fmt.Println("Set the short name to " + siteShortName) + // ? - We could compute this based on the first letter of each word in the site's name, if it's name spans multiple words. I'm not sure how to do this for single word names. + fmt.Println("Can we have a short abbreviation for your site? Default: " + defaultSiteShortName) + if !scanner.Scan() { + return false + } + siteShortName = scanner.Text() + if siteShortName == "" { + siteShortName = defaultSiteShortName + } + fmt.Println("Set the short name to " + siteShortName) - fmt.Println("What's your site's url? Default: " + defaultsiteURL) - if !scanner.Scan() { - return false - } - siteURL = scanner.Text() - if siteURL == "" { - siteURL = defaultsiteURL - } - fmt.Println("Set the site url to " + siteURL) + fmt.Println("What's your site's url? Default: " + defaultsiteURL) + if !scanner.Scan() { + return false + } + siteURL = scanner.Text() + if siteURL == "" { + siteURL = defaultsiteURL + } + fmt.Println("Set the site url to " + siteURL) - fmt.Println("What port do you want the server to listen on? If you don't know what this means, you should probably leave it on the default. Default: " + defaultServerPort) - if !scanner.Scan() { - return false - } - serverPort = scanner.Text() - if serverPort == "" { - serverPort = defaultServerPort - } - _, err := strconv.Atoi(serverPort) - if err != nil { - fmt.Println("That's not a valid number!") - return false - } - fmt.Println("Set the server port to " + serverPort) - return true + fmt.Println("What port do you want the server to listen on? If you don't know what this means, you should probably leave it on the default. Default: " + defaultServerPort) + if !scanner.Scan() { + return false + } + serverPort = scanner.Text() + if serverPort == "" { + serverPort = defaultServerPort + } + _, err := strconv.Atoi(serverPort) + if err != nil { + fmt.Println("That's not a valid number!") + return false + } + fmt.Println("Set the server port to " + serverPort) + return true } func obfuscatePassword(password string) (out string) { - for i := 0; i < len(password); i++ { - out += "*" - } - return out + for i := 0; i < len(password); i++ { + out += "*" + } + return out } func pressAnyKey() { - //fmt.Println("Press any key to exit...") - fmt.Println("Please press enter to exit...") - for scanner.Scan() { - _ = scanner.Text() - return - } + //fmt.Println("Press any key to exit...") + fmt.Println("Please press enter to exit...") + for scanner.Scan() { + _ = scanner.Text() + return + } } diff --git a/cmd/query_gen/build.bat b/cmd/query_gen/build.bat index 3484c3e0..e046ecc7 100644 --- a/cmd/query_gen/build.bat +++ b/cmd/query_gen/build.bat @@ -2,8 +2,8 @@ echo Building the query generator go build -ldflags="-s -w" if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) echo The query generator was successfully built pause \ No newline at end of file diff --git a/cmd/query_gen/main.go b/cmd/query_gen/main.go index dace5726..a7080517 100644 --- a/cmd/query_gen/main.go +++ b/cmd/query_gen/main.go @@ -2,407 +2,407 @@ package main // import "github.com/Azareal/Gosora/query_gen" import ( - "encoding/json" - "fmt" - "log" - "os" - "runtime/debug" - "strconv" - "strings" + "encoding/json" + "fmt" + "log" + "os" + "runtime/debug" + "strconv" + "strings" - c "github.com/Azareal/Gosora/common" - qgen "github.com/Azareal/Gosora/query_gen" + c "github.com/Azareal/Gosora/common" + qgen "github.com/Azareal/Gosora/query_gen" ) // TODO: Make sure all the errors in this file propagate upwards properly func main() { - // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows - defer func() { - if r := recover(); r != nil { - fmt.Println(r) - debug.PrintStack() - return - } - }() + // Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows + defer func() { + if r := recover(); r != nil { + fmt.Println(r) + debug.PrintStack() + return + } + }() - log.Println("Running the query generator") - for _, a := range qgen.Registry { - log.Printf("Building the queries for the %s adapter", a.GetName()) - qgen.Install.SetAdapterInstance(a) - qgen.Install.AddPlugins(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter? + log.Println("Running the query generator") + for _, a := range qgen.Registry { + log.Printf("Building the queries for the %s adapter", a.GetName()) + qgen.Install.SetAdapterInstance(a) + qgen.Install.AddPlugins(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter? - e := writeStatements(a) - if e != nil { - log.Print(e) - } - e = qgen.Install.Write() - if e != nil { - log.Print(e) - } - e = a.Write() - if e != nil { - log.Print(e) - } - } + e := writeStatements(a) + if e != nil { + log.Print(e) + } + e = qgen.Install.Write() + if e != nil { + log.Print(e) + } + e = a.Write() + if e != nil { + log.Print(e) + } + } } // nolint func writeStatements(a qgen.Adapter) (err error) { - e := func(f func(qgen.Adapter) error) { - if err != nil { - return - } - err = f(a) - } - e(createTables) - e(seedTables) - e(writeSelects) - e(writeLeftJoins) - e(writeInnerJoins) - e(writeInserts) - e(writeUpdates) - e(writeDeletes) - e(writeSimpleCounts) - e(writeInsertSelects) - e(writeInsertLeftJoins) - e(writeInsertInnerJoins) - return err + e := func(f func(qgen.Adapter) error) { + if err != nil { + return + } + err = f(a) + } + e(createTables) + e(seedTables) + e(writeSelects) + e(writeLeftJoins) + e(writeInnerJoins) + e(writeInserts) + e(writeUpdates) + e(writeDeletes) + e(writeSimpleCounts) + e(writeInsertSelects) + e(writeInsertLeftJoins) + e(writeInsertInnerJoins) + return err } type si = map[string]interface{} type tK = tblKey func seedTables(a qgen.Adapter) error { - qgen.Install.AddIndex("topics", "parentID", "parentID") - qgen.Install.AddIndex("replies", "tid", "tid") - qgen.Install.AddIndex("polls", "parentID", "parentID") - qgen.Install.AddIndex("likes", "targetItem", "targetItem") - qgen.Install.AddIndex("emails", "uid", "uid") - qgen.Install.AddIndex("attachments", "originID", "originID") - qgen.Install.AddIndex("attachments", "path", "path") - qgen.Install.AddIndex("activity_stream_matches", "watcher", "watcher") - // TODO: Remove these keys to save space when Elasticsearch is active? - //qgen.Install.AddKey("topics", "title", tK{"title", "fulltext", "", false}) - //qgen.Install.AddKey("topics", "content", tK{"content", "fulltext", "", false}) - //qgen.Install.AddKey("topics", "title,content", tK{"title,content", "fulltext", "", false}) - //qgen.Install.AddKey("replies", "content", tK{"content", "fulltext", "", false}) + qgen.Install.AddIndex("topics", "parentID", "parentID") + qgen.Install.AddIndex("replies", "tid", "tid") + qgen.Install.AddIndex("polls", "parentID", "parentID") + qgen.Install.AddIndex("likes", "targetItem", "targetItem") + qgen.Install.AddIndex("emails", "uid", "uid") + qgen.Install.AddIndex("attachments", "originID", "originID") + qgen.Install.AddIndex("attachments", "path", "path") + qgen.Install.AddIndex("activity_stream_matches", "watcher", "watcher") + // TODO: Remove these keys to save space when Elasticsearch is active? + //qgen.Install.AddKey("topics", "title", tK{"title", "fulltext", "", false}) + //qgen.Install.AddKey("topics", "content", tK{"content", "fulltext", "", false}) + //qgen.Install.AddKey("topics", "title,content", tK{"title,content", "fulltext", "", false}) + //qgen.Install.AddKey("replies", "content", tK{"content", "fulltext", "", false}) - insert := func(tbl, cols, vals string) { - qgen.Install.SimpleInsert(tbl, cols, vals) - } - insert("sync", "last_update", "UTC_TIMESTAMP()") - addSetting := func(name, content, stype string, constraints ...string) { - if strings.Contains(name, "'") { - panic("name contains '") - } - if strings.Contains(stype, "'") { - panic("stype contains '") - } - // TODO: Add more field validators - cols := "name,content,type" - if len(constraints) > 0 { - cols += ",constraints" - } - q := func(s string) string { - return "'" + s + "'" - } - c := func() string { - if len(constraints) == 0 { - return "" - } - return "," + q(constraints[0]) - } - insert("settings", cols, q(name)+","+q(content)+","+q(stype)+c()) - } - addSetting("activation_type", "1", "list", "1-3") - addSetting("bigpost_min_words", "250", "int") - addSetting("megapost_min_words", "1000", "int") - addSetting("meta_desc", "", "html-attribute") - addSetting("rapid_loading", "1", "bool") - addSetting("google_site_verify", "", "html-attribute") - addSetting("avatar_visibility", "0", "list", "0-1") - insert("themes", "uname, default", "'cosora',1") - insert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation? + insert := func(tbl, cols, vals string) { + qgen.Install.SimpleInsert(tbl, cols, vals) + } + insert("sync", "last_update", "UTC_TIMESTAMP()") + addSetting := func(name, content, stype string, constraints ...string) { + if strings.Contains(name, "'") { + panic("name contains '") + } + if strings.Contains(stype, "'") { + panic("stype contains '") + } + // TODO: Add more field validators + cols := "name,content,type" + if len(constraints) > 0 { + cols += ",constraints" + } + q := func(s string) string { + return "'" + s + "'" + } + c := func() string { + if len(constraints) == 0 { + return "" + } + return "," + q(constraints[0]) + } + insert("settings", cols, q(name)+","+q(content)+","+q(stype)+c()) + } + addSetting("activation_type", "1", "list", "1-3") + addSetting("bigpost_min_words", "250", "int") + addSetting("megapost_min_words", "1000", "int") + addSetting("meta_desc", "", "html-attribute") + addSetting("rapid_loading", "1", "bool") + addSetting("google_site_verify", "", "html-attribute") + addSetting("avatar_visibility", "0", "list", "0-1") + insert("themes", "uname, default", "'cosora',1") + insert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation? - /* - The Permissions: + /* + The Permissions: - Global Permissions: - BanUsers - ActivateUsers - EditUser - EditUserEmail - EditUserPassword - EditUserGroup - EditUserGroupSuperMod - EditUserGroupAdmin - EditGroup - EditGroupLocalPerms - EditGroupGlobalPerms - EditGroupSuperMod - EditGroupAdmin - ManageForums - EditSettings - ManageThemes - ManagePlugins - ViewAdminLogs - ViewIPs + Global Permissions: + BanUsers + ActivateUsers + EditUser + EditUserEmail + EditUserPassword + EditUserGroup + EditUserGroupSuperMod + EditUserGroupAdmin + EditGroup + EditGroupLocalPerms + EditGroupGlobalPerms + EditGroupSuperMod + EditGroupAdmin + ManageForums + EditSettings + ManageThemes + ManagePlugins + ViewAdminLogs + ViewIPs - Non-staff Global Permissions: - UploadFiles - UploadAvatars - UseConvos - UseConvosOnlyWithMod - CreateProfileReply - AutoEmbed - AutoLink - // CreateConvo ? - // CreateConvoReply ? + Non-staff Global Permissions: + UploadFiles + UploadAvatars + UseConvos + UseConvosOnlyWithMod + CreateProfileReply + AutoEmbed + AutoLink + // CreateConvo ? + // CreateConvoReply ? - Forum Permissions: - ViewTopic - LikeItem - CreateTopic - EditTopic - DeleteTopic - CreateReply - EditReply - DeleteReply - PinTopic - CloseTopic - MoveTopic - */ + Forum Permissions: + ViewTopic + LikeItem + CreateTopic + EditTopic + DeleteTopic + CreateReply + EditReply + DeleteReply + PinTopic + CloseTopic + MoveTopic + */ - p := func(perms *c.Perms) string { - jBytes, err := json.Marshal(perms) - if err != nil { - panic(err) - } - return string(jBytes) - } - addGroup := func(name string, perms c.Perms, mod, admin, banned bool, tag string) { - mi, ai, bi := "0", "0", "0" - if mod { - mi = "1" - } - if admin { - ai = "1" - } - if banned { - bi = "1" - } - insert("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", `'`+name+`','`+p(&perms)+`','{}',`+mi+`,`+ai+`,`+bi+`,"`+tag+`"`) - } + p := func(perms *c.Perms) string { + jBytes, err := json.Marshal(perms) + if err != nil { + panic(err) + } + return string(jBytes) + } + addGroup := func(name string, perms c.Perms, mod, admin, banned bool, tag string) { + mi, ai, bi := "0", "0", "0" + if mod { + mi = "1" + } + if admin { + ai = "1" + } + if banned { + bi = "1" + } + insert("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", `'`+name+`','`+p(&perms)+`','{}',`+mi+`,`+ai+`,`+bi+`,"`+tag+`"`) + } - perms := c.AllPerms - perms.EditUserGroupAdmin = false - perms.EditGroupAdmin = false - addGroup("Administrator", perms, true, true, false, "Admin") + perms := c.AllPerms + perms.EditUserGroupAdmin = false + perms.EditGroupAdmin = false + addGroup("Administrator", perms, true, true, false, "Admin") - perms = c.Perms{BanUsers: true, ActivateUsers: true, EditUser: true, EditUserEmail: false, EditUserGroup: true, ViewIPs: true, UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, EditTopic: true, DeleteTopic: true, CreateReply: true, EditReply: true, DeleteReply: true, PinTopic: true, CloseTopic: true, MoveTopic: true} - addGroup("Moderator", perms, true, false, false, "Mod") + perms = c.Perms{BanUsers: true, ActivateUsers: true, EditUser: true, EditUserEmail: false, EditUserGroup: true, ViewIPs: true, UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, EditTopic: true, DeleteTopic: true, CreateReply: true, EditReply: true, DeleteReply: true, PinTopic: true, CloseTopic: true, MoveTopic: true} + addGroup("Moderator", perms, true, false, false, "Mod") - perms = c.Perms{UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, CreateReply: true} - addGroup("Member", perms, false, false, false, "") + perms = c.Perms{UploadFiles: true, UploadAvatars: true, UseConvos: true, UseConvosOnlyWithMod: true, CreateProfileReply: true, AutoEmbed: true, AutoLink: true, ViewTopic: true, LikeItem: true, CreateTopic: true, CreateReply: true} + addGroup("Member", perms, false, false, false, "") - perms = c.Perms{ViewTopic: true} - addGroup("Banned", perms, false, false, true, "") - addGroup("Awaiting Activation", c.Perms{ViewTopic: true, UseConvosOnlyWithMod: true}, false, false, false, "") - addGroup("Not Loggedin", perms, false, false, false, "Guest") + perms = c.Perms{ViewTopic: true} + addGroup("Banned", perms, false, false, true, "") + addGroup("Awaiting Activation", c.Perms{ViewTopic: true, UseConvosOnlyWithMod: true}, false, false, false, "") + addGroup("Not Loggedin", perms, false, false, false, "Guest") - // - // TODO: Stop processFields() from stripping the spaces in the descriptions in the next commit + // + // TODO: Stop processFields() from stripping the spaces in the descriptions in the next commit - insert("forums", "name, active, desc, tmpl", "'Reports',0,'All the reports go here',''") - insert("forums", "name, lastTopicID, lastReplyerID, desc, tmpl", "'General',1,1,'A place for general discussions which don't fit elsewhere',''") + insert("forums", "name, active, desc, tmpl", "'Reports',0,'All the reports go here',''") + insert("forums", "name, lastTopicID, lastReplyerID, desc, tmpl", "'General',1,1,'A place for general discussions which don't fit elsewhere',''") - // + // - /*var addForumPerm = func(gid, fid int, permStr string) { - insert("forums_permissions", "gid, fid, permissions", strconv.Itoa(gid)+`,`+strconv.Itoa(fid)+`,'`+permStr+`'`) - }*/ + /*var addForumPerm = func(gid, fid int, permStr string) { + insert("forums_permissions", "gid, fid, permissions", strconv.Itoa(gid)+`,`+strconv.Itoa(fid)+`,'`+permStr+`'`) + }*/ - insert("forums_permissions", "gid, fid, permissions", `1,1,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"PinTopic":true,"CloseTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", `2,1,'{"ViewTopic":true,"CreateReply":true,"CloseTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", "3,1,'{}'") - insert("forums_permissions", "gid, fid, permissions", "4,1,'{}'") - insert("forums_permissions", "gid, fid, permissions", "5,1,'{}'") - insert("forums_permissions", "gid, fid, permissions", "6,1,'{}'") + insert("forums_permissions", "gid, fid, permissions", `1,1,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"PinTopic":true,"CloseTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `2,1,'{"ViewTopic":true,"CreateReply":true,"CloseTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", "3,1,'{}'") + insert("forums_permissions", "gid, fid, permissions", "4,1,'{}'") + insert("forums_permissions", "gid, fid, permissions", "5,1,'{}'") + insert("forums_permissions", "gid, fid, permissions", "6,1,'{}'") - // + // - insert("forums_permissions", "gid, fid, permissions", `1,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `1,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", `2,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `2,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true,"EditTopic":true,"DeleteTopic":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", `3,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true}'`) + insert("forums_permissions", "gid, fid, permissions", `3,2,'{"ViewTopic":true,"CreateReply":true,"CreateTopic":true,"LikeItem":true}'`) - insert("forums_permissions", "gid, fid, permissions", `4,2,'{"ViewTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `4,2,'{"ViewTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", `5,2,'{"ViewTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `5,2,'{"ViewTopic":true}'`) - insert("forums_permissions", "gid, fid, permissions", `6,2,'{"ViewTopic":true}'`) + insert("forums_permissions", "gid, fid, permissions", `6,2,'{"ViewTopic":true}'`) - // + // - insert("topics", "title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, createdBy, parentID, ip", "'Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,''") + insert("topics", "title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, createdBy, parentID, ip", "'Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,''") - insert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ip", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,''") + insert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ip", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,''") - insert("menus", "", "") + insert("menus", "", "") - // Go maps have a random iteration order, so we have to do this, otherwise the schema files will become unstable and harder to audit - order := 0 - mOrder := "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly" - addMenuItem := func(data map[string]interface{}) { - if data["mid"] == nil { - data["mid"] = 1 - } - if data["position"] == nil { - data["position"] = "left" - } - cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder) - insert("menu_items", cols+", order", values+","+strconv.Itoa(order)) - order++ - } + // Go maps have a random iteration order, so we have to do this, otherwise the schema files will become unstable and harder to audit + order := 0 + mOrder := "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly" + addMenuItem := func(data map[string]interface{}) { + if data["mid"] == nil { + data["mid"] = 1 + } + if data["position"] == nil { + data["position"] = "left" + } + cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder) + insert("menu_items", cols+", order", values+","+strconv.Itoa(order)) + order++ + } - addMenuItem(si{"name": "{lang.menu_forums}", "htmlID": "menu_forums", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"}) + addMenuItem(si{"name": "{lang.menu_forums}", "htmlID": "menu_forums", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"}) - addMenuItem(si{"name": "{lang.menu_topics}", "htmlID": "menu_topics", "cssClass": "menu_topics", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"}) + addMenuItem(si{"name": "{lang.menu_topics}", "htmlID": "menu_topics", "cssClass": "menu_topics", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"}) - addMenuItem(si{"htmlID": "general_alerts", "cssClass": "menu_alerts", "position": "right", "tmplName": "menu_alerts"}) + addMenuItem(si{"htmlID": "general_alerts", "cssClass": "menu_alerts", "position": "right", "tmplName": "menu_alerts"}) - addMenuItem(si{"name": "{lang.menu_account}", "cssClass": "menu_account", "path": "/user/edit/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true}) + addMenuItem(si{"name": "{lang.menu_account}", "cssClass": "menu_account", "path": "/user/edit/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true}) - addMenuItem(si{"name": "{lang.menu_profile}", "cssClass": "menu_profile", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true}) + addMenuItem(si{"name": "{lang.menu_profile}", "cssClass": "menu_profile", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true}) - addMenuItem(si{"name": "{lang.menu_panel}", "cssClass": "menu_panel menu_account", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true}) + addMenuItem(si{"name": "{lang.menu_panel}", "cssClass": "menu_panel menu_account", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true}) - addMenuItem(si{"name": "{lang.menu_logout}", "cssClass": "menu_logout", "path": "/accounts/logout/?s={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true}) + addMenuItem(si{"name": "{lang.menu_logout}", "cssClass": "menu_logout", "path": "/accounts/logout/?s={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true}) - addMenuItem(si{"name": "{lang.menu_register}", "cssClass": "menu_register", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true}) + addMenuItem(si{"name": "{lang.menu_register}", "cssClass": "menu_register", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true}) - addMenuItem(si{"name": "{lang.menu_login}", "cssClass": "menu_login", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true}) + addMenuItem(si{"name": "{lang.menu_login}", "cssClass": "menu_login", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true}) - /*var fSet []string - for _, table := range tables { - fSet = append(fSet, "'"+table+"'") - } - qgen.Install.SimpleBulkInsert("tables", "name", fSet)*/ + /*var fSet []string + for _, table := range tables { + fSet = append(fSet, "'"+table+"'") + } + qgen.Install.SimpleBulkInsert("tables", "name", fSet)*/ - return nil + return nil } // ? - What is this for? /*func copyInsertMap(in map[string]interface{}) (out map[string]interface{}) { - out = make(map[string]interface{}) - for col, value := range in { - out[col] = value - } - return out + out = make(map[string]interface{}) + for col, value := range in { + out[col] = value + } + return out }*/ type LitStr string func writeSelects(a qgen.Adapter) error { - b := a.Builder() + b := a.Builder() - // Looking for getTopic? Your statement is in another castle + // Looking for getTopic? Your statement is in another castle - //b.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse() + //b.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse() - b.Select("forumEntryExists").Table("forums").Columns("fid").Where("name = ''").Orderby("fid ASC").Limit("0,1").Parse() + b.Select("forumEntryExists").Table("forums").Columns("fid").Where("name = ''").Orderby("fid ASC").Limit("0,1").Parse() - b.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse() + b.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse() - return nil + return nil } func writeLeftJoins(a qgen.Adapter) error { - a.SimpleLeftJoin("getForumTopics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "") + a.SimpleLeftJoin("getForumTopics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "") - return nil + return nil } func writeInnerJoins(a qgen.Adapter) (err error) { - return nil + return nil } func writeInserts(a qgen.Adapter) error { - b := a.Builder() + b := a.Builder() - b.Insert("addForumPermsToForum").Table("forums_permissions").Columns("gid,fid,preset,permissions").Fields("?,?,?,?").Parse() + b.Insert("addForumPermsToForum").Table("forums_permissions").Columns("gid,fid,preset,permissions").Fields("?,?,?,?").Parse() - return nil + return nil } func writeUpdates(a qgen.Adapter) error { - b := a.Builder() + b := a.Builder() - b.Update("updateEmail").Table("emails").Set("email = ?, uid = ?, validated = ?, token = ?").Where("email = ?").Parse() + b.Update("updateEmail").Table("emails").Set("email = ?, uid = ?, validated = ?, token = ?").Where("email = ?").Parse() - b.Update("setTempGroup").Table("users").Set("temp_group = ?").Where("uid = ?").Parse() + b.Update("setTempGroup").Table("users").Set("temp_group = ?").Where("uid = ?").Parse() - b.Update("bumpSync").Table("sync").Set("last_update = UTC_TIMESTAMP()").Parse() + b.Update("bumpSync").Table("sync").Set("last_update = UTC_TIMESTAMP()").Parse() - return nil + return nil } func writeDeletes(a qgen.Adapter) error { - b := a.Builder() + b := a.Builder() - //b.Delete("deleteForumPermsByForum").Table("forums_permissions").Where("fid=?").Parse() + //b.Delete("deleteForumPermsByForum").Table("forums_permissions").Where("fid=?").Parse() - b.Delete("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher=? AND asid=?").Parse() - //b.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher=?").Parse() + b.Delete("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher=? AND asid=?").Parse() + //b.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher=?").Parse() - return nil + return nil } func writeSimpleCounts(a qgen.Adapter) error { - return nil + return nil } func writeInsertSelects(a qgen.Adapter) error { - /*a.SimpleInsertSelect("addForumPermsToForumAdmins", - qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, - qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""}, - )*/ + /*a.SimpleInsertSelect("addForumPermsToForumAdmins", + qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""}, + )*/ - /*a.SimpleInsertSelect("addForumPermsToForumStaff", - qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, - qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""}, - )*/ + /*a.SimpleInsertSelect("addForumPermsToForumStaff", + qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""}, + )*/ - /*a.SimpleInsertSelect("addForumPermsToForumMembers", - qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, - qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""}, - )*/ + /*a.SimpleInsertSelect("addForumPermsToForumMembers", + qgen.DB_Insert{"forums_permissions", "gid, fid, preset, permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""}, + )*/ - return nil + return nil } // nolint func writeInsertLeftJoins(a qgen.Adapter) error { - return nil + return nil } func writeInsertInnerJoins(a qgen.Adapter) error { - return nil + return nil } func writeFile(name, content string) (err error) { - f, err := os.Create(name) - if err != nil { - return err - } - _, err = f.WriteString(content) - if err != nil { - return err - } - err = f.Sync() - if err != nil { - return err - } - return f.Close() + f, err := os.Create(name) + if err != nil { + return err + } + _, err = f.WriteString(content) + if err != nil { + return err + } + err = f.Sync() + if err != nil { + return err + } + return f.Close() } diff --git a/cmd/query_gen/run.bat b/cmd/query_gen/run.bat index 9e05f5b4..ed0e4fa1 100644 --- a/cmd/query_gen/run.bat +++ b/cmd/query_gen/run.bat @@ -2,8 +2,8 @@ echo Building the query generator go build -ldflags="-s -w" if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) echo The query generator was successfully built query_gen.exe diff --git a/cmd/query_gen/spitter.go b/cmd/query_gen/spitter.go index 5ddd4f01..a2f68b9b 100644 --- a/cmd/query_gen/spitter.go +++ b/cmd/query_gen/spitter.go @@ -4,42 +4,42 @@ import "strings" import "github.com/Azareal/Gosora/query_gen" type PrimaryKeySpitter struct { - keys map[string]string + keys map[string]string } func NewPrimaryKeySpitter() *PrimaryKeySpitter { - return &PrimaryKeySpitter{make(map[string]string)} + return &PrimaryKeySpitter{make(map[string]string)} } func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error { - if name == "CreateTableStart" { - var found string - var table = args[0].(*qgen.DBInstallTable) - for _, key := range table.Keys { - if key.Type == "primary" { - expl := strings.Split(key.Columns, ",") - if len(expl) > 1 { - continue - } - found = key.Columns - } - if found != "" { - table := table.Name - spit.keys[table] = found - } - } - } - return nil + if name == "CreateTableStart" { + var found string + var table = args[0].(*qgen.DBInstallTable) + for _, key := range table.Keys { + if key.Type == "primary" { + expl := strings.Split(key.Columns, ",") + if len(expl) > 1 { + continue + } + found = key.Columns + } + if found != "" { + table := table.Name + spit.keys[table] = found + } + } + } + return nil } func (spit *PrimaryKeySpitter) Write() error { - out := `// Generated by Gosora's Query Generator. DO NOT EDIT. + out := `// Generated by Gosora's Query Generator. DO NOT EDIT. package main var dbTablePrimaryKeys = map[string]string{ ` - for table, key := range spit.keys { - out += "\t\"" + table + "\":\"" + key + "\",\n" - } - return writeFile("./gen_tables.go", out+"}\n") + for table, key := range spit.keys { + out += "\t\"" + table + "\":\"" + key + "\",\n" + } + return writeFile("./gen_tables.go", out+"}\n") } diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 807e69cd..4b9ecd25 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -12,820 +12,820 @@ type tC = tblColumn type tblKey = qgen.DBTableKey func createTables(a qgen.Adapter) error { - tables = nil - f := func(table, charset, collation string, cols []tC, keys []tblKey) error { - tables = append(tables, table) - return qgen.Install.CreateTable(table, charset, collation, cols, keys) - } - return createTables2(a, f) + tables = nil + f := func(table, charset, collation string, cols []tC, keys []tblKey) error { + tables = append(tables, table) + return qgen.Install.CreateTable(table, charset, collation, cols, keys) + } + return createTables2(a, f) } func createTables2(a qgen.Adapter, f func(table, charset, collation string, columns []tC, keys []tblKey) error) (err error) { - createTable := func(table, charset, collation string, cols []tC, keys []tblKey) { - if err != nil { - return - } - err = f(table, charset, collation, cols, keys) - } - bcol := func(col string, val bool) qgen.DBTableColumn { - if val { - return tC{col, "boolean", 0, false, false, "1"} - } - return tC{col, "boolean", 0, false, false, "0"} - } - ccol := func(col string, size int, sdefault string) qgen.DBTableColumn { - return tC{col, "varchar", size, false, false, sdefault} - } - text := func(params ...string) qgen.DBTableColumn { - if len(params) == 0 { - return tC{"", "text", 0, false, false, ""} - } - col, sdefault := params[0], "" - if len(params) > 1 { - sdefault = params[1] - if sdefault == "" { - sdefault = "''" - } - } - return tC{col, "text", 0, false, false, sdefault} - } - createdAt := func(coll ...string) qgen.DBTableColumn { - var col string - if len(coll) > 0 { - col = coll[0] - } - if col == "" { - col = "createdAt" - } - return tC{col, "createdAt", 0, false, false, ""} - } - - createTable("users", mysqlPre, mysqlCol, - []tC{ - {"uid", "int", 0, false, true, ""}, - ccol("name", 100, ""), - ccol("password", 100, ""), - - ccol("salt", 80, "''"), - {"group", "int", 0, false, false, ""}, // TODO: Make this a foreign key - bcol("active", false), - bcol("is_super_admin", false), - createdAt(), - {"lastActiveAt", "datetime", 0, false, false, ""}, - ccol("session", 200, "''"), - //ccol("authToken", 200, "''"), - ccol("last_ip", 200, "''"), - {"profile_comments", "int", 0, false, false, "0"}, - {"who_can_convo", "int", 0, false, false, "0"}, - {"enable_embeds", "int", 0, false, false, "-1"}, - ccol("email", 200, "''"), - ccol("avatar", 100, "''"), - text("message"), - - // TODO: Drop these columns? - ccol("url_prefix", 20, "''"), - ccol("url_name", 100, "''"), - //text("pub_key"), - - {"level", "smallint", 0, false, false, "0"}, - {"score", "int", 0, false, false, "0"}, - {"posts", "int", 0, false, false, "0"}, - {"bigposts", "int", 0, false, false, "0"}, - {"megaposts", "int", 0, false, false, "0"}, - {"topics", "int", 0, false, false, "0"}, - {"liked", "int", 0, false, false, "0"}, - - // These two are to bound liked queries with little bits of information we know about the user to reduce the server load - {"oldestItemLikedCreatedAt", "datetime", 0, false, false, ""}, // For internal use only, semantics may change - {"lastLiked", "datetime", 0, false, false, ""}, // For internal use only, semantics may change - - //{"penalty_count","int",0,false,false,"0"}, - {"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect - }, - []tK{ - {"uid", "primary", "", false}, - {"name", "unique", "", false}, - }, - ) - - createTable("users_groups", mysqlPre, mysqlCol, - []tC{ - {"gid", "int", 0, false, true, ""}, - ccol("name", 100, ""), - text("permissions"), - text("plugin_perms"), - bcol("is_mod", false), - bcol("is_admin", false), - bcol("is_banned", false), - {"user_count", "int", 0, false, false, "0"}, // TODO: Implement this - - ccol("tag", 50, "''"), - }, - []tK{ - {"gid", "primary", "", false}, - }, - ) - - createTable("users_groups_promotions", mysqlPre, mysqlCol, - []tC{ - {"pid", "int", 0, false, true, ""}, - {"from_gid", "int", 0, false, false, ""}, - {"to_gid", "int", 0, false, false, ""}, - bcol("two_way", false), // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set - - // Requirements - {"level", "int", 0, false, false, ""}, - {"posts", "int", 0, false, false, "0"}, - {"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted - {"registeredFor", "int", 0, false, false, "0"}, // minutes - }, - []tK{ - {"pid", "primary", "", false}, - }, - ) - - /* - createTable("users_groups_promotions_scheduled","","", - []tC{ - {"prid","int",0,false,false,""}, - {"uid","int",0,false,false,""}, - {"runAt","datetime",0,false,false,""}, - }, - []tK{ - // TODO: Test to see that the compound primary key works - {"prid,uid", "primary", "", false}, - }, - ) - */ - - createTable("users_2fa_keys", mysqlPre, mysqlCol, - []tC{ - {"uid", "int", 0, false, false, ""}, - ccol("secret", 100, ""), - ccol("scratch1", 50, ""), - ccol("scratch2", 50, ""), - ccol("scratch3", 50, ""), - ccol("scratch4", 50, ""), - ccol("scratch5", 50, ""), - ccol("scratch6", 50, ""), - ccol("scratch7", 50, ""), - ccol("scratch8", 50, ""), - {"createdAt", "createdAt", 0, false, false, ""}, - }, - []tK{ - {"uid", "primary", "", false}, - }, - ) - - // What should we do about global penalties? Put them on the users table for speed? Or keep them here? - // Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans? - // TODO: Perm overrides - // TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag - // TODO: Add a penalty type where a user is stopped from creating plugin_guilds social groups - // TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly. - /*createTable("users_penalties","","", - []tC{ - {"uid","int",0,false,false,""}, - {"element_id","int",0,false,false,""}, - ccol("element_type",50,""), //forum, profile?, and social_group. Leave blank for global. - text("overrides","{}"), - - bcol("mod_queue",false), - bcol("shadow_ban",false), - bcol("no_avatar",false), // Coming Soon. Should this be a perm override instead? - - // Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something? - //{"posts_per_hour","int",0,false,false,"0"}, - //{"topics_per_hour","int",0,false,false,"0"}, - //{"posts_count","int",0,false,false,"0"}, - //{"topic_count","int",0,false,false,"0"}, - //{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters. - - {"issued_by","int",0,false,false,""}, - createdAt("issued_at"), - {"expires_at","datetime",0,false,false,""}, - }, nil, - )*/ - - createTable("users_groups_scheduler", "", "", - []tC{ - {"uid", "int", 0, false, false, ""}, - {"set_group", "int", 0, false, false, ""}, - - {"issued_by", "int", 0, false, false, ""}, - createdAt("issued_at"), - {"revert_at", "datetime", 0, false, false, ""}, - {"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future - }, - []tK{ - {"uid", "primary", "", false}, - }, - ) - - // TODO: Can we use a piece of software dedicated to persistent queues for this rather than relying on the database for it? - createTable("users_avatar_queue", "", "", - []tC{ - {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - }, - []tK{ - {"uid", "primary", "", false}, - }, - ) - - // TODO: Should we add a users prefix to this table to fit the "unofficial convention"? - // TODO: Add an autoincrement key? - createTable("emails", "", "", - []tC{ - ccol("email", 200, ""), - {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - bcol("validated", false), - ccol("token", 200, "''"), - }, nil, - ) - - // TODO: Allow for patterns in domains, if the bots try to shake things up there? - /* - createTable("email_domain_blacklist", "", "", - []tC{ - ccol("domain", 200, ""), - bcol("gtld", false), - }, - []tK{ - {"domain", "primary"}, - }, - ) - */ - - // TODO: Implement password resets - createTable("password_resets", "", "", - []tC{ - ccol("email", 200, ""), - {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token - ccol("token", 200, ""), - createdAt(), - }, nil, - ) - - createTable("forums", mysqlPre, mysqlCol, - []tC{ - {"fid", "int", 0, false, true, ""}, - ccol("name", 100, ""), - ccol("desc", 200, ""), - ccol("tmpl", 200, "''"), - bcol("active", true), - {"order", "int", 0, false, false, "0"}, - {"topicCount", "int", 0, false, false, "0"}, - ccol("preset", 100, "''"), - {"parentID", "int", 0, false, false, "0"}, - ccol("parentType", 50, "''"), - {"lastTopicID", "int", 0, false, false, "0"}, - {"lastReplyerID", "int", 0, false, false, "0"}, - }, - []tK{ - {"fid", "primary", "", false}, - }, - ) - - createTable("forums_permissions", "", "", - []tC{ - {"fid", "int", 0, false, false, ""}, - {"gid", "int", 0, false, false, ""}, - ccol("preset", 100, "''"), - text("permissions", "{}"), - }, - []tK{ - // TODO: Test to see that the compound primary key works - {"fid,gid", "primary", "", false}, - }, - ) - - createTable("topics", mysqlPre, mysqlCol, - []tC{ - {"tid", "int", 0, false, true, ""}, - ccol("title", 100, ""), // TODO: Increase the max length to 200? - text("content"), - text("parsed_content"), - createdAt(), - {"lastReplyAt", "datetime", 0, false, false, ""}, - {"lastReplyBy", "int", 0, false, false, ""}, - {"lastReplyID", "int", 0, false, false, "0"}, - {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - bcol("is_closed", false), - bcol("sticky", false), - // TODO: Add an index for this - {"parentID", "int", 0, false, false, "2"}, - ccol("ip", 200, "''"), - {"postCount", "int", 0, false, false, "1"}, - {"likeCount", "int", 0, false, false, "0"}, - {"attachCount", "int", 0, false, false, "0"}, - {"words", "int", 0, false, false, "0"}, - {"views", "int", 0, false, false, "0"}, - //{"dayViews", "int", 0, false, false, "0"}, - {"weekEvenViews", "int", 0, false, false, "0"}, - {"weekOddViews", "int", 0, false, false, "0"}, - ///{"weekViews", "int", 0, false, false, "0"}, - ///{"lastWeekViews", "int", 0, false, false, "0"}, - //{"monthViews", "int", 0, false, false, "0"}, - // ? - A little hacky, maybe we could do something less likely to bite us with huge numbers of topics? - // TODO: Add an index for this? - //{"lastMonth", "datetime", 0, false, false, ""}, - ccol("css_class", 100, "''"), - {"poll", "int", 0, false, false, "0"}, - ccol("data", 200, "''"), - }, - []tK{ - {"tid", "primary", "", false}, - {"title", "fulltext", "", false}, - {"content", "fulltext", "", false}, - }, - ) - - createTable("replies", mysqlPre, mysqlCol, - []tC{ - {"rid", "int", 0, false, true, ""}, // TODO: Rename to replyID? - {"tid", "int", 0, false, false, ""}, // TODO: Rename to topicID? - text("content"), - text("parsed_content"), - createdAt(), - {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"lastEdit", "int", 0, false, false, "0"}, - {"lastEditBy", "int", 0, false, false, "0"}, - {"lastUpdated", "datetime", 0, false, false, ""}, - ccol("ip", 200, "''"), - {"likeCount", "int", 0, false, false, "0"}, - {"attachCount", "int", 0, false, false, "0"}, - {"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why? - ccol("actionType", 20, "''"), - {"poll", "int", 0, false, false, "0"}, - }, - []tK{ - {"rid", "primary", "", false}, - {"content", "fulltext", "", false}, - }, - ) - - createTable("attachments", mysqlPre, mysqlCol, - []tC{ - {"attachID", "int", 0, false, true, ""}, - {"sectionID", "int", 0, false, false, "0"}, - ccol("sectionTable", 200, "forums"), - {"originID", "int", 0, false, false, ""}, - ccol("originTable", 200, "replies"), - {"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key - ccol("path", 200, ""), - ccol("extra", 200, ""), - }, - []tK{ - {"attachID", "primary", "", false}, - }, - ) - - createTable("revisions", mysqlPre, mysqlCol, - []tC{ - {"reviseID", "int", 0, false, true, ""}, - text("content"), - {"contentID", "int", 0, false, false, ""}, - ccol("contentType", 100, "replies"), - createdAt(), - // TODO: Add a createdBy column? - }, - []tK{ - {"reviseID", "primary", "", false}, - }, - ) - - createTable("polls", mysqlPre, mysqlCol, - []tC{ - {"pollID", "int", 0, false, true, ""}, - {"parentID", "int", 0, false, false, "0"}, - ccol("parentTable", 100, "topics"), // topics, replies - {"type", "int", 0, false, false, "0"}, - {"options", "json", 0, false, false, ""}, - {"votes", "int", 0, false, false, "0"}, - }, - []tK{ - {"pollID", "primary", "", false}, - }, - ) - - createTable("polls_options", "", "", - []tC{ - {"pollID", "int", 0, false, false, ""}, - {"option", "int", 0, false, false, "0"}, - {"votes", "int", 0, false, false, "0"}, - }, nil, - ) - - createTable("polls_votes", mysqlPre, mysqlCol, - []tC{ - {"pollID", "int", 0, false, false, ""}, - {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"option", "int", 0, false, false, "0"}, - createdAt("castAt"), - ccol("ip", 200, "''"), - }, nil, - ) - - createTable("users_replies", mysqlPre, mysqlCol, - []tC{ - {"rid", "int", 0, false, true, ""}, - {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - text("content"), - text("parsed_content"), - createdAt(), - {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"lastEdit", "int", 0, false, false, "0"}, - {"lastEditBy", "int", 0, false, false, "0"}, - ccol("ip", 200, "''"), - }, - []tK{ - {"rid", "primary", "", false}, - }, - ) - - createTable("likes", "", "", - []tC{ - {"weight", "tinyint", 0, false, false, "1"}, - {"targetItem", "int", 0, false, false, ""}, - ccol("targetType", 50, "replies"), - {"sentBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - createdAt(), - {"recalc", "tinyint", 0, false, false, "0"}, - }, nil, - ) - - //columns("participants,createdBy,createdAt,lastReplyBy,lastReplyAt").Where("cid=?") - createTable("conversations", "", "", - []tC{ - {"cid", "int", 0, false, true, ""}, - {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - createdAt(), - {"lastReplyAt", "datetime", 0, false, false, ""}, - {"lastReplyBy", "int", 0, false, false, ""}, - }, - []tK{ - {"cid", "primary", "", false}, - }, - ) - - createTable("conversations_posts", "", "", - []tC{ - {"pid", "int", 0, false, true, ""}, - {"cid", "int", 0, false, false, ""}, - {"createdBy", "int", 0, false, false, ""}, - ccol("body", 50, ""), - ccol("post", 50, "''"), - }, - []tK{ - {"pid", "primary", "", false}, - }, - ) - - createTable("conversations_participants", "", "", - []tC{ - {"uid", "int", 0, false, false, ""}, - {"cid", "int", 0, false, false, ""}, - }, nil, - ) - - /* - createTable("users_friends", "", "", - []tC{ - {"uid", "int", 0, false, false, ""}, - {"uid2", "int", 0, false, false, ""}, - }, nil, - ) - createTable("users_friends_invites", "", "", - []tC{ - {"requester", "int", 0, false, false, ""}, - {"target", "int", 0, false, false, ""}, - }, nil, - ) - */ - - createTable("users_blocks", "", "", - []tC{ - {"blocker", "int", 0, false, false, ""}, - {"blockedUser", "int", 0, false, false, ""}, - }, nil, - ) - - createTable("activity_stream_matches", "", "", - []tC{ - {"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - }, - []tK{ - {"asid,asid", "foreign", "activity_stream", true}, - }, - ) - - createTable("activity_stream", "", "", - []tC{ - {"asid", "int", 0, false, true, ""}, - {"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key - {"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */ - ccol("event", 50, ""), /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */ - ccol("elementType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ - - // replacement for elementType - tC{"elementTable", "int", 0, false, false, "0"}, - - {"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ - createdAt(), - ccol("extra", 200, "''"), - }, - []tK{ - {"asid", "primary", "", false}, - }, - ) - - createTable("activity_subscriptions", "", "", - []tC{ - {"user", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ - ccol("targetType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ - {"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/ - }, nil, - ) - - /* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */ - createTable("settings", "", "", - []tC{ - ccol("name", 180, ""), - ccol("content", 250, ""), - ccol("type", 50, ""), - ccol("constraints", 200, "''"), - }, - []tK{ - {"name", "unique", "", false}, - }, - ) - - createTable("word_filters", "", "", - []tC{ - {"wfid", "int", 0, false, true, ""}, - ccol("find", 200, ""), - ccol("replacement", 200, ""), - }, - []tK{ - {"wfid", "primary", "", false}, - }, - ) - - createTable("plugins", "", "", - []tC{ - ccol("uname", 180, ""), - bcol("active", false), - bcol("installed", false), - }, - []tK{ - {"uname", "unique", "", false}, - }, - ) - - createTable("themes", "", "", - []tC{ - ccol("uname", 180, ""), - bcol("default", false), - //text("profileUserVars"), - }, - []tK{ - {"uname", "unique", "", false}, - }, - ) - - createTable("widgets", "", "", - []tC{ - {"wid", "int", 0, false, true, ""}, - {"position", "int", 0, false, false, ""}, - ccol("side", 100, ""), - ccol("type", 100, ""), - bcol("active", false), - ccol("location", 100, ""), - text("data"), - }, - []tK{ - {"wid", "primary", "", false}, - }, - ) - - createTable("menus", "", "", - []tC{ - {"mid", "int", 0, false, true, ""}, - }, - []tK{ - {"mid", "primary", "", false}, - }, - ) - - createTable("menu_items", "", "", - []tC{ - {"miid", "int", 0, false, true, ""}, - {"mid", "int", 0, false, false, ""}, - ccol("name", 200, "''"), - ccol("htmlID", 200, "''"), - ccol("cssClass", 200, "''"), - ccol("position", 100, ""), - ccol("path", 200, "''"), - ccol("aria", 200, "''"), - ccol("tooltip", 200, "''"), - ccol("tmplName", 200, "''"), - {"order", "int", 0, false, false, "0"}, - - bcol("guestOnly", false), - bcol("memberOnly", false), - bcol("staffOnly", false), - bcol("adminOnly", false), - }, - []tK{ - {"miid", "primary", "", false}, - }, - ) - - createTable("pages", mysqlPre, mysqlCol, - []tC{ - {"pid", "int", 0, false, true, ""}, - //ccol("path", 200, ""), - ccol("name", 200, ""), - ccol("title", 200, ""), - text("body"), - // TODO: Make this a table? - text("allowedGroups"), - {"menuID", "int", 0, false, false, "-1"}, // simple sidebar menu - }, - []tK{ - {"pid", "primary", "", false}, - }, - ) - - createTable("registration_logs", "", "", - []tC{ - {"rlid", "int", 0, false, true, ""}, - ccol("username", 100, ""), - {"email", "varchar", 100, false, false, ""}, - ccol("failureReason", 100, ""), - bcol("success", false), // Did this attempt succeed? - ccol("ipaddress", 200, ""), - createdAt("doneAt"), - }, - []tK{ - {"rlid", "primary", "", false}, - }, - ) - - createTable("login_logs", "", "", - []tC{ - {"lid", "int", 0, false, true, ""}, - {"uid", "int", 0, false, false, ""}, - - bcol("success", false), // Did this attempt succeed? - ccol("ipaddress", 200, ""), - createdAt("doneAt"), - }, - []tK{ - {"lid", "primary", "", false}, - }, - ) - - createTable("moderation_logs", "", "", - []tC{ - ccol("action", 100, ""), - {"elementID", "int", 0, false, false, ""}, - ccol("elementType", 100, ""), - ccol("ipaddress", 200, ""), - {"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"doneAt", "datetime", 0, false, false, ""}, - text("extra"), - }, nil, - ) - - createTable("administration_logs", "", "", - []tC{ - ccol("action", 100, ""), - {"elementID", "int", 0, false, false, ""}, - ccol("elementType", 100, ""), - ccol("ipaddress", 200, ""), - {"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key - {"doneAt", "datetime", 0, false, false, ""}, - text("extra"), - }, nil, - ) - - createTable("viewchunks", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"avg", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - ccol("route", 200, ""), // TODO: set a default empty here - }, nil, - ) - - createTable("viewchunks_agents", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - ccol("browser", 200, ""), // googlebot, firefox, opera, etc. - //ccol("version",0,""), // the version of the browser or bot - }, nil, - ) - - createTable("viewchunks_systems", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - ccol("system", 200, ""), // windows, android, unknown, etc. - }, nil, - ) - - createTable("viewchunks_langs", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - ccol("lang", 200, ""), // en, ru, etc. - }, nil, - ) - - createTable("viewchunks_referrers", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - ccol("domain", 200, ""), - }, nil, - ) - - createTable("viewchunks_forums", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - {"forum", "int", 0, false, false, ""}, - }, nil, - ) - - createTable("topicchunks", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - // TODO: Add a column for the parent forum? - }, nil, - ) - - createTable("postchunks", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - // TODO: Add a column for the parent topic / profile? - }, nil, - ) - - createTable("memchunks", "", "", - []tC{ - {"count", "int", 0, false, false, "0"}, - {"stack", "int", 0, false, false, "0"}, - {"heap", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - }, nil, - ) - - createTable("perfchunks", "", "", - []tC{ - {"low", "int", 0, false, false, "0"}, - {"high", "int", 0, false, false, "0"}, - {"avg", "int", 0, false, false, "0"}, - {"createdAt", "datetime", 0, false, false, ""}, - }, nil, - ) - - createTable("sync", "", "", - []tC{ - {"last_update", "datetime", 0, false, false, ""}, - }, nil, - ) - - createTable("updates", "", "", - []tC{ - {"dbVersion", "int", 0, false, false, "0"}, - }, nil, - ) - - createTable("meta", "", "", - []tC{ - ccol("name", 200, ""), - ccol("value", 200, ""), - }, nil, - ) - - /*createTable("tables", "", "", - []tC{ - {"id", "int", 0, false, true, ""}, - ccol("name", 200, ""), - }, - []tK{ - {"id", "primary", "", false}, - {"name", "unique", "", false}, - }, - )*/ - - return err + createTable := func(table, charset, collation string, cols []tC, keys []tblKey) { + if err != nil { + return + } + err = f(table, charset, collation, cols, keys) + } + bcol := func(col string, val bool) qgen.DBTableColumn { + if val { + return tC{col, "boolean", 0, false, false, "1"} + } + return tC{col, "boolean", 0, false, false, "0"} + } + ccol := func(col string, size int, sdefault string) qgen.DBTableColumn { + return tC{col, "varchar", size, false, false, sdefault} + } + text := func(params ...string) qgen.DBTableColumn { + if len(params) == 0 { + return tC{"", "text", 0, false, false, ""} + } + col, sdefault := params[0], "" + if len(params) > 1 { + sdefault = params[1] + if sdefault == "" { + sdefault = "''" + } + } + return tC{col, "text", 0, false, false, sdefault} + } + createdAt := func(coll ...string) qgen.DBTableColumn { + var col string + if len(coll) > 0 { + col = coll[0] + } + if col == "" { + col = "createdAt" + } + return tC{col, "createdAt", 0, false, false, ""} + } + + createTable("users", mysqlPre, mysqlCol, + []tC{ + {"uid", "int", 0, false, true, ""}, + ccol("name", 100, ""), + ccol("password", 100, ""), + + ccol("salt", 80, "''"), + {"group", "int", 0, false, false, ""}, // TODO: Make this a foreign key + bcol("active", false), + bcol("is_super_admin", false), + createdAt(), + {"lastActiveAt", "datetime", 0, false, false, ""}, + ccol("session", 200, "''"), + //ccol("authToken", 200, "''"), + ccol("last_ip", 200, "''"), + {"profile_comments", "int", 0, false, false, "0"}, + {"who_can_convo", "int", 0, false, false, "0"}, + {"enable_embeds", "int", 0, false, false, "-1"}, + ccol("email", 200, "''"), + ccol("avatar", 100, "''"), + text("message"), + + // TODO: Drop these columns? + ccol("url_prefix", 20, "''"), + ccol("url_name", 100, "''"), + //text("pub_key"), + + {"level", "smallint", 0, false, false, "0"}, + {"score", "int", 0, false, false, "0"}, + {"posts", "int", 0, false, false, "0"}, + {"bigposts", "int", 0, false, false, "0"}, + {"megaposts", "int", 0, false, false, "0"}, + {"topics", "int", 0, false, false, "0"}, + {"liked", "int", 0, false, false, "0"}, + + // These two are to bound liked queries with little bits of information we know about the user to reduce the server load + {"oldestItemLikedCreatedAt", "datetime", 0, false, false, ""}, // For internal use only, semantics may change + {"lastLiked", "datetime", 0, false, false, ""}, // For internal use only, semantics may change + + //{"penalty_count","int",0,false,false,"0"}, + {"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect + }, + []tK{ + {"uid", "primary", "", false}, + {"name", "unique", "", false}, + }, + ) + + createTable("users_groups", mysqlPre, mysqlCol, + []tC{ + {"gid", "int", 0, false, true, ""}, + ccol("name", 100, ""), + text("permissions"), + text("plugin_perms"), + bcol("is_mod", false), + bcol("is_admin", false), + bcol("is_banned", false), + {"user_count", "int", 0, false, false, "0"}, // TODO: Implement this + + ccol("tag", 50, "''"), + }, + []tK{ + {"gid", "primary", "", false}, + }, + ) + + createTable("users_groups_promotions", mysqlPre, mysqlCol, + []tC{ + {"pid", "int", 0, false, true, ""}, + {"from_gid", "int", 0, false, false, ""}, + {"to_gid", "int", 0, false, false, ""}, + bcol("two_way", false), // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set + + // Requirements + {"level", "int", 0, false, false, ""}, + {"posts", "int", 0, false, false, "0"}, + {"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted + {"registeredFor", "int", 0, false, false, "0"}, // minutes + }, + []tK{ + {"pid", "primary", "", false}, + }, + ) + + /* + createTable("users_groups_promotions_scheduled","","", + []tC{ + {"prid","int",0,false,false,""}, + {"uid","int",0,false,false,""}, + {"runAt","datetime",0,false,false,""}, + }, + []tK{ + // TODO: Test to see that the compound primary key works + {"prid,uid", "primary", "", false}, + }, + ) + */ + + createTable("users_2fa_keys", mysqlPre, mysqlCol, + []tC{ + {"uid", "int", 0, false, false, ""}, + ccol("secret", 100, ""), + ccol("scratch1", 50, ""), + ccol("scratch2", 50, ""), + ccol("scratch3", 50, ""), + ccol("scratch4", 50, ""), + ccol("scratch5", 50, ""), + ccol("scratch6", 50, ""), + ccol("scratch7", 50, ""), + ccol("scratch8", 50, ""), + {"createdAt", "createdAt", 0, false, false, ""}, + }, + []tK{ + {"uid", "primary", "", false}, + }, + ) + + // What should we do about global penalties? Put them on the users table for speed? Or keep them here? + // Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans? + // TODO: Perm overrides + // TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag + // TODO: Add a penalty type where a user is stopped from creating plugin_guilds social groups + // TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly. + /*createTable("users_penalties","","", + []tC{ + {"uid","int",0,false,false,""}, + {"element_id","int",0,false,false,""}, + ccol("element_type",50,""), //forum, profile?, and social_group. Leave blank for global. + text("overrides","{}"), + + bcol("mod_queue",false), + bcol("shadow_ban",false), + bcol("no_avatar",false), // Coming Soon. Should this be a perm override instead? + + // Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something? + //{"posts_per_hour","int",0,false,false,"0"}, + //{"topics_per_hour","int",0,false,false,"0"}, + //{"posts_count","int",0,false,false,"0"}, + //{"topic_count","int",0,false,false,"0"}, + //{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters. + + {"issued_by","int",0,false,false,""}, + createdAt("issued_at"), + {"expires_at","datetime",0,false,false,""}, + }, nil, + )*/ + + createTable("users_groups_scheduler", "", "", + []tC{ + {"uid", "int", 0, false, false, ""}, + {"set_group", "int", 0, false, false, ""}, + + {"issued_by", "int", 0, false, false, ""}, + createdAt("issued_at"), + {"revert_at", "datetime", 0, false, false, ""}, + {"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future + }, + []tK{ + {"uid", "primary", "", false}, + }, + ) + + // TODO: Can we use a piece of software dedicated to persistent queues for this rather than relying on the database for it? + createTable("users_avatar_queue", "", "", + []tC{ + {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + }, + []tK{ + {"uid", "primary", "", false}, + }, + ) + + // TODO: Should we add a users prefix to this table to fit the "unofficial convention"? + // TODO: Add an autoincrement key? + createTable("emails", "", "", + []tC{ + ccol("email", 200, ""), + {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + bcol("validated", false), + ccol("token", 200, "''"), + }, nil, + ) + + // TODO: Allow for patterns in domains, if the bots try to shake things up there? + /* + createTable("email_domain_blacklist", "", "", + []tC{ + ccol("domain", 200, ""), + bcol("gtld", false), + }, + []tK{ + {"domain", "primary"}, + }, + ) + */ + + // TODO: Implement password resets + createTable("password_resets", "", "", + []tC{ + ccol("email", 200, ""), + {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token + ccol("token", 200, ""), + createdAt(), + }, nil, + ) + + createTable("forums", mysqlPre, mysqlCol, + []tC{ + {"fid", "int", 0, false, true, ""}, + ccol("name", 100, ""), + ccol("desc", 200, ""), + ccol("tmpl", 200, "''"), + bcol("active", true), + {"order", "int", 0, false, false, "0"}, + {"topicCount", "int", 0, false, false, "0"}, + ccol("preset", 100, "''"), + {"parentID", "int", 0, false, false, "0"}, + ccol("parentType", 50, "''"), + {"lastTopicID", "int", 0, false, false, "0"}, + {"lastReplyerID", "int", 0, false, false, "0"}, + }, + []tK{ + {"fid", "primary", "", false}, + }, + ) + + createTable("forums_permissions", "", "", + []tC{ + {"fid", "int", 0, false, false, ""}, + {"gid", "int", 0, false, false, ""}, + ccol("preset", 100, "''"), + text("permissions", "{}"), + }, + []tK{ + // TODO: Test to see that the compound primary key works + {"fid,gid", "primary", "", false}, + }, + ) + + createTable("topics", mysqlPre, mysqlCol, + []tC{ + {"tid", "int", 0, false, true, ""}, + ccol("title", 100, ""), // TODO: Increase the max length to 200? + text("content"), + text("parsed_content"), + createdAt(), + {"lastReplyAt", "datetime", 0, false, false, ""}, + {"lastReplyBy", "int", 0, false, false, ""}, + {"lastReplyID", "int", 0, false, false, "0"}, + {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + bcol("is_closed", false), + bcol("sticky", false), + // TODO: Add an index for this + {"parentID", "int", 0, false, false, "2"}, + ccol("ip", 200, "''"), + {"postCount", "int", 0, false, false, "1"}, + {"likeCount", "int", 0, false, false, "0"}, + {"attachCount", "int", 0, false, false, "0"}, + {"words", "int", 0, false, false, "0"}, + {"views", "int", 0, false, false, "0"}, + //{"dayViews", "int", 0, false, false, "0"}, + {"weekEvenViews", "int", 0, false, false, "0"}, + {"weekOddViews", "int", 0, false, false, "0"}, + ///{"weekViews", "int", 0, false, false, "0"}, + ///{"lastWeekViews", "int", 0, false, false, "0"}, + //{"monthViews", "int", 0, false, false, "0"}, + // ? - A little hacky, maybe we could do something less likely to bite us with huge numbers of topics? + // TODO: Add an index for this? + //{"lastMonth", "datetime", 0, false, false, ""}, + ccol("css_class", 100, "''"), + {"poll", "int", 0, false, false, "0"}, + ccol("data", 200, "''"), + }, + []tK{ + {"tid", "primary", "", false}, + {"title", "fulltext", "", false}, + {"content", "fulltext", "", false}, + }, + ) + + createTable("replies", mysqlPre, mysqlCol, + []tC{ + {"rid", "int", 0, false, true, ""}, // TODO: Rename to replyID? + {"tid", "int", 0, false, false, ""}, // TODO: Rename to topicID? + text("content"), + text("parsed_content"), + createdAt(), + {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"lastEdit", "int", 0, false, false, "0"}, + {"lastEditBy", "int", 0, false, false, "0"}, + {"lastUpdated", "datetime", 0, false, false, ""}, + ccol("ip", 200, "''"), + {"likeCount", "int", 0, false, false, "0"}, + {"attachCount", "int", 0, false, false, "0"}, + {"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why? + ccol("actionType", 20, "''"), + {"poll", "int", 0, false, false, "0"}, + }, + []tK{ + {"rid", "primary", "", false}, + {"content", "fulltext", "", false}, + }, + ) + + createTable("attachments", mysqlPre, mysqlCol, + []tC{ + {"attachID", "int", 0, false, true, ""}, + {"sectionID", "int", 0, false, false, "0"}, + ccol("sectionTable", 200, "forums"), + {"originID", "int", 0, false, false, ""}, + ccol("originTable", 200, "replies"), + {"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key + ccol("path", 200, ""), + ccol("extra", 200, ""), + }, + []tK{ + {"attachID", "primary", "", false}, + }, + ) + + createTable("revisions", mysqlPre, mysqlCol, + []tC{ + {"reviseID", "int", 0, false, true, ""}, + text("content"), + {"contentID", "int", 0, false, false, ""}, + ccol("contentType", 100, "replies"), + createdAt(), + // TODO: Add a createdBy column? + }, + []tK{ + {"reviseID", "primary", "", false}, + }, + ) + + createTable("polls", mysqlPre, mysqlCol, + []tC{ + {"pollID", "int", 0, false, true, ""}, + {"parentID", "int", 0, false, false, "0"}, + ccol("parentTable", 100, "topics"), // topics, replies + {"type", "int", 0, false, false, "0"}, + {"options", "json", 0, false, false, ""}, + {"votes", "int", 0, false, false, "0"}, + }, + []tK{ + {"pollID", "primary", "", false}, + }, + ) + + createTable("polls_options", "", "", + []tC{ + {"pollID", "int", 0, false, false, ""}, + {"option", "int", 0, false, false, "0"}, + {"votes", "int", 0, false, false, "0"}, + }, nil, + ) + + createTable("polls_votes", mysqlPre, mysqlCol, + []tC{ + {"pollID", "int", 0, false, false, ""}, + {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"option", "int", 0, false, false, "0"}, + createdAt("castAt"), + ccol("ip", 200, "''"), + }, nil, + ) + + createTable("users_replies", mysqlPre, mysqlCol, + []tC{ + {"rid", "int", 0, false, true, ""}, + {"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + text("content"), + text("parsed_content"), + createdAt(), + {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"lastEdit", "int", 0, false, false, "0"}, + {"lastEditBy", "int", 0, false, false, "0"}, + ccol("ip", 200, "''"), + }, + []tK{ + {"rid", "primary", "", false}, + }, + ) + + createTable("likes", "", "", + []tC{ + {"weight", "tinyint", 0, false, false, "1"}, + {"targetItem", "int", 0, false, false, ""}, + ccol("targetType", 50, "replies"), + {"sentBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + createdAt(), + {"recalc", "tinyint", 0, false, false, "0"}, + }, nil, + ) + + //columns("participants,createdBy,createdAt,lastReplyBy,lastReplyAt").Where("cid=?") + createTable("conversations", "", "", + []tC{ + {"cid", "int", 0, false, true, ""}, + {"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + createdAt(), + {"lastReplyAt", "datetime", 0, false, false, ""}, + {"lastReplyBy", "int", 0, false, false, ""}, + }, + []tK{ + {"cid", "primary", "", false}, + }, + ) + + createTable("conversations_posts", "", "", + []tC{ + {"pid", "int", 0, false, true, ""}, + {"cid", "int", 0, false, false, ""}, + {"createdBy", "int", 0, false, false, ""}, + ccol("body", 50, ""), + ccol("post", 50, "''"), + }, + []tK{ + {"pid", "primary", "", false}, + }, + ) + + createTable("conversations_participants", "", "", + []tC{ + {"uid", "int", 0, false, false, ""}, + {"cid", "int", 0, false, false, ""}, + }, nil, + ) + + /* + createTable("users_friends", "", "", + []tC{ + {"uid", "int", 0, false, false, ""}, + {"uid2", "int", 0, false, false, ""}, + }, nil, + ) + createTable("users_friends_invites", "", "", + []tC{ + {"requester", "int", 0, false, false, ""}, + {"target", "int", 0, false, false, ""}, + }, nil, + ) + */ + + createTable("users_blocks", "", "", + []tC{ + {"blocker", "int", 0, false, false, ""}, + {"blockedUser", "int", 0, false, false, ""}, + }, nil, + ) + + createTable("activity_stream_matches", "", "", + []tC{ + {"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + }, + []tK{ + {"asid,asid", "foreign", "activity_stream", true}, + }, + ) + + createTable("activity_stream", "", "", + []tC{ + {"asid", "int", 0, false, true, ""}, + {"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key + {"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */ + ccol("event", 50, ""), /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */ + ccol("elementType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ + + // replacement for elementType + tC{"elementTable", "int", 0, false, false, "0"}, + + {"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ + createdAt(), + ccol("extra", 200, "''"), + }, + []tK{ + {"asid", "primary", "", false}, + }, + ) + + createTable("activity_subscriptions", "", "", + []tC{ + {"user", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ + ccol("targetType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ + {"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/ + }, nil, + ) + + /* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */ + createTable("settings", "", "", + []tC{ + ccol("name", 180, ""), + ccol("content", 250, ""), + ccol("type", 50, ""), + ccol("constraints", 200, "''"), + }, + []tK{ + {"name", "unique", "", false}, + }, + ) + + createTable("word_filters", "", "", + []tC{ + {"wfid", "int", 0, false, true, ""}, + ccol("find", 200, ""), + ccol("replacement", 200, ""), + }, + []tK{ + {"wfid", "primary", "", false}, + }, + ) + + createTable("plugins", "", "", + []tC{ + ccol("uname", 180, ""), + bcol("active", false), + bcol("installed", false), + }, + []tK{ + {"uname", "unique", "", false}, + }, + ) + + createTable("themes", "", "", + []tC{ + ccol("uname", 180, ""), + bcol("default", false), + //text("profileUserVars"), + }, + []tK{ + {"uname", "unique", "", false}, + }, + ) + + createTable("widgets", "", "", + []tC{ + {"wid", "int", 0, false, true, ""}, + {"position", "int", 0, false, false, ""}, + ccol("side", 100, ""), + ccol("type", 100, ""), + bcol("active", false), + ccol("location", 100, ""), + text("data"), + }, + []tK{ + {"wid", "primary", "", false}, + }, + ) + + createTable("menus", "", "", + []tC{ + {"mid", "int", 0, false, true, ""}, + }, + []tK{ + {"mid", "primary", "", false}, + }, + ) + + createTable("menu_items", "", "", + []tC{ + {"miid", "int", 0, false, true, ""}, + {"mid", "int", 0, false, false, ""}, + ccol("name", 200, "''"), + ccol("htmlID", 200, "''"), + ccol("cssClass", 200, "''"), + ccol("position", 100, ""), + ccol("path", 200, "''"), + ccol("aria", 200, "''"), + ccol("tooltip", 200, "''"), + ccol("tmplName", 200, "''"), + {"order", "int", 0, false, false, "0"}, + + bcol("guestOnly", false), + bcol("memberOnly", false), + bcol("staffOnly", false), + bcol("adminOnly", false), + }, + []tK{ + {"miid", "primary", "", false}, + }, + ) + + createTable("pages", mysqlPre, mysqlCol, + []tC{ + {"pid", "int", 0, false, true, ""}, + //ccol("path", 200, ""), + ccol("name", 200, ""), + ccol("title", 200, ""), + text("body"), + // TODO: Make this a table? + text("allowedGroups"), + {"menuID", "int", 0, false, false, "-1"}, // simple sidebar menu + }, + []tK{ + {"pid", "primary", "", false}, + }, + ) + + createTable("registration_logs", "", "", + []tC{ + {"rlid", "int", 0, false, true, ""}, + ccol("username", 100, ""), + {"email", "varchar", 100, false, false, ""}, + ccol("failureReason", 100, ""), + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), + createdAt("doneAt"), + }, + []tK{ + {"rlid", "primary", "", false}, + }, + ) + + createTable("login_logs", "", "", + []tC{ + {"lid", "int", 0, false, true, ""}, + {"uid", "int", 0, false, false, ""}, + + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), + createdAt("doneAt"), + }, + []tK{ + {"lid", "primary", "", false}, + }, + ) + + createTable("moderation_logs", "", "", + []tC{ + ccol("action", 100, ""), + {"elementID", "int", 0, false, false, ""}, + ccol("elementType", 100, ""), + ccol("ipaddress", 200, ""), + {"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"doneAt", "datetime", 0, false, false, ""}, + text("extra"), + }, nil, + ) + + createTable("administration_logs", "", "", + []tC{ + ccol("action", 100, ""), + {"elementID", "int", 0, false, false, ""}, + ccol("elementType", 100, ""), + ccol("ipaddress", 200, ""), + {"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key + {"doneAt", "datetime", 0, false, false, ""}, + text("extra"), + }, nil, + ) + + createTable("viewchunks", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"avg", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + ccol("route", 200, ""), // TODO: set a default empty here + }, nil, + ) + + createTable("viewchunks_agents", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + ccol("browser", 200, ""), // googlebot, firefox, opera, etc. + //ccol("version",0,""), // the version of the browser or bot + }, nil, + ) + + createTable("viewchunks_systems", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + ccol("system", 200, ""), // windows, android, unknown, etc. + }, nil, + ) + + createTable("viewchunks_langs", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + ccol("lang", 200, ""), // en, ru, etc. + }, nil, + ) + + createTable("viewchunks_referrers", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + ccol("domain", 200, ""), + }, nil, + ) + + createTable("viewchunks_forums", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + {"forum", "int", 0, false, false, ""}, + }, nil, + ) + + createTable("topicchunks", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + // TODO: Add a column for the parent forum? + }, nil, + ) + + createTable("postchunks", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + // TODO: Add a column for the parent topic / profile? + }, nil, + ) + + createTable("memchunks", "", "", + []tC{ + {"count", "int", 0, false, false, "0"}, + {"stack", "int", 0, false, false, "0"}, + {"heap", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + }, nil, + ) + + createTable("perfchunks", "", "", + []tC{ + {"low", "int", 0, false, false, "0"}, + {"high", "int", 0, false, false, "0"}, + {"avg", "int", 0, false, false, "0"}, + {"createdAt", "datetime", 0, false, false, ""}, + }, nil, + ) + + createTable("sync", "", "", + []tC{ + {"last_update", "datetime", 0, false, false, ""}, + }, nil, + ) + + createTable("updates", "", "", + []tC{ + {"dbVersion", "int", 0, false, false, "0"}, + }, nil, + ) + + createTable("meta", "", "", + []tC{ + ccol("name", 200, ""), + ccol("value", 200, ""), + }, nil, + ) + + /*createTable("tables", "", "", + []tC{ + {"id", "int", 0, false, true, ""}, + ccol("name", 200, ""), + }, + []tK{ + {"id", "primary", "", false}, + {"name", "unique", "", false}, + }, + )*/ + + return err } diff --git a/dev-update.bat b/dev-update.bat index 5e1f4be8..3f30a8a6 100644 --- a/dev-update.bat +++ b/dev-update.bat @@ -3,8 +3,8 @@ echo Updating the dependencies go get if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) go get -u github.com/mailru/easyjson/... @@ -12,18 +12,18 @@ go get -u github.com/mailru/easyjson/... echo Updating Gosora git stash if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) git pull origin master if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) git stash apply if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% + pause + exit /b %errorlevel% ) echo Patching Gosora diff --git a/gen_mysql.go b/gen_mysql.go index f54c7386..9bc78325 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -11,80 +11,80 @@ import "github.com/Azareal/Gosora/common" // nolint type Stmts struct { - forumEntryExists *sql.Stmt - groupEntryExists *sql.Stmt - getForumTopics *sql.Stmt - addForumPermsToForum *sql.Stmt - updateEmail *sql.Stmt - setTempGroup *sql.Stmt - bumpSync *sql.Stmt - deleteActivityStreamMatch *sql.Stmt + forumEntryExists *sql.Stmt + groupEntryExists *sql.Stmt + getForumTopics *sql.Stmt + addForumPermsToForum *sql.Stmt + updateEmail *sql.Stmt + setTempGroup *sql.Stmt + bumpSync *sql.Stmt + deleteActivityStreamMatch *sql.Stmt - getActivityFeedByWatcher *sql.Stmt - getActivityCountByWatcher *sql.Stmt + getActivityFeedByWatcher *sql.Stmt + getActivityCountByWatcher *sql.Stmt - Mocks bool + Mocks bool } // nolint func _gen_mysql() (err error) { - common.DebugLog("Building the generated statements") - - common.DebugLog("Preparing forumEntryExists statement.") - stmts.forumEntryExists, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY `fid` ASC LIMIT 0,1") - if err != nil { - log.Print("Error in forumEntryExists statement.") - return err - } - - common.DebugLog("Preparing groupEntryExists statement.") - stmts.groupEntryExists, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' ORDER BY `gid` ASC LIMIT 0,1") - if err != nil { - log.Print("Error in groupEntryExists statement.") - return err - } - - common.DebugLog("Preparing getForumTopics statement.") - stmts.getForumTopics, err = db.Prepare("SELECT `topics`.`tid`, `topics`.`title`, `topics`.`content`, `topics`.`createdBy`, `topics`.`is_closed`, `topics`.`sticky`, `topics`.`createdAt`, `topics`.`lastReplyAt`, `topics`.`parentID`, `users`.`name`, `users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY `topics`.`sticky` DESC,`topics`.`lastReplyAt` DESC,`topics`.`createdBy` DESC") - if err != nil { - log.Print("Error in getForumTopics statement.") - return err - } - - common.DebugLog("Preparing addForumPermsToForum statement.") - stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)") - if err != nil { - log.Print("Error in addForumPermsToForum statement.") - return err - } - - common.DebugLog("Preparing updateEmail statement.") - stmts.updateEmail, err = db.Prepare("UPDATE `emails` SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") - if err != nil { - log.Print("Error in updateEmail statement.") - return err - } - - common.DebugLog("Preparing setTempGroup statement.") - stmts.setTempGroup, err = db.Prepare("UPDATE `users` SET `temp_group`= ? WHERE `uid` = ?") - if err != nil { - log.Print("Error in setTempGroup statement.") - return err - } - - common.DebugLog("Preparing bumpSync statement.") - stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update`= UTC_TIMESTAMP()") - if err != nil { - log.Print("Error in bumpSync statement.") - return err - } - - common.DebugLog("Preparing deleteActivityStreamMatch statement.") - stmts.deleteActivityStreamMatch, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?") - if err != nil { - log.Print("Error in deleteActivityStreamMatch statement.") - return err - } - - return nil + common.DebugLog("Building the generated statements") + + common.DebugLog("Preparing forumEntryExists statement.") + stmts.forumEntryExists, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY `fid` ASC LIMIT 0,1") + if err != nil { + log.Print("Error in forumEntryExists statement.") + return err + } + + common.DebugLog("Preparing groupEntryExists statement.") + stmts.groupEntryExists, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' ORDER BY `gid` ASC LIMIT 0,1") + if err != nil { + log.Print("Error in groupEntryExists statement.") + return err + } + + common.DebugLog("Preparing getForumTopics statement.") + stmts.getForumTopics, err = db.Prepare("SELECT `topics`.`tid`, `topics`.`title`, `topics`.`content`, `topics`.`createdBy`, `topics`.`is_closed`, `topics`.`sticky`, `topics`.`createdAt`, `topics`.`lastReplyAt`, `topics`.`parentID`, `users`.`name`, `users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY `topics`.`sticky` DESC,`topics`.`lastReplyAt` DESC,`topics`.`createdBy` DESC") + if err != nil { + log.Print("Error in getForumTopics statement.") + return err + } + + common.DebugLog("Preparing addForumPermsToForum statement.") + stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)") + if err != nil { + log.Print("Error in addForumPermsToForum statement.") + return err + } + + common.DebugLog("Preparing updateEmail statement.") + stmts.updateEmail, err = db.Prepare("UPDATE `emails` SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") + if err != nil { + log.Print("Error in updateEmail statement.") + return err + } + + common.DebugLog("Preparing setTempGroup statement.") + stmts.setTempGroup, err = db.Prepare("UPDATE `users` SET `temp_group`= ? WHERE `uid` = ?") + if err != nil { + log.Print("Error in setTempGroup statement.") + return err + } + + common.DebugLog("Preparing bumpSync statement.") + stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update`= UTC_TIMESTAMP()") + if err != nil { + log.Print("Error in bumpSync statement.") + return err + } + + common.DebugLog("Preparing deleteActivityStreamMatch statement.") + stmts.deleteActivityStreamMatch, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?") + if err != nil { + log.Print("Error in deleteActivityStreamMatch statement.") + return err + } + + return nil } diff --git a/gen_pgsql.go b/gen_pgsql.go index 34ac47b6..eee23d8a 100644 --- a/gen_pgsql.go +++ b/gen_pgsql.go @@ -9,48 +9,48 @@ import "github.com/Azareal/Gosora/common" // nolint type Stmts struct { - addForumPermsToForum *sql.Stmt - updateEmail *sql.Stmt - setTempGroup *sql.Stmt - bumpSync *sql.Stmt + addForumPermsToForum *sql.Stmt + updateEmail *sql.Stmt + setTempGroup *sql.Stmt + bumpSync *sql.Stmt - getActivityFeedByWatcher *sql.Stmt - getActivityCountByWatcher *sql.Stmt + getActivityFeedByWatcher *sql.Stmt + getActivityCountByWatcher *sql.Stmt - Mocks bool + Mocks bool } // nolint func _gen_pgsql() (err error) { - common.DebugLog("Building the generated statements") - - common.DebugLog("Preparing addForumPermsToForum statement.") - stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO \"forums_permissions\"(\"gid\",\"fid\",\"preset\",\"permissions\") VALUES (?,?,?,?)") - if err != nil { - log.Print("Error in addForumPermsToForum statement.") - return err - } - - common.DebugLog("Preparing updateEmail statement.") - stmts.updateEmail, err = db.Prepare("UPDATE \"emails\" SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") - if err != nil { - log.Print("Error in updateEmail statement.") - return err - } - - common.DebugLog("Preparing setTempGroup statement.") - stmts.setTempGroup, err = db.Prepare("UPDATE \"users\" SET `temp_group`= ? WHERE `uid` = ?") - if err != nil { - log.Print("Error in setTempGroup statement.") - return err - } - - common.DebugLog("Preparing bumpSync statement.") - stmts.bumpSync, err = db.Prepare("UPDATE \"sync\" SET `last_update`= LOCALTIMESTAMP()") - if err != nil { - log.Print("Error in bumpSync statement.") - return err - } - - return nil + common.DebugLog("Building the generated statements") + + common.DebugLog("Preparing addForumPermsToForum statement.") + stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO \"forums_permissions\"(\"gid\",\"fid\",\"preset\",\"permissions\") VALUES (?,?,?,?)") + if err != nil { + log.Print("Error in addForumPermsToForum statement.") + return err + } + + common.DebugLog("Preparing updateEmail statement.") + stmts.updateEmail, err = db.Prepare("UPDATE \"emails\" SET `email`= ?,`uid`= ?,`validated`= ?,`token`= ? WHERE `email` = ?") + if err != nil { + log.Print("Error in updateEmail statement.") + return err + } + + common.DebugLog("Preparing setTempGroup statement.") + stmts.setTempGroup, err = db.Prepare("UPDATE \"users\" SET `temp_group`= ? WHERE `uid` = ?") + if err != nil { + log.Print("Error in setTempGroup statement.") + return err + } + + common.DebugLog("Preparing bumpSync statement.") + stmts.bumpSync, err = db.Prepare("UPDATE \"sync\" SET `last_update`= LOCALTIMESTAMP()") + if err != nil { + log.Print("Error in bumpSync statement.") + return err + } + + return nil } diff --git a/gosora_example.service b/gosora_example.service deleted file mode 100644 index 97f147cd..00000000 --- a/gosora_example.service +++ /dev/null @@ -1,21 +0,0 @@ -# An example systemd service file -[Unit] -Description=Gosora - -[Service] -User=gosora -Group=www-data - -Restart=on-failure -RestartSec=10 -# Set these to the location of Gosora -WorkingDirectory=/home/gosora/src -AmbientCapabilities=CAP_NET_BIND_SERVICE -# Make sure you manually run pre-run-linux before you start the service -ExecStart=/home/gosora/src/Gosora - -ProtectSystem=full -PrivateDevices=true - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/install-docker b/install-docker deleted file mode 100644 index db918af3..00000000 --- a/install-docker +++ /dev/null @@ -1,7 +0,0 @@ -go get -u github.com/mailru/easyjson/... -easyjson -pkg common -go get - -go build -ldflags="-s -w" -o Installer "./cmd/install" - -./Installer --dbType=mysql --dbHost=localhost --dbUser=$MYSQL_USER --dbPassword=$MYSQL_PASSWORD --dbName=$MYSQL_DATABASE --shortSiteName=$SITE_SHORT_NAME --siteName=$SITE_NAME --siteURL=$SITE_URL --serverPort=$SERVER_PORT--secureServerPort=$SECURE_SERVER_PORT \ No newline at end of file diff --git a/install-linux b/install-linux deleted file mode 100644 index 4ea4e7a5..00000000 --- a/install-linux +++ /dev/null @@ -1,8 +0,0 @@ -echo "Installing the dependencies" -./update-deps-linux - -echo "Building the installer" -go build -ldflags="-s -w" -o Installer "./cmd/install" - -echo "Running the installer" -./Installer diff --git a/install.bat b/install.bat deleted file mode 100644 index 65254def..00000000 --- a/install.bat +++ /dev/null @@ -1,29 +0,0 @@ -@echo off - -echo Installing the dependencies -go get -u github.com/mailru/easyjson/... -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -easyjson -pkg common -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -go get -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) - -echo Building the installer -go generate -go build -ldflags="-s -w" "./cmd/install" -if %errorlevel% neq 0 ( - pause - exit /b %errorlevel% -) -install.exe diff --git a/install/install.go b/install/install.go index c0536977..4dff8bd9 100644 --- a/install/install.go +++ b/install/install.go @@ -1,48 +1,48 @@ package install import ( - "fmt" + "fmt" - qgen "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" ) var adapters = make(map[string]InstallAdapter) type InstallAdapter interface { - Name() string - DefaultPort() string - SetConfig(dbHost, dbUsername, dbPassword, dbName, dbPort string) - InitDatabase() error - TableDefs() error - InitialData() error - CreateAdmin() error + Name() string + DefaultPort() string + SetConfig(dbHost, dbUsername, dbPassword, dbName, dbPort string) + InitDatabase() error + TableDefs() error + InitialData() error + CreateAdmin() error - DBHost() string - DBUsername() string - DBPassword() string - DBName() string - DBPort() string + DBHost() string + DBUsername() string + DBPassword() string + DBName() string + DBPort() string } func Lookup(name string) (InstallAdapter, bool) { - adap, ok := adapters[name] - return adap, ok + adap, ok := adapters[name] + return adap, ok } func createAdmin() error { - fmt.Println("Creating the admin user") - hashedPassword, salt, e := BcryptGeneratePassword("password") - if e != nil { - return e - } + fmt.Println("Creating the admin user") + hashedPassword, salt, e := BcryptGeneratePassword("password") + if e != nil { + return e + } - // Build the admin user query - adminUserStmt, e := qgen.Builder.SimpleInsert("users", "name, password, salt, email, group, is_super_admin, active, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt, message, last_ip", "'Admin',?,?,'admin@localhost',1,1,1,UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''") - if e != nil { - return e - } + // Build the admin user query + adminUserStmt, e := qgen.Builder.SimpleInsert("users", "name, password, salt, email, group, is_super_admin, active, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt, message, last_ip", "'Admin',?,?,'admin@localhost',1,1,1,UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''") + if e != nil { + return e + } - // Run the admin user query - _, e = adminUserStmt.Exec(hashedPassword, salt) - return e + // Run the admin user query + _, e = adminUserStmt.Exec(hashedPassword, salt) + return e } diff --git a/install/mssql.go b/install/mssql.go index 7d176542..4c2023ea 100644 --- a/install/mssql.go +++ b/install/mssql.go @@ -7,165 +7,165 @@ package install import ( - "bytes" - "database/sql" - "fmt" - "io/ioutil" - "log" - "net/url" - "path/filepath" - "strconv" - "strings" + "bytes" + "database/sql" + "fmt" + "io/ioutil" + "log" + "net/url" + "path/filepath" + "strconv" + "strings" - "github.com/Azareal/Gosora/query_gen" - _ "github.com/denisenkom/go-mssqldb" + "github.com/Azareal/Gosora/query_gen" + _ "github.com/denisenkom/go-mssqldb" ) func init() { - adapters["mssql"] = &MssqlInstaller{dbHost: ""} + adapters["mssql"] = &MssqlInstaller{dbHost: ""} } type MssqlInstaller struct { - db *sql.DB - dbHost string - dbUsername string - dbPassword string - dbName string - dbInstance string - dbPort string + db *sql.DB + dbHost string + dbUsername string + dbPassword string + dbName string + dbInstance string + dbPort string } func (ins *MssqlInstaller) SetConfig(dbHost string, dbUsername string, dbPassword string, dbName string, dbPort string) { - ins.dbHost = dbHost - ins.dbUsername = dbUsername - ins.dbPassword = dbPassword - ins.dbName = dbName - ins.dbInstance = "" // You can't set this from the installer right now, it allows you to connect to a named instance instead of a port - ins.dbPort = dbPort + ins.dbHost = dbHost + ins.dbUsername = dbUsername + ins.dbPassword = dbPassword + ins.dbName = dbName + ins.dbInstance = "" // You can't set this from the installer right now, it allows you to connect to a named instance instead of a port + ins.dbPort = dbPort } func (ins *MssqlInstaller) Name() string { - return "mssql" + return "mssql" } func (ins *MssqlInstaller) DefaultPort() string { - return "1433" + return "1433" } func (ins *MssqlInstaller) InitDatabase() (err error) { - query := url.Values{} - query.Add("database", ins.dbName) - u := &url.URL{ - Scheme: "sqlserver", - User: url.UserPassword(ins.dbUsername, ins.dbPassword), - Host: ins.dbHost + ":" + ins.dbPort, - Path: ins.dbInstance, - RawQuery: query.Encode(), - } - log.Print("u.String() ", u.String()) + query := url.Values{} + query.Add("database", ins.dbName) + u := &url.URL{ + Scheme: "sqlserver", + User: url.UserPassword(ins.dbUsername, ins.dbPassword), + Host: ins.dbHost + ":" + ins.dbPort, + Path: ins.dbInstance, + RawQuery: query.Encode(), + } + log.Print("u.String() ", u.String()) - db, err := sql.Open("mssql", u.String()) - if err != nil { - return err - } + db, err := sql.Open("mssql", u.String()) + if err != nil { + return err + } - // Make sure that the connection is alive.. - err = db.Ping() - if err != nil { - return err - } - fmt.Println("Successfully connected to the database") + // Make sure that the connection is alive.. + err = db.Ping() + if err != nil { + return err + } + fmt.Println("Successfully connected to the database") - // TODO: Create the database, if it doesn't exist + // TODO: Create the database, if it doesn't exist - // Ready the query builder - ins.db = db - qgen.Builder.SetConn(db) - return qgen.Builder.SetAdapter("mssql") + // Ready the query builder + ins.db = db + qgen.Builder.SetConn(db) + return qgen.Builder.SetAdapter("mssql") } func (ins *MssqlInstaller) TableDefs() (err error) { - //fmt.Println("Creating the tables") - files, _ := ioutil.ReadDir("./schema/mssql/") - for _, f := range files { - if !strings.HasPrefix(f.Name(), "query_") { - continue - } + //fmt.Println("Creating the tables") + files, _ := ioutil.ReadDir("./schema/mssql/") + for _, f := range files { + if !strings.HasPrefix(f.Name(), "query_") { + continue + } - var table, ext string - table = strings.TrimPrefix(f.Name(), "query_") - ext = filepath.Ext(table) - if ext != ".sql" { - continue - } - table = strings.TrimSuffix(table, ext) + var table, ext string + table = strings.TrimPrefix(f.Name(), "query_") + ext = filepath.Ext(table) + if ext != ".sql" { + continue + } + table = strings.TrimSuffix(table, ext) - // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution - _, err = ins.db.Exec("DROP TABLE IF EXISTS [" + table + "];") - if err != nil { - fmt.Println("Failed query:", "DROP TABLE IF EXISTS ["+table+"]") - return err - } + // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution + _, err = ins.db.Exec("DROP TABLE IF EXISTS [" + table + "];") + if err != nil { + fmt.Println("Failed query:", "DROP TABLE IF EXISTS ["+table+"]") + return err + } - fmt.Println("Creating table '" + table + "'") - data, err := ioutil.ReadFile("./schema/mssql/" + f.Name()) - if err != nil { - return err - } - data = bytes.TrimSpace(data) + fmt.Println("Creating table '" + table + "'") + data, err := ioutil.ReadFile("./schema/mssql/" + f.Name()) + if err != nil { + return err + } + data = bytes.TrimSpace(data) - _, err = ins.db.Exec(string(data)) - if err != nil { - fmt.Println("Failed query:", string(data)) - return err - } - } - return nil + _, err = ins.db.Exec(string(data)) + if err != nil { + fmt.Println("Failed query:", string(data)) + return err + } + } + return nil } func (ins *MssqlInstaller) InitialData() (err error) { - //fmt.Println("Seeding the tables") - data, err := ioutil.ReadFile("./schema/mssql/inserts.sql") - if err != nil { - return err - } - data = bytes.TrimSpace(data) + //fmt.Println("Seeding the tables") + data, err := ioutil.ReadFile("./schema/mssql/inserts.sql") + if err != nil { + return err + } + data = bytes.TrimSpace(data) - statements := bytes.Split(data, []byte(";")) - for key, statement := range statements { - if len(statement) == 0 { - continue - } + statements := bytes.Split(data, []byte(";")) + for key, statement := range statements { + if len(statement) == 0 { + continue + } - fmt.Println("Executing query #" + strconv.Itoa(key) + " " + string(statement)) - _, err = ins.db.Exec(string(statement)) - if err != nil { - return err - } - } - return nil + fmt.Println("Executing query #" + strconv.Itoa(key) + " " + string(statement)) + _, err = ins.db.Exec(string(statement)) + if err != nil { + return err + } + } + return nil } func (ins *MssqlInstaller) CreateAdmin() error { - return createAdmin() + return createAdmin() } func (ins *MssqlInstaller) DBHost() string { - return ins.dbHost + return ins.dbHost } func (ins *MssqlInstaller) DBUsername() string { - return ins.dbUsername + return ins.dbUsername } func (ins *MssqlInstaller) DBPassword() string { - return ins.dbPassword + return ins.dbPassword } func (ins *MssqlInstaller) DBName() string { - return ins.dbName + return ins.dbName } func (ins *MssqlInstaller) DBPort() string { - return ins.dbPort + return ins.dbPort } diff --git a/install/mysql.go b/install/mysql.go index 09d2258d..16cb992c 100644 --- a/install/mysql.go +++ b/install/mysql.go @@ -7,177 +7,177 @@ package install import ( - "bytes" - "database/sql" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" + "bytes" + "database/sql" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" - "github.com/Azareal/Gosora/query_gen" - _ "github.com/go-sql-driver/mysql" + "github.com/Azareal/Gosora/query_gen" + _ "github.com/go-sql-driver/mysql" ) //var dbCollation string = "utf8mb4_general_ci" func init() { - adapters["mysql"] = &MysqlInstaller{dbHost: ""} + adapters["mysql"] = &MysqlInstaller{dbHost: ""} } type MysqlInstaller struct { - db *sql.DB - dbHost string - dbUsername string - dbPassword string - dbName string - dbPort string + db *sql.DB + dbHost string + dbUsername string + dbPassword string + dbName string + dbPort string } func (ins *MysqlInstaller) SetConfig(dbHost string, dbUsername string, dbPassword string, dbName string, dbPort string) { - ins.dbHost = dbHost - ins.dbUsername = dbUsername - ins.dbPassword = dbPassword - ins.dbName = dbName - ins.dbPort = dbPort + ins.dbHost = dbHost + ins.dbUsername = dbUsername + ins.dbPassword = dbPassword + ins.dbName = dbName + ins.dbPort = dbPort } func (ins *MysqlInstaller) Name() string { - return "mysql" + return "mysql" } func (ins *MysqlInstaller) DefaultPort() string { - return "3306" + return "3306" } func (ins *MysqlInstaller) dbExists(dbName string) (bool, error) { - var waste string - err := ins.db.QueryRow("SHOW DATABASES LIKE '" + dbName + "'").Scan(&waste) - if err != nil && err != sql.ErrNoRows { - return false, err - } else if err == sql.ErrNoRows { - return false, nil - } - return true, nil + var waste string + err := ins.db.QueryRow("SHOW DATABASES LIKE '" + dbName + "'").Scan(&waste) + if err != nil && err != sql.ErrNoRows { + return false, err + } else if err == sql.ErrNoRows { + return false, nil + } + return true, nil } func (ins *MysqlInstaller) InitDatabase() (err error) { - _dbPassword := ins.dbPassword - if _dbPassword != "" { - _dbPassword = ":" + _dbPassword - } - db, err := sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/") - if err != nil { - return err - } + _dbPassword := ins.dbPassword + if _dbPassword != "" { + _dbPassword = ":" + _dbPassword + } + db, err := sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/") + if err != nil { + return err + } - // Make sure that the connection is alive.. - err = db.Ping() - if err != nil { - return err - } - fmt.Println("Successfully connected to the database") + // Make sure that the connection is alive.. + err = db.Ping() + if err != nil { + return err + } + fmt.Println("Successfully connected to the database") - ins.db = db - ok, err := ins.dbExists(ins.dbName) - if err != nil { - return err - } + ins.db = db + ok, err := ins.dbExists(ins.dbName) + if err != nil { + return err + } - if !ok { - fmt.Println("Unable to find the database. Attempting to create it") - _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + ins.dbName) - if err != nil { - return err - } - fmt.Println("The database was successfully created") - } + if !ok { + fmt.Println("Unable to find the database. Attempting to create it") + _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + ins.dbName) + if err != nil { + return err + } + fmt.Println("The database was successfully created") + } - /*fmt.Println("Switching to database ", ins.dbName) - _, err = db.Exec("USE " + ins.dbName) - if err != nil { - return err - }*/ - db.Close() + /*fmt.Println("Switching to database ", ins.dbName) + _, err = db.Exec("USE " + ins.dbName) + if err != nil { + return err + }*/ + db.Close() - db, err = sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/"+ins.dbName) - if err != nil { - return err - } + db, err = sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/"+ins.dbName) + if err != nil { + return err + } - // Make sure that the connection is alive.. - err = db.Ping() - if err != nil { - return err - } - fmt.Println("Successfully connected to the database") + // Make sure that the connection is alive.. + err = db.Ping() + if err != nil { + return err + } + fmt.Println("Successfully connected to the database") - // Ready the query builder - ins.db = db - qgen.Builder.SetConn(db) - return qgen.Builder.SetAdapter("mysql") + // Ready the query builder + ins.db = db + qgen.Builder.SetConn(db) + return qgen.Builder.SetAdapter("mysql") } func (ins *MysqlInstaller) createTable(f os.FileInfo) error { - table := strings.TrimPrefix(f.Name(), "query_") - ext := filepath.Ext(table) - if ext != ".sql" { - return nil - } - table = strings.TrimSuffix(table, ext) + table := strings.TrimPrefix(f.Name(), "query_") + ext := filepath.Ext(table) + if ext != ".sql" { + return nil + } + table = strings.TrimSuffix(table, ext) - // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution - q := "DROP TABLE IF EXISTS `" + table + "`;" - _, err := ins.db.Exec(q) - if err != nil { - fmt.Println("Failed query:", q) - fmt.Println("e:", err) - return err - } + // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution + q := "DROP TABLE IF EXISTS `" + table + "`;" + _, err := ins.db.Exec(q) + if err != nil { + fmt.Println("Failed query:", q) + fmt.Println("e:", err) + return err + } - data, err := ioutil.ReadFile("./schema/mysql/" + f.Name()) - if err != nil { - return err - } - data = bytes.TrimSpace(data) + data, err := ioutil.ReadFile("./schema/mysql/" + f.Name()) + if err != nil { + return err + } + data = bytes.TrimSpace(data) - q = string(data) - _, err = ins.db.Exec(q) - if err != nil { - fmt.Println("Failed query:", q) - fmt.Println("e:", err) - return err - } - fmt.Printf("Created table '%s'\n", table) + q = string(data) + _, err = ins.db.Exec(q) + if err != nil { + fmt.Println("Failed query:", q) + fmt.Println("e:", err) + return err + } + fmt.Printf("Created table '%s'\n", table) - return nil + return nil } func (ins *MysqlInstaller) TableDefs() (err error) { - fmt.Println("Creating the tables") - files, err := ioutil.ReadDir("./schema/mysql/") - if err != nil { - return err - } + fmt.Println("Creating the tables") + files, err := ioutil.ReadDir("./schema/mysql/") + if err != nil { + return err + } - _, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 0;") - if err != nil { - return err - } + _, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 0;") + if err != nil { + return err + } - for _, f := range files { - if !strings.HasPrefix(f.Name(), "query_") { - continue - } - err := ins.createTable(f) - if err != nil { - return err - } - } + for _, f := range files { + if !strings.HasPrefix(f.Name(), "query_") { + continue + } + err := ins.createTable(f) + if err != nil { + return err + } + } - _, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 1;") - return err + _, err = ins.db.Exec("SET FOREIGN_KEY_CHECKS = 1;") + return err } // ? - Moved this here since it was breaking the installer, we need to add this at some point @@ -185,50 +185,50 @@ func (ins *MysqlInstaller) TableDefs() (err error) { /*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/ func (ins *MysqlInstaller) InitialData() error { - fmt.Println("Seeding the tables") - data, err := ioutil.ReadFile("./schema/mysql/inserts.sql") - if err != nil { - return err - } - data = bytes.TrimSpace(data) + fmt.Println("Seeding the tables") + data, err := ioutil.ReadFile("./schema/mysql/inserts.sql") + if err != nil { + return err + } + data = bytes.TrimSpace(data) - statements := bytes.Split(data, []byte(";")) - for key, sBytes := range statements { - statement := string(sBytes) - if statement == "" { - continue - } - statement += ";" + statements := bytes.Split(data, []byte(";")) + for key, sBytes := range statements { + statement := string(sBytes) + if statement == "" { + continue + } + statement += ";" - fmt.Println("Executing query #" + strconv.Itoa(key) + " " + statement) - _, err = ins.db.Exec(statement) - if err != nil { - return err - } - } - return nil + fmt.Println("Executing query #" + strconv.Itoa(key) + " " + statement) + _, err = ins.db.Exec(statement) + if err != nil { + return err + } + } + return nil } func (ins *MysqlInstaller) CreateAdmin() error { - return createAdmin() + return createAdmin() } func (ins *MysqlInstaller) DBHost() string { - return ins.dbHost + return ins.dbHost } func (ins *MysqlInstaller) DBUsername() string { - return ins.dbUsername + return ins.dbUsername } func (ins *MysqlInstaller) DBPassword() string { - return ins.dbPassword + return ins.dbPassword } func (ins *MysqlInstaller) DBName() string { - return ins.dbName + return ins.dbName } func (ins *MysqlInstaller) DBPort() string { - return ins.dbPort + return ins.dbPort } diff --git a/install/pgsql.go b/install/pgsql.go index 8d429ea1..163bd493 100644 --- a/install/pgsql.go +++ b/install/pgsql.go @@ -8,105 +8,105 @@ package install import ( - "database/sql" - "errors" - "fmt" - "strings" + "database/sql" + "errors" + "fmt" + "strings" - "github.com/Azareal/Gosora/query_gen" - _ "github.com/go-sql-driver/mysql" + "github.com/Azareal/Gosora/query_gen" + _ "github.com/go-sql-driver/mysql" ) // We don't need SSL to run an installer... Do we? var dbSslmode = "disable" func init() { - adapters["pgsql"] = &PgsqlInstaller{dbHost: ""} + adapters["pgsql"] = &PgsqlInstaller{dbHost: ""} } type PgsqlInstaller struct { - db *sql.DB - dbHost string - dbUsername string - dbPassword string - dbName string - dbPort string + db *sql.DB + dbHost string + dbUsername string + dbPassword string + dbName string + dbPort string } func (ins *PgsqlInstaller) SetConfig(dbHost string, dbUsername string, dbPassword string, dbName string, dbPort string) { - ins.dbHost = dbHost - ins.dbUsername = dbUsername - ins.dbPassword = dbPassword - ins.dbName = dbName - ins.dbPort = dbPort + ins.dbHost = dbHost + ins.dbUsername = dbUsername + ins.dbPassword = dbPassword + ins.dbName = dbName + ins.dbPort = dbPort } func (ins *PgsqlInstaller) Name() string { - return "pgsql" + return "pgsql" } func (ins *PgsqlInstaller) DefaultPort() string { - return "5432" + return "5432" } func (ins *PgsqlInstaller) InitDatabase() (err error) { - _dbPassword := ins.dbPassword - if _dbPassword != "" { - _dbPassword = " password=" + pgEscapeBit(_dbPassword) - } - db, err := sql.Open("postgres", "host='"+pgEscapeBit(ins.dbHost)+"' port='"+pgEscapeBit(ins.dbPort)+"' user='"+pgEscapeBit(ins.dbUsername)+"' dbname='"+pgEscapeBit(ins.dbName)+"'"+_dbPassword+" sslmode='"+dbSslmode+"'") - if err != nil { - return err - } + _dbPassword := ins.dbPassword + if _dbPassword != "" { + _dbPassword = " password=" + pgEscapeBit(_dbPassword) + } + db, err := sql.Open("postgres", "host='"+pgEscapeBit(ins.dbHost)+"' port='"+pgEscapeBit(ins.dbPort)+"' user='"+pgEscapeBit(ins.dbUsername)+"' dbname='"+pgEscapeBit(ins.dbName)+"'"+_dbPassword+" sslmode='"+dbSslmode+"'") + if err != nil { + return err + } - // Make sure that the connection is alive.. - err = db.Ping() - if err != nil { - return err - } - fmt.Println("Successfully connected to the database") + // Make sure that the connection is alive.. + err = db.Ping() + if err != nil { + return err + } + fmt.Println("Successfully connected to the database") - // TODO: Create the database, if it doesn't exist + // TODO: Create the database, if it doesn't exist - // Ready the query builder - ins.db = db - qgen.Builder.SetConn(db) - return qgen.Builder.SetAdapter("pgsql") + // Ready the query builder + ins.db = db + qgen.Builder.SetConn(db) + return qgen.Builder.SetAdapter("pgsql") } func (ins *PgsqlInstaller) TableDefs() (err error) { - return errors.New("TableDefs() not implemented") + return errors.New("TableDefs() not implemented") } func (ins *PgsqlInstaller) InitialData() (err error) { - return errors.New("InitialData() not implemented") + return errors.New("InitialData() not implemented") } func (ins *PgsqlInstaller) CreateAdmin() error { - return createAdmin() + return createAdmin() } func (ins *PgsqlInstaller) DBHost() string { - return ins.dbHost + return ins.dbHost } func (ins *PgsqlInstaller) DBUsername() string { - return ins.dbUsername + return ins.dbUsername } func (ins *PgsqlInstaller) DBPassword() string { - return ins.dbPassword + return ins.dbPassword } func (ins *PgsqlInstaller) DBName() string { - return ins.dbName + return ins.dbName } func (ins *PgsqlInstaller) DBPort() string { - return ins.dbPort + return ins.dbPort } func pgEscapeBit(bit string) string { - // TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? - return strings.Replace(bit, "'", "\\'", -1) + // TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? + return strings.Replace(bit, "'", "\\'", -1) } diff --git a/install/utils.go b/install/utils.go index 12567caf..7323f95b 100644 --- a/install/utils.go +++ b/install/utils.go @@ -8,20 +8,20 @@ const saltLength int = 32 // Generate a cryptographically secure set of random bytes.. func GenerateSafeString(length int) (string, error) { - rb := make([]byte, length) - _, err := rand.Read(rb) - if err != nil { - return "", err - } - return base64.StdEncoding.EncodeToString(rb), nil + rb := make([]byte, length) + _, err := rand.Read(rb) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(rb), nil } // Generate a bcrypt hash // Note: The salt is in the hash, therefore the salt value is blank func BcryptGeneratePassword(password string) (hash string, salt string, err error) { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return "", "", err - } - return string(hashedPassword), salt, nil + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", "", err + } + return string(hashedPassword), salt, nil } diff --git a/last_version.txt b/last_version.txt deleted file mode 100644 index 0d4d1249..00000000 --- a/last_version.txt +++ /dev/null @@ -1 +0,0 @@ -0.1.0-dev diff --git a/main.go b/main.go index 2b34d1dc..6297c342 100644 --- a/main.go +++ b/main.go @@ -1,43 +1,43 @@ /* * -* Gosora Main File -* Copyright Azareal 2016 - 2020 +* Gosora Main File +* Copyright Azareal 2016 - 2020 * */ // Package main contains the main initialisation logic for Gosora package main // import "github.com/Azareal/Gosora" import ( - "bytes" - "crypto/tls" - "flag" - "fmt" - "io" - "log" - "mime" - "net/http" - "os" - "os/signal" - "runtime" - "runtime/debug" - "runtime/pprof" - "strconv" - "strings" - "syscall" - "time" + "bytes" + "crypto/tls" + "flag" + "fmt" + "io" + "log" + "mime" + "net/http" + "os" + "os/signal" + "runtime" + "runtime/debug" + "runtime/pprof" + "strconv" + "strings" + "syscall" + "time" - c "github.com/Azareal/Gosora/common" - co "github.com/Azareal/Gosora/common/counters" - meta "github.com/Azareal/Gosora/common/meta" - p "github.com/Azareal/Gosora/common/phrases" - _ "github.com/Azareal/Gosora/extend" - qgen "github.com/Azareal/Gosora/query_gen" - "github.com/Azareal/Gosora/routes" - "github.com/Azareal/Gosora/uutils" - "github.com/fsnotify/fsnotify" + c "github.com/Azareal/Gosora/common" + co "github.com/Azareal/Gosora/common/counters" + meta "github.com/Azareal/Gosora/common/meta" + p "github.com/Azareal/Gosora/common/phrases" + _ "github.com/Azareal/Gosora/extend" + qgen "github.com/Azareal/Gosora/query_gen" + "github.com/Azareal/Gosora/routes" + "github.com/Azareal/Gosora/uutils" + "github.com/fsnotify/fsnotify" - //"github.com/lucas-clemente/quic-go/http3" - "github.com/pkg/errors" + //"github.com/lucas-clemente/quic-go/http3" + "github.com/pkg/errors" ) var router *GenRouter @@ -46,698 +46,698 @@ var router *GenRouter var globs *Globs type Globs struct { - stmts *Stmts + stmts *Stmts } // Temporary alias for renderTemplate func init() { - c.RenderTemplateAlias = routes.RenderTemplate + c.RenderTemplateAlias = routes.RenderTemplate } func afterDBInit() (err error) { - if err := storeInit(); err != nil { - return err - } - log.Print("Exitted storeInit") + if err := storeInit(); err != nil { + return err + } + log.Print("Exitted storeInit") - c.GzipStartEtag = "\"" + strconv.FormatInt(c.StartTime.Unix(), 10) + "-ng\"" - c.StartEtag = "\"" + strconv.FormatInt(c.StartTime.Unix(), 10) + "-n\"" + c.GzipStartEtag = "\"" + strconv.FormatInt(c.StartTime.Unix(), 10) + "-ng\"" + c.StartEtag = "\"" + strconv.FormatInt(c.StartTime.Unix(), 10) + "-n\"" - var uids []int - tc := c.Topics.GetCache() - if tc != nil { - log.Print("Preloading topics") - // Preload ten topics to get the wheels going - var count = 10 - if tc.GetCapacity() <= 10 { - count = 2 - if tc.GetCapacity() <= 2 { - count = 0 - } - } - group, err := c.Groups.Get(c.GuestUser.Group) - if err != nil { - return err - } + var uids []int + tc := c.Topics.GetCache() + if tc != nil { + log.Print("Preloading topics") + // Preload ten topics to get the wheels going + var count = 10 + if tc.GetCapacity() <= 10 { + count = 2 + if tc.GetCapacity() <= 2 { + count = 0 + } + } + group, err := c.Groups.Get(c.GuestUser.Group) + if err != nil { + return err + } - // TODO: Use the same cached data for both the topic list and the topic fetches... - tList, _, _, err := c.TopicList.GetListByCanSee(group.CanSee, 1, 0, nil) - if err != nil { - return err - } - ctList := make([]*c.TopicsRow, len(tList)) - copy(ctList, tList) + // TODO: Use the same cached data for both the topic list and the topic fetches... + tList, _, _, err := c.TopicList.GetListByCanSee(group.CanSee, 1, 0, nil) + if err != nil { + return err + } + ctList := make([]*c.TopicsRow, len(tList)) + copy(ctList, tList) - tList, _, _, err = c.TopicList.GetListByCanSee(group.CanSee, 2, 0, nil) - if err != nil { - return err - } - for _, tItem := range tList { - ctList = append(ctList, tItem) - } + tList, _, _, err = c.TopicList.GetListByCanSee(group.CanSee, 2, 0, nil) + if err != nil { + return err + } + for _, tItem := range tList { + ctList = append(ctList, tItem) + } - tList, _, _, err = c.TopicList.GetListByCanSee(group.CanSee, 3, 0, nil) - if err != nil { - return err - } - for _, tItem := range tList { - ctList = append(ctList, tItem) - } + tList, _, _, err = c.TopicList.GetListByCanSee(group.CanSee, 3, 0, nil) + if err != nil { + return err + } + for _, tItem := range tList { + ctList = append(ctList, tItem) + } - if count > len(ctList) { - count = len(ctList) - } - for i := 0; i < count; i++ { - _, _ = c.Topics.Get(ctList[i].ID) - } - } + if count > len(ctList) { + count = len(ctList) + } + for i := 0; i < count; i++ { + _, _ = c.Topics.Get(ctList[i].ID) + } + } - uc := c.Users.GetCache() - if uc != nil { - // Preload associated users too... - for _, uid := range uids { - _, _ = c.Users.Get(uid) - } - } + uc := c.Users.GetCache() + if uc != nil { + // Preload associated users too... + for _, uid := range uids { + _, _ = c.Users.Get(uid) + } + } - log.Print("Exitted afterDBInit") - return nil + log.Print("Exitted afterDBInit") + return nil } // Experimenting with a new error package here to try to reduce the amount of debugging we have to do // TODO: Dynamically register these items to avoid maintaining as much code here? func storeInit() (e error) { - acc := qgen.NewAcc() - ws := errors.WithStack - var rcache c.ReplyCache - if c.Config.ReplyCache == "static" { - rcache = c.NewMemoryReplyCache(c.Config.ReplyCacheCapacity) - } - c.Rstore, e = c.NewSQLReplyStore(acc, rcache) - if e != nil { - return ws(e) - } - c.Prstore, e = c.NewSQLProfileReplyStore(acc) - if e != nil { - return ws(e) - } - c.Likes, e = c.NewDefaultLikeStore(acc) - if e != nil { - return ws(e) - } - c.ForumActionStore, e = c.NewDefaultForumActionStore(acc) - if e != nil { - return ws(e) - } - c.Convos, e = c.NewDefaultConversationStore(acc) - if e != nil { - return ws(e) - } - c.UserBlocks, e = c.NewDefaultBlockStore(acc) - if e != nil { - return ws(e) - } - c.GroupPromotions, e = c.NewDefaultGroupPromotionStore(acc) - if e != nil { - return ws(e) - } + acc := qgen.NewAcc() + ws := errors.WithStack + var rcache c.ReplyCache + if c.Config.ReplyCache == "static" { + rcache = c.NewMemoryReplyCache(c.Config.ReplyCacheCapacity) + } + c.Rstore, e = c.NewSQLReplyStore(acc, rcache) + if e != nil { + return ws(e) + } + c.Prstore, e = c.NewSQLProfileReplyStore(acc) + if e != nil { + return ws(e) + } + c.Likes, e = c.NewDefaultLikeStore(acc) + if e != nil { + return ws(e) + } + c.ForumActionStore, e = c.NewDefaultForumActionStore(acc) + if e != nil { + return ws(e) + } + c.Convos, e = c.NewDefaultConversationStore(acc) + if e != nil { + return ws(e) + } + c.UserBlocks, e = c.NewDefaultBlockStore(acc) + if e != nil { + return ws(e) + } + c.GroupPromotions, e = c.NewDefaultGroupPromotionStore(acc) + if e != nil { + return ws(e) + } - if e = p.InitPhrases(c.Site.Language); e != nil { - return ws(e) - } - if e = c.InitEmoji(); e != nil { - return ws(e) - } - if e = c.InitWeakPasswords(); e != nil { - return ws(e) - } + if e = p.InitPhrases(c.Site.Language); e != nil { + return ws(e) + } + if e = c.InitEmoji(); e != nil { + return ws(e) + } + if e = c.InitWeakPasswords(); e != nil { + return ws(e) + } - log.Print("Loading the static files.") - if e = c.Themes.LoadStaticFiles(); e != nil { - return ws(e) - } - if e = c.StaticFiles.Init(); e != nil { - return ws(e) - } - if e = c.StaticFiles.JSTmplInit(); e != nil { - return ws(e) - } + log.Print("Loading the static files.") + if e = c.Themes.LoadStaticFiles(); e != nil { + return ws(e) + } + if e = c.StaticFiles.Init(); e != nil { + return ws(e) + } + if e = c.StaticFiles.JSTmplInit(); e != nil { + return ws(e) + } - log.Print("Initialising the widgets") - c.Widgets = c.NewDefaultWidgetStore() - if e = c.InitWidgets(); e != nil { - return ws(e) - } + log.Print("Initialising the widgets") + c.Widgets = c.NewDefaultWidgetStore() + if e = c.InitWidgets(); e != nil { + return ws(e) + } - log.Print("Initialising the menu item list") - c.Menus = c.NewDefaultMenuStore() - if e = c.Menus.Load(1); e != nil { // 1 = the default menu - return ws(e) - } - menuHold, e := c.Menus.Get(1) - if e != nil { - return ws(e) - } - fmt.Printf("menuHold: %+v\n", menuHold) - var b bytes.Buffer - menuHold.Build(&b, &c.GuestUser, "/") - fmt.Println("menuHold output: ", string(b.Bytes())) + log.Print("Initialising the menu item list") + c.Menus = c.NewDefaultMenuStore() + if e = c.Menus.Load(1); e != nil { // 1 = the default menu + return ws(e) + } + menuHold, e := c.Menus.Get(1) + if e != nil { + return ws(e) + } + fmt.Printf("menuHold: %+v\n", menuHold) + var b bytes.Buffer + menuHold.Build(&b, &c.GuestUser, "/") + fmt.Println("menuHold output: ", string(b.Bytes())) - log.Print("Initialising the authentication system") - c.Auth, e = c.NewDefaultAuth() - if e != nil { - return ws(e) - } + log.Print("Initialising the authentication system") + c.Auth, e = c.NewDefaultAuth() + if e != nil { + return ws(e) + } - log.Print("Initialising the stores") - c.WordFilters, e = c.NewDefaultWordFilterStore(acc) - if e != nil { - return ws(e) - } - c.MFAstore, e = c.NewSQLMFAStore(acc) - if e != nil { - return ws(e) - } - c.Pages, e = c.NewDefaultPageStore(acc) - if e != nil { - return ws(e) - } - c.Reports, e = c.NewDefaultReportStore(acc) - if e != nil { - return ws(e) - } - c.Emails, e = c.NewDefaultEmailStore(acc) - if e != nil { - return ws(e) - } - c.LoginLogs, e = c.NewLoginLogStore(acc) - if e != nil { - return ws(e) - } - c.RegLogs, e = c.NewRegLogStore(acc) - if e != nil { - return ws(e) - } - c.ModLogs, e = c.NewModLogStore(acc) - if e != nil { - return ws(e) - } - c.AdminLogs, e = c.NewAdminLogStore(acc) - if e != nil { - return ws(e) - } - c.IPSearch, e = c.NewDefaultIPSearcher() - if e != nil { - return ws(e) - } - if c.Config.Search == "" || c.Config.Search == "sql" { - c.RepliesSearch, e = c.NewSQLSearcher(acc) - if e != nil { - return ws(e) - } - } - c.Subscriptions, e = c.NewDefaultSubscriptionStore() - if e != nil { - return ws(e) - } - c.Attachments, e = c.NewDefaultAttachmentStore(acc) - if e != nil { - return ws(e) - } - c.Polls, e = c.NewDefaultPollStore(c.NewMemoryPollCache(100)) // TODO: Max number of polls held in cache, make this a config item - if e != nil { - return ws(e) - } - c.TopicList, e = c.NewDefaultTopicList(acc) - if e != nil { - return ws(e) - } - c.PasswordResetter, e = c.NewDefaultPasswordResetter(acc) - if e != nil { - return ws(e) - } - c.Analytics = c.NewDefaultAnalytics() - c.Activity, e = c.NewDefaultActivityStream(acc) - if e != nil { - return ws(e) - } - c.ActivityMatches, e = c.NewDefaultActivityStreamMatches(acc) - if e != nil { - return ws(e) - } - // TODO: Let the admin choose other thumbnailers, maybe ones defined in plugins - c.Thumbnailer = c.NewCaireThumbnailer() - c.Recalc, e = c.NewDefaultRecalc(acc) - if e != nil { - return ws(e) - } + log.Print("Initialising the stores") + c.WordFilters, e = c.NewDefaultWordFilterStore(acc) + if e != nil { + return ws(e) + } + c.MFAstore, e = c.NewSQLMFAStore(acc) + if e != nil { + return ws(e) + } + c.Pages, e = c.NewDefaultPageStore(acc) + if e != nil { + return ws(e) + } + c.Reports, e = c.NewDefaultReportStore(acc) + if e != nil { + return ws(e) + } + c.Emails, e = c.NewDefaultEmailStore(acc) + if e != nil { + return ws(e) + } + c.LoginLogs, e = c.NewLoginLogStore(acc) + if e != nil { + return ws(e) + } + c.RegLogs, e = c.NewRegLogStore(acc) + if e != nil { + return ws(e) + } + c.ModLogs, e = c.NewModLogStore(acc) + if e != nil { + return ws(e) + } + c.AdminLogs, e = c.NewAdminLogStore(acc) + if e != nil { + return ws(e) + } + c.IPSearch, e = c.NewDefaultIPSearcher() + if e != nil { + return ws(e) + } + if c.Config.Search == "" || c.Config.Search == "sql" { + c.RepliesSearch, e = c.NewSQLSearcher(acc) + if e != nil { + return ws(e) + } + } + c.Subscriptions, e = c.NewDefaultSubscriptionStore() + if e != nil { + return ws(e) + } + c.Attachments, e = c.NewDefaultAttachmentStore(acc) + if e != nil { + return ws(e) + } + c.Polls, e = c.NewDefaultPollStore(c.NewMemoryPollCache(100)) // TODO: Max number of polls held in cache, make this a config item + if e != nil { + return ws(e) + } + c.TopicList, e = c.NewDefaultTopicList(acc) + if e != nil { + return ws(e) + } + c.PasswordResetter, e = c.NewDefaultPasswordResetter(acc) + if e != nil { + return ws(e) + } + c.Analytics = c.NewDefaultAnalytics() + c.Activity, e = c.NewDefaultActivityStream(acc) + if e != nil { + return ws(e) + } + c.ActivityMatches, e = c.NewDefaultActivityStreamMatches(acc) + if e != nil { + return ws(e) + } + // TODO: Let the admin choose other thumbnailers, maybe ones defined in plugins + c.Thumbnailer = c.NewCaireThumbnailer() + c.Recalc, e = c.NewDefaultRecalc(acc) + if e != nil { + return ws(e) + } - log.Print("Initialising the meta store") - c.Meta, e = meta.NewDefaultMetaStore(acc) - if e != nil { - return ws(e) - } + log.Print("Initialising the meta store") + c.Meta, e = meta.NewDefaultMetaStore(acc) + if e != nil { + return ws(e) + } - log.Print("Initialising the view counters") - if !c.Config.DisableAnalytics { - co.GlobalViewCounter, e = co.NewGlobalViewCounter(acc) - if e != nil { - return ws(e) - } - co.AgentViewCounter, e = co.NewDefaultAgentViewCounter(acc) - if e != nil { - return ws(e) - } - co.OSViewCounter, e = co.NewDefaultOSViewCounter(acc) - if e != nil { - return ws(e) - } - co.LangViewCounter, e = co.NewDefaultLangViewCounter(acc) - if e != nil { - return ws(e) - } - if !c.Config.RefNoTrack { - co.ReferrerTracker, e = co.NewDefaultReferrerTracker() - if e != nil { - return ws(e) - } - } - co.MemoryCounter, e = co.NewMemoryCounter(acc) - if e != nil { - return ws(e) - } - co.PerfCounter, e = co.NewDefaultPerfCounter(acc) - if e != nil { - return ws(e) - } - } - co.RouteViewCounter, e = co.NewDefaultRouteViewCounter(acc) - if e != nil { - return ws(e) - } - co.PostCounter, e = co.NewPostCounter() - if e != nil { - return ws(e) - } - co.TopicCounter, e = co.NewTopicCounter() - if e != nil { - return ws(e) - } - co.TopicViewCounter, e = co.NewDefaultTopicViewCounter() - if e != nil { - return ws(e) - } - co.ForumViewCounter, e = co.NewDefaultForumViewCounter() - if e != nil { - return ws(e) - } + log.Print("Initialising the view counters") + if !c.Config.DisableAnalytics { + co.GlobalViewCounter, e = co.NewGlobalViewCounter(acc) + if e != nil { + return ws(e) + } + co.AgentViewCounter, e = co.NewDefaultAgentViewCounter(acc) + if e != nil { + return ws(e) + } + co.OSViewCounter, e = co.NewDefaultOSViewCounter(acc) + if e != nil { + return ws(e) + } + co.LangViewCounter, e = co.NewDefaultLangViewCounter(acc) + if e != nil { + return ws(e) + } + if !c.Config.RefNoTrack { + co.ReferrerTracker, e = co.NewDefaultReferrerTracker() + if e != nil { + return ws(e) + } + } + co.MemoryCounter, e = co.NewMemoryCounter(acc) + if e != nil { + return ws(e) + } + co.PerfCounter, e = co.NewDefaultPerfCounter(acc) + if e != nil { + return ws(e) + } + } + co.RouteViewCounter, e = co.NewDefaultRouteViewCounter(acc) + if e != nil { + return ws(e) + } + co.PostCounter, e = co.NewPostCounter() + if e != nil { + return ws(e) + } + co.TopicCounter, e = co.NewTopicCounter() + if e != nil { + return ws(e) + } + co.TopicViewCounter, e = co.NewDefaultTopicViewCounter() + if e != nil { + return ws(e) + } + co.ForumViewCounter, e = co.NewDefaultForumViewCounter() + if e != nil { + return ws(e) + } - return nil + return nil } // TODO: Split this function up func main() { - // TODO: Recover from panics - defer func() { - if r := recover(); r != nil { - log.Print(r) - debug.PrintStack() - log.Fatal("Fatal error.") - } - }() - c.StartTime = time.Now() + // TODO: Recover from panics + defer func() { + if r := recover(); r != nil { + log.Print(r) + debug.PrintStack() + log.Fatal("Fatal error.") + } + }() + c.StartTime = time.Now() - // TODO: Have a file for each run with the time/date the server started as the file name? - // TODO: Log panics with recover() - f, err := os.OpenFile("./logs/ops-"+strconv.FormatInt(c.StartTime.Unix(), 10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) - if err != nil { - log.Fatal(err) - } - //c.LogWriter = io.MultiWriter(os.Stderr, f) - c.LogWriter = io.MultiWriter(os.Stdout, f) - c.ErrLogWriter = io.MultiWriter(os.Stderr, f) - log.SetOutput(c.LogWriter) - c.ErrLogger = log.New(c.ErrLogWriter, "", log.LstdFlags) - log.Print("Running Gosora v" + c.SoftwareVersion.String()) - fmt.Println("") + // TODO: Have a file for each run with the time/date the server started as the file name? + // TODO: Log panics with recover() + f, err := os.OpenFile("./logs/ops-"+strconv.FormatInt(c.StartTime.Unix(), 10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) + if err != nil { + log.Fatal(err) + } + //c.LogWriter = io.MultiWriter(os.Stderr, f) + c.LogWriter = io.MultiWriter(os.Stdout, f) + c.ErrLogWriter = io.MultiWriter(os.Stderr, f) + log.SetOutput(c.LogWriter) + c.ErrLogger = log.New(c.ErrLogWriter, "", log.LstdFlags) + log.Print("Running Gosora v" + c.SoftwareVersion.String()) + fmt.Println("") - // TODO: Add a flag for enabling the profiler - if false { - f, err := os.Create(c.Config.LogDir + "cpu.prof") - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - } + // TODO: Add a flag for enabling the profiler + if false { + f, err := os.Create(c.Config.LogDir + "cpu.prof") + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + } - err = mime.AddExtensionType(".avif", "image/avif") - if err != nil { - log.Fatal(err) - } + err = mime.AddExtensionType(".avif", "image/avif") + if err != nil { + log.Fatal(err) + } - jsToken, err := c.GenerateSafeString(80) - if err != nil { - log.Fatal(err) - } - c.JSTokenBox.Store(jsToken) + jsToken, err := c.GenerateSafeString(80) + if err != nil { + log.Fatal(err) + } + c.JSTokenBox.Store(jsToken) - log.Print("Loading the configuration data") - err = c.LoadConfig() - if err != nil { - log.Fatal(err) - } - log.Print("Processing configuration data") - err = c.ProcessConfig() - if err != nil { - log.Fatal(err) - } - if c.Config.DisableStdout { - c.LogWriter = f - log.SetOutput(c.LogWriter) - } - if c.Config.DisableStderr { - c.ErrLogWriter = f - c.ErrLogger = log.New(c.ErrLogWriter, "", log.LstdFlags) - } - c.Tasks = c.NewScheduledTasks() + log.Print("Loading the configuration data") + err = c.LoadConfig() + if err != nil { + log.Fatal(err) + } + log.Print("Processing configuration data") + err = c.ProcessConfig() + if err != nil { + log.Fatal(err) + } + if c.Config.DisableStdout { + c.LogWriter = f + log.SetOutput(c.LogWriter) + } + if c.Config.DisableStderr { + c.ErrLogWriter = f + c.ErrLogger = log.New(c.ErrLogWriter, "", log.LstdFlags) + } + c.Tasks = c.NewScheduledTasks() - err = c.InitTemplates() - if err != nil { - log.Fatal(err) - } - c.Themes, err = c.NewThemeList() - if err != nil { - log.Fatal(err) - } - c.TopicListThaw = c.NewSingleServerThaw() + err = c.InitTemplates() + if err != nil { + log.Fatal(err) + } + c.Themes, err = c.NewThemeList() + if err != nil { + log.Fatal(err) + } + c.TopicListThaw = c.NewSingleServerThaw() - err = InitDatabase() - if err != nil { - log.Fatal(err) - } - defer db.Close() + err = InitDatabase() + if err != nil { + log.Fatal(err) + } + defer db.Close() - buildTemplates := flag.Bool("build-templates", false, "build the templates") - flag.Parse() - if *buildTemplates { - if err = c.CompileTemplates(); err != nil { - log.Fatal(err) - } - if err = c.CompileJSTemplates(); err != nil { - log.Fatal(err) - } - return - } + buildTemplates := flag.Bool("build-templates", false, "build the templates") + flag.Parse() + if *buildTemplates { + if err = c.CompileTemplates(); err != nil { + log.Fatal(err) + } + if err = c.CompileJSTemplates(); err != nil { + log.Fatal(err) + } + return + } - err = afterDBInit() - if err != nil { - log.Fatalf("%+v", err) - } - err = c.VerifyConfig() - if err != nil { - log.Fatal(err) - } + err = afterDBInit() + if err != nil { + log.Fatalf("%+v", err) + } + err = c.VerifyConfig() + if err != nil { + log.Fatal(err) + } - if !c.Dev.NoFsnotify { - log.Print("Initialising the file watcher") - watcher, err := fsnotify.NewWatcher() - if err != nil { - log.Fatal(err) - } - defer watcher.Close() + if !c.Dev.NoFsnotify { + log.Print("Initialising the file watcher") + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() - go func() { - defer c.EatPanics() - var ErrFileSkip = errors.New("skip mod file") - modifiedFileEvent := func(path string) error { - pathBits := strings.Split(path, "\\") - if len(pathBits) == 0 { - return nil - } - if pathBits[0] == "themes" { - var themeName string - if len(pathBits) >= 2 { - themeName = pathBits[1] - } - if len(pathBits) >= 3 && pathBits[2] == "public" { - // TODO: Handle new themes freshly plopped into the folder? - theme, ok := c.Themes[themeName] - if ok { - return theme.LoadStaticFiles() - } - } - } - return ErrFileSkip - } + go func() { + defer c.EatPanics() + var ErrFileSkip = errors.New("skip mod file") + modifiedFileEvent := func(path string) error { + pathBits := strings.Split(path, "\\") + if len(pathBits) == 0 { + return nil + } + if pathBits[0] == "themes" { + var themeName string + if len(pathBits) >= 2 { + themeName = pathBits[1] + } + if len(pathBits) >= 3 && pathBits[2] == "public" { + // TODO: Handle new themes freshly plopped into the folder? + theme, ok := c.Themes[themeName] + if ok { + return theme.LoadStaticFiles() + } + } + } + return ErrFileSkip + } - // TODO: Expand this to more types of files - var err error - for { - select { - case ev := <-watcher.Events: - // TODO: Handle file deletes (and renames more graciously by removing the old version of it) - if ev.Op&fsnotify.Write == fsnotify.Write { - err = modifiedFileEvent(ev.Name) - if err != ErrFileSkip { - log.Println("modified file:", ev.Name) - } else { - err = nil - } - } else if ev.Op&fsnotify.Create == fsnotify.Create { - log.Println("new file:", ev.Name) - err = modifiedFileEvent(ev.Name) - } else { - log.Println("unknown event:", ev) - err = nil - } - if err != nil { - c.LogError(err) - } - case err = <-watcher.Errors: - c.LogWarning(err) - } - } - }() + // TODO: Expand this to more types of files + var err error + for { + select { + case ev := <-watcher.Events: + // TODO: Handle file deletes (and renames more graciously by removing the old version of it) + if ev.Op&fsnotify.Write == fsnotify.Write { + err = modifiedFileEvent(ev.Name) + if err != ErrFileSkip { + log.Println("modified file:", ev.Name) + } else { + err = nil + } + } else if ev.Op&fsnotify.Create == fsnotify.Create { + log.Println("new file:", ev.Name) + err = modifiedFileEvent(ev.Name) + } else { + log.Println("unknown event:", ev) + err = nil + } + if err != nil { + c.LogError(err) + } + case err = <-watcher.Errors: + c.LogWarning(err) + } + } + }() - // TODO: Keep tabs on the (non-resource) theme stuff, and the langpacks - err = watcher.Add("./public") - if err != nil { - log.Fatal(err) - } - err = watcher.Add("./templates") - if err != nil { - log.Fatal(err) - } - for _, theme := range c.Themes { - err = watcher.Add("./themes/" + theme.Name + "/public") - if err != nil { - log.Fatal(err) - } - } - } + // TODO: Keep tabs on the (non-resource) theme stuff, and the langpacks + err = watcher.Add("./public") + if err != nil { + log.Fatal(err) + } + err = watcher.Add("./templates") + if err != nil { + log.Fatal(err) + } + for _, theme := range c.Themes { + err = watcher.Add("./themes/" + theme.Name + "/public") + if err != nil { + log.Fatal(err) + } + } + } - /*if err = c.StaticFiles.GenJS(); err != nil { - c.LogError(err) - }*/ + /*if err = c.StaticFiles.GenJS(); err != nil { + c.LogError(err) + }*/ - log.Print("Checking for init tasks") - if err = sched(); err != nil { - c.LogError(err) - } + log.Print("Checking for init tasks") + if err = sched(); err != nil { + c.LogError(err) + } - log.Print("Initialising the task system") + log.Print("Initialising the task system") - // Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests - // TODO: Could we expand this to attachments and other things too? - thumbChan := make(chan bool) - go c.ThumbTask(thumbChan) - if err = tickLoop(thumbChan); err != nil { - c.LogError(err) - } - go TickLoop.Loop() + // Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests + // TODO: Could we expand this to attachments and other things too? + thumbChan := make(chan bool) + go c.ThumbTask(thumbChan) + if err = tickLoop(thumbChan); err != nil { + c.LogError(err) + } + go TickLoop.Loop() - // Resource Management Goroutine - go func() { - defer c.EatPanics() - uc, tc := c.Users.GetCache(), c.Topics.GetCache() - if uc == nil && tc == nil { - return - } + // Resource Management Goroutine + go func() { + defer c.EatPanics() + uc, tc := c.Users.GetCache(), c.Topics.GetCache() + if uc == nil && tc == nil { + return + } - var lastEvictedCount int - var couldNotDealloc bool - secondTicker := time.NewTicker(time.Second) - for { - select { - case <-secondTicker.C: - // TODO: Add a LastRequested field to cached User structs to avoid evicting the same things which wind up getting loaded again anyway? - if uc != nil { - ucap := uc.GetCapacity() - if uc.Length() <= ucap || c.Users.Count() <= ucap { - couldNotDealloc = false - continue - } - lastEvictedCount = uc.DeallocOverflow(couldNotDealloc) - couldNotDealloc = (lastEvictedCount == 0) - } - } - } - }() + var lastEvictedCount int + var couldNotDealloc bool + secondTicker := time.NewTicker(time.Second) + for { + select { + case <-secondTicker.C: + // TODO: Add a LastRequested field to cached User structs to avoid evicting the same things which wind up getting loaded again anyway? + if uc != nil { + ucap := uc.GetCapacity() + if uc.Length() <= ucap || c.Users.Count() <= ucap { + couldNotDealloc = false + continue + } + lastEvictedCount = uc.DeallocOverflow(couldNotDealloc) + couldNotDealloc = (lastEvictedCount == 0) + } + } + } + }() - log.Print("Initialising the router") - router, err = NewGenRouter(&RouterConfig{ - Uploads: http.FileServer(http.Dir("./uploads")), - }) - if err != nil { - log.Fatal(err) - } + log.Print("Initialising the router") + router, err = NewGenRouter(&RouterConfig{ + Uploads: http.FileServer(http.Dir("./uploads")), + }) + if err != nil { + log.Fatal(err) + } - log.Print("Initialising the plugins") - c.InitPlugins() + log.Print("Initialising the plugins") + c.InitPlugins() - log.Print("Setting up the signal handler") - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - go func() { - defer c.EatPanics() - sig := <-sigs - log.Print("Received a signal to shutdown: ", sig) - // TODO: Gracefully shutdown the HTTP server - tw, cn := c.NewTickWatch(), uutils.Nanotime() - tw.Name = "shutdown" - tw.Set(&tw.Start, cn) - tw.Set(&tw.DBCheck, cn) - tw.Run() - n, e := func() (string, error) { - if e := runHook("before_shutdown_tick"); e != nil { - return "before_shutdown_tick ", e - } - tw.Set(&tw.StartHook, uutils.Nanotime()) - log.Print("Running shutdown tasks") - if e := c.Tasks.Shutdown.Run(); e != nil { - return "shutdown tasks ", e - } - tw.Set(&tw.Tasks, uutils.Nanotime()) - log.Print("Ran shutdown tasks") - if e := runHook("after_shutdown_tick"); e != nil { - return "after_shutdown_tick ", e - } - tw.Set(&tw.EndHook, uutils.Nanotime()) - return "", nil - }() - if e != nil { - log.Print(n+" err:", e) - } - tw.Stop() - log.Print("Stopping server") - c.StoppedServer("Stopped server") - }() + log.Print("Setting up the signal handler") + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + defer c.EatPanics() + sig := <-sigs + log.Print("Received a signal to shutdown: ", sig) + // TODO: Gracefully shutdown the HTTP server + tw, cn := c.NewTickWatch(), uutils.Nanotime() + tw.Name = "shutdown" + tw.Set(&tw.Start, cn) + tw.Set(&tw.DBCheck, cn) + tw.Run() + n, e := func() (string, error) { + if e := runHook("before_shutdown_tick"); e != nil { + return "before_shutdown_tick ", e + } + tw.Set(&tw.StartHook, uutils.Nanotime()) + log.Print("Running shutdown tasks") + if e := c.Tasks.Shutdown.Run(); e != nil { + return "shutdown tasks ", e + } + tw.Set(&tw.Tasks, uutils.Nanotime()) + log.Print("Ran shutdown tasks") + if e := runHook("after_shutdown_tick"); e != nil { + return "after_shutdown_tick ", e + } + tw.Set(&tw.EndHook, uutils.Nanotime()) + return "", nil + }() + if e != nil { + log.Print(n+" err:", e) + } + tw.Stop() + log.Print("Stopping server") + c.StoppedServer("Stopped server") + }() - // Start up the WebSocket ticks - c.WsHub.Start() + // Start up the WebSocket ticks + c.WsHub.Start() - if false { - f, e := os.Create(c.Config.LogDir + "cpu.prof") - if e != nil { - log.Fatal(e) - } - pprof.StartCPUProfile(f) - } + if false { + f, e := os.Create(c.Config.LogDir + "cpu.prof") + if e != nil { + log.Fatal(e) + } + pprof.StartCPUProfile(f) + } - //if profiling { - // pprof.StopCPUProfile() - //} - startServer() - args := <-c.StopServerChan - if false { - pprof.StopCPUProfile() - f, err := os.Create(c.Config.LogDir + "mem.prof") - if err != nil { - log.Fatal(err) - } - defer f.Close() + //if profiling { + // pprof.StopCPUProfile() + //} + startServer() + args := <-c.StopServerChan + if false { + pprof.StopCPUProfile() + f, err := os.Create(c.Config.LogDir + "mem.prof") + if err != nil { + log.Fatal(err) + } + defer f.Close() - runtime.GC() - err = pprof.WriteHeapProfile(f) - if err != nil { - log.Fatal(err) - } - } - // Why did the server stop? - log.Fatal(args...) + runtime.GC() + err = pprof.WriteHeapProfile(f) + if err != nil { + log.Fatal(err) + } + } + // Why did the server stop? + log.Fatal(args...) } func startServer() { - // We might not need timeouts, if we're behind a reverse-proxy like Nginx - newServer := func(addr string, h http.Handler) *http.Server { - f := func(timeout, dval int) int { - if timeout == 0 { - timeout = dval - } else if timeout == -1 { - timeout = 0 - } - return timeout - } - rtime := f(c.Config.ReadTimeout, 8) - wtime := f(c.Config.WriteTimeout, 10) - itime := f(c.Config.IdleTimeout, 120) - return &http.Server{ - Addr: addr, - Handler: h, - ConnState: c.ConnWatch.StateChange, + // We might not need timeouts, if we're behind a reverse-proxy like Nginx + newServer := func(addr string, h http.Handler) *http.Server { + f := func(timeout, dval int) int { + if timeout == 0 { + timeout = dval + } else if timeout == -1 { + timeout = 0 + } + return timeout + } + rtime := f(c.Config.ReadTimeout, 8) + wtime := f(c.Config.WriteTimeout, 10) + itime := f(c.Config.IdleTimeout, 120) + return &http.Server{ + Addr: addr, + Handler: h, + ConnState: c.ConnWatch.StateChange, - ReadTimeout: time.Duration(rtime) * time.Second, - WriteTimeout: time.Duration(wtime) * time.Second, - IdleTimeout: time.Duration(itime) * time.Second, + ReadTimeout: time.Duration(rtime) * time.Second, + WriteTimeout: time.Duration(wtime) * time.Second, + IdleTimeout: time.Duration(itime) * time.Second, - TLSConfig: &tls.Config{ - PreferServerCipherSuites: true, - CurvePreferences: []tls.CurveID{ - tls.CurveP256, - tls.X25519, - }, - }, - } - } + TLSConfig: &tls.Config{ + PreferServerCipherSuites: true, + CurvePreferences: []tls.CurveID{ + tls.CurveP256, + tls.X25519, + }, + }, + } + } - // TODO: Let users run *both* HTTP and HTTPS - log.Print("Initialising the HTTP server") - /*if c.Dev.QuicPort != 0 { - sQuicPort := strconv.Itoa(c.Dev.QuicPort) - log.Print("Listening on quic port " + sQuicPort) - go func() { - defer c.EatPanics() - c.StoppedServer(http3.ListenAndServeQUIC(":"+sQuicPort, c.Config.SslFullchain, c.Config.SslPrivkey, router)) - }() - }*/ + // TODO: Let users run *both* HTTP and HTTPS + log.Print("Initialising the HTTP server") + /*if c.Dev.QuicPort != 0 { + sQuicPort := strconv.Itoa(c.Dev.QuicPort) + log.Print("Listening on quic port " + sQuicPort) + go func() { + defer c.EatPanics() + c.StoppedServer(http3.ListenAndServeQUIC(":"+sQuicPort, c.Config.SslFullchain, c.Config.SslPrivkey, router)) + }() + }*/ - if !c.Site.EnableSsl { - if c.Site.Port == "" { - c.Site.Port = "80" - } - log.Print("Listening on port " + c.Site.Port) - go func() { - defer c.EatPanics() - c.StoppedServer(newServer(":"+c.Site.Port, router).ListenAndServe()) - }() - return - } + if !c.Site.EnableSsl { + if c.Site.Port == "" { + c.Site.Port = "80" + } + log.Print("Listening on port " + c.Site.Port) + go func() { + defer c.EatPanics() + c.StoppedServer(newServer(":"+c.Site.Port, router).ListenAndServe()) + }() + return + } - if c.Site.Port == "" { - c.Site.Port = "443" - } - if c.Site.Port == "80" || c.Site.Port == "443" { - // We should also run the server on port 80 - // TODO: Redirect to port 443 - go func() { - defer c.EatPanics() - log.Print("Listening on port 80") - c.StoppedServer(newServer(":80", &HTTPSRedirect{}).ListenAndServe()) - }() - } - log.Printf("Listening on port %s", c.Site.Port) - go func() { - defer c.EatPanics() - c.StoppedServer(newServer(":"+c.Site.Port, router).ListenAndServeTLS(c.Config.SslFullchain, c.Config.SslPrivkey)) - }() + if c.Site.Port == "" { + c.Site.Port = "443" + } + if c.Site.Port == "80" || c.Site.Port == "443" { + // We should also run the server on port 80 + // TODO: Redirect to port 443 + go func() { + defer c.EatPanics() + log.Print("Listening on port 80") + c.StoppedServer(newServer(":80", &HTTPSRedirect{}).ListenAndServe()) + }() + } + log.Printf("Listening on port %s", c.Site.Port) + go func() { + defer c.EatPanics() + c.StoppedServer(newServer(":"+c.Site.Port, router).ListenAndServeTLS(c.Config.SslFullchain, c.Config.SslPrivkey)) + }() } diff --git a/misc_test.go b/misc_test.go index 4eeeb718..ca880d5f 100644 --- a/misc_test.go +++ b/misc_test.go @@ -1,3325 +1,3325 @@ package main import ( - "bytes" - "database/sql" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "runtime/debug" - "strconv" - "testing" - "time" + "bytes" + "database/sql" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "os" + "runtime/debug" + "strconv" + "testing" + "time" - c "github.com/Azareal/Gosora/common" - "github.com/Azareal/Gosora/common/gauth" - "github.com/Azareal/Gosora/common/phrases" - "github.com/Azareal/Gosora/routes" - "github.com/pkg/errors" + c "github.com/Azareal/Gosora/common" + "github.com/Azareal/Gosora/common/gauth" + "github.com/Azareal/Gosora/common/phrases" + "github.com/Azareal/Gosora/routes" + "github.com/pkg/errors" ) func miscinit(t *testing.T) { - if err := gloinit(); err != nil { - t.Fatal(err) - } + if err := gloinit(); err != nil { + t.Fatal(err) + } } func recordMustExist(t *testing.T, err error, errmsg string, args ...interface{}) { - if err == ErrNoRows { - debug.PrintStack() - t.Errorf(errmsg, args...) - } else if err != nil { - debug.PrintStack() - t.Fatal(err) - } + if err == ErrNoRows { + debug.PrintStack() + t.Errorf(errmsg, args...) + } else if err != nil { + debug.PrintStack() + t.Fatal(err) + } } func recordMustNotExist(t *testing.T, err error, errmsg string, args ...interface{}) { - if err == nil { - debug.PrintStack() - t.Errorf(errmsg, args...) - } else if err != ErrNoRows { - debug.PrintStack() - t.Fatal(err) - } + if err == nil { + debug.PrintStack() + t.Errorf(errmsg, args...) + } else if err != ErrNoRows { + debug.PrintStack() + t.Fatal(err) + } } func TestUserStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } - var err error - uc := c.NewMemoryUserCache(c.Config.UserCacheCapacity) - c.Users, err = c.NewDefaultUserStore(uc) - expectNilErr(t, err) - uc.Flush() - userStoreTest(t, 2) - c.Users, err = c.NewDefaultUserStore(nil) - expectNilErr(t, err) - userStoreTest(t, 5) + var err error + uc := c.NewMemoryUserCache(c.Config.UserCacheCapacity) + c.Users, err = c.NewDefaultUserStore(uc) + expectNilErr(t, err) + uc.Flush() + userStoreTest(t, 2) + c.Users, err = c.NewDefaultUserStore(nil) + expectNilErr(t, err) + userStoreTest(t, 5) } func userStoreTest(t *testing.T, newUserID int) { - uc := c.Users.GetCache() - // Go doesn't have short-circuiting, so this'll allow us to do one liner tests - cacheLength := func(uc c.UserCache) int { - if uc == nil { - return 0 - } - return uc.Length() - } - isCacheLengthZero := func(uc c.UserCache) bool { - return cacheLength(uc) == 0 - } - ex, exf := exp(t), expf(t) - exf(isCacheLengthZero(uc), "The initial ucache length should be zero, not %d", cacheLength(uc)) + uc := c.Users.GetCache() + // Go doesn't have short-circuiting, so this'll allow us to do one liner tests + cacheLength := func(uc c.UserCache) int { + if uc == nil { + return 0 + } + return uc.Length() + } + isCacheLengthZero := func(uc c.UserCache) bool { + return cacheLength(uc) == 0 + } + ex, exf := exp(t), expf(t) + exf(isCacheLengthZero(uc), "The initial ucache length should be zero, not %d", cacheLength(uc)) - _, err := c.Users.Get(-1) - recordMustNotExist(t, err, "UID #-1 shouldn't exist") - exf(isCacheLengthZero(uc), "We found %d items in the user cache and it's supposed to be empty", cacheLength(uc)) - _, err = c.Users.Get(0) - recordMustNotExist(t, err, "UID #0 shouldn't exist") - exf(isCacheLengthZero(uc), "We found %d items in the user cache and it's supposed to be empty", cacheLength(uc)) + _, err := c.Users.Get(-1) + recordMustNotExist(t, err, "UID #-1 shouldn't exist") + exf(isCacheLengthZero(uc), "We found %d items in the user cache and it's supposed to be empty", cacheLength(uc)) + _, err = c.Users.Get(0) + recordMustNotExist(t, err, "UID #0 shouldn't exist") + exf(isCacheLengthZero(uc), "We found %d items in the user cache and it's supposed to be empty", cacheLength(uc)) - user, err := c.Users.Get(1) - recordMustExist(t, err, "Couldn't find UID #1") + user, err := c.Users.Get(1) + recordMustExist(t, err, "Couldn't find UID #1") - expectW := func(cond, expec bool, prefix, suffix string) { - midfix := "should not be" - if expec { - midfix = "should be" - } - ex(cond, prefix+" "+midfix+" "+suffix) - } + expectW := func(cond, expec bool, prefix, suffix string) { + midfix := "should not be" + if expec { + midfix = "should be" + } + ex(cond, prefix+" "+midfix+" "+suffix) + } - // TODO: Add email checks too? Do them separately? - expectUser := func(u *c.User, uid int, name string, group int, super, admin, mod, banned bool) { - exf(u.ID == uid, "u.ID should be %d. Got '%d' instead.", uid, u.ID) - exf(u.Name == name, "u.Name should be '%s', not '%s'", name, u.Name) - expectW(u.Group == group, true, u.Name, "in group"+strconv.Itoa(group)) - expectW(u.IsSuperAdmin == super, super, u.Name, "a super admin") - expectW(u.IsAdmin == admin, admin, u.Name, "an admin") - expectW(u.IsSuperMod == mod, mod, u.Name, "a super mod") - expectW(u.IsMod == mod, mod, u.Name, "a mod") - expectW(u.IsBanned == banned, banned, u.Name, "banned") - } - expectUser(user, 1, "Admin", 1, true, true, true, false) + // TODO: Add email checks too? Do them separately? + expectUser := func(u *c.User, uid int, name string, group int, super, admin, mod, banned bool) { + exf(u.ID == uid, "u.ID should be %d. Got '%d' instead.", uid, u.ID) + exf(u.Name == name, "u.Name should be '%s', not '%s'", name, u.Name) + expectW(u.Group == group, true, u.Name, "in group"+strconv.Itoa(group)) + expectW(u.IsSuperAdmin == super, super, u.Name, "a super admin") + expectW(u.IsAdmin == admin, admin, u.Name, "an admin") + expectW(u.IsSuperMod == mod, mod, u.Name, "a super mod") + expectW(u.IsMod == mod, mod, u.Name, "a mod") + expectW(u.IsBanned == banned, banned, u.Name, "banned") + } + expectUser(user, 1, "Admin", 1, true, true, true, false) - user, err = c.Users.GetByName("Admin") - recordMustExist(t, err, "Couldn't find user 'Admin'") - expectUser(user, 1, "Admin", 1, true, true, true, false) - us, err := c.Users.BulkGetByName([]string{"Admin"}) - recordMustExist(t, err, "Couldn't find user 'Admin'") - exf(len(us) == 1, "len(us) should be 1, not %d", len(us)) - expectUser(us[0], 1, "Admin", 1, true, true, true, false) + user, err = c.Users.GetByName("Admin") + recordMustExist(t, err, "Couldn't find user 'Admin'") + expectUser(user, 1, "Admin", 1, true, true, true, false) + us, err := c.Users.BulkGetByName([]string{"Admin"}) + recordMustExist(t, err, "Couldn't find user 'Admin'") + exf(len(us) == 1, "len(us) should be 1, not %d", len(us)) + expectUser(us[0], 1, "Admin", 1, true, true, true, false) - _, err = c.Users.Get(newUserID) - recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID)) + _, err = c.Users.Get(newUserID) + recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID)) - // TODO: GetByName tests for newUserID + // TODO: GetByName tests for newUserID - if uc != nil { - expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") - _, err = uc.Get(-1) - recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache") - _, err = uc.Get(0) - recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache") - user, err = uc.Get(1) - recordMustExist(t, err, "Couldn't find UID #1 in the cache") + if uc != nil { + expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") + _, err = uc.Get(-1) + recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache") + _, err = uc.Get(0) + recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache") + user, err = uc.Get(1) + recordMustExist(t, err, "Couldn't find UID #1 in the cache") - exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - exf(user.Name == "Admin", "user.Name should be 'Admin', not '%s'", user.Name) + exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + exf(user.Name == "Admin", "user.Name should be 'Admin', not '%s'", user.Name) - _, err = uc.Get(newUserID) - recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID) - uc.Flush() - expectIntToBeX(t, uc.Length(), 0, "User cache length should be 0, not %d") - } + _, err = uc.Get(newUserID) + recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID) + uc.Flush() + expectIntToBeX(t, uc.Length(), 0, "User cache length should be 0, not %d") + } - // TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message? - bulkGetMapEmpty := func(id int) { - userList, _ := c.Users.BulkGetMap([]int{id}) - exf(len(userList) == 0, "The userList length should be 0, not %d", len(userList)) - exf(isCacheLengthZero(uc), "User cache length should be 0, not %d", cacheLength(uc)) - } - bulkGetMapEmpty(-1) - bulkGetMapEmpty(0) + // TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message? + bulkGetMapEmpty := func(id int) { + userList, _ := c.Users.BulkGetMap([]int{id}) + exf(len(userList) == 0, "The userList length should be 0, not %d", len(userList)) + exf(isCacheLengthZero(uc), "User cache length should be 0, not %d", cacheLength(uc)) + } + bulkGetMapEmpty(-1) + bulkGetMapEmpty(0) - userList, _ := c.Users.BulkGetMap([]int{1}) - exf(len(userList) == 1, "Returned map should have one result (UID #1), not %d", len(userList)) - user, ok := userList[1] - if !ok { - t.Error("We couldn't find UID #1 in the returned map") - t.Error("userList", userList) - return - } - exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + userList, _ := c.Users.BulkGetMap([]int{1}) + exf(len(userList) == 1, "Returned map should have one result (UID #1), not %d", len(userList)) + user, ok := userList[1] + if !ok { + t.Error("We couldn't find UID #1 in the returned map") + t.Error("userList", userList) + return + } + exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - if uc != nil { - expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") - user, err = uc.Get(1) - recordMustExist(t, err, "Couldn't find UID #1 in the cache") + if uc != nil { + expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") + user, err = uc.Get(1) + recordMustExist(t, err, "Couldn't find UID #1 in the cache") - exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - uc.Flush() - } + exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + uc.Flush() + } - ex(!c.Users.Exists(-1), "UID #-1 shouldn't exist") - ex(!c.Users.Exists(0), "UID #0 shouldn't exist") - ex(c.Users.Exists(1), "UID #1 should exist") - exf(!c.Users.Exists(newUserID), "UID #%d shouldn't exist", newUserID) + ex(!c.Users.Exists(-1), "UID #-1 shouldn't exist") + ex(!c.Users.Exists(0), "UID #0 shouldn't exist") + ex(c.Users.Exists(1), "UID #1 should exist") + exf(!c.Users.Exists(newUserID), "UID #%d shouldn't exist", newUserID) - exf(isCacheLengthZero(uc), "User cache length should be 0, not %d", cacheLength(uc)) - expectIntToBeX(t, c.Users.Count(), 1, "The number of users should be 1, not %d") - searchUser := func(name, email string, gid, count int) { - f := func(name, email string, gid, count int, m string) { - expectIntToBeX(t, c.Users.CountSearch(name, email, gid), count, "The number of users for "+m+", not %d") - } - f(name, email, 0, count, fmt.Sprintf("name '%s' and email '%s' should be %d", name, email, count)) - f(name, "", 0, count, fmt.Sprintf("name '%s' should be %d", name, count)) - f("", email, 0, count, fmt.Sprintf("email '%s' should be %d", email, count)) + exf(isCacheLengthZero(uc), "User cache length should be 0, not %d", cacheLength(uc)) + expectIntToBeX(t, c.Users.Count(), 1, "The number of users should be 1, not %d") + searchUser := func(name, email string, gid, count int) { + f := func(name, email string, gid, count int, m string) { + expectIntToBeX(t, c.Users.CountSearch(name, email, gid), count, "The number of users for "+m+", not %d") + } + f(name, email, 0, count, fmt.Sprintf("name '%s' and email '%s' should be %d", name, email, count)) + f(name, "", 0, count, fmt.Sprintf("name '%s' should be %d", name, count)) + f("", email, 0, count, fmt.Sprintf("email '%s' should be %d", email, count)) - f2 := func(name, email string, gid, offset int, m string, args ...interface{}) { - ulist, err := c.Users.SearchOffset(name, email, gid, offset, 15) - expectNilErr(t, err) - expectIntToBeX(t, len(ulist), count, "The number of users for "+fmt.Sprintf(m, args...)+", not %d") - } - f2(name, email, 0, 0, "name '%s' and email '%s' should be %d", name, email, count) - f2(name, "", 0, 0, "name '%s' should be %d", name, count) - f2("", email, 0, 0, "email '%s' should be %d", email, count) + f2 := func(name, email string, gid, offset int, m string, args ...interface{}) { + ulist, err := c.Users.SearchOffset(name, email, gid, offset, 15) + expectNilErr(t, err) + expectIntToBeX(t, len(ulist), count, "The number of users for "+fmt.Sprintf(m, args...)+", not %d") + } + f2(name, email, 0, 0, "name '%s' and email '%s' should be %d", name, email, count) + f2(name, "", 0, 0, "name '%s' should be %d", name, count) + f2("", email, 0, 0, "email '%s' should be %d", email, count) - count = 0 - f2(name, email, 0, 10, "name '%s' and email '%s' should be %d", name, email, count) - f2(name, "", 0, 10, "name '%s' should be %d", name, count) - f2("", email, 0, 10, "email '%s' should be %d", email, count) + count = 0 + f2(name, email, 0, 10, "name '%s' and email '%s' should be %d", name, email, count) + f2(name, "", 0, 10, "name '%s' should be %d", name, count) + f2("", email, 0, 10, "email '%s' should be %d", email, count) - f2(name, email, 999, 0, "name '%s' and email '%s' should be %d", name, email, 0) - f2(name, "", 999, 0, "name '%s' should be %d", name, 0) - f2("", email, 999, 0, "email '%s' should be %d", email, 0) + f2(name, email, 999, 0, "name '%s' and email '%s' should be %d", name, email, 0) + f2(name, "", 999, 0, "name '%s' should be %d", name, 0) + f2("", email, 999, 0, "email '%s' should be %d", email, 0) - f2(name, email, 999, 10, "name '%s' and email '%s' should be %d", name, email, 0) - f2(name, "", 999, 10, "name '%s' should be %d", name, 0) - f2("", email, 999, 10, "email '%s' should be %d", email, 0) - } - searchUser("Sam", "sam@localhost.loc", 0, 0) - // TODO: CountSearch gid test + f2(name, email, 999, 10, "name '%s' and email '%s' should be %d", name, email, 0) + f2(name, "", 999, 10, "name '%s' should be %d", name, 0) + f2("", email, 999, 10, "email '%s' should be %d", email, 0) + } + searchUser("Sam", "sam@localhost.loc", 0, 0) + // TODO: CountSearch gid test - awaitingActivation := 5 - // TODO: Write tests for the registration validators - uid, err := c.Users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, false) - expectNilErr(t, err) - exf(uid == newUserID, "The UID of the new user should be %d not %d", newUserID, uid) - exf(c.Users.Exists(newUserID), "UID #%d should exist", newUserID) - expectIntToBeX(t, c.Users.Count(), 2, "The number of users should be 2, not %d") - searchUser("Sam", "sam@localhost.loc", 0, 1) - // TODO: CountSearch gid test + awaitingActivation := 5 + // TODO: Write tests for the registration validators + uid, err := c.Users.Create("Sam", "ReallyBadPassword", "sam@localhost.loc", awaitingActivation, false) + expectNilErr(t, err) + exf(uid == newUserID, "The UID of the new user should be %d not %d", newUserID, uid) + exf(c.Users.Exists(newUserID), "UID #%d should exist", newUserID) + expectIntToBeX(t, c.Users.Count(), 2, "The number of users should be 2, not %d") + searchUser("Sam", "sam@localhost.loc", 0, 1) + // TODO: CountSearch gid test - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - expectUser(user, newUserID, "Sam", 5, false, false, false, false) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + expectUser(user, newUserID, "Sam", 5, false, false, false, false) - if uc != nil { - expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") - user, err = uc.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) - exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - } + if uc != nil { + expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") + user, err = uc.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) + exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + } - userList, _ = c.Users.BulkGetMap([]int{1, uid}) - exf(len(userList) == 2, "Returned map should have 2 results, not %d", len(userList)) - // TODO: More tests on userList + userList, _ = c.Users.BulkGetMap([]int{1, uid}) + exf(len(userList) == 2, "Returned map should have 2 results, not %d", len(userList)) + // TODO: More tests on userList - { - userList, _ := c.Users.BulkGetByName([]string{"Admin", "Sam"}) - exf(len(userList) == 2, "Returned list should have 2 results, not %d", len(userList)) - } + { + userList, _ := c.Users.BulkGetByName([]string{"Admin", "Sam"}) + exf(len(userList) == 2, "Returned list should have 2 results, not %d", len(userList)) + } - if uc != nil { - expectIntToBeX(t, uc.Length(), 2, "User cache length should be 2, not %d") - user, err = uc.Get(1) - recordMustExist(t, err, "Couldn't find UID #%d in the cache", 1) - exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - user, err = uc.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) - exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - uc.Flush() - } + if uc != nil { + expectIntToBeX(t, uc.Length(), 2, "User cache length should be 2, not %d") + user, err = uc.Get(1) + recordMustExist(t, err, "Couldn't find UID #%d in the cache", 1) + exf(user.ID == 1, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + user, err = uc.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) + exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + uc.Flush() + } - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - expectUser(user, newUserID, "Sam", 5, false, false, false, false) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + expectUser(user, newUserID, "Sam", 5, false, false, false, false) - if uc != nil { - expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") - user, err = uc.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) - exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) - } + if uc != nil { + expectIntToBeX(t, uc.Length(), 1, "User cache length should be 1, not %d") + user, err = uc.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID) + exf(user.ID == newUserID, "user.ID does not match the requested UID. Got '%d' instead.", user.ID) + } - expectNilErr(t, user.Activate()) - expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy") + expectNilErr(t, user.Activate()) + expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy") - // ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug - afterUserFlush := func(uid int) { - if uc != nil { - expectIntToBeX(t, uc.Length(), 0, "User cache length should be 0, not %d") - _, err = uc.Get(uid) - recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", uid) - } - } - afterUserFlush(newUserID) + // ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug + afterUserFlush := func(uid int) { + if uc != nil { + expectIntToBeX(t, uc.Length(), 0, "User cache length should be 0, not %d") + _, err = uc.Get(uid) + recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", uid) + } + } + afterUserFlush(newUserID) - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - expectUser(user, newUserID, "Sam", c.Config.DefaultGroup, false, false, false, false) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + expectUser(user, newUserID, "Sam", c.Config.DefaultGroup, false, false, false, false) - // Permanent ban - duration, _ := time.ParseDuration("0") + // Permanent ban + duration, _ := time.ParseDuration("0") - // TODO: Attempt a double ban, double activation, and double unban - expectNilErr(t, user.Ban(duration, 1)) - exf(user.Group == c.Config.DefaultGroup, "Sam should be in group %d, not %d", c.Config.DefaultGroup, user.Group) - afterUserFlush(newUserID) + // TODO: Attempt a double ban, double activation, and double unban + expectNilErr(t, user.Ban(duration, 1)) + exf(user.Group == c.Config.DefaultGroup, "Sam should be in group %d, not %d", c.Config.DefaultGroup, user.Group) + afterUserFlush(newUserID) - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - expectUser(user, newUserID, "Sam", c.BanGroup, false, false, false, true) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + expectUser(user, newUserID, "Sam", c.BanGroup, false, false, false, true) - // TODO: Do tests against the scheduled updates table and the task system to make sure the ban exists there and gets revoked when it should + // TODO: Do tests against the scheduled updates table and the task system to make sure the ban exists there and gets revoked when it should - expectNilErr(t, user.Unban()) - expectIntToBeX(t, user.Group, c.BanGroup, "Sam should still be in the ban group in this copy") - afterUserFlush(newUserID) + expectNilErr(t, user.Unban()) + expectIntToBeX(t, user.Group, c.BanGroup, "Sam should still be in the ban group in this copy") + afterUserFlush(newUserID) - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - expectUser(user, newUserID, "Sam", c.Config.DefaultGroup, false, false, false, false) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + expectUser(user, newUserID, "Sam", c.Config.DefaultGroup, false, false, false, false) - reportsForumID := 1 // TODO: Use the constant in common? - generalForumID := 2 - dummyResponseRecorder := httptest.NewRecorder() - bytesBuffer := bytes.NewBuffer([]byte("")) - dummyRequest1 := httptest.NewRequest("", "/forum/"+strconv.Itoa(reportsForumID), bytesBuffer) - dummyRequest2 := httptest.NewRequest("", "/forum/"+strconv.Itoa(generalForumID), bytesBuffer) - var user2 *c.User + reportsForumID := 1 // TODO: Use the constant in common? + generalForumID := 2 + dummyResponseRecorder := httptest.NewRecorder() + bytesBuffer := bytes.NewBuffer([]byte("")) + dummyRequest1 := httptest.NewRequest("", "/forum/"+strconv.Itoa(reportsForumID), bytesBuffer) + dummyRequest2 := httptest.NewRequest("", "/forum/"+strconv.Itoa(generalForumID), bytesBuffer) + var user2 *c.User - changeGroupTest := func(oldGroup, newGroup int) { - expectNilErr(t, user.ChangeGroup(newGroup)) - // ! I don't think ChangeGroup should be changing the value of user... Investigate this. - ex(oldGroup == user.Group, "Someone's mutated this pointer elsewhere") + changeGroupTest := func(oldGroup, newGroup int) { + expectNilErr(t, user.ChangeGroup(newGroup)) + // ! I don't think ChangeGroup should be changing the value of user... Investigate this. + ex(oldGroup == user.Group, "Someone's mutated this pointer elsewhere") - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - user2 = c.BlankUser() - *user2 = *user - } + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + user2 = c.BlankUser() + *user2 = *user + } - changeGroupTest2 := func(rank string, firstShouldBe, secondShouldBe bool) { - head, e := c.UserCheck(dummyResponseRecorder, dummyRequest1, user) - if e != nil { - t.Fatal(e) - } - head2, e := c.UserCheck(dummyResponseRecorder, dummyRequest2, user2) - if e != nil { - t.Fatal(e) - } - ferr := c.ForumUserCheck(head, dummyResponseRecorder, dummyRequest1, user, reportsForumID) - ex(ferr == nil, "There shouldn't be any errors in forumUserCheck") - ex(user.Perms.ViewTopic == firstShouldBe, rank+" should be able to access the reports forum") - ferr = c.ForumUserCheck(head2, dummyResponseRecorder, dummyRequest2, user2, generalForumID) - ex(ferr == nil, "There shouldn't be any errors in forumUserCheck") - ex(user2.Perms.ViewTopic == secondShouldBe, "Sam should be able to access the general forum") - } + changeGroupTest2 := func(rank string, firstShouldBe, secondShouldBe bool) { + head, e := c.UserCheck(dummyResponseRecorder, dummyRequest1, user) + if e != nil { + t.Fatal(e) + } + head2, e := c.UserCheck(dummyResponseRecorder, dummyRequest2, user2) + if e != nil { + t.Fatal(e) + } + ferr := c.ForumUserCheck(head, dummyResponseRecorder, dummyRequest1, user, reportsForumID) + ex(ferr == nil, "There shouldn't be any errors in forumUserCheck") + ex(user.Perms.ViewTopic == firstShouldBe, rank+" should be able to access the reports forum") + ferr = c.ForumUserCheck(head2, dummyResponseRecorder, dummyRequest2, user2, generalForumID) + ex(ferr == nil, "There shouldn't be any errors in forumUserCheck") + ex(user2.Perms.ViewTopic == secondShouldBe, "Sam should be able to access the general forum") + } - changeGroupTest(c.Config.DefaultGroup, 1) - expectUser(user, newUserID, "Sam", 1, false, true, true, false) - changeGroupTest2("Admins", true, true) + changeGroupTest(c.Config.DefaultGroup, 1) + expectUser(user, newUserID, "Sam", 1, false, true, true, false) + changeGroupTest2("Admins", true, true) - changeGroupTest(1, 2) - expectUser(user, newUserID, "Sam", 2, false, false, true, false) - changeGroupTest2("Mods", true, true) + changeGroupTest(1, 2) + expectUser(user, newUserID, "Sam", 2, false, false, true, false) + changeGroupTest2("Mods", true, true) - changeGroupTest(2, 3) - expectUser(user, newUserID, "Sam", 3, false, false, false, false) - changeGroupTest2("Members", false, true) - ex(user.Perms.ViewTopic != user2.Perms.ViewTopic, "user.Perms.ViewTopic and user2.Perms.ViewTopic should never match") + changeGroupTest(2, 3) + expectUser(user, newUserID, "Sam", 3, false, false, false, false) + changeGroupTest2("Members", false, true) + ex(user.Perms.ViewTopic != user2.Perms.ViewTopic, "user.Perms.ViewTopic and user2.Perms.ViewTopic should never match") - changeGroupTest(3, 4) - expectUser(user, newUserID, "Sam", 4, false, false, false, true) - changeGroupTest2("Members", false, true) + changeGroupTest(3, 4) + expectUser(user, newUserID, "Sam", 4, false, false, false, true) + changeGroupTest2("Members", false, true) - changeGroupTest(4, 5) - expectUser(user, newUserID, "Sam", 5, false, false, false, false) - changeGroupTest2("Members", false, true) + changeGroupTest(4, 5) + expectUser(user, newUserID, "Sam", 5, false, false, false, false) + changeGroupTest2("Members", false, true) - changeGroupTest(5, 6) - expectUser(user, newUserID, "Sam", 6, false, false, false, false) - changeGroupTest2("Members", false, true) + changeGroupTest(5, 6) + expectUser(user, newUserID, "Sam", 6, false, false, false, false) + changeGroupTest2("Members", false, true) - err = user.ChangeGroup(c.Config.DefaultGroup) - expectNilErr(t, err) - ex(user.Group == 6, "Someone's mutated this pointer elsewhere") + err = user.ChangeGroup(c.Config.DefaultGroup) + expectNilErr(t, err) + ex(user.Group == 6, "Someone's mutated this pointer elsewhere") - exf(user.LastIP == "", "user.LastIP should be blank not %s", user.LastIP) - expectNilErr(t, user.UpdateIP("::1")) - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - exf(user.LastIP == "::1", "user.LastIP should be %s not %s", "::1", user.LastIP) - expectNilErr(t, c.Users.ClearLastIPs()) - expectNilErr(t, c.Users.Reload(newUserID)) - user, err = c.Users.Get(newUserID) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID) - exf(user.LastIP == "", "user.LastIP should be blank not %s", user.LastIP) + exf(user.LastIP == "", "user.LastIP should be blank not %s", user.LastIP) + expectNilErr(t, user.UpdateIP("::1")) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + exf(user.LastIP == "::1", "user.LastIP should be %s not %s", "::1", user.LastIP) + expectNilErr(t, c.Users.ClearLastIPs()) + expectNilErr(t, c.Users.Reload(newUserID)) + user, err = c.Users.Get(newUserID) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID) + exf(user.LastIP == "", "user.LastIP should be blank not %s", user.LastIP) - expectNilErr(t, user.Delete()) - exf(!c.Users.Exists(newUserID), "UID #%d should no longer exist", newUserID) - afterUserFlush(newUserID) - expectIntToBeX(t, c.Users.Count(), 1, "The number of users should be 1, not %d") - searchUser("Sam", "sam@localhost.loc", 0, 0) - // TODO: CountSearch gid test + expectNilErr(t, user.Delete()) + exf(!c.Users.Exists(newUserID), "UID #%d should no longer exist", newUserID) + afterUserFlush(newUserID) + expectIntToBeX(t, c.Users.Count(), 1, "The number of users should be 1, not %d") + searchUser("Sam", "sam@localhost.loc", 0, 0) + // TODO: CountSearch gid test - _, err = c.Users.Get(newUserID) - recordMustNotExist(t, err, "UID #%d shouldn't exist", newUserID) + _, err = c.Users.Get(newUserID) + recordMustNotExist(t, err, "UID #%d shouldn't exist", newUserID) - // And a unicode test, even though I doubt it'll fail - uid, err = c.Users.Create("サム", "😀😀😀", "sam@localhost.loc", awaitingActivation, false) - expectNilErr(t, err) - exf(uid == newUserID+1, "The UID of the new user should be %d", newUserID+1) - exf(c.Users.Exists(newUserID+1), "UID #%d should exist", newUserID+1) + // And a unicode test, even though I doubt it'll fail + uid, err = c.Users.Create("サム", "😀😀😀", "sam@localhost.loc", awaitingActivation, false) + expectNilErr(t, err) + exf(uid == newUserID+1, "The UID of the new user should be %d", newUserID+1) + exf(c.Users.Exists(newUserID+1), "UID #%d should exist", newUserID+1) - user, err = c.Users.Get(newUserID + 1) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID+1) - expectUser(user, newUserID+1, "サム", 5, false, false, false, false) + user, err = c.Users.Get(newUserID + 1) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID+1) + expectUser(user, newUserID+1, "サム", 5, false, false, false, false) - expectNilErr(t, user.Delete()) - exf(!c.Users.Exists(newUserID+1), "UID #%d should no longer exist", newUserID+1) + expectNilErr(t, user.Delete()) + exf(!c.Users.Exists(newUserID+1), "UID #%d should no longer exist", newUserID+1) - // MySQL utf8mb4 username test - uid, err = c.Users.Create("😀😀😀", "😀😀😀", "sam@localhost.loc", awaitingActivation, false) - expectNilErr(t, err) - exf(uid == newUserID+2, "The UID of the new user should be %d", newUserID+2) - exf(c.Users.Exists(newUserID+2), "UID #%d should exist", newUserID+2) + // MySQL utf8mb4 username test + uid, err = c.Users.Create("😀😀😀", "😀😀😀", "sam@localhost.loc", awaitingActivation, false) + expectNilErr(t, err) + exf(uid == newUserID+2, "The UID of the new user should be %d", newUserID+2) + exf(c.Users.Exists(newUserID+2), "UID #%d should exist", newUserID+2) - user, err = c.Users.Get(newUserID + 2) - recordMustExist(t, err, "Couldn't find UID #%d", newUserID+1) - expectUser(user, newUserID+2, "😀😀😀", 5, false, false, false, false) + user, err = c.Users.Get(newUserID + 2) + recordMustExist(t, err, "Couldn't find UID #%d", newUserID+1) + expectUser(user, newUserID+2, "😀😀😀", 5, false, false, false, false) - expectNilErr(t, user.Delete()) - exf(!c.Users.Exists(newUserID+2), "UID #%d should no longer exist", newUserID+2) + expectNilErr(t, user.Delete()) + exf(!c.Users.Exists(newUserID+2), "UID #%d should no longer exist", newUserID+2) - // TODO: Add unicode login tests somewhere? Probably with the rest of the auth tests - // TODO: Add tests for the Cache* methods + // TODO: Add unicode login tests somewhere? Probably with the rest of the auth tests + // TODO: Add tests for the Cache* methods } // TODO: Add an error message to this? func expectNilErr(t *testing.T, item error) { - if item != nil { - debug.PrintStack() - t.Fatal(item) - } + if item != nil { + debug.PrintStack() + t.Fatal(item) + } } func expectIntToBeX(t *testing.T, item, expect int, errmsg string) { - if item != expect { - debug.PrintStack() - t.Fatalf(errmsg, item) - } + if item != expect { + debug.PrintStack() + t.Fatalf(errmsg, item) + } } func expect(t *testing.T, item bool, errmsg string) { - if !item { - debug.PrintStack() - t.Fatal(errmsg) - } + if !item { + debug.PrintStack() + t.Fatal(errmsg) + } } func expectf(t *testing.T, item bool, errmsg string, args ...interface{}) { - if !item { - debug.PrintStack() - t.Fatalf(errmsg, args...) - } + if !item { + debug.PrintStack() + t.Fatalf(errmsg, args...) + } } func exp(t *testing.T) func(bool, string) { - return func(val bool, errmsg string) { - if !val { - debug.PrintStack() - t.Fatal(errmsg) - } - } + return func(val bool, errmsg string) { + if !val { + debug.PrintStack() + t.Fatal(errmsg) + } + } } func expf(t *testing.T) func(bool, string, ...interface{}) { - return func(val bool, errmsg string, params ...interface{}) { - if !val { - debug.PrintStack() - t.Fatalf(errmsg, params...) - } - } + return func(val bool, errmsg string, params ...interface{}) { + if !val { + debug.PrintStack() + t.Fatalf(errmsg, params...) + } + } } func TestPermsMiddleware(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } - dummyResponseRecorder := httptest.NewRecorder() - bytesBuffer := bytes.NewBuffer([]byte("")) - dummyRequest := httptest.NewRequest("", "/forum/1", bytesBuffer) - user := c.BlankUser() - ex := exp(t) + dummyResponseRecorder := httptest.NewRecorder() + bytesBuffer := bytes.NewBuffer([]byte("")) + dummyRequest := httptest.NewRequest("", "/forum/1", bytesBuffer) + user := c.BlankUser() + ex := exp(t) - f := func(ff func(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError) bool { - ferr := ff(dummyResponseRecorder, dummyRequest, user) - return ferr == nil - } + f := func(ff func(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError) bool { + ferr := ff(dummyResponseRecorder, dummyRequest, user) + return ferr == nil + } - ex(!f(c.SuperModOnly), "Blank users shouldn't be supermods") - user.IsSuperMod = false - ex(!f(c.SuperModOnly), "Non-supermods shouldn't be allowed through supermod gates") - user.IsSuperMod = true - ex(f(c.SuperModOnly), "Supermods should be allowed through supermod gates") + ex(!f(c.SuperModOnly), "Blank users shouldn't be supermods") + user.IsSuperMod = false + ex(!f(c.SuperModOnly), "Non-supermods shouldn't be allowed through supermod gates") + user.IsSuperMod = true + ex(f(c.SuperModOnly), "Supermods should be allowed through supermod gates") - // TODO: Loop over the Control Panel routes and make sure only supermods can get in + // TODO: Loop over the Control Panel routes and make sure only supermods can get in - user = c.BlankUser() + user = c.BlankUser() - ex(!f(c.MemberOnly), "Blank users shouldn't be considered loggedin") - user.Loggedin = false - ex(!f(c.MemberOnly), "Guests shouldn't be able to access member areas") - user.Loggedin = true - ex(f(c.MemberOnly), "Logged in users should be able to access member areas") + ex(!f(c.MemberOnly), "Blank users shouldn't be considered loggedin") + user.Loggedin = false + ex(!f(c.MemberOnly), "Guests shouldn't be able to access member areas") + user.Loggedin = true + ex(f(c.MemberOnly), "Logged in users should be able to access member areas") - // TODO: Loop over the /user/ routes and make sure only members can access the ones other than /user/username + // TODO: Loop over the /user/ routes and make sure only members can access the ones other than /user/username - user = c.BlankUser() + user = c.BlankUser() - ex(!f(c.AdminOnly), "Blank users shouldn't be considered admins") - user.IsAdmin = false - ex(!f(c.AdminOnly), "Non-admins shouldn't be able to access admin areas") - user.IsAdmin = true - ex(f(c.AdminOnly), "Admins should be able to access admin areas") + ex(!f(c.AdminOnly), "Blank users shouldn't be considered admins") + user.IsAdmin = false + ex(!f(c.AdminOnly), "Non-admins shouldn't be able to access admin areas") + user.IsAdmin = true + ex(f(c.AdminOnly), "Admins should be able to access admin areas") - user = c.BlankUser() + user = c.BlankUser() - ex(!f(c.SuperAdminOnly), "Blank users shouldn't be considered super admins") - user.IsSuperAdmin = false - ex(!f(c.SuperAdminOnly), "Non-super admins shouldn't be allowed through the super admin gate") - user.IsSuperAdmin = true - ex(f(c.SuperAdminOnly), "Super admins should be allowed through super admin gates") + ex(!f(c.SuperAdminOnly), "Blank users shouldn't be considered super admins") + user.IsSuperAdmin = false + ex(!f(c.SuperAdminOnly), "Non-super admins shouldn't be allowed through the super admin gate") + user.IsSuperAdmin = true + ex(f(c.SuperAdminOnly), "Super admins should be allowed through super admin gates") - // TODO: Make sure only super admins can access the backups route + // TODO: Make sure only super admins can access the backups route - //dummyResponseRecorder = httptest.NewRecorder() - //bytesBuffer = bytes.NewBuffer([]byte("")) - //dummyRequest = httptest.NewRequest("", "/panel/backups/", bytesBuffer) + //dummyResponseRecorder = httptest.NewRecorder() + //bytesBuffer = bytes.NewBuffer([]byte("")) + //dummyRequest = httptest.NewRequest("", "/panel/backups/", bytesBuffer) } func TestTopicStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } - var err error - tcache := c.NewMemoryTopicCache(c.Config.TopicCacheCapacity) - c.Topics, err = c.NewDefaultTopicStore(tcache) - expectNilErr(t, err) - c.Config.DisablePostIP = false - topicStoreTest(t, 2, "::1") - c.Config.DisablePostIP = true - topicStoreTest(t, 3, "") + var err error + tcache := c.NewMemoryTopicCache(c.Config.TopicCacheCapacity) + c.Topics, err = c.NewDefaultTopicStore(tcache) + expectNilErr(t, err) + c.Config.DisablePostIP = false + topicStoreTest(t, 2, "::1") + c.Config.DisablePostIP = true + topicStoreTest(t, 3, "") - c.Topics, err = c.NewDefaultTopicStore(nil) - expectNilErr(t, err) - c.Config.DisablePostIP = false - topicStoreTest(t, 4, "::1") - c.Config.DisablePostIP = true - topicStoreTest(t, 5, "") + c.Topics, err = c.NewDefaultTopicStore(nil) + expectNilErr(t, err) + c.Config.DisablePostIP = false + topicStoreTest(t, 4, "::1") + c.Config.DisablePostIP = true + topicStoreTest(t, 5, "") } func topicStoreTest(t *testing.T, newID int, ip string) { - var topic *c.Topic - var err error - ex, exf := exp(t), expf(t) + var topic *c.Topic + var err error + ex, exf := exp(t), expf(t) - _, err = c.Topics.Get(-1) - recordMustNotExist(t, err, "TID #-1 shouldn't exist") - _, err = c.Topics.Get(0) - recordMustNotExist(t, err, "TID #0 shouldn't exist") + _, err = c.Topics.Get(-1) + recordMustNotExist(t, err, "TID #-1 shouldn't exist") + _, err = c.Topics.Get(0) + recordMustNotExist(t, err, "TID #0 shouldn't exist") - topic, err = c.Topics.Get(1) - recordMustExist(t, err, "Couldn't find TID #1") - exf(topic.ID == 1, "topic.ID does not match the requested TID. Got '%d' instead.", topic.ID) + topic, err = c.Topics.Get(1) + recordMustExist(t, err, "Couldn't find TID #1") + exf(topic.ID == 1, "topic.ID does not match the requested TID. Got '%d' instead.", topic.ID) - // TODO: Add BulkGetMap() to the TopicStore + // TODO: Add BulkGetMap() to the TopicStore - ex(!c.Topics.Exists(-1), "TID #-1 shouldn't exist") - ex(!c.Topics.Exists(0), "TID #0 shouldn't exist") - ex(c.Topics.Exists(1), "TID #1 should exist") + ex(!c.Topics.Exists(-1), "TID #-1 shouldn't exist") + ex(!c.Topics.Exists(0), "TID #0 shouldn't exist") + ex(c.Topics.Exists(1), "TID #1 should exist") - count := c.Topics.Count() - exf(count == 1, "Global count for topics should be 1, not %d", count) + count := c.Topics.Count() + exf(count == 1, "Global count for topics should be 1, not %d", count) - //Create(fid int, topicName string, content string, uid int, ip string) (tid int, err error) - tid, err := c.Topics.Create(2, "Test Topic", "Topic Content", 1, ip) - expectNilErr(t, err) - exf(tid == newID, "TID for the new topic should be %d, not %d", newID, tid) - exf(c.Topics.Exists(newID), "TID #%d should exist", newID) + //Create(fid int, topicName string, content string, uid int, ip string) (tid int, err error) + tid, err := c.Topics.Create(2, "Test Topic", "Topic Content", 1, ip) + expectNilErr(t, err) + exf(tid == newID, "TID for the new topic should be %d, not %d", newID, tid) + exf(c.Topics.Exists(newID), "TID #%d should exist", newID) - count = c.Topics.Count() - exf(count == 2, "Global count for topics should be 2, not %d", count) + count = c.Topics.Count() + exf(count == 2, "Global count for topics should be 2, not %d", count) - iFrag := func(cond bool) string { - if !cond { - return "n't" - } - return "" - } + iFrag := func(cond bool) string { + if !cond { + return "n't" + } + return "" + } - testTopic := func(tid int, title, content string, createdBy int, ip string, parentID int, isClosed, sticky bool) { - topic, err = c.Topics.Get(tid) - recordMustExist(t, err, fmt.Sprintf("Couldn't find TID #%d", tid)) - exf(topic.ID == tid, "topic.ID does not match the requested TID. Got '%d' instead.", topic.ID) - exf(topic.GetID() == tid, "topic.ID does not match the requested TID. Got '%d' instead.", topic.GetID()) - exf(topic.Title == title, "The topic's name should be '%s', not %s", title, topic.Title) - exf(topic.Content == content, "The topic's body should be '%s', not %s", content, topic.Content) - exf(topic.CreatedBy == createdBy, "The topic's creator should be %d, not %d", createdBy, topic.CreatedBy) - exf(topic.IP == ip, "The topic's IP should be '%s', not %s", ip, topic.IP) - exf(topic.ParentID == parentID, "The topic's parent forum should be %d, not %d", parentID, topic.ParentID) - exf(topic.IsClosed == isClosed, "This topic should%s be locked", iFrag(topic.IsClosed)) - exf(topic.Sticky == sticky, "This topic should%s be sticky", iFrag(topic.Sticky)) - exf(topic.GetTable() == "topics", "The topic's table should be 'topics', not %s", topic.GetTable()) - } + testTopic := func(tid int, title, content string, createdBy int, ip string, parentID int, isClosed, sticky bool) { + topic, err = c.Topics.Get(tid) + recordMustExist(t, err, fmt.Sprintf("Couldn't find TID #%d", tid)) + exf(topic.ID == tid, "topic.ID does not match the requested TID. Got '%d' instead.", topic.ID) + exf(topic.GetID() == tid, "topic.ID does not match the requested TID. Got '%d' instead.", topic.GetID()) + exf(topic.Title == title, "The topic's name should be '%s', not %s", title, topic.Title) + exf(topic.Content == content, "The topic's body should be '%s', not %s", content, topic.Content) + exf(topic.CreatedBy == createdBy, "The topic's creator should be %d, not %d", createdBy, topic.CreatedBy) + exf(topic.IP == ip, "The topic's IP should be '%s', not %s", ip, topic.IP) + exf(topic.ParentID == parentID, "The topic's parent forum should be %d, not %d", parentID, topic.ParentID) + exf(topic.IsClosed == isClosed, "This topic should%s be locked", iFrag(topic.IsClosed)) + exf(topic.Sticky == sticky, "This topic should%s be sticky", iFrag(topic.Sticky)) + exf(topic.GetTable() == "topics", "The topic's table should be 'topics', not %s", topic.GetTable()) + } - tc := c.Topics.GetCache() - shouldNotBeIn := func(tid int) { - if tc != nil { - _, err = tc.Get(tid) - recordMustNotExist(t, err, "Topic cache should be empty") - } - } - if tc != nil { - _, err = tc.Get(newID) - expectNilErr(t, err) - } + tc := c.Topics.GetCache() + shouldNotBeIn := func(tid int) { + if tc != nil { + _, err = tc.Get(tid) + recordMustNotExist(t, err, "Topic cache should be empty") + } + } + if tc != nil { + _, err = tc.Get(newID) + expectNilErr(t, err) + } - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) - expectNilErr(t, topic.Lock()) - shouldNotBeIn(newID) - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, true, false) + expectNilErr(t, topic.Lock()) + shouldNotBeIn(newID) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, true, false) - expectNilErr(t, topic.Unlock()) - shouldNotBeIn(newID) - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) + expectNilErr(t, topic.Unlock()) + shouldNotBeIn(newID) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) - expectNilErr(t, topic.Stick()) - shouldNotBeIn(newID) - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, true) + expectNilErr(t, topic.Stick()) + shouldNotBeIn(newID) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, true) - expectNilErr(t, topic.Unstick()) - shouldNotBeIn(newID) - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) + expectNilErr(t, topic.Unstick()) + shouldNotBeIn(newID) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 2, false, false) - expectNilErr(t, topic.MoveTo(1)) - shouldNotBeIn(newID) - testTopic(newID, "Test Topic", "Topic Content", 1, ip, 1, false, false) - // TODO: Add more tests for more *Topic methods + expectNilErr(t, topic.MoveTo(1)) + shouldNotBeIn(newID) + testTopic(newID, "Test Topic", "Topic Content", 1, ip, 1, false, false) + // TODO: Add more tests for more *Topic methods - expectNilErr(t, c.Topics.ClearIPs()) - expectNilErr(t, c.Topics.Reload(topic.ID)) - testTopic(newID, "Test Topic", "Topic Content", 1, "", 1, false, false) - // TODO: Add more tests for more *Topic methods + expectNilErr(t, c.Topics.ClearIPs()) + expectNilErr(t, c.Topics.Reload(topic.ID)) + testTopic(newID, "Test Topic", "Topic Content", 1, "", 1, false, false) + // TODO: Add more tests for more *Topic methods - expectNilErr(t, topic.Delete()) - shouldNotBeIn(newID) + expectNilErr(t, topic.Delete()) + shouldNotBeIn(newID) - _, err = c.Topics.Get(newID) - recordMustNotExist(t, err, fmt.Sprintf("TID #%d shouldn't exist", newID)) - exf(!c.Topics.Exists(newID), "TID #%d shouldn't exist", newID) + _, err = c.Topics.Get(newID) + recordMustNotExist(t, err, fmt.Sprintf("TID #%d shouldn't exist", newID)) + exf(!c.Topics.Exists(newID), "TID #%d shouldn't exist", newID) - // TODO: Test topic creation and retrieving that created topic plus reload and inspecting the cache + // TODO: Test topic creation and retrieving that created topic plus reload and inspecting the cache } func TestForumStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) - // TODO: Test ForumStore.Reload + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) + // TODO: Test ForumStore.Reload - fcache, ok := c.Forums.(c.ForumCache) - ex(ok, "Unable to cast ForumStore to ForumCache") - ex(c.Forums.Count() == 2, "The forumstore global count should be 2") - ex(fcache.Length() == 2, "The forum cache length should be 2") + fcache, ok := c.Forums.(c.ForumCache) + ex(ok, "Unable to cast ForumStore to ForumCache") + ex(c.Forums.Count() == 2, "The forumstore global count should be 2") + ex(fcache.Length() == 2, "The forum cache length should be 2") - _, err := c.Forums.Get(-1) - recordMustNotExist(t, err, "FID #-1 shouldn't exist") - _, err = c.Forums.Get(0) - recordMustNotExist(t, err, "FID #0 shouldn't exist") + _, err := c.Forums.Get(-1) + recordMustNotExist(t, err, "FID #-1 shouldn't exist") + _, err = c.Forums.Get(0) + recordMustNotExist(t, err, "FID #0 shouldn't exist") - testForum := func(f *c.Forum, fid int, name string, active bool, desc string) { - exf(f.ID == fid, "forum.ID should be %d, not %d.", fid, f.ID) - // TODO: Check the preset and forum permissions - exf(f.Name == name, "forum.Name should be %s, not %s", name, f.Name) - str := "" - if !active { - str = "n't" - } - exf(f.Active == active, "The reports forum should%s be active", str) - exf(f.Desc == desc, "forum.Desc should be '%s' not '%s'", desc, f.Desc) - } + testForum := func(f *c.Forum, fid int, name string, active bool, desc string) { + exf(f.ID == fid, "forum.ID should be %d, not %d.", fid, f.ID) + // TODO: Check the preset and forum permissions + exf(f.Name == name, "forum.Name should be %s, not %s", name, f.Name) + str := "" + if !active { + str = "n't" + } + exf(f.Active == active, "The reports forum should%s be active", str) + exf(f.Desc == desc, "forum.Desc should be '%s' not '%s'", desc, f.Desc) + } - forum, err := c.Forums.Get(1) - recordMustExist(t, err, "Couldn't find FID #1") - expectDesc := "All the reports go here" - testForum(forum, 1, "Reports", false, expectDesc) - forum, err = c.Forums.BypassGet(1) - recordMustExist(t, err, "Couldn't find FID #1") + forum, err := c.Forums.Get(1) + recordMustExist(t, err, "Couldn't find FID #1") + expectDesc := "All the reports go here" + testForum(forum, 1, "Reports", false, expectDesc) + forum, err = c.Forums.BypassGet(1) + recordMustExist(t, err, "Couldn't find FID #1") - forum, err = c.Forums.Get(2) - recordMustExist(t, err, "Couldn't find FID #2") - forum, err = c.Forums.BypassGet(2) - recordMustExist(t, err, "Couldn't find FID #2") - expectDesc = "A place for general discussions which don't fit elsewhere" - testForum(forum, 2, "General", true, expectDesc) + forum, err = c.Forums.Get(2) + recordMustExist(t, err, "Couldn't find FID #2") + forum, err = c.Forums.BypassGet(2) + recordMustExist(t, err, "Couldn't find FID #2") + expectDesc = "A place for general discussions which don't fit elsewhere" + testForum(forum, 2, "General", true, expectDesc) - // Forum reload test, kind of hacky but gets the job done - /* - CacheGet(id int) (*Forum, error) - CacheSet(forum *Forum) error - */ - ex(ok, "ForumCache should be available") - forum.Name = "nanana" - fcache.CacheSet(forum) - forum, err = c.Forums.Get(2) - recordMustExist(t, err, "Couldn't find FID #2") - exf(forum.Name == "nanana", "The faux name should be nanana not %s", forum.Name) - expectNilErr(t, c.Forums.Reload(2)) - forum, err = c.Forums.Get(2) - recordMustExist(t, err, "Couldn't find FID #2") - exf(forum.Name == "General", "The proper name should be 2 not %s", forum.Name) + // Forum reload test, kind of hacky but gets the job done + /* + CacheGet(id int) (*Forum, error) + CacheSet(forum *Forum) error + */ + ex(ok, "ForumCache should be available") + forum.Name = "nanana" + fcache.CacheSet(forum) + forum, err = c.Forums.Get(2) + recordMustExist(t, err, "Couldn't find FID #2") + exf(forum.Name == "nanana", "The faux name should be nanana not %s", forum.Name) + expectNilErr(t, c.Forums.Reload(2)) + forum, err = c.Forums.Get(2) + recordMustExist(t, err, "Couldn't find FID #2") + exf(forum.Name == "General", "The proper name should be 2 not %s", forum.Name) - ex(!c.Forums.Exists(-1), "FID #-1 shouldn't exist") - ex(!c.Forums.Exists(0), "FID #0 shouldn't exist") - ex(c.Forums.Exists(1), "FID #1 should exist") - ex(c.Forums.Exists(2), "FID #2 should exist") - ex(!c.Forums.Exists(3), "FID #3 shouldn't exist") + ex(!c.Forums.Exists(-1), "FID #-1 shouldn't exist") + ex(!c.Forums.Exists(0), "FID #0 shouldn't exist") + ex(c.Forums.Exists(1), "FID #1 should exist") + ex(c.Forums.Exists(2), "FID #2 should exist") + ex(!c.Forums.Exists(3), "FID #3 shouldn't exist") - _, err = c.Forums.Create("", "", true, "all") - ex(err != nil, "A forum shouldn't be successfully created, if it has a blank name") + _, err = c.Forums.Create("", "", true, "all") + ex(err != nil, "A forum shouldn't be successfully created, if it has a blank name") - fid, err := c.Forums.Create("Test Forum", "", true, "all") - expectNilErr(t, err) - ex(fid == 3, "The first forum we create should have an ID of 3") - ex(c.Forums.Exists(3), "FID #2 should exist") + fid, err := c.Forums.Create("Test Forum", "", true, "all") + expectNilErr(t, err) + ex(fid == 3, "The first forum we create should have an ID of 3") + ex(c.Forums.Exists(3), "FID #2 should exist") - ex(c.Forums.Count() == 3, "The forumstore global count should be 3") - ex(fcache.Length() == 3, "The forum cache length should be 3") + ex(c.Forums.Count() == 3, "The forumstore global count should be 3") + ex(fcache.Length() == 3, "The forum cache length should be 3") - forum, err = c.Forums.Get(3) - recordMustExist(t, err, "Couldn't find FID #3") - forum, err = c.Forums.BypassGet(3) - recordMustExist(t, err, "Couldn't find FID #3") - testForum(forum, 3, "Test Forum", true, "") + forum, err = c.Forums.Get(3) + recordMustExist(t, err, "Couldn't find FID #3") + forum, err = c.Forums.BypassGet(3) + recordMustExist(t, err, "Couldn't find FID #3") + testForum(forum, 3, "Test Forum", true, "") - // TODO: More forum creation tests + // TODO: More forum creation tests - expectNilErr(t, c.Forums.Delete(3)) - ex(forum.ID == 3, "forum pointer shenanigans") - ex(c.Forums.Count() == 2, "The forumstore global count should be 2") - ex(fcache.Length() == 2, "The forum cache length should be 2") - ex(!c.Forums.Exists(3), "FID #3 shouldn't exist after being deleted") - _, err = c.Forums.Get(3) - recordMustNotExist(t, err, "FID #3 shouldn't exist after being deleted") - _, err = c.Forums.BypassGet(3) - recordMustNotExist(t, err, "FID #3 shouldn't exist after being deleted") + expectNilErr(t, c.Forums.Delete(3)) + ex(forum.ID == 3, "forum pointer shenanigans") + ex(c.Forums.Count() == 2, "The forumstore global count should be 2") + ex(fcache.Length() == 2, "The forum cache length should be 2") + ex(!c.Forums.Exists(3), "FID #3 shouldn't exist after being deleted") + _, err = c.Forums.Get(3) + recordMustNotExist(t, err, "FID #3 shouldn't exist after being deleted") + _, err = c.Forums.BypassGet(3) + recordMustNotExist(t, err, "FID #3 shouldn't exist after being deleted") - ex(c.Forums.Delete(c.ReportForumID) != nil, "The reports forum shouldn't be deletable") - exf(c.Forums.Exists(c.ReportForumID), "FID #%d should still exist", c.ReportForumID) - _, err = c.Forums.Get(c.ReportForumID) - exf(err == nil, "FID #%d should still exist", c.ReportForumID) - _, err = c.Forums.BypassGet(c.ReportForumID) - exf(err == nil, "FID #%d should still exist", c.ReportForumID) + ex(c.Forums.Delete(c.ReportForumID) != nil, "The reports forum shouldn't be deletable") + exf(c.Forums.Exists(c.ReportForumID), "FID #%d should still exist", c.ReportForumID) + _, err = c.Forums.Get(c.ReportForumID) + exf(err == nil, "FID #%d should still exist", c.ReportForumID) + _, err = c.Forums.BypassGet(c.ReportForumID) + exf(err == nil, "FID #%d should still exist", c.ReportForumID) - eforums := map[int]bool{1: true, 2: true} - { - forums, err := c.Forums.GetAll() - expectNilErr(t, err) - found := make(map[int]*c.Forum) - for _, forum := range forums { - _, ok := eforums[forum.ID] - exf(ok, "unknown forum #%d in forums", forum.ID) - found[forum.ID] = forum - } - for fid, _ := range eforums { - _, ok := found[fid] - exf(ok, "unable to find expected forum #%d in forums", fid) - } - } + eforums := map[int]bool{1: true, 2: true} + { + forums, err := c.Forums.GetAll() + expectNilErr(t, err) + found := make(map[int]*c.Forum) + for _, forum := range forums { + _, ok := eforums[forum.ID] + exf(ok, "unknown forum #%d in forums", forum.ID) + found[forum.ID] = forum + } + for fid, _ := range eforums { + _, ok := found[fid] + exf(ok, "unable to find expected forum #%d in forums", fid) + } + } - { - fids, err := c.Forums.GetAllIDs() - expectNilErr(t, err) - found := make(map[int]bool) - for _, fid := range fids { - _, ok := eforums[fid] - exf(ok, "unknown fid #%d in fids", fid) - found[fid] = true - } - for fid, _ := range eforums { - _, ok := found[fid] - exf(ok, "unable to find expected fid #%d in fids", fid) - } - } + { + fids, err := c.Forums.GetAllIDs() + expectNilErr(t, err) + found := make(map[int]bool) + for _, fid := range fids { + _, ok := eforums[fid] + exf(ok, "unknown fid #%d in fids", fid) + found[fid] = true + } + for fid, _ := range eforums { + _, ok := found[fid] + exf(ok, "unable to find expected fid #%d in fids", fid) + } + } - vforums := map[int]bool{2: true} - { - forums, err := c.Forums.GetAllVisible() - expectNilErr(t, err) - found := make(map[int]*c.Forum) - for _, forum := range forums { - _, ok := vforums[forum.ID] - exf(ok, "unknown forum #%d in forums", forum.ID) - found[forum.ID] = forum - } - for fid, _ := range vforums { - _, ok := found[fid] - exf(ok, "unable to find expected forum #%d in forums", fid) - } - } + vforums := map[int]bool{2: true} + { + forums, err := c.Forums.GetAllVisible() + expectNilErr(t, err) + found := make(map[int]*c.Forum) + for _, forum := range forums { + _, ok := vforums[forum.ID] + exf(ok, "unknown forum #%d in forums", forum.ID) + found[forum.ID] = forum + } + for fid, _ := range vforums { + _, ok := found[fid] + exf(ok, "unable to find expected forum #%d in forums", fid) + } + } - { - fids, err := c.Forums.GetAllVisibleIDs() - expectNilErr(t, err) - found := make(map[int]bool) - for _, fid := range fids { - _, ok := vforums[fid] - exf(ok, "unknown fid #%d in fids", fid) - found[fid] = true - } - for fid, _ := range vforums { - _, ok := found[fid] - exf(ok, "unable to find expected fid #%d in fids", fid) - } - } + { + fids, err := c.Forums.GetAllVisibleIDs() + expectNilErr(t, err) + found := make(map[int]bool) + for _, fid := range fids { + _, ok := vforums[fid] + exf(ok, "unknown fid #%d in fids", fid) + found[fid] = true + } + for fid, _ := range vforums { + _, ok := found[fid] + exf(ok, "unable to find expected fid #%d in fids", fid) + } + } - forum, err = c.Forums.Get(2) - expectNilErr(t, err) - prevTopicCount := forum.TopicCount - tid, err := c.Topics.Create(forum.ID, "Forum Meta Test", "Forum Meta Test", 1, "") - expectNilErr(t, err) - forum, err = c.Forums.Get(2) - expectNilErr(t, err) - exf(forum.TopicCount == (prevTopicCount+1), "forum.TopicCount should be %d not %d", prevTopicCount+1, forum.TopicCount) - exf(forum.LastTopicID == tid, "forum.LastTopicID should be %d not %d", tid, forum.LastTopicID) - exf(forum.LastPage == 1, "forum.LastPage should be %d not %d", 1, forum.LastPage) + forum, err = c.Forums.Get(2) + expectNilErr(t, err) + prevTopicCount := forum.TopicCount + tid, err := c.Topics.Create(forum.ID, "Forum Meta Test", "Forum Meta Test", 1, "") + expectNilErr(t, err) + forum, err = c.Forums.Get(2) + expectNilErr(t, err) + exf(forum.TopicCount == (prevTopicCount+1), "forum.TopicCount should be %d not %d", prevTopicCount+1, forum.TopicCount) + exf(forum.LastTopicID == tid, "forum.LastTopicID should be %d not %d", tid, forum.LastTopicID) + exf(forum.LastPage == 1, "forum.LastPage should be %d not %d", 1, forum.LastPage) - // TODO: Test topic creation and forum topic metadata + // TODO: Test topic creation and forum topic metadata - // TODO: Test forum update - // TODO: Other forumstore stuff and forumcache? + // TODO: Test forum update + // TODO: Other forumstore stuff and forumcache? } // TODO: Implement this func TestForumPermsStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex := exp(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex := exp(t) - f := func(fid, gid int, msg string, inv ...bool) { - fp, err := c.FPStore.Get(fid, gid) - if err == ErrNoRows { - fp = c.BlankForumPerms() - } else { - expectNilErr(t, err) - } - vt := fp.ViewTopic - if len(inv) > 0 && inv[0] == true { - vt = !vt - } - ex(vt, msg) - } + f := func(fid, gid int, msg string, inv ...bool) { + fp, err := c.FPStore.Get(fid, gid) + if err == ErrNoRows { + fp = c.BlankForumPerms() + } else { + expectNilErr(t, err) + } + vt := fp.ViewTopic + if len(inv) > 0 && inv[0] == true { + vt = !vt + } + ex(vt, msg) + } - // TODO: Test reporting - initialState := func() { - f(1, 1, "admins should be able to see reports") - f(1, 2, "mods should be able to see reports") - f(1, 3, "members should not be able to see reports", true) - f(1, 4, "banned users should not be able to see reports", true) - f(2, 1, "admins should be able to see general") - f(2, 3, "members should be able to see general") - f(2, 6, "guests should be able to see general") - } - initialState() + // TODO: Test reporting + initialState := func() { + f(1, 1, "admins should be able to see reports") + f(1, 2, "mods should be able to see reports") + f(1, 3, "members should not be able to see reports", true) + f(1, 4, "banned users should not be able to see reports", true) + f(2, 1, "admins should be able to see general") + f(2, 3, "members should be able to see general") + f(2, 6, "guests should be able to see general") + } + initialState() - expectNilErr(t, c.FPStore.Reload(1)) - initialState() - expectNilErr(t, c.FPStore.Reload(2)) - initialState() + expectNilErr(t, c.FPStore.Reload(1)) + initialState() + expectNilErr(t, c.FPStore.Reload(2)) + initialState() - gid, err := c.Groups.Create("FP Test", "FP Test", false, false, false) - expectNilErr(t, err) - fid, err := c.Forums.Create("FP Test", "FP Test", true, "") - expectNilErr(t, err) + gid, err := c.Groups.Create("FP Test", "FP Test", false, false, false) + expectNilErr(t, err) + fid, err := c.Forums.Create("FP Test", "FP Test", true, "") + expectNilErr(t, err) - u := c.GuestUser.Copy() - rt := func(gid, fid int, shouldSucceed bool) { - w := httptest.NewRecorder() - bytesBuffer := bytes.NewBuffer([]byte("")) - sfid := strconv.Itoa(fid) - req := httptest.NewRequest("", "/forum/"+sfid, bytesBuffer) - u.Group = gid - h, err := c.UserCheck(w, req, &u) - expectNilErr(t, err) - rerr := routes.ViewForum(w, req, &u, h, sfid) - if shouldSucceed { - ex(rerr == nil, "ViewForum should succeed") - } else { - ex(rerr != nil, "ViewForum should not succeed") - } - } - rt(1, fid, false) - rt(2, fid, false) - rt(3, fid, false) - rt(4, fid, false) - rt(gid, fid, false) + u := c.GuestUser.Copy() + rt := func(gid, fid int, shouldSucceed bool) { + w := httptest.NewRecorder() + bytesBuffer := bytes.NewBuffer([]byte("")) + sfid := strconv.Itoa(fid) + req := httptest.NewRequest("", "/forum/"+sfid, bytesBuffer) + u.Group = gid + h, err := c.UserCheck(w, req, &u) + expectNilErr(t, err) + rerr := routes.ViewForum(w, req, &u, h, sfid) + if shouldSucceed { + ex(rerr == nil, "ViewForum should succeed") + } else { + ex(rerr != nil, "ViewForum should not succeed") + } + } + rt(1, fid, false) + rt(2, fid, false) + rt(3, fid, false) + rt(4, fid, false) + rt(gid, fid, false) - fp, err := c.FPStore.GetCopy(fid, gid) - if err == sql.ErrNoRows { - fp = *c.BlankForumPerms() - } else if err != nil { - expectNilErr(t, err) - } - fmt.Printf("fp: %+v\n", fp) + fp, err := c.FPStore.GetCopy(fid, gid) + if err == sql.ErrNoRows { + fp = *c.BlankForumPerms() + } else if err != nil { + expectNilErr(t, err) + } + fmt.Printf("fp: %+v\n", fp) - f(fid, 1, "admins should not be able to see fp test", true) - f(fid, 2, "mods should not be able to see fp test", true) - f(fid, 3, "members should not be able to see fp test", true) - f(fid, 4, "banned users should not be able to see fp test", true) - f(fid, gid, "fp test should not be able to see fp test", true) + f(fid, 1, "admins should not be able to see fp test", true) + f(fid, 2, "mods should not be able to see fp test", true) + f(fid, 3, "members should not be able to see fp test", true) + f(fid, 4, "banned users should not be able to see fp test", true) + f(fid, gid, "fp test should not be able to see fp test", true) - fp.ViewTopic = true + fp.ViewTopic = true - forum, err := c.Forums.Get(fid) - expectNilErr(t, err) - expectNilErr(t, forum.SetPerms(&fp, "custom", gid)) + forum, err := c.Forums.Get(fid) + expectNilErr(t, err) + expectNilErr(t, forum.SetPerms(&fp, "custom", gid)) - rt(1, fid, false) - rt(2, fid, false) - rt(3, fid, false) - rt(4, fid, false) - rt(gid, fid, true) + rt(1, fid, false) + rt(2, fid, false) + rt(3, fid, false) + rt(4, fid, false) + rt(gid, fid, true) - fp, err = c.FPStore.GetCopy(fid, gid) - if err == sql.ErrNoRows { - fp = *c.BlankForumPerms() - } else if err != nil { - expectNilErr(t, err) - } + fp, err = c.FPStore.GetCopy(fid, gid) + if err == sql.ErrNoRows { + fp = *c.BlankForumPerms() + } else if err != nil { + expectNilErr(t, err) + } - f(fid, 1, "admins should not be able to see fp test", true) - f(fid, 2, "mods should not be able to see fp test", true) - f(fid, 3, "members should not be able to see fp test", true) - f(fid, 4, "banned users should not be able to see fp test", true) - f(fid, gid, "fp test should be able to see fp test") + f(fid, 1, "admins should not be able to see fp test", true) + f(fid, 2, "mods should not be able to see fp test", true) + f(fid, 3, "members should not be able to see fp test", true) + f(fid, 4, "banned users should not be able to see fp test", true) + f(fid, gid, "fp test should be able to see fp test") - expectNilErr(t, c.Forums.Delete(fid)) - rt(1, fid, false) - rt(2, fid, false) - rt(3, fid, false) - rt(4, fid, false) - rt(gid, fid, false) + expectNilErr(t, c.Forums.Delete(fid)) + rt(1, fid, false) + rt(2, fid, false) + rt(3, fid, false) + rt(4, fid, false) + rt(gid, fid, false) - // TODO: Test changing forum permissions + // TODO: Test changing forum permissions } // TODO: Test the group permissions // TODO: Test group.CanSee for forum presets + group perms func TestGroupStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) - _, err := c.Groups.Get(-1) - recordMustNotExist(t, err, "GID #-1 shouldn't exist") + _, err := c.Groups.Get(-1) + recordMustNotExist(t, err, "GID #-1 shouldn't exist") - // TODO: Refactor the group store to remove GID #0 - g, err := c.Groups.Get(0) - recordMustExist(t, err, "Couldn't find GID #0") - exf(g.ID == 0, "g.ID doesn't not match the requested GID. Got '%d' instead.", g.ID) - exf(g.Name == "Unknown", "GID #0 is named '%s' and not 'Unknown'", g.Name) + // TODO: Refactor the group store to remove GID #0 + g, err := c.Groups.Get(0) + recordMustExist(t, err, "Couldn't find GID #0") + exf(g.ID == 0, "g.ID doesn't not match the requested GID. Got '%d' instead.", g.ID) + exf(g.Name == "Unknown", "GID #0 is named '%s' and not 'Unknown'", g.Name) - g, err = c.Groups.Get(1) - recordMustExist(t, err, "Couldn't find GID #1") - exf(g.ID == 1, "g.ID doesn't not match the requested GID. Got '%d' instead.'", g.ID) - ex(len(g.CanSee) > 0, "g.CanSee should not be zero") + g, err = c.Groups.Get(1) + recordMustExist(t, err, "Couldn't find GID #1") + exf(g.ID == 1, "g.ID doesn't not match the requested GID. Got '%d' instead.'", g.ID) + ex(len(g.CanSee) > 0, "g.CanSee should not be zero") - ex(!c.Groups.Exists(-1), "GID #-1 shouldn't exist") - // 0 aka Unknown, for system posts and other oddities - ex(c.Groups.Exists(0), "GID #0 should exist") - ex(c.Groups.Exists(1), "GID #1 should exist") + ex(!c.Groups.Exists(-1), "GID #-1 shouldn't exist") + // 0 aka Unknown, for system posts and other oddities + ex(c.Groups.Exists(0), "GID #0 should exist") + ex(c.Groups.Exists(1), "GID #1 should exist") - isAdmin, isMod, isBanned := true, true, false - gid, err := c.Groups.Create("Testing", "Test", isAdmin, isMod, isBanned) - expectNilErr(t, err) - ex(c.Groups.Exists(gid), "The group we just made doesn't exist") + isAdmin, isMod, isBanned := true, true, false + gid, err := c.Groups.Create("Testing", "Test", isAdmin, isMod, isBanned) + expectNilErr(t, err) + ex(c.Groups.Exists(gid), "The group we just made doesn't exist") - ff := func(i bool) string { - if !i { - return "n't" - } - return "" - } - f := func(gid int, isBanned, isMod, isAdmin bool) { - ex(g.ID == gid, "The group ID should match the requested ID") - exf(g.IsAdmin == isAdmin, "This should%s be an admin group", ff(isAdmin)) - exf(g.IsMod == isMod, "This should%s be a mod group", ff(isMod)) - exf(g.IsBanned == isBanned, "This should%s be a ban group", ff(isBanned)) - } + ff := func(i bool) string { + if !i { + return "n't" + } + return "" + } + f := func(gid int, isBanned, isMod, isAdmin bool) { + ex(g.ID == gid, "The group ID should match the requested ID") + exf(g.IsAdmin == isAdmin, "This should%s be an admin group", ff(isAdmin)) + exf(g.IsMod == isMod, "This should%s be a mod group", ff(isMod)) + exf(g.IsBanned == isBanned, "This should%s be a ban group", ff(isBanned)) + } - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, false, true, true) - ex(len(g.CanSee) == 0, "g.CanSee should be empty") + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, false, true, true) + ex(len(g.CanSee) == 0, "g.CanSee should be empty") - isAdmin, isMod, isBanned = false, true, true - gid, err = c.Groups.Create("Testing 2", "Test", isAdmin, isMod, isBanned) - expectNilErr(t, err) - ex(c.Groups.Exists(gid), "The group we just made doesn't exist") + isAdmin, isMod, isBanned = false, true, true + gid, err = c.Groups.Create("Testing 2", "Test", isAdmin, isMod, isBanned) + expectNilErr(t, err) + ex(c.Groups.Exists(gid), "The group we just made doesn't exist") - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, false, true, false) + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, false, true, false) - // TODO: Make sure this pointer doesn't change once we refactor the group store to stop updating the pointer - expectNilErr(t, g.ChangeRank(false, false, true)) + // TODO: Make sure this pointer doesn't change once we refactor the group store to stop updating the pointer + expectNilErr(t, g.ChangeRank(false, false, true)) - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, true, false, false) + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, true, false, false) - expectNilErr(t, g.ChangeRank(true, true, true)) + expectNilErr(t, g.ChangeRank(true, true, true)) - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, false, true, true) - ex(len(g.CanSee) == 0, "len(g.CanSee) should be 0") + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, false, true, true) + ex(len(g.CanSee) == 0, "len(g.CanSee) should be 0") - expectNilErr(t, g.ChangeRank(false, true, true)) + expectNilErr(t, g.ChangeRank(false, true, true)) - forum, err := c.Forums.Get(2) - expectNilErr(t, err) - forumPerms, err := c.FPStore.GetCopy(2, gid) - if err == sql.ErrNoRows { - forumPerms = *c.BlankForumPerms() - } else if err != nil { - expectNilErr(t, err) - } - forumPerms.ViewTopic = true + forum, err := c.Forums.Get(2) + expectNilErr(t, err) + forumPerms, err := c.FPStore.GetCopy(2, gid) + if err == sql.ErrNoRows { + forumPerms = *c.BlankForumPerms() + } else if err != nil { + expectNilErr(t, err) + } + forumPerms.ViewTopic = true - err = forum.SetPerms(&forumPerms, "custom", gid) - expectNilErr(t, err) + err = forum.SetPerms(&forumPerms, "custom", gid) + expectNilErr(t, err) - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, false, true, false) - ex(g.CanSee != nil, "g.CanSee must not be nil") - ex(len(g.CanSee) == 1, "len(g.CanSee) should not be one") - ex(g.CanSee[0] == 2, "g.CanSee[0] should be 2") - canSee := g.CanSee + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, false, true, false) + ex(g.CanSee != nil, "g.CanSee must not be nil") + ex(len(g.CanSee) == 1, "len(g.CanSee) should not be one") + ex(g.CanSee[0] == 2, "g.CanSee[0] should be 2") + canSee := g.CanSee - // Make sure the data is static - expectNilErr(t, c.Groups.Reload(gid)) + // Make sure the data is static + expectNilErr(t, c.Groups.Reload(gid)) - g, err = c.Groups.Get(gid) - expectNilErr(t, err) - f(gid, false, true, false) + g, err = c.Groups.Get(gid) + expectNilErr(t, err) + f(gid, false, true, false) - // TODO: Don't enforce a specific order here - canSeeTest := func(a, b []int) bool { - if (a == nil) != (b == nil) { - return false - } - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true - } + // TODO: Don't enforce a specific order here + canSeeTest := func(a, b []int) bool { + if (a == nil) != (b == nil) { + return false + } + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != b[i] { + return false + } + } + return true + } - ex(canSeeTest(g.CanSee, canSee), "g.CanSee is not being reused") + ex(canSeeTest(g.CanSee, canSee), "g.CanSee is not being reused") - // TODO: Test group deletion - // TODO: Test group reload - // TODO: Test group cache set + // TODO: Test group deletion + // TODO: Test group reload + // TODO: Test group cache set } func TestGroupPromotions(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) - _, err := c.GroupPromotions.Get(-1) - recordMustNotExist(t, err, "GP #-1 shouldn't exist") - _, err = c.GroupPromotions.Get(0) - recordMustNotExist(t, err, "GP #0 shouldn't exist") - _, err = c.GroupPromotions.Get(1) - recordMustNotExist(t, err, "GP #1 shouldn't exist") - expectNilErr(t, c.GroupPromotions.Delete(1)) + _, err := c.GroupPromotions.Get(-1) + recordMustNotExist(t, err, "GP #-1 shouldn't exist") + _, err = c.GroupPromotions.Get(0) + recordMustNotExist(t, err, "GP #0 shouldn't exist") + _, err = c.GroupPromotions.Get(1) + recordMustNotExist(t, err, "GP #1 shouldn't exist") + expectNilErr(t, c.GroupPromotions.Delete(1)) - //GetByGroup(gid int) (gps []*GroupPromotion, err error) + //GetByGroup(gid int) (gps []*GroupPromotion, err error) - testPromo := func(exid, from, to, level, posts, registeredFor int, shouldFail bool) { - gpid, err := c.GroupPromotions.Create(from, to, false, level, posts, registeredFor) - exf(gpid == exid, "gpid should be %d not %d", exid, gpid) - //fmt.Println("gpid:", gpid) - gp, err := c.GroupPromotions.Get(gpid) - expectNilErr(t, err) - exf(gp.ID == gpid, "gp.ID should be %d not %d", gpid, gp.ID) - exf(gp.From == from, "gp.From should be %d not %d", from, gp.From) - exf(gp.To == to, "gp.To should be %d not %d", to, gp.To) - ex(!gp.TwoWay, "gp.TwoWay should be false not true") - exf(gp.Level == level, "gp.Level should be %d not %d", level, gp.Level) - exf(gp.Posts == posts, "gp.Posts should be %d not %d", posts, gp.Posts) - exf(gp.MinTime == 0, "gp.MinTime should be %d not %d", 0, gp.MinTime) - exf(gp.RegisteredFor == registeredFor, "gp.RegisteredFor should be %d not %d", registeredFor, gp.RegisteredFor) + testPromo := func(exid, from, to, level, posts, registeredFor int, shouldFail bool) { + gpid, err := c.GroupPromotions.Create(from, to, false, level, posts, registeredFor) + exf(gpid == exid, "gpid should be %d not %d", exid, gpid) + //fmt.Println("gpid:", gpid) + gp, err := c.GroupPromotions.Get(gpid) + expectNilErr(t, err) + exf(gp.ID == gpid, "gp.ID should be %d not %d", gpid, gp.ID) + exf(gp.From == from, "gp.From should be %d not %d", from, gp.From) + exf(gp.To == to, "gp.To should be %d not %d", to, gp.To) + ex(!gp.TwoWay, "gp.TwoWay should be false not true") + exf(gp.Level == level, "gp.Level should be %d not %d", level, gp.Level) + exf(gp.Posts == posts, "gp.Posts should be %d not %d", posts, gp.Posts) + exf(gp.MinTime == 0, "gp.MinTime should be %d not %d", 0, gp.MinTime) + exf(gp.RegisteredFor == registeredFor, "gp.RegisteredFor should be %d not %d", registeredFor, gp.RegisteredFor) - uid, err := c.Users.Create("Lord_"+strconv.Itoa(gpid), "I_Rule", "", from, false) - expectNilErr(t, err) - u, err := c.Users.Get(uid) - expectNilErr(t, err) - exf(u.ID == uid, "u.ID should be %d not %d", uid, u.ID) - exf(u.Group == from, "u.Group should be %d not %d", from, u.Group) - err = c.GroupPromotions.PromoteIfEligible(u, u.Level, u.Posts, u.CreatedAt) - expectNilErr(t, err) - u.CacheRemove() - u, err = c.Users.Get(uid) - expectNilErr(t, err) - exf(u.ID == uid, "u.ID should be %d not %d", uid, u.ID) - if shouldFail { - exf(u.Group == from, "u.Group should be (from-group) %d not %d", from, u.Group) - } else { - exf(u.Group == to, "u.Group should be (to-group)%d not %d", to, u.Group) - } + uid, err := c.Users.Create("Lord_"+strconv.Itoa(gpid), "I_Rule", "", from, false) + expectNilErr(t, err) + u, err := c.Users.Get(uid) + expectNilErr(t, err) + exf(u.ID == uid, "u.ID should be %d not %d", uid, u.ID) + exf(u.Group == from, "u.Group should be %d not %d", from, u.Group) + err = c.GroupPromotions.PromoteIfEligible(u, u.Level, u.Posts, u.CreatedAt) + expectNilErr(t, err) + u.CacheRemove() + u, err = c.Users.Get(uid) + expectNilErr(t, err) + exf(u.ID == uid, "u.ID should be %d not %d", uid, u.ID) + if shouldFail { + exf(u.Group == from, "u.Group should be (from-group) %d not %d", from, u.Group) + } else { + exf(u.Group == to, "u.Group should be (to-group)%d not %d", to, u.Group) + } - expectNilErr(t, c.GroupPromotions.Delete(gpid)) - _, err = c.GroupPromotions.Get(gpid) - recordMustNotExist(t, err, fmt.Sprintf("GP #%d should no longer exist", gpid)) - } - testPromo(1, 1, 2, 0, 0, 0, false) - testPromo(2, 1, 2, 5, 5, 0, true) - testPromo(3, 1, 2, 0, 0, 1, true) + expectNilErr(t, c.GroupPromotions.Delete(gpid)) + _, err = c.GroupPromotions.Get(gpid) + recordMustNotExist(t, err, fmt.Sprintf("GP #%d should no longer exist", gpid)) + } + testPromo(1, 1, 2, 0, 0, 0, false) + testPromo(2, 1, 2, 5, 5, 0, true) + testPromo(3, 1, 2, 0, 0, 1, true) } func TestReplyStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - _, e := c.Rstore.Get(-1) - recordMustNotExist(t, e, "RID #-1 shouldn't exist") - _, e = c.Rstore.Get(0) - recordMustNotExist(t, e, "RID #0 shouldn't exist") + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + _, e := c.Rstore.Get(-1) + recordMustNotExist(t, e, "RID #-1 shouldn't exist") + _, e = c.Rstore.Get(0) + recordMustNotExist(t, e, "RID #0 shouldn't exist") - c.Config.DisablePostIP = false - testReplyStore(t, 2, "::1") - c.Config.DisablePostIP = true - testReplyStore(t, 5, "") + c.Config.DisablePostIP = false + testReplyStore(t, 2, "::1") + c.Config.DisablePostIP = true + testReplyStore(t, 5, "") } func testReplyStore(t *testing.T, newID int, ip string) { - ex, exf := exp(t), expf(t) - replyTest2 := func(r *c.Reply, e error, rid, parentID, createdBy int, content, ip string) { - expectNilErr(t, e) - exf(r.ID == rid, "RID #%d has the wrong ID. It should be %d not %d", rid, rid, r.ID) - exf(r.ParentID == parentID, "The parent topic of RID #%d should be %d not %d", rid, parentID, r.ParentID) - exf(r.CreatedBy == createdBy, "The creator of RID #%d should be %d not %d", rid, createdBy, r.CreatedBy) - exf(r.Content == content, "The contents of RID #%d should be '%s' not %s", rid, content, r.Content) - exf(r.IP == ip, "The IP of RID#%d should be '%s' not %s", rid, ip, r.IP) - } + ex, exf := exp(t), expf(t) + replyTest2 := func(r *c.Reply, e error, rid, parentID, createdBy int, content, ip string) { + expectNilErr(t, e) + exf(r.ID == rid, "RID #%d has the wrong ID. It should be %d not %d", rid, rid, r.ID) + exf(r.ParentID == parentID, "The parent topic of RID #%d should be %d not %d", rid, parentID, r.ParentID) + exf(r.CreatedBy == createdBy, "The creator of RID #%d should be %d not %d", rid, createdBy, r.CreatedBy) + exf(r.Content == content, "The contents of RID #%d should be '%s' not %s", rid, content, r.Content) + exf(r.IP == ip, "The IP of RID#%d should be '%s' not %s", rid, ip, r.IP) + } - replyTest := func(rid, parentID, createdBy int, content, ip string) { - r, e := c.Rstore.Get(rid) - replyTest2(r, e, rid, parentID, createdBy, content, ip) - r, e = c.Rstore.GetCache().Get(rid) - replyTest2(r, e, rid, parentID, createdBy, content, ip) - } - replyTest(1, 1, 1, "A reply!", "") + replyTest := func(rid, parentID, createdBy int, content, ip string) { + r, e := c.Rstore.Get(rid) + replyTest2(r, e, rid, parentID, createdBy, content, ip) + r, e = c.Rstore.GetCache().Get(rid) + replyTest2(r, e, rid, parentID, createdBy, content, ip) + } + replyTest(1, 1, 1, "A reply!", "") - // ! This is hard to do deterministically as the system may pre-load certain items but let's give it a try: - //_, err = c.Rstore.GetCache().Get(1) - //recordMustNotExist(t, err, "RID #1 shouldn't be in the cache") + // ! This is hard to do deterministically as the system may pre-load certain items but let's give it a try: + //_, err = c.Rstore.GetCache().Get(1) + //recordMustNotExist(t, err, "RID #1 shouldn't be in the cache") - _, err := c.Rstore.Get(newID) - recordMustNotExist(t, err, "RID #2 shouldn't exist") + _, err := c.Rstore.Get(newID) + recordMustNotExist(t, err, "RID #2 shouldn't exist") - newPostCount := 1 - tid, err := c.Topics.Create(2, "Reply Test Topic", "Reply Test Topic", 1, "") - expectNilErr(t, err) + newPostCount := 1 + tid, err := c.Topics.Create(2, "Reply Test Topic", "Reply Test Topic", 1, "") + expectNilErr(t, err) - topic, err := c.Topics.Get(tid) - expectNilErr(t, err) - exf(topic.PostCount == newPostCount, "topic.PostCount should be %d, not %d", newPostCount, topic.PostCount) - exf(topic.LastReplyID == 0, "topic.LastReplyID should be %d not %d", 0, topic.LastReplyID) - ex(topic.CreatedAt == topic.LastReplyAt, "topic.LastReplyAt should equal it's topic.CreatedAt") - exf(topic.LastReplyBy == 1, "topic.LastReplyBy should be %d not %d", 1, topic.LastReplyBy) + topic, err := c.Topics.Get(tid) + expectNilErr(t, err) + exf(topic.PostCount == newPostCount, "topic.PostCount should be %d, not %d", newPostCount, topic.PostCount) + exf(topic.LastReplyID == 0, "topic.LastReplyID should be %d not %d", 0, topic.LastReplyID) + ex(topic.CreatedAt == topic.LastReplyAt, "topic.LastReplyAt should equal it's topic.CreatedAt") + exf(topic.LastReplyBy == 1, "topic.LastReplyBy should be %d not %d", 1, topic.LastReplyBy) - _, err = c.Rstore.GetCache().Get(newID) - recordMustNotExist(t, err, "RID #%d shouldn't be in the cache", newID) + _, err = c.Rstore.GetCache().Get(newID) + recordMustNotExist(t, err, "RID #%d shouldn't be in the cache", newID) - time.Sleep(2 * time.Second) + time.Sleep(2 * time.Second) - uid, err := c.Users.Create("Reply Topic Test User"+strconv.Itoa(newID), "testpassword", "", 2, true) - expectNilErr(t, err) - rid, err := c.Rstore.Create(topic, "Fofofo", ip, uid) - expectNilErr(t, err) - exf(rid == newID, "The next reply ID should be %d not %d", newID, rid) - exf(topic.PostCount == newPostCount, "The old topic in memory's post count should be %d, not %d", newPostCount+1, topic.PostCount) - // TODO: Test the reply count on the topic - exf(topic.LastReplyID == 0, "topic.LastReplyID should be %d not %d", 0, topic.LastReplyID) - ex(topic.CreatedAt == topic.LastReplyAt, "topic.LastReplyAt should equal it's topic.CreatedAt") + uid, err := c.Users.Create("Reply Topic Test User"+strconv.Itoa(newID), "testpassword", "", 2, true) + expectNilErr(t, err) + rid, err := c.Rstore.Create(topic, "Fofofo", ip, uid) + expectNilErr(t, err) + exf(rid == newID, "The next reply ID should be %d not %d", newID, rid) + exf(topic.PostCount == newPostCount, "The old topic in memory's post count should be %d, not %d", newPostCount+1, topic.PostCount) + // TODO: Test the reply count on the topic + exf(topic.LastReplyID == 0, "topic.LastReplyID should be %d not %d", 0, topic.LastReplyID) + ex(topic.CreatedAt == topic.LastReplyAt, "topic.LastReplyAt should equal it's topic.CreatedAt") - replyTest(newID, tid, uid, "Fofofo", ip) + replyTest(newID, tid, uid, "Fofofo", ip) - topic, err = c.Topics.Get(tid) - expectNilErr(t, err) - exf(topic.PostCount == newPostCount+1, "topic.PostCount should be %d, not %d", newPostCount+1, topic.PostCount) - exf(topic.LastReplyID == rid, "topic.LastReplyID should be %d not %d", rid, topic.LastReplyID) - ex(topic.CreatedAt != topic.LastReplyAt, "topic.LastReplyAt should not equal it's topic.CreatedAt") - exf(topic.LastReplyBy == uid, "topic.LastReplyBy should be %d not %d", uid, topic.LastReplyBy) + topic, err = c.Topics.Get(tid) + expectNilErr(t, err) + exf(topic.PostCount == newPostCount+1, "topic.PostCount should be %d, not %d", newPostCount+1, topic.PostCount) + exf(topic.LastReplyID == rid, "topic.LastReplyID should be %d not %d", rid, topic.LastReplyID) + ex(topic.CreatedAt != topic.LastReplyAt, "topic.LastReplyAt should not equal it's topic.CreatedAt") + exf(topic.LastReplyBy == uid, "topic.LastReplyBy should be %d not %d", uid, topic.LastReplyBy) - expectNilErr(t, topic.CreateActionReply("destroy", ip, 1)) - exf(topic.PostCount == newPostCount+1, "The old topic in memory's post count should be %d, not %d", newPostCount+1, topic.PostCount) - replyTest(newID+1, tid, 1, "", ip) - // TODO: Check the actionType field of the reply, this might not be loaded by TopicStore, maybe we should add it there? + expectNilErr(t, topic.CreateActionReply("destroy", ip, 1)) + exf(topic.PostCount == newPostCount+1, "The old topic in memory's post count should be %d, not %d", newPostCount+1, topic.PostCount) + replyTest(newID+1, tid, 1, "", ip) + // TODO: Check the actionType field of the reply, this might not be loaded by TopicStore, maybe we should add it there? - topic, err = c.Topics.Get(tid) - expectNilErr(t, err) - exf(topic.PostCount == newPostCount+2, "topic.PostCount should be %d, not %d", newPostCount+2, topic.PostCount) - exf(topic.LastReplyID != rid, "topic.LastReplyID should not be %d", rid) - arid := topic.LastReplyID + topic, err = c.Topics.Get(tid) + expectNilErr(t, err) + exf(topic.PostCount == newPostCount+2, "topic.PostCount should be %d, not %d", newPostCount+2, topic.PostCount) + exf(topic.LastReplyID != rid, "topic.LastReplyID should not be %d", rid) + arid := topic.LastReplyID - // TODO: Expand upon this - rid, err = c.Rstore.Create(topic, "hiii", ip, 1) - expectNilErr(t, err) - replyTest(rid, topic.ID, 1, "hiii", ip) + // TODO: Expand upon this + rid, err = c.Rstore.Create(topic, "hiii", ip, 1) + expectNilErr(t, err) + replyTest(rid, topic.ID, 1, "hiii", ip) - reply, err := c.Rstore.Get(rid) - expectNilErr(t, err) - expectNilErr(t, reply.SetPost("huuu")) - exf(reply.Content == "hiii", "topic.Content should be hiii, not %s", reply.Content) + reply, err := c.Rstore.Get(rid) + expectNilErr(t, err) + expectNilErr(t, reply.SetPost("huuu")) + exf(reply.Content == "hiii", "topic.Content should be hiii, not %s", reply.Content) - reply, err = c.Rstore.Get(rid) - replyTest2(reply, err, rid, topic.ID, 1, "huuu", ip) - expectNilErr(t, c.Rstore.ClearIPs()) - _ = c.Rstore.GetCache().Remove(rid) - replyTest(rid, topic.ID, 1, "huuu", "") + reply, err = c.Rstore.Get(rid) + replyTest2(reply, err, rid, topic.ID, 1, "huuu", ip) + expectNilErr(t, c.Rstore.ClearIPs()) + _ = c.Rstore.GetCache().Remove(rid) + replyTest(rid, topic.ID, 1, "huuu", "") - expectNilErr(t, reply.Delete()) - // No pointer shenanigans x.x - // TODO: Log reply.ID and rid in cases of pointer shenanigans? - ex(reply.ID == rid, "pointer shenanigans") + expectNilErr(t, reply.Delete()) + // No pointer shenanigans x.x + // TODO: Log reply.ID and rid in cases of pointer shenanigans? + ex(reply.ID == rid, "pointer shenanigans") - _, err = c.Rstore.GetCache().Get(rid) - recordMustNotExist(t, err, fmt.Sprintf("RID #%d shouldn't be in the cache", rid)) - _, err = c.Rstore.Get(rid) - recordMustNotExist(t, err, fmt.Sprintf("RID #%d shouldn't exist", rid)) + _, err = c.Rstore.GetCache().Get(rid) + recordMustNotExist(t, err, fmt.Sprintf("RID #%d shouldn't be in the cache", rid)) + _, err = c.Rstore.Get(rid) + recordMustNotExist(t, err, fmt.Sprintf("RID #%d shouldn't exist", rid)) - topic, err = c.Topics.Get(tid) - expectNilErr(t, err) - exf(topic.LastReplyID == arid, "topic.LastReplyID should be %d not %d", arid, topic.LastReplyID) + topic, err = c.Topics.Get(tid) + expectNilErr(t, err) + exf(topic.LastReplyID == arid, "topic.LastReplyID should be %d not %d", arid, topic.LastReplyID) - // TODO: Write a test for this - //(topic *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUser, ogdesc string, err error) + // TODO: Write a test for this + //(topic *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUser, ogdesc string, err error) - // TODO: Add tests for *Reply - // TODO: Add tests for ReplyCache + // TODO: Add tests for *Reply + // TODO: Add tests for ReplyCache } func TestLikes(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - _, exf := exp(t), expf(t) - bulkExists := func(iids []int, sentBy int, targetType string, expCount int) { - ids, e := c.Likes.BulkExists(iids, sentBy, "replies") - //recordMustNotExist(t, e, "no likes should be found") - expectNilErr(t, e) - exf(len(ids) == expCount, "len ids should be %d", expCount) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + _, exf := exp(t), expf(t) + bulkExists := func(iids []int, sentBy int, targetType string, expCount int) { + ids, e := c.Likes.BulkExists(iids, sentBy, "replies") + //recordMustNotExist(t, e, "no likes should be found") + expectNilErr(t, e) + exf(len(ids) == expCount, "len ids should be %d", expCount) - idMap := make(map[int]struct{}) - for _, id := range ids { - idMap[id] = struct{}{} - } - for _, iid := range iids { - _, ok := idMap[iid] - exf(ok, "missing iid %d in idMap", iid) - } + idMap := make(map[int]struct{}) + for _, id := range ids { + idMap[id] = struct{}{} + } + for _, iid := range iids { + _, ok := idMap[iid] + exf(ok, "missing iid %d in idMap", iid) + } - idCount := 0 - expectNilErr(t, c.Likes.BulkExistsFunc(iids, sentBy, targetType, func(_ int) error { - idCount++ - return nil - })) - exf(idCount == expCount, "idCount should be %d not %d", expCount, idCount) - } + idCount := 0 + expectNilErr(t, c.Likes.BulkExistsFunc(iids, sentBy, targetType, func(_ int) error { + idCount++ + return nil + })) + exf(idCount == expCount, "idCount should be %d not %d", expCount, idCount) + } - uid := 1 - bulkExists([]int{}, uid, "replies", 0) + uid := 1 + bulkExists([]int{}, uid, "replies", 0) - topic, e := c.Topics.Get(1) - expectNilErr(t, e) - rid, e := c.Rstore.Create(topic, "hiii", "", uid) - expectNilErr(t, e) - r, e := c.Rstore.Get(rid) - expectNilErr(t, e) - expectNilErr(t, r.Like(uid)) - bulkExists([]int{rid}, uid, "replies", 1) + topic, e := c.Topics.Get(1) + expectNilErr(t, e) + rid, e := c.Rstore.Create(topic, "hiii", "", uid) + expectNilErr(t, e) + r, e := c.Rstore.Get(rid) + expectNilErr(t, e) + expectNilErr(t, r.Like(uid)) + bulkExists([]int{rid}, uid, "replies", 1) - rid2, e := c.Rstore.Create(topic, "hi 2 u 2", "", uid) - expectNilErr(t, e) - r2, e := c.Rstore.Get(rid2) - expectNilErr(t, e) - expectNilErr(t, r2.Like(uid)) - bulkExists([]int{rid, rid2}, uid, "replies", 2) + rid2, e := c.Rstore.Create(topic, "hi 2 u 2", "", uid) + expectNilErr(t, e) + r2, e := c.Rstore.Get(rid2) + expectNilErr(t, e) + expectNilErr(t, r2.Like(uid)) + bulkExists([]int{rid, rid2}, uid, "replies", 2) - expectNilErr(t, r.Unlike(uid)) - bulkExists([]int{rid2}, uid, "replies", 1) - expectNilErr(t, r2.Unlike(uid)) - bulkExists([]int{}, uid, "replies", 0) + expectNilErr(t, r.Unlike(uid)) + bulkExists([]int{rid2}, uid, "replies", 1) + expectNilErr(t, r2.Unlike(uid)) + bulkExists([]int{}, uid, "replies", 0) - //BulkExists(ids []int, sentBy int, targetType string) (eids []int, err error) + //BulkExists(ids []int, sentBy int, targetType string) (eids []int, err error) - expectNilErr(t, topic.Like(1, uid)) - expectNilErr(t, topic.Unlike(uid)) + expectNilErr(t, topic.Like(1, uid)) + expectNilErr(t, topic.Unlike(uid)) } func TestAttachments(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) - filename := "n0-48.png" - srcFile := "./test_data/" + filename - destFile := "./attachs/" + filename + filename := "n0-48.png" + srcFile := "./test_data/" + filename + destFile := "./attachs/" + filename - ft := func(e error) { - if e != nil && e != sql.ErrNoRows { - t.Error(e) - } - } + ft := func(e error) { + if e != nil && e != sql.ErrNoRows { + t.Error(e) + } + } - ex(c.Attachments.Count() == 0, "the number of attachments should be 0") - ex(c.Attachments.CountIn("topics", 1) == 0, "the number of attachments in topic 1 should be 0") - exf(c.Attachments.CountInPath(filename) == 0, "the number of attachments with path '%s' should be 0", filename) - _, e := c.Attachments.FGet(1) - ft(e) - ex(e == sql.ErrNoRows, ".FGet should have no results") - _, e = c.Attachments.Get(1) - ft(e) - ex(e == sql.ErrNoRows, ".Get should have no results") - _, e = c.Attachments.MiniGetList("topics", 1) - ft(e) - ex(e == sql.ErrNoRows, ".MiniGetList should have no results") - _, e = c.Attachments.BulkMiniGetList("topics", []int{1}) - ft(e) - ex(e == sql.ErrNoRows, ".BulkMiniGetList should have no results") + ex(c.Attachments.Count() == 0, "the number of attachments should be 0") + ex(c.Attachments.CountIn("topics", 1) == 0, "the number of attachments in topic 1 should be 0") + exf(c.Attachments.CountInPath(filename) == 0, "the number of attachments with path '%s' should be 0", filename) + _, e := c.Attachments.FGet(1) + ft(e) + ex(e == sql.ErrNoRows, ".FGet should have no results") + _, e = c.Attachments.Get(1) + ft(e) + ex(e == sql.ErrNoRows, ".Get should have no results") + _, e = c.Attachments.MiniGetList("topics", 1) + ft(e) + ex(e == sql.ErrNoRows, ".MiniGetList should have no results") + _, e = c.Attachments.BulkMiniGetList("topics", []int{1}) + ft(e) + ex(e == sql.ErrNoRows, ".BulkMiniGetList should have no results") - simUpload := func() { - // Sim an upload, try a proper upload through the proper pathway later on - _, e = os.Stat(destFile) - if e != nil && !os.IsNotExist(e) { - expectNilErr(t, e) - } else if e == nil { - expectNilErr(t, os.Remove(destFile)) - } + simUpload := func() { + // Sim an upload, try a proper upload through the proper pathway later on + _, e = os.Stat(destFile) + if e != nil && !os.IsNotExist(e) { + expectNilErr(t, e) + } else if e == nil { + expectNilErr(t, os.Remove(destFile)) + } - input, e := ioutil.ReadFile(srcFile) - expectNilErr(t, e) - expectNilErr(t, ioutil.WriteFile(destFile, input, 0644)) - } - simUpload() + input, e := ioutil.ReadFile(srcFile) + expectNilErr(t, e) + expectNilErr(t, ioutil.WriteFile(destFile, input, 0644)) + } + simUpload() - tid, e := c.Topics.Create(2, "Attach Test", "Filler Body", 1, "") - expectNilErr(t, e) - aid, e := c.Attachments.Add(2, "forums", tid, "topics", 1, filename, "") - expectNilErr(t, e) - exf(aid == 1, "aid should be 1 not %d", aid) - expectNilErr(t, c.Attachments.AddLinked("topics", tid)) - ex(c.Attachments.Count() == 1, "the number of attachments should be 1") - exf(c.Attachments.CountIn("topics", tid) == 1, "the number of attachments in topic %d should be 1", tid) - exf(c.Attachments.CountInPath(filename) == 1, "the number of attachments with path '%s' should be 1", filename) + tid, e := c.Topics.Create(2, "Attach Test", "Filler Body", 1, "") + expectNilErr(t, e) + aid, e := c.Attachments.Add(2, "forums", tid, "topics", 1, filename, "") + expectNilErr(t, e) + exf(aid == 1, "aid should be 1 not %d", aid) + expectNilErr(t, c.Attachments.AddLinked("topics", tid)) + ex(c.Attachments.Count() == 1, "the number of attachments should be 1") + exf(c.Attachments.CountIn("topics", tid) == 1, "the number of attachments in topic %d should be 1", tid) + exf(c.Attachments.CountInPath(filename) == 1, "the number of attachments with path '%s' should be 1", filename) - et := func(a *c.MiniAttachment, aid, sid, oid, uploadedBy int, path, extra, ext string) { - exf(a.ID == aid, "ID should be %d not %d", aid, a.ID) - exf(a.SectionID == sid, "SectionID should be %d not %d", sid, a.SectionID) - exf(a.OriginID == oid, "OriginID should be %d not %d", oid, a.OriginID) - exf(a.UploadedBy == uploadedBy, "UploadedBy should be %d not %d", uploadedBy, a.UploadedBy) - exf(a.Path == path, "Path should be %s not %s", path, a.Path) - exf(a.Extra == extra, "Extra should be %s not %s", extra, a.Extra) - ex(a.Image, "Image should be true") - exf(a.Ext == ext, "Ext should be %s not %s", ext, a.Ext) - } - et2 := func(a *c.Attachment, aid, sid, oid, uploadedBy int, path, extra, ext string) { - exf(a.ID == aid, "ID should be %d not %d", aid, a.ID) - exf(a.SectionID == sid, "SectionID should be %d not %d", sid, a.SectionID) - exf(a.OriginID == oid, "OriginID should be %d not %d", oid, a.OriginID) - exf(a.UploadedBy == uploadedBy, "UploadedBy should be %d not %d", uploadedBy, a.UploadedBy) - exf(a.Path == path, "Path should be %s not %s", path, a.Path) - exf(a.Extra == extra, "Extra should be %s not %s", extra, a.Extra) - ex(a.Image, "Image should be true") - exf(a.Ext == ext, "Ext should be %s not %s", ext, a.Ext) - } + et := func(a *c.MiniAttachment, aid, sid, oid, uploadedBy int, path, extra, ext string) { + exf(a.ID == aid, "ID should be %d not %d", aid, a.ID) + exf(a.SectionID == sid, "SectionID should be %d not %d", sid, a.SectionID) + exf(a.OriginID == oid, "OriginID should be %d not %d", oid, a.OriginID) + exf(a.UploadedBy == uploadedBy, "UploadedBy should be %d not %d", uploadedBy, a.UploadedBy) + exf(a.Path == path, "Path should be %s not %s", path, a.Path) + exf(a.Extra == extra, "Extra should be %s not %s", extra, a.Extra) + ex(a.Image, "Image should be true") + exf(a.Ext == ext, "Ext should be %s not %s", ext, a.Ext) + } + et2 := func(a *c.Attachment, aid, sid, oid, uploadedBy int, path, extra, ext string) { + exf(a.ID == aid, "ID should be %d not %d", aid, a.ID) + exf(a.SectionID == sid, "SectionID should be %d not %d", sid, a.SectionID) + exf(a.OriginID == oid, "OriginID should be %d not %d", oid, a.OriginID) + exf(a.UploadedBy == uploadedBy, "UploadedBy should be %d not %d", uploadedBy, a.UploadedBy) + exf(a.Path == path, "Path should be %s not %s", path, a.Path) + exf(a.Extra == extra, "Extra should be %s not %s", extra, a.Extra) + ex(a.Image, "Image should be true") + exf(a.Ext == ext, "Ext should be %s not %s", ext, a.Ext) + } - f2 := func(aid, sid, oid int, extra string, topic bool) { - var tbl string - if topic { - tbl = "topics" - } else { - tbl = "replies" - } - fa, e := c.Attachments.FGet(aid) - expectNilErr(t, e) - et2(fa, aid, sid, oid, 1, filename, extra, "png") + f2 := func(aid, sid, oid int, extra string, topic bool) { + var tbl string + if topic { + tbl = "topics" + } else { + tbl = "replies" + } + fa, e := c.Attachments.FGet(aid) + expectNilErr(t, e) + et2(fa, aid, sid, oid, 1, filename, extra, "png") - a, e := c.Attachments.Get(aid) - expectNilErr(t, e) - et(a, aid, sid, oid, 1, filename, extra, "png") + a, e := c.Attachments.Get(aid) + expectNilErr(t, e) + et(a, aid, sid, oid, 1, filename, extra, "png") - alist, e := c.Attachments.MiniGetList(tbl, oid) - expectNilErr(t, e) - exf(len(alist) == 1, "len(alist) should be 1 not %d", len(alist)) - a = alist[0] - et(a, aid, sid, oid, 1, filename, extra, "png") + alist, e := c.Attachments.MiniGetList(tbl, oid) + expectNilErr(t, e) + exf(len(alist) == 1, "len(alist) should be 1 not %d", len(alist)) + a = alist[0] + et(a, aid, sid, oid, 1, filename, extra, "png") - amap, e := c.Attachments.BulkMiniGetList(tbl, []int{oid}) - expectNilErr(t, e) - exf(len(amap) == 1, "len(amap) should be 1 not %d", len(amap)) - alist, ok := amap[oid] - if !ok { - t.Logf("key %d not found in amap", oid) - } - exf(len(alist) == 1, "len(alist) should be 1 not %d", len(alist)) - a = alist[0] - et(a, aid, sid, oid, 1, filename, extra, "png") - } + amap, e := c.Attachments.BulkMiniGetList(tbl, []int{oid}) + expectNilErr(t, e) + exf(len(amap) == 1, "len(amap) should be 1 not %d", len(amap)) + alist, ok := amap[oid] + if !ok { + t.Logf("key %d not found in amap", oid) + } + exf(len(alist) == 1, "len(alist) should be 1 not %d", len(alist)) + a = alist[0] + et(a, aid, sid, oid, 1, filename, extra, "png") + } - topic, e := c.Topics.Get(tid) - expectNilErr(t, e) - exf(topic.AttachCount == 1, "topic.AttachCount should be 1 not %d", topic.AttachCount) - f2(aid, 2, tid, "", true) - expectNilErr(t, topic.MoveTo(1)) - f2(aid, 1, tid, "", true) - expectNilErr(t, c.Attachments.MoveTo(2, tid, "topics")) - f2(aid, 2, tid, "", true) + topic, e := c.Topics.Get(tid) + expectNilErr(t, e) + exf(topic.AttachCount == 1, "topic.AttachCount should be 1 not %d", topic.AttachCount) + f2(aid, 2, tid, "", true) + expectNilErr(t, topic.MoveTo(1)) + f2(aid, 1, tid, "", true) + expectNilErr(t, c.Attachments.MoveTo(2, tid, "topics")) + f2(aid, 2, tid, "", true) - // TODO: ShowAttachment test + // TODO: ShowAttachment test - deleteTest := func(aid, oid int, topic bool) { - var tbl string - if topic { - tbl = "topics" - } else { - tbl = "replies" - } - //expectNilErr(t, c.Attachments.Delete(aid)) - expectNilErr(t, c.DeleteAttachment(aid)) - ex(c.Attachments.Count() == 0, "the number of attachments should be 0") - exf(c.Attachments.CountIn(tbl, oid) == 0, "the number of attachments in topic %d should be 0", tid) - exf(c.Attachments.CountInPath(filename) == 0, "the number of attachments with path '%s' should be 0", filename) - _, e = c.Attachments.FGet(aid) - ft(e) - ex(e == sql.ErrNoRows, ".FGet should have no results") - _, e = c.Attachments.Get(aid) - ft(e) - ex(e == sql.ErrNoRows, ".Get should have no results") - _, e = c.Attachments.MiniGetList(tbl, oid) - ft(e) - ex(e == sql.ErrNoRows, ".MiniGetList should have no results") - _, e = c.Attachments.BulkMiniGetList(tbl, []int{oid}) - ft(e) - ex(e == sql.ErrNoRows, ".BulkMiniGetList should have no results") - } - deleteTest(aid, tid, true) - topic, e = c.Topics.Get(tid) - expectNilErr(t, e) - exf(topic.AttachCount == 0, "topic.AttachCount should be 0 not %d", topic.AttachCount) + deleteTest := func(aid, oid int, topic bool) { + var tbl string + if topic { + tbl = "topics" + } else { + tbl = "replies" + } + //expectNilErr(t, c.Attachments.Delete(aid)) + expectNilErr(t, c.DeleteAttachment(aid)) + ex(c.Attachments.Count() == 0, "the number of attachments should be 0") + exf(c.Attachments.CountIn(tbl, oid) == 0, "the number of attachments in topic %d should be 0", tid) + exf(c.Attachments.CountInPath(filename) == 0, "the number of attachments with path '%s' should be 0", filename) + _, e = c.Attachments.FGet(aid) + ft(e) + ex(e == sql.ErrNoRows, ".FGet should have no results") + _, e = c.Attachments.Get(aid) + ft(e) + ex(e == sql.ErrNoRows, ".Get should have no results") + _, e = c.Attachments.MiniGetList(tbl, oid) + ft(e) + ex(e == sql.ErrNoRows, ".MiniGetList should have no results") + _, e = c.Attachments.BulkMiniGetList(tbl, []int{oid}) + ft(e) + ex(e == sql.ErrNoRows, ".BulkMiniGetList should have no results") + } + deleteTest(aid, tid, true) + topic, e = c.Topics.Get(tid) + expectNilErr(t, e) + exf(topic.AttachCount == 0, "topic.AttachCount should be 0 not %d", topic.AttachCount) - simUpload() - rid, e := c.Rstore.Create(topic, "Reply Filler", "", 1) - expectNilErr(t, e) - aid, e = c.Attachments.Add(2, "forums", rid, "replies", 1, filename, strconv.Itoa(topic.ID)) - expectNilErr(t, e) - exf(aid == 2, "aid should be 2 not %d", aid) - expectNilErr(t, c.Attachments.AddLinked("replies", rid)) - r, e := c.Rstore.Get(rid) - expectNilErr(t, e) - exf(r.AttachCount == 1, "r.AttachCount should be 1 not %d", r.AttachCount) - f2(aid, 2, rid, strconv.Itoa(topic.ID), false) - expectNilErr(t, c.Attachments.MoveTo(1, rid, "replies")) - f2(aid, 1, rid, strconv.Itoa(topic.ID), false) - deleteTest(aid, rid, false) - r, e = c.Rstore.Get(rid) - expectNilErr(t, e) - exf(r.AttachCount == 0, "r.AttachCount should be 0 not %d", r.AttachCount) + simUpload() + rid, e := c.Rstore.Create(topic, "Reply Filler", "", 1) + expectNilErr(t, e) + aid, e = c.Attachments.Add(2, "forums", rid, "replies", 1, filename, strconv.Itoa(topic.ID)) + expectNilErr(t, e) + exf(aid == 2, "aid should be 2 not %d", aid) + expectNilErr(t, c.Attachments.AddLinked("replies", rid)) + r, e := c.Rstore.Get(rid) + expectNilErr(t, e) + exf(r.AttachCount == 1, "r.AttachCount should be 1 not %d", r.AttachCount) + f2(aid, 2, rid, strconv.Itoa(topic.ID), false) + expectNilErr(t, c.Attachments.MoveTo(1, rid, "replies")) + f2(aid, 1, rid, strconv.Itoa(topic.ID), false) + deleteTest(aid, rid, false) + r, e = c.Rstore.Get(rid) + expectNilErr(t, e) + exf(r.AttachCount == 0, "r.AttachCount should be 0 not %d", r.AttachCount) - // TODO: Path overlap tests + // TODO: Path overlap tests } func TestPolls(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) - shouldNotExist := func(id int) { - exf(!c.Polls.Exists(id), "poll %d should not exist", id) - _, e := c.Polls.Get(id) - recordMustNotExist(t, e, fmt.Sprintf("poll %d shouldn't exist", id)) - } - shouldNotExist(-1) - shouldNotExist(0) - shouldNotExist(1) - exf(c.Polls.Count() == 0, "count should be %d not %d", 0, c.Polls.Count()) + shouldNotExist := func(id int) { + exf(!c.Polls.Exists(id), "poll %d should not exist", id) + _, e := c.Polls.Get(id) + recordMustNotExist(t, e, fmt.Sprintf("poll %d shouldn't exist", id)) + } + shouldNotExist(-1) + shouldNotExist(0) + shouldNotExist(1) + exf(c.Polls.Count() == 0, "count should be %d not %d", 0, c.Polls.Count()) - tid, e := c.Topics.Create(2, "Poll Test", "Filler Body", 1, "") - expectNilErr(t, e) - topic, e := c.Topics.Get(tid) - expectNilErr(t, e) - exf(topic.Poll == 0, "t.Poll should be %d not %d", 0, topic.Poll) - /*Options map[int]string - Results map[int]int // map[optionIndex]points - QuickOptions []PollOption // TODO: Fix up the template transpiler so we don't need to use this hack anymore - }*/ - pollType := 0 // Basic single choice - pid, e := c.Polls.Create(topic, pollType, map[int]string{0: "item 1", 1: "item 2", 2: "item 3"}) - expectNilErr(t, e) - exf(pid == 1, "poll id should be 1 not %d", pid) - ex(c.Polls.Exists(1), "poll 1 should exist") - exf(c.Polls.Count() == 1, "count should be %d not %d", 1, c.Polls.Count()) - topic, e = c.Topics.BypassGet(tid) - expectNilErr(t, e) - exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) + tid, e := c.Topics.Create(2, "Poll Test", "Filler Body", 1, "") + expectNilErr(t, e) + topic, e := c.Topics.Get(tid) + expectNilErr(t, e) + exf(topic.Poll == 0, "t.Poll should be %d not %d", 0, topic.Poll) + /*Options map[int]string + Results map[int]int // map[optionIndex]points + QuickOptions []PollOption // TODO: Fix up the template transpiler so we don't need to use this hack anymore + }*/ + pollType := 0 // Basic single choice + pid, e := c.Polls.Create(topic, pollType, map[int]string{0: "item 1", 1: "item 2", 2: "item 3"}) + expectNilErr(t, e) + exf(pid == 1, "poll id should be 1 not %d", pid) + ex(c.Polls.Exists(1), "poll 1 should exist") + exf(c.Polls.Count() == 1, "count should be %d not %d", 1, c.Polls.Count()) + topic, e = c.Topics.BypassGet(tid) + expectNilErr(t, e) + exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) - testPoll := func(p *c.Poll, id, parentID int, parentTable string, ptype int, antiCheat bool, voteCount int) { - ef := exf - ef(p.ID == id, "p.ID should be %d not %d", id, p.ID) - ef(p.ParentID == parentID, "p.ParentID should be %d not %d", parentID, p.ParentID) - ef(p.ParentTable == parentTable, "p.ParentID should be %s not %s", parentTable, p.ParentTable) - ef(p.Type == ptype, "p.ParentID should be %d not %d", ptype, p.Type) - s := "false" - if p.AntiCheat { - s = "true" - } - ef(p.AntiCheat == antiCheat, "p.AntiCheat should be ", s) - // TODO: More fields - ef(p.VoteCount == voteCount, "p.VoteCount should be %d not %d", voteCount, p.VoteCount) - } + testPoll := func(p *c.Poll, id, parentID int, parentTable string, ptype int, antiCheat bool, voteCount int) { + ef := exf + ef(p.ID == id, "p.ID should be %d not %d", id, p.ID) + ef(p.ParentID == parentID, "p.ParentID should be %d not %d", parentID, p.ParentID) + ef(p.ParentTable == parentTable, "p.ParentID should be %s not %s", parentTable, p.ParentTable) + ef(p.Type == ptype, "p.ParentID should be %d not %d", ptype, p.Type) + s := "false" + if p.AntiCheat { + s = "true" + } + ef(p.AntiCheat == antiCheat, "p.AntiCheat should be ", s) + // TODO: More fields + ef(p.VoteCount == voteCount, "p.VoteCount should be %d not %d", voteCount, p.VoteCount) + } - p, e := c.Polls.Get(1) - expectNilErr(t, e) - testPoll(p, 1, tid, "topics", 0, false, 0) + p, e := c.Polls.Get(1) + expectNilErr(t, e) + testPoll(p, 1, tid, "topics", 0, false, 0) - expectNilErr(t, p.CastVote(0, 1, "")) - expectNilErr(t, c.Polls.Reload(p.ID)) - p, e = c.Polls.Get(1) - expectNilErr(t, e) - testPoll(p, 1, tid, "topics", 0, false, 1) + expectNilErr(t, p.CastVote(0, 1, "")) + expectNilErr(t, c.Polls.Reload(p.ID)) + p, e = c.Polls.Get(1) + expectNilErr(t, e) + testPoll(p, 1, tid, "topics", 0, false, 1) - var vslice []int - expectNilErr(t, p.Resultsf(func(votes int) error { - vslice = append(vslice, votes) - return nil - })) - //fmt.Printf("vslice: %+v\n", vslice) - exf(vslice[0] == 1, "vslice[0] should be %d not %d", 0, vslice[0]) - exf(vslice[1] == 0, "vslice[1] should be %d not %d", 1, vslice[1]) - exf(vslice[2] == 0, "vslice[2] should be %d not %d", 0, vslice[2]) + var vslice []int + expectNilErr(t, p.Resultsf(func(votes int) error { + vslice = append(vslice, votes) + return nil + })) + //fmt.Printf("vslice: %+v\n", vslice) + exf(vslice[0] == 1, "vslice[0] should be %d not %d", 0, vslice[0]) + exf(vslice[1] == 0, "vslice[1] should be %d not %d", 1, vslice[1]) + exf(vslice[2] == 0, "vslice[2] should be %d not %d", 0, vslice[2]) - expectNilErr(t, p.CastVote(2, 1, "")) - expectNilErr(t, c.Polls.Reload(p.ID)) - p, e = c.Polls.Get(1) - expectNilErr(t, e) - testPoll(p, 1, tid, "topics", 0, false, 2) + expectNilErr(t, p.CastVote(2, 1, "")) + expectNilErr(t, c.Polls.Reload(p.ID)) + p, e = c.Polls.Get(1) + expectNilErr(t, e) + testPoll(p, 1, tid, "topics", 0, false, 2) - vslice = nil - expectNilErr(t, p.Resultsf(func(votes int) error { - vslice = append(vslice, votes) - return nil - })) - //fmt.Printf("vslice: %+v\n", vslice) - exf(vslice[0] == 1, "vslice[0] should be %d not %d", 1, vslice[0]) - exf(vslice[1] == 0, "vslice[1] should be %d not %d", 0, vslice[1]) - exf(vslice[2] == 1, "vslice[2] should be %d not %d", 1, vslice[2]) + vslice = nil + expectNilErr(t, p.Resultsf(func(votes int) error { + vslice = append(vslice, votes) + return nil + })) + //fmt.Printf("vslice: %+v\n", vslice) + exf(vslice[0] == 1, "vslice[0] should be %d not %d", 1, vslice[0]) + exf(vslice[1] == 0, "vslice[1] should be %d not %d", 0, vslice[1]) + exf(vslice[2] == 1, "vslice[2] should be %d not %d", 1, vslice[2]) - expectNilErr(t, c.Polls.ClearIPs()) - // TODO: Test to see if it worked + expectNilErr(t, c.Polls.ClearIPs()) + // TODO: Test to see if it worked - expectNilErr(t, p.Delete()) - ex(!c.Polls.Exists(1), "poll 1 should no longer exist") - _, e = c.Polls.Get(1) - recordMustNotExist(t, e, "poll 1 should no longer exist") - exf(c.Polls.Count() == 0, "count should be %d not %d", 0, c.Polls.Count()) - topic, e = c.Topics.BypassGet(tid) - expectNilErr(t, e) - exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) + expectNilErr(t, p.Delete()) + ex(!c.Polls.Exists(1), "poll 1 should no longer exist") + _, e = c.Polls.Get(1) + recordMustNotExist(t, e, "poll 1 should no longer exist") + exf(c.Polls.Count() == 0, "count should be %d not %d", 0, c.Polls.Count()) + topic, e = c.Topics.BypassGet(tid) + expectNilErr(t, e) + exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) - expectNilErr(t, topic.SetPoll(999)) - topic, e = c.Topics.BypassGet(tid) - expectNilErr(t, e) - exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) + expectNilErr(t, topic.SetPoll(999)) + topic, e = c.Topics.BypassGet(tid) + expectNilErr(t, e) + exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) - expectNilErr(t, topic.SetPoll(0)) - topic, e = c.Topics.BypassGet(tid) - expectNilErr(t, e) - exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) + expectNilErr(t, topic.SetPoll(0)) + topic, e = c.Topics.BypassGet(tid) + expectNilErr(t, e) + exf(topic.Poll == pid, "t.Poll should be %d not %d", pid, topic.Poll) - expectNilErr(t, topic.RemovePoll()) - topic, e = c.Topics.BypassGet(tid) - expectNilErr(t, e) - exf(topic.Poll == 0, "t.Poll should be %d not %d", 0, topic.Poll) + expectNilErr(t, topic.RemovePoll()) + topic, e = c.Topics.BypassGet(tid) + expectNilErr(t, e) + exf(topic.Poll == 0, "t.Poll should be %d not %d", 0, topic.Poll) } func TestSearch(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - exf := expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + exf := expf(t) - title := "search" - body := "bab bab bab bab" - q := "search" - tid, e := c.Topics.Create(2, title, body, 1, "") - expectNilErr(t, e) + title := "search" + body := "bab bab bab bab" + q := "search" + tid, e := c.Topics.Create(2, title, body, 1, "") + expectNilErr(t, e) - tids, e := c.RepliesSearch.Query(q, []int{2}) - //fmt.Printf("tids: %+v\n", tids) - expectNilErr(t, e) - exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) + tids, e := c.RepliesSearch.Query(q, []int{2}) + //fmt.Printf("tids: %+v\n", tids) + expectNilErr(t, e) + exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) - topic, e := c.Topics.Get(tids[0]) - expectNilErr(t, e) - exf(topic.ID == tid, "topic.ID should be %d not %d", tid, topic.ID) - exf(topic.Title == title, "topic.Title should be %s not %s", title, topic.Title) + topic, e := c.Topics.Get(tids[0]) + expectNilErr(t, e) + exf(topic.ID == tid, "topic.ID should be %d not %d", tid, topic.ID) + exf(topic.Title == title, "topic.Title should be %s not %s", title, topic.Title) - tids, e = c.RepliesSearch.Query(q, []int{1, 2}) - //fmt.Printf("tids: %+v\n", tids) - expectNilErr(t, e) - exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) + tids, e = c.RepliesSearch.Query(q, []int{1, 2}) + //fmt.Printf("tids: %+v\n", tids) + expectNilErr(t, e) + exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) - q = "bab" - tids, e = c.RepliesSearch.Query(q, []int{1, 2}) - //fmt.Printf("tids: %+v\n", tids) - expectNilErr(t, e) - exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) + q = "bab" + tids, e = c.RepliesSearch.Query(q, []int{1, 2}) + //fmt.Printf("tids: %+v\n", tids) + expectNilErr(t, e) + exf(len(tids) == 1, "len(tids) should be 1 not %d", len(tids)) } func TestProfileReplyStore(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } - _, e := c.Prstore.Get(-1) - recordMustNotExist(t, e, "PRID #-1 shouldn't exist") - _, e = c.Prstore.Get(0) - recordMustNotExist(t, e, "PRID #0 shouldn't exist") - _, e = c.Prstore.Get(1) - recordMustNotExist(t, e, "PRID #1 shouldn't exist") + _, e := c.Prstore.Get(-1) + recordMustNotExist(t, e, "PRID #-1 shouldn't exist") + _, e = c.Prstore.Get(0) + recordMustNotExist(t, e, "PRID #0 shouldn't exist") + _, e = c.Prstore.Get(1) + recordMustNotExist(t, e, "PRID #1 shouldn't exist") - c.Config.DisablePostIP = false - testProfileReplyStore(t, 1, "::1") - c.Config.DisablePostIP = true - testProfileReplyStore(t, 2, "") + c.Config.DisablePostIP = false + testProfileReplyStore(t, 1, "::1") + c.Config.DisablePostIP = true + testProfileReplyStore(t, 2, "") } func testProfileReplyStore(t *testing.T, newID int, ip string) { - exf := expf(t) - // ? - Commented this one out as strong constraints like this put an unreasonable load on the database, we only want errors if a delete which should succeed fails - //profileReply := c.BlankProfileReply(1) - //e = profileReply.Delete() - //expect(t,e != nil,"You shouldn't be able to delete profile replies which don't exist") + exf := expf(t) + // ? - Commented this one out as strong constraints like this put an unreasonable load on the database, we only want errors if a delete which should succeed fails + //profileReply := c.BlankProfileReply(1) + //e = profileReply.Delete() + //expect(t,e != nil,"You shouldn't be able to delete profile replies which don't exist") - profileID := 1 - prid, e := c.Prstore.Create(profileID, "Haha", 1, ip) - expectNilErr(t, e) - exf(prid == newID, "The first profile reply should have an ID of %d", newID) + profileID := 1 + prid, e := c.Prstore.Create(profileID, "Haha", 1, ip) + expectNilErr(t, e) + exf(prid == newID, "The first profile reply should have an ID of %d", newID) - pr, e := c.Prstore.Get(newID) - expectNilErr(t, e) - exf(pr.ID == newID, "The profile reply should have an ID of %d not %d", newID, pr.ID) - exf(pr.ParentID == 1, "The parent ID of the profile reply should be 1 not %d", pr.ParentID) - exf(pr.Content == "Haha", "The profile reply's contents should be 'Haha' not '%s'", pr.Content) - exf(pr.CreatedBy == 1, "The profile reply's creator should be 1 not %d", pr.CreatedBy) - exf(pr.IP == ip, "The profile reply's IP should be '%s' not '%s'", ip, pr.IP) + pr, e := c.Prstore.Get(newID) + expectNilErr(t, e) + exf(pr.ID == newID, "The profile reply should have an ID of %d not %d", newID, pr.ID) + exf(pr.ParentID == 1, "The parent ID of the profile reply should be 1 not %d", pr.ParentID) + exf(pr.Content == "Haha", "The profile reply's contents should be 'Haha' not '%s'", pr.Content) + exf(pr.CreatedBy == 1, "The profile reply's creator should be 1 not %d", pr.CreatedBy) + exf(pr.IP == ip, "The profile reply's IP should be '%s' not '%s'", ip, pr.IP) - expectNilErr(t, c.Prstore.ClearIPs()) + expectNilErr(t, c.Prstore.ClearIPs()) - pr, e = c.Prstore.Get(newID) - expectNilErr(t, e) - exf(pr.ID == newID, "The profile reply should have an ID of %d not %d", newID, pr.ID) - exf(pr.ParentID == 1, "The parent ID of the profile reply should be 1 not %d", pr.ParentID) - exf(pr.Content == "Haha", "The profile reply's contents should be 'Haha' not '%s'", pr.Content) - exf(pr.CreatedBy == 1, "The profile reply's creator should be 1 not %d", pr.CreatedBy) - ip = "" - exf(pr.IP == ip, "The profile reply's IP should be '%s' not '%s'", ip, pr.IP) + pr, e = c.Prstore.Get(newID) + expectNilErr(t, e) + exf(pr.ID == newID, "The profile reply should have an ID of %d not %d", newID, pr.ID) + exf(pr.ParentID == 1, "The parent ID of the profile reply should be 1 not %d", pr.ParentID) + exf(pr.Content == "Haha", "The profile reply's contents should be 'Haha' not '%s'", pr.Content) + exf(pr.CreatedBy == 1, "The profile reply's creator should be 1 not %d", pr.CreatedBy) + ip = "" + exf(pr.IP == ip, "The profile reply's IP should be '%s' not '%s'", ip, pr.IP) - expectNilErr(t, pr.Delete()) - _, e = c.Prstore.Get(newID) - exf(e != nil, "PRID #%d shouldn't exist after being deleted", newID) + expectNilErr(t, pr.Delete()) + _, e = c.Prstore.Get(newID) + exf(e != nil, "PRID #%d shouldn't exist after being deleted", newID) - // TODO: Test pr.SetBody() and pr.Creator() + // TODO: Test pr.SetBody() and pr.Creator() } func TestConvos(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) - sf := func(i interface{}, e error) error { - return e - } - mf := func(e error, msg string, exists bool) { - if !exists { - recordMustNotExist(t, e, msg) - } else { - recordMustExist(t, e, msg) - } - } - gu := func(uid, offset int, exists bool) { - s := "" - if !exists { - s = " not" - } - mf(sf(c.Convos.GetUser(uid, offset)), fmt.Sprintf("convo getuser %d %d should%s exist", uid, offset, s), exists) - } - gue := func(uid, offset int, exists bool) { - s := "" - if !exists { - s = " not" - } - mf(sf(c.Convos.GetUserExtra(uid, offset)), fmt.Sprintf("convo getuserextra %d %d should%s exist", uid, offset, s), exists) - } + sf := func(i interface{}, e error) error { + return e + } + mf := func(e error, msg string, exists bool) { + if !exists { + recordMustNotExist(t, e, msg) + } else { + recordMustExist(t, e, msg) + } + } + gu := func(uid, offset int, exists bool) { + s := "" + if !exists { + s = " not" + } + mf(sf(c.Convos.GetUser(uid, offset)), fmt.Sprintf("convo getuser %d %d should%s exist", uid, offset, s), exists) + } + gue := func(uid, offset int, exists bool) { + s := "" + if !exists { + s = " not" + } + mf(sf(c.Convos.GetUserExtra(uid, offset)), fmt.Sprintf("convo getuserextra %d %d should%s exist", uid, offset, s), exists) + } - ex(c.Convos.GetUserCount(-1) == 0, "getusercount should be 0") - ex(c.Convos.GetUserCount(0) == 0, "getusercount should be 0") - mf(sf(c.Convos.Get(-1)), "convo -1 should not exist", false) - mf(sf(c.Convos.Get(0)), "convo 0 should not exist", false) - gu(-1, -1, false) - gu(-1, 0, false) - gu(0, 0, false) - gue(-1, -1, false) - gue(-1, 0, false) - gue(0, 0, false) + ex(c.Convos.GetUserCount(-1) == 0, "getusercount should be 0") + ex(c.Convos.GetUserCount(0) == 0, "getusercount should be 0") + mf(sf(c.Convos.Get(-1)), "convo -1 should not exist", false) + mf(sf(c.Convos.Get(0)), "convo 0 should not exist", false) + gu(-1, -1, false) + gu(-1, 0, false) + gu(0, 0, false) + gue(-1, -1, false) + gue(-1, 0, false) + gue(0, 0, false) - nf := func(cid, count int) { - ex := count > 0 - s := "" - if !ex { - s = " not" - } - mf(sf(c.Convos.Get(cid)), fmt.Sprintf("convo %d should%s exist", cid, s), ex) - gu(1, 0, ex) - gu(1, 5, false) // invariant may change in future tests + nf := func(cid, count int) { + ex := count > 0 + s := "" + if !ex { + s = " not" + } + mf(sf(c.Convos.Get(cid)), fmt.Sprintf("convo %d should%s exist", cid, s), ex) + gu(1, 0, ex) + gu(1, 5, false) // invariant may change in future tests - exf(c.Convos.GetUserCount(1) == count, "getusercount should be %d", count) - gue(1, 0, ex) - gue(1, 5, false) // invariant may change in future tests - exf(c.Convos.Count() == count, "convos count should be %d", count) - } - nf(1, 0) + exf(c.Convos.GetUserCount(1) == count, "getusercount should be %d", count) + gue(1, 0, ex) + gue(1, 5, false) // invariant may change in future tests + exf(c.Convos.Count() == count, "convos count should be %d", count) + } + nf(1, 0) - awaitingActivation := 5 - uid, err := c.Users.Create("Saturn", "ReallyBadPassword", "", awaitingActivation, false) - expectNilErr(t, err) + awaitingActivation := 5 + uid, err := c.Users.Create("Saturn", "ReallyBadPassword", "", awaitingActivation, false) + expectNilErr(t, err) - cid, err := c.Convos.Create("hehe", 1, []int{uid}) - expectNilErr(t, err) - ex(cid == 1, "cid should be 1") - ex(c.Convos.Count() == 1, "convos count should be 1") + cid, err := c.Convos.Create("hehe", 1, []int{uid}) + expectNilErr(t, err) + ex(cid == 1, "cid should be 1") + ex(c.Convos.Count() == 1, "convos count should be 1") - co, err := c.Convos.Get(cid) - expectNilErr(t, err) - ex(co.ID == 1, "co.ID should be 1") - ex(co.CreatedBy == 1, "co.CreatedBy should be 1") - // TODO: CreatedAt test - ex(co.LastReplyBy == 1, "co.LastReplyBy should be 1") - // TODO: LastReplyAt test - expectIntToBeX(t, co.PostsCount(), 1, "postscount should be 1, not %d") - ex(co.Has(uid), "saturn should be in the conversation") - ex(!co.Has(9999), "uid 9999 should not be in the conversation") - uids, err := co.Uids() - expectNilErr(t, err) - expectIntToBeX(t, len(uids), 2, "uids length should be 2, not %d") - exf(uids[0] == uid, "uids[0] should be %d, not %d", uid, uids[0]) - exf(uids[1] == 1, "uids[1] should be %d, not %d", 1, uids[1]) - nf(cid, 1) + co, err := c.Convos.Get(cid) + expectNilErr(t, err) + ex(co.ID == 1, "co.ID should be 1") + ex(co.CreatedBy == 1, "co.CreatedBy should be 1") + // TODO: CreatedAt test + ex(co.LastReplyBy == 1, "co.LastReplyBy should be 1") + // TODO: LastReplyAt test + expectIntToBeX(t, co.PostsCount(), 1, "postscount should be 1, not %d") + ex(co.Has(uid), "saturn should be in the conversation") + ex(!co.Has(9999), "uid 9999 should not be in the conversation") + uids, err := co.Uids() + expectNilErr(t, err) + expectIntToBeX(t, len(uids), 2, "uids length should be 2, not %d") + exf(uids[0] == uid, "uids[0] should be %d, not %d", uid, uids[0]) + exf(uids[1] == 1, "uids[1] should be %d, not %d", 1, uids[1]) + nf(cid, 1) - expectNilErr(t, c.Convos.Delete(cid)) - expectIntToBeX(t, co.PostsCount(), 0, "postscount should be 0, not %d") - ex(!co.Has(uid), "saturn should not be in a deleted conversation") - uids, err = co.Uids() - expectNilErr(t, err) - expectIntToBeX(t, len(uids), 0, "uids length should be 0, not %d") - nf(cid, 0) + expectNilErr(t, c.Convos.Delete(cid)) + expectIntToBeX(t, co.PostsCount(), 0, "postscount should be 0, not %d") + ex(!co.Has(uid), "saturn should not be in a deleted conversation") + uids, err = co.Uids() + expectNilErr(t, err) + expectIntToBeX(t, len(uids), 0, "uids length should be 0, not %d") + nf(cid, 0) - // TODO: More tests + // TODO: More tests - // Block tests + // Block tests - ok, err := c.UserBlocks.IsBlockedBy(1, 1) - expectNilErr(t, err) - ex(!ok, "there shouldn't be any blocks") - ok, err = c.UserBlocks.BulkIsBlockedBy([]int{1}, 1) - expectNilErr(t, err) - ex(!ok, "there shouldn't be any blocks") - bf := func(blocker, offset, perPage, expectLen, blockee int) { - l, err := c.UserBlocks.BlockedByOffset(blocker, offset, perPage) - expectNilErr(t, err) - exf(len(l) == expectLen, "there should be %d users blocked by %d not %d", expectLen, blocker, len(l)) - if len(l) > 0 { - exf(l[0] == blockee, "blocked uid should be %d not %d", blockee, l[0]) - } - } - nbf := func(blocker, blockee int) { - ok, err := c.UserBlocks.IsBlockedBy(1, 2) - expectNilErr(t, err) - ex(!ok, "there shouldn't be any blocks") - ok, err = c.UserBlocks.BulkIsBlockedBy([]int{1}, 2) - expectNilErr(t, err) - ex(!ok, "there shouldn't be any blocks") - expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 0, "blockedbycount for 1 should be 1, not %d") - bf(1, 0, 1, 0, 0) - bf(1, 0, 15, 0, 0) - bf(1, 1, 15, 0, 0) - bf(1, 5, 15, 0, 0) - } - nbf(1, 2) + ok, err := c.UserBlocks.IsBlockedBy(1, 1) + expectNilErr(t, err) + ex(!ok, "there shouldn't be any blocks") + ok, err = c.UserBlocks.BulkIsBlockedBy([]int{1}, 1) + expectNilErr(t, err) + ex(!ok, "there shouldn't be any blocks") + bf := func(blocker, offset, perPage, expectLen, blockee int) { + l, err := c.UserBlocks.BlockedByOffset(blocker, offset, perPage) + expectNilErr(t, err) + exf(len(l) == expectLen, "there should be %d users blocked by %d not %d", expectLen, blocker, len(l)) + if len(l) > 0 { + exf(l[0] == blockee, "blocked uid should be %d not %d", blockee, l[0]) + } + } + nbf := func(blocker, blockee int) { + ok, err := c.UserBlocks.IsBlockedBy(1, 2) + expectNilErr(t, err) + ex(!ok, "there shouldn't be any blocks") + ok, err = c.UserBlocks.BulkIsBlockedBy([]int{1}, 2) + expectNilErr(t, err) + ex(!ok, "there shouldn't be any blocks") + expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 0, "blockedbycount for 1 should be 1, not %d") + bf(1, 0, 1, 0, 0) + bf(1, 0, 15, 0, 0) + bf(1, 1, 15, 0, 0) + bf(1, 5, 15, 0, 0) + } + nbf(1, 2) - expectNilErr(t, c.UserBlocks.Add(1, 2)) - ok, err = c.UserBlocks.IsBlockedBy(1, 2) - expectNilErr(t, err) - ex(ok, "2 should be blocked by 1") - expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 1, "blockedbycount for 1 should be 1, not %d") - bf(1, 0, 1, 1, 2) - bf(1, 0, 15, 1, 2) - bf(1, 1, 15, 0, 0) - bf(1, 5, 15, 0, 0) + expectNilErr(t, c.UserBlocks.Add(1, 2)) + ok, err = c.UserBlocks.IsBlockedBy(1, 2) + expectNilErr(t, err) + ex(ok, "2 should be blocked by 1") + expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 1, "blockedbycount for 1 should be 1, not %d") + bf(1, 0, 1, 1, 2) + bf(1, 0, 15, 1, 2) + bf(1, 1, 15, 0, 0) + bf(1, 5, 15, 0, 0) - // Double add test - expectNilErr(t, c.UserBlocks.Add(1, 2)) - ok, err = c.UserBlocks.IsBlockedBy(1, 2) - expectNilErr(t, err) - ex(ok, "2 should be blocked by 1") - //expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 1, "blockedbycount for 1 should be 1, not %d") // todo: fix this - //bf(1, 0, 1, 1, 2) // todo: fix this - //bf(1, 0, 15, 1, 2) // todo: fix this - //bf(1, 1, 15, 0, 0) // todo: fix this - bf(1, 5, 15, 0, 0) + // Double add test + expectNilErr(t, c.UserBlocks.Add(1, 2)) + ok, err = c.UserBlocks.IsBlockedBy(1, 2) + expectNilErr(t, err) + ex(ok, "2 should be blocked by 1") + //expectIntToBeX(t, c.UserBlocks.BlockedByCount(1), 1, "blockedbycount for 1 should be 1, not %d") // todo: fix this + //bf(1, 0, 1, 1, 2) // todo: fix this + //bf(1, 0, 15, 1, 2) // todo: fix this + //bf(1, 1, 15, 0, 0) // todo: fix this + bf(1, 5, 15, 0, 0) - expectNilErr(t, c.UserBlocks.Remove(1, 2)) - nbf(1, 2) - // Double remove test - expectNilErr(t, c.UserBlocks.Remove(1, 2)) - nbf(1, 2) + expectNilErr(t, c.UserBlocks.Remove(1, 2)) + nbf(1, 2) + // Double remove test + expectNilErr(t, c.UserBlocks.Remove(1, 2)) + nbf(1, 2) - // TODO: Self-block test + // TODO: Self-block test - // TODO: More Block tests + // TODO: More Block tests } func TestActivityStream(t *testing.T) { - miscinit(t) - ex, exf := exp(t), expf(t) + miscinit(t) + ex, exf := exp(t), expf(t) - ex(c.Activity.Count() == 0, "activity stream count should be 0") - gNone := func(id int) { - _, e := c.Activity.Get(id) - recordMustNotExist(t, e, "activity item "+strconv.Itoa(id)+" shouldn't exist") - } - gNone(-1) - gNone(0) - gNone(1) - countAsid := func(asid, count int) { - exf(c.ActivityMatches.CountAsid(asid) == count, "activity stream matches count for asid %d should be %d not %d", asid, count, c.ActivityMatches.CountAsid(asid)) - } - countAsid(-1, 0) - countAsid(0, 0) - countAsid(1, 0) + ex(c.Activity.Count() == 0, "activity stream count should be 0") + gNone := func(id int) { + _, e := c.Activity.Get(id) + recordMustNotExist(t, e, "activity item "+strconv.Itoa(id)+" shouldn't exist") + } + gNone(-1) + gNone(0) + gNone(1) + countAsid := func(asid, count int) { + exf(c.ActivityMatches.CountAsid(asid) == count, "activity stream matches count for asid %d should be %d not %d", asid, count, c.ActivityMatches.CountAsid(asid)) + } + countAsid(-1, 0) + countAsid(0, 0) + countAsid(1, 0) - a := c.Alert{ActorID: 1, TargetUserID: 1, Event: "like", ElementType: "topic", ElementID: 1} - id, e := c.Activity.Add(a) - expectNilErr(t, e) - ex(id == 1, "new activity item id should be 1") + a := c.Alert{ActorID: 1, TargetUserID: 1, Event: "like", ElementType: "topic", ElementID: 1} + id, e := c.Activity.Add(a) + expectNilErr(t, e) + ex(id == 1, "new activity item id should be 1") - ex(c.Activity.Count() == 1, "activity stream count should be 1") - al, e := c.Activity.Get(1) - expectNilErr(t, e) - exf(al.ASID == id, "alert asid should be %d not %d", id, al.ASID) - ex(al.ActorID == 1, "alert actorid should be 1") - ex(al.TargetUserID == 1, "alert targetuserid should be 1") - ex(al.Event == "like", "alert event type should be like") - ex(al.ElementType == "topic", "alert element type should be topic") - ex(al.ElementID == 1, "alert element id should be 1") + ex(c.Activity.Count() == 1, "activity stream count should be 1") + al, e := c.Activity.Get(1) + expectNilErr(t, e) + exf(al.ASID == id, "alert asid should be %d not %d", id, al.ASID) + ex(al.ActorID == 1, "alert actorid should be 1") + ex(al.TargetUserID == 1, "alert targetuserid should be 1") + ex(al.Event == "like", "alert event type should be like") + ex(al.ElementType == "topic", "alert element type should be topic") + ex(al.ElementID == 1, "alert element id should be 1") - countAsid(id, 0) + countAsid(id, 0) - tuid, e := c.Users.Create("Activity Target", "Activity Target", "", 1, true) - expectNilErr(t, e) - expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) - countAsid(id, 1) - expectNilErr(t, c.ActivityMatches.Delete(tuid, id)) - countAsid(id, 0) + tuid, e := c.Users.Create("Activity Target", "Activity Target", "", 1, true) + expectNilErr(t, e) + expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) + countAsid(id, 1) + expectNilErr(t, c.ActivityMatches.Delete(tuid, id)) + countAsid(id, 0) - expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) - countAsid(id, 1) - changed, e := c.ActivityMatches.DeleteAndCountChanged(tuid, id) - expectNilErr(t, e) - exf(changed == 1, "changed should be %d not %d", 1, changed) - countAsid(id, 0) + expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) + countAsid(id, 1) + changed, e := c.ActivityMatches.DeleteAndCountChanged(tuid, id) + expectNilErr(t, e) + exf(changed == 1, "changed should be %d not %d", 1, changed) + countAsid(id, 0) - expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) - countAsid(id, 1) + expectNilErr(t, c.ActivityMatches.Add(tuid, 1)) + countAsid(id, 1) - // TODO: Add more tests + // TODO: Add more tests - expectNilErr(t, c.Activity.Delete(id)) - ex(c.Activity.Count() == 0, "activity stream count should be 0") - gNone(id) - countAsid(id, 0) + expectNilErr(t, c.Activity.Delete(id)) + ex(c.Activity.Count() == 0, "activity stream count should be 0") + gNone(id) + countAsid(id, 0) - // TODO: More tests + // TODO: More tests } func TestLogs(t *testing.T) { - ex, exf := exp(t), expf(t) - miscinit(t) - gTests := func(s c.LogStore, phrase string) { - ex(s.Count() == 0, "There shouldn't be any "+phrase) - logs, err := s.GetOffset(0, 25) - expectNilErr(t, err) - ex(len(logs) == 0, "The log slice should be empty") - } - gTests(c.ModLogs, "modlogs") - gTests(c.AdminLogs, "adminlogs") + ex, exf := exp(t), expf(t) + miscinit(t) + gTests := func(s c.LogStore, phrase string) { + ex(s.Count() == 0, "There shouldn't be any "+phrase) + logs, err := s.GetOffset(0, 25) + expectNilErr(t, err) + ex(len(logs) == 0, "The log slice should be empty") + } + gTests(c.ModLogs, "modlogs") + gTests(c.AdminLogs, "adminlogs") - gTests2 := func(s c.LogStore, phrase string) { - err := s.Create("something", 0, "bumblefly", "::1", 1) - expectNilErr(t, err) - count := s.Count() - exf(count == 1, "store.Count should return one, not %d", count) - logs, err := s.GetOffset(0, 25) - recordMustExist(t, err, "We should have at-least one "+phrase) - ex(len(logs) == 1, "The length of the log slice should be one") + gTests2 := func(s c.LogStore, phrase string) { + err := s.Create("something", 0, "bumblefly", "::1", 1) + expectNilErr(t, err) + count := s.Count() + exf(count == 1, "store.Count should return one, not %d", count) + logs, err := s.GetOffset(0, 25) + recordMustExist(t, err, "We should have at-least one "+phrase) + ex(len(logs) == 1, "The length of the log slice should be one") - l := logs[0] - ex(l.Action == "something", "l.Action is not something") - ex(l.ElementID == 0, "l.ElementID is not 0") - ex(l.ElementType == "bumblefly", "l.ElementType is not bumblefly") - ex(l.IP == "::1", "l.IP is not ::1") - ex(l.ActorID == 1, "l.ActorID is not 1") - // TODO: Add a test for log.DoneAt? Maybe throw in some dates and times which are clearly impossible but which may occur due to timezone bugs? - } - gTests2(c.ModLogs, "modlog") - gTests2(c.AdminLogs, "adminlog") + l := logs[0] + ex(l.Action == "something", "l.Action is not something") + ex(l.ElementID == 0, "l.ElementID is not 0") + ex(l.ElementType == "bumblefly", "l.ElementType is not bumblefly") + ex(l.IP == "::1", "l.IP is not ::1") + ex(l.ActorID == 1, "l.ActorID is not 1") + // TODO: Add a test for log.DoneAt? Maybe throw in some dates and times which are clearly impossible but which may occur due to timezone bugs? + } + gTests2(c.ModLogs, "modlog") + gTests2(c.AdminLogs, "adminlog") } func TestRegLogs(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - exf := expf(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + exf := expf(t) - mustNone := func() { - exf(c.RegLogs.Count() == 0, "count should be %d not %d", 0, c.RegLogs.Count()) - items, e := c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) - expectNilErr(t, c.RegLogs.Purge()) - exf(c.RegLogs.Count() == 0, "count should be %d not %d", 0, c.RegLogs.Count()) - items, e = c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) - } - mustNone() + mustNone := func() { + exf(c.RegLogs.Count() == 0, "count should be %d not %d", 0, c.RegLogs.Count()) + items, e := c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) + expectNilErr(t, c.RegLogs.Purge()) + exf(c.RegLogs.Count() == 0, "count should be %d not %d", 0, c.RegLogs.Count()) + items, e = c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) + } + mustNone() - regLog := &c.RegLogItem{Username: "Aa", Email: "aa@example.com", FailureReason: "fake", Success: false, IP: ""} - id, e := regLog.Create() - exf(id == 1, "id should be %d not %d", 1, id) - expectNilErr(t, e) + regLog := &c.RegLogItem{Username: "Aa", Email: "aa@example.com", FailureReason: "fake", Success: false, IP: ""} + id, e := regLog.Create() + exf(id == 1, "id should be %d not %d", 1, id) + expectNilErr(t, e) - exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) - items, e := c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: Add more tests + exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) + items, e := c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: Add more tests - expectNilErr(t, c.RegLogs.DeleteOlderThanDays(2)) + expectNilErr(t, c.RegLogs.DeleteOlderThanDays(2)) - exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) - items, e = c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: Add more tests + exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) + items, e = c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: Add more tests - // TODO: Commit() test? - dayAgo := time.Now().AddDate(0, 0, -5) - items[0].DoneAt = dayAgo.Format("2006-01-02 15:04:05") - expectNilErr(t, items[0].Commit()) + // TODO: Commit() test? + dayAgo := time.Now().AddDate(0, 0, -5) + items[0].DoneAt = dayAgo.Format("2006-01-02 15:04:05") + expectNilErr(t, items[0].Commit()) - exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) - items, e = c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: Add more tests + exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) + items, e = c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: Add more tests - expectNilErr(t, c.RegLogs.DeleteOlderThanDays(2)) - mustNone() + expectNilErr(t, c.RegLogs.DeleteOlderThanDays(2)) + mustNone() - regLog = &c.RegLogItem{Username: "Aa", Email: "aa@example.com", FailureReason: "fake", Success: false, IP: ""} - id, e = regLog.Create() - exf(id == 2, "id should be %d not %d", 2, id) - expectNilErr(t, e) + regLog = &c.RegLogItem{Username: "Aa", Email: "aa@example.com", FailureReason: "fake", Success: false, IP: ""} + id, e = regLog.Create() + exf(id == 2, "id should be %d not %d", 2, id) + expectNilErr(t, e) - exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) - items, e = c.RegLogs.GetOffset(0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: Add more tests + exf(c.RegLogs.Count() == 1, "count should be %d not %d", 1, c.RegLogs.Count()) + items, e = c.RegLogs.GetOffset(0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: Add more tests - expectNilErr(t, c.RegLogs.Purge()) - mustNone() + expectNilErr(t, c.RegLogs.Purge()) + mustNone() - // TODO: Add more tests + // TODO: Add more tests } func TestLoginLogs(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex, exf := exp(t), expf(t) - uid, e := c.Users.Create("Log Test", "Log Test", "", 1, true) - expectNilErr(t, e) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex, exf := exp(t), expf(t) + uid, e := c.Users.Create("Log Test", "Log Test", "", 1, true) + expectNilErr(t, e) - exf(c.LoginLogs.CountUser(-1) == 0, "countuser(-1) should be %d not %d", 0, c.LoginLogs.CountUser(-1)) - exf(c.LoginLogs.CountUser(0) == 0, "countuser(0) should be %d not %d", 0, c.LoginLogs.CountUser(0)) - exf(c.LoginLogs.CountUser(1) == 0, "countuser(1) should be %d not %d", 0, c.LoginLogs.CountUser(1)) - goNone := func(uid, offset, perPage int) { - items, e := c.LoginLogs.GetOffset(uid, offset, perPage) - expectNilErr(t, e) - exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) - } - goNone(-1, 0, 10) - goNone(0, 0, 10) - goNone(1, 0, 10) - goNone(1, 1, 10) - goNone(1, 0, 0) + exf(c.LoginLogs.CountUser(-1) == 0, "countuser(-1) should be %d not %d", 0, c.LoginLogs.CountUser(-1)) + exf(c.LoginLogs.CountUser(0) == 0, "countuser(0) should be %d not %d", 0, c.LoginLogs.CountUser(0)) + exf(c.LoginLogs.CountUser(1) == 0, "countuser(1) should be %d not %d", 0, c.LoginLogs.CountUser(1)) + goNone := func(uid, offset, perPage int) { + items, e := c.LoginLogs.GetOffset(uid, offset, perPage) + expectNilErr(t, e) + exf(len(items) == 0, "len(items) should be %d not %d", 0, len(items)) + } + goNone(-1, 0, 10) + goNone(0, 0, 10) + goNone(1, 0, 10) + goNone(1, 1, 10) + goNone(1, 0, 0) - mustNone := func() { - exf(c.LoginLogs.Count() == 0, "count should be %d not %d", 0, c.LoginLogs.Count()) - exf(c.LoginLogs.CountUser(uid) == 0, "countuser(%d) should be %d not %d", uid, 0, c.LoginLogs.CountUser(uid)) - goNone(uid, 0, 10) - goNone(uid, 1, 10) - goNone(uid, 0, 0) - } - mustNone() + mustNone := func() { + exf(c.LoginLogs.Count() == 0, "count should be %d not %d", 0, c.LoginLogs.Count()) + exf(c.LoginLogs.CountUser(uid) == 0, "countuser(%d) should be %d not %d", uid, 0, c.LoginLogs.CountUser(uid)) + goNone(uid, 0, 10) + goNone(uid, 1, 10) + goNone(uid, 0, 0) + } + mustNone() - logItem := &c.LoginLogItem{UID: uid, Success: true, IP: ""} - _, e = logItem.Create() - expectNilErr(t, e) + logItem := &c.LoginLogItem{UID: uid, Success: true, IP: ""} + _, e = logItem.Create() + expectNilErr(t, e) - exf(c.LoginLogs.Count() == 1, "count should be %d not %d", 1, c.LoginLogs.Count()) - exf(c.LoginLogs.CountUser(uid) == 1, "countuser(%d) should be %d not %d", uid, 1, c.LoginLogs.CountUser(uid)) - items, e := c.LoginLogs.GetOffset(uid, 0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: More tests - exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) - ex(items[0].Success, "Success should be true") - ex(items[0].IP == "", "IP should be blank") - goNone(uid, 1, 10) - goNone(uid, 0, 0) + exf(c.LoginLogs.Count() == 1, "count should be %d not %d", 1, c.LoginLogs.Count()) + exf(c.LoginLogs.CountUser(uid) == 1, "countuser(%d) should be %d not %d", uid, 1, c.LoginLogs.CountUser(uid)) + items, e := c.LoginLogs.GetOffset(uid, 0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: More tests + exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) + ex(items[0].Success, "Success should be true") + ex(items[0].IP == "", "IP should be blank") + goNone(uid, 1, 10) + goNone(uid, 0, 0) - dayAgo := time.Now().AddDate(0, 0, -5) - items[0].DoneAt = dayAgo.Format("2006-01-02 15:04:05") - prevDoneAt := items[0].DoneAt - expectNilErr(t, items[0].Commit()) + dayAgo := time.Now().AddDate(0, 0, -5) + items[0].DoneAt = dayAgo.Format("2006-01-02 15:04:05") + prevDoneAt := items[0].DoneAt + expectNilErr(t, items[0].Commit()) - items, e = c.LoginLogs.GetOffset(uid, 0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: More tests - exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) - ex(items[0].Success, "Success should be true") - ex(items[0].IP == "", "IP should be blank") - exf(items[0].DoneAt == prevDoneAt, "DoneAt should be %s not %s", prevDoneAt, items[0].DoneAt) - goNone(uid, 1, 10) - goNone(uid, 0, 0) + items, e = c.LoginLogs.GetOffset(uid, 0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: More tests + exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) + ex(items[0].Success, "Success should be true") + ex(items[0].IP == "", "IP should be blank") + exf(items[0].DoneAt == prevDoneAt, "DoneAt should be %s not %s", prevDoneAt, items[0].DoneAt) + goNone(uid, 1, 10) + goNone(uid, 0, 0) - expectNilErr(t, c.LoginLogs.DeleteOlderThanDays(2)) - mustNone() + expectNilErr(t, c.LoginLogs.DeleteOlderThanDays(2)) + mustNone() - logItem = &c.LoginLogItem{UID: uid, Success: true, IP: ""} - _, e = logItem.Create() - expectNilErr(t, e) + logItem = &c.LoginLogItem{UID: uid, Success: true, IP: ""} + _, e = logItem.Create() + expectNilErr(t, e) - exf(c.LoginLogs.Count() == 1, "count should be %d not %d", 1, c.LoginLogs.Count()) - exf(c.LoginLogs.CountUser(uid) == 1, "countuser(%d) should be %d not %d", uid, 1, c.LoginLogs.CountUser(uid)) - items, e = c.LoginLogs.GetOffset(uid, 0, 10) - expectNilErr(t, e) - exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) - // TODO: More tests - exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) - ex(items[0].Success, "Success should be true") - ex(items[0].IP == "", "IP should be blank") - goNone(uid, 1, 10) - goNone(uid, 0, 0) + exf(c.LoginLogs.Count() == 1, "count should be %d not %d", 1, c.LoginLogs.Count()) + exf(c.LoginLogs.CountUser(uid) == 1, "countuser(%d) should be %d not %d", uid, 1, c.LoginLogs.CountUser(uid)) + items, e = c.LoginLogs.GetOffset(uid, 0, 10) + expectNilErr(t, e) + exf(len(items) == 1, "len(items) should be %d not %d", 1, len(items)) + // TODO: More tests + exf(items[0].UID == uid, "UID should be %d not %d", uid, items[0].UID) + ex(items[0].Success, "Success should be true") + ex(items[0].IP == "", "IP should be blank") + goNone(uid, 1, 10) + goNone(uid, 0, 0) - expectNilErr(t, c.LoginLogs.Purge()) - mustNone() + expectNilErr(t, c.LoginLogs.Purge()) + mustNone() } func TestPluginManager(t *testing.T) { - miscinit(t) - if !c.PluginsInited { - c.InitPlugins() - } - ex := exp(t) + miscinit(t) + if !c.PluginsInited { + c.InitPlugins() + } + ex := exp(t) - _, ok := c.Plugins["fairy-dust"] - ex(!ok, "Plugin fairy-dust shouldn't exist") - pl, ok := c.Plugins["bbcode"] - ex(ok, "Plugin bbcode should exist") - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e := pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database either") - hasPlugin, e := pl.InDatabase() - expectNilErr(t, e) - ex(!hasPlugin, "Plugin bbcode shouldn't exist in the database") - // TODO: Add some test cases for SetActive and SetInstalled before calling AddToDatabase + _, ok := c.Plugins["fairy-dust"] + ex(!ok, "Plugin fairy-dust shouldn't exist") + pl, ok := c.Plugins["bbcode"] + ex(ok, "Plugin bbcode should exist") + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e := pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database either") + hasPlugin, e := pl.InDatabase() + expectNilErr(t, e) + ex(!hasPlugin, "Plugin bbcode shouldn't exist in the database") + // TODO: Add some test cases for SetActive and SetInstalled before calling AddToDatabase - expectNilErr(t, pl.AddToDatabase(true, false)) - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(pl.Active, "Plugin bbcode should be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(active, "Plugin bbcode should be active in the database too") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should exist in the database") - ex(pl.Init != nil, "Plugin bbcode should have an init function") - expectNilErr(t, pl.Init(pl)) + expectNilErr(t, pl.AddToDatabase(true, false)) + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(pl.Active, "Plugin bbcode should be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(active, "Plugin bbcode should be active in the database too") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should exist in the database") + ex(pl.Init != nil, "Plugin bbcode should have an init function") + expectNilErr(t, pl.Init(pl)) - expectNilErr(t, pl.SetActive(true)) - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(pl.Active, "Plugin bbcode should still be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(active, "Plugin bbcode should still be active in the database too") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") + expectNilErr(t, pl.SetActive(true)) + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(pl.Active, "Plugin bbcode should still be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(active, "Plugin bbcode should still be active in the database too") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") - expectNilErr(t, pl.SetActive(false)) - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") - ex(pl.Deactivate != nil, "Plugin bbcode should have an init function") - pl.Deactivate(pl) // Returns nothing + expectNilErr(t, pl.SetActive(false)) + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") + ex(pl.Deactivate != nil, "Plugin bbcode should have an init function") + pl.Deactivate(pl) // Returns nothing - // Not installable, should not be mutated - ex(pl.SetInstalled(true) == c.ErrPluginNotInstallable, "Plugin was set as installed despite not being installable") - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database either") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") + // Not installable, should not be mutated + ex(pl.SetInstalled(true) == c.ErrPluginNotInstallable, "Plugin was set as installed despite not being installable") + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database either") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") - ex(pl.SetInstalled(false) == c.ErrPluginNotInstallable, "Plugin was set as not installed despite not being installable") - ex(!pl.Installable, "Plugin bbcode shouldn't be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database either") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") + ex(pl.SetInstalled(false) == c.ErrPluginNotInstallable, "Plugin was set as not installed despite not being installable") + ex(!pl.Installable, "Plugin bbcode shouldn't be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database either") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") - // This isn't really installable, but we want to get a few tests done before getting plugins which are stateful - pl.Installable = true - expectNilErr(t, pl.SetInstalled(true)) - ex(pl.Installable, "Plugin bbcode should be installable") - ex(pl.Installed, "Plugin bbcode should be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database either") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") + // This isn't really installable, but we want to get a few tests done before getting plugins which are stateful + pl.Installable = true + expectNilErr(t, pl.SetInstalled(true)) + ex(pl.Installable, "Plugin bbcode should be installable") + ex(pl.Installed, "Plugin bbcode should be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database either") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") - expectNilErr(t, pl.SetInstalled(false)) - ex(pl.Installable, "Plugin bbcode should be installable") - ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") - ex(!pl.Active, "Plugin bbcode shouldn't be active") - active, e = pl.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin bbcode shouldn't be active in the database either") - hasPlugin, e = pl.InDatabase() - expectNilErr(t, e) - ex(hasPlugin, "Plugin bbcode should still exist in the database") + expectNilErr(t, pl.SetInstalled(false)) + ex(pl.Installable, "Plugin bbcode should be installable") + ex(!pl.Installed, "Plugin bbcode shouldn't be 'installed'") + ex(!pl.Active, "Plugin bbcode shouldn't be active") + active, e = pl.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin bbcode shouldn't be active in the database either") + hasPlugin, e = pl.InDatabase() + expectNilErr(t, e) + ex(hasPlugin, "Plugin bbcode should still exist in the database") - // Bugs sometimes arise when we try to delete a hook when there are multiple, so test for that - // TODO: Do a finer grained test for that case...? A bigger test might catch more odd cases with multiple plugins - pl2, ok := c.Plugins["markdown"] - ex(ok, "Plugin markdown should exist") - ex(!pl2.Installable, "Plugin markdown shouldn't be installable") - ex(!pl2.Installed, "Plugin markdown shouldn't be 'installed'") - ex(!pl2.Active, "Plugin markdown shouldn't be active") - active, e = pl2.BypassActive() - expectNilErr(t, e) - ex(!active, "Plugin markdown shouldn't be active in the database either") - hasPlugin, e = pl2.InDatabase() - expectNilErr(t, e) - ex(!hasPlugin, "Plugin markdown shouldn't exist in the database") + // Bugs sometimes arise when we try to delete a hook when there are multiple, so test for that + // TODO: Do a finer grained test for that case...? A bigger test might catch more odd cases with multiple plugins + pl2, ok := c.Plugins["markdown"] + ex(ok, "Plugin markdown should exist") + ex(!pl2.Installable, "Plugin markdown shouldn't be installable") + ex(!pl2.Installed, "Plugin markdown shouldn't be 'installed'") + ex(!pl2.Active, "Plugin markdown shouldn't be active") + active, e = pl2.BypassActive() + expectNilErr(t, e) + ex(!active, "Plugin markdown shouldn't be active in the database either") + hasPlugin, e = pl2.InDatabase() + expectNilErr(t, e) + ex(!hasPlugin, "Plugin markdown shouldn't exist in the database") - expectNilErr(t, pl2.AddToDatabase(true, false)) - expectNilErr(t, pl2.Init(pl2)) - expectNilErr(t, pl.SetActive(true)) - expectNilErr(t, pl.Init(pl)) - pl2.Deactivate(pl2) - expectNilErr(t, pl2.SetActive(false)) - pl.Deactivate(pl) - expectNilErr(t, pl.SetActive(false)) + expectNilErr(t, pl2.AddToDatabase(true, false)) + expectNilErr(t, pl2.Init(pl2)) + expectNilErr(t, pl.SetActive(true)) + expectNilErr(t, pl.Init(pl)) + pl2.Deactivate(pl2) + expectNilErr(t, pl2.SetActive(false)) + pl.Deactivate(pl) + expectNilErr(t, pl.SetActive(false)) - // Hook tests - ht := func() *c.HookTable { - return c.GetHookTable() - } - ex(ht().Sshook("haha", "ho") == "ho", "Sshook shouldn't have anything bound to it yet") - handle := func(in string) (out string) { - return in + "hi" - } - pl.AddHook("haha", handle) - ex(ht().Sshook("haha", "ho") == "hohi", "Sshook didn't give hohi") - pl.RemoveHook("haha", handle) - ex(ht().Sshook("haha", "ho") == "ho", "Sshook shouldn't have anything bound to it anymore") + // Hook tests + ht := func() *c.HookTable { + return c.GetHookTable() + } + ex(ht().Sshook("haha", "ho") == "ho", "Sshook shouldn't have anything bound to it yet") + handle := func(in string) (out string) { + return in + "hi" + } + pl.AddHook("haha", handle) + ex(ht().Sshook("haha", "ho") == "hohi", "Sshook didn't give hohi") + pl.RemoveHook("haha", handle) + ex(ht().Sshook("haha", "ho") == "ho", "Sshook shouldn't have anything bound to it anymore") - /*ex(ht().Hook("haha", "ho") == "ho", "Hook shouldn't have anything bound to it yet") - handle2 := func(inI interface{}) (out interface{}) { - return inI.(string) + "hi" - } - pl.AddHook("hehe", handle2) - ex(ht().Hook("hehe", "ho").(string) == "hohi", "Hook didn't give hohi") - pl.RemoveHook("hehe", handle2) - ex(ht().Hook("hehe", "ho").(string) == "ho", "Hook shouldn't have anything bound to it anymore")*/ + /*ex(ht().Hook("haha", "ho") == "ho", "Hook shouldn't have anything bound to it yet") + handle2 := func(inI interface{}) (out interface{}) { + return inI.(string) + "hi" + } + pl.AddHook("hehe", handle2) + ex(ht().Hook("hehe", "ho").(string) == "hohi", "Hook didn't give hohi") + pl.RemoveHook("hehe", handle2) + ex(ht().Hook("hehe", "ho").(string) == "ho", "Hook shouldn't have anything bound to it anymore")*/ - // TODO: Add tests for more hook types + // TODO: Add tests for more hook types } func TestPhrases(t *testing.T) { - getPhrase := phrases.GetPermPhrase - tp := func(name, expects string) { - res := getPhrase(name) - expect(t, res == expects, "Not the expected phrase, got '"+res+"' instead") - } - tp("BanUsers", "Can ban users") - tp("NoSuchPerm", "{lang.perms[NoSuchPerm]}") - tp("ViewTopic", "Can view topics") - tp("NoSuchPerm", "{lang.perms[NoSuchPerm]}") + getPhrase := phrases.GetPermPhrase + tp := func(name, expects string) { + res := getPhrase(name) + expect(t, res == expects, "Not the expected phrase, got '"+res+"' instead") + } + tp("BanUsers", "Can ban users") + tp("NoSuchPerm", "{lang.perms[NoSuchPerm]}") + tp("ViewTopic", "Can view topics") + tp("NoSuchPerm", "{lang.perms[NoSuchPerm]}") - // TODO: Cover the other phrase types, also try switching between languages to see if anything strange happens + // TODO: Cover the other phrase types, also try switching between languages to see if anything strange happens } func TestMetaStore(t *testing.T) { - ex, exf := exp(t), expf(t) - m, e := c.Meta.Get("magic") - ex(m == "", "meta var magic should be empty") - recordMustNotExist(t, e, "meta var magic should not exist") + ex, exf := exp(t), expf(t) + m, e := c.Meta.Get("magic") + ex(m == "", "meta var magic should be empty") + recordMustNotExist(t, e, "meta var magic should not exist") - expectVal := func(name, expect string) { - m, e = c.Meta.Get(name) - expectNilErr(t, e) - exf(m == expect, "meta var %s should be %s", name, expect) - } + expectVal := func(name, expect string) { + m, e = c.Meta.Get(name) + expectNilErr(t, e) + exf(m == expect, "meta var %s should be %s", name, expect) + } - expectNilErr(t, c.Meta.Set("magic", "lol")) - expectVal("magic", "lol") - expectNilErr(t, c.Meta.Set("magic", "wha")) - expectVal("magic", "wha") + expectNilErr(t, c.Meta.Set("magic", "lol")) + expectVal("magic", "lol") + expectNilErr(t, c.Meta.Set("magic", "wha")) + expectVal("magic", "wha") - m, e = c.Meta.Get("giggle") - ex(m == "", "meta var giggle should be empty") - recordMustNotExist(t, e, "meta var giggle should not exist") + m, e = c.Meta.Get("giggle") + ex(m == "", "meta var giggle should be empty") + recordMustNotExist(t, e, "meta var giggle should not exist") - expectNilErr(t, c.Meta.SetInt("magic", 1)) - expectVal("magic", "1") - expectNilErr(t, c.Meta.SetInt64("magic", 5)) - expectVal("magic", "5") + expectNilErr(t, c.Meta.SetInt("magic", 1)) + expectVal("magic", "1") + expectNilErr(t, c.Meta.SetInt64("magic", 5)) + expectVal("magic", "5") } func TestPages(t *testing.T) { - ex := exp(t) - ex(c.Pages.Count() == 0, "Page count should be 0") - _, e := c.Pages.Get(1) - recordMustNotExist(t, e, "Page 1 should not exist yet") - expectNilErr(t, c.Pages.Delete(-1)) - expectNilErr(t, c.Pages.Delete(0)) - expectNilErr(t, c.Pages.Delete(1)) - _, e = c.Pages.Get(1) - recordMustNotExist(t, e, "Page 1 should not exist yet") - //e = c.Pages.Reload(1) - //recordMustNotExist(t,e,"Page 1 should not exist yet") + ex := exp(t) + ex(c.Pages.Count() == 0, "Page count should be 0") + _, e := c.Pages.Get(1) + recordMustNotExist(t, e, "Page 1 should not exist yet") + expectNilErr(t, c.Pages.Delete(-1)) + expectNilErr(t, c.Pages.Delete(0)) + expectNilErr(t, c.Pages.Delete(1)) + _, e = c.Pages.Get(1) + recordMustNotExist(t, e, "Page 1 should not exist yet") + //e = c.Pages.Reload(1) + //recordMustNotExist(t,e,"Page 1 should not exist yet") - ipage := c.BlankCustomPage() - ipage.Name = "test" - ipage.Title = "Test" - ipage.Body = "A test page" - pid, e := ipage.Create() - expectNilErr(t, e) - ex(pid == 1, "The first page should have an ID of 1") - ex(c.Pages.Count() == 1, "Page count should be 1") + ipage := c.BlankCustomPage() + ipage.Name = "test" + ipage.Title = "Test" + ipage.Body = "A test page" + pid, e := ipage.Create() + expectNilErr(t, e) + ex(pid == 1, "The first page should have an ID of 1") + ex(c.Pages.Count() == 1, "Page count should be 1") - test := func(pid int, ep *c.CustomPage) { - p, e := c.Pages.Get(pid) - expectNilErr(t, e) - ex(p.Name == ep.Name, "The page name should be "+ep.Name) - ex(p.Title == ep.Title, "The page title should be "+ep.Title) - ex(p.Body == ep.Body, "The page body should be "+ep.Body) - } - test(1, ipage) + test := func(pid int, ep *c.CustomPage) { + p, e := c.Pages.Get(pid) + expectNilErr(t, e) + ex(p.Name == ep.Name, "The page name should be "+ep.Name) + ex(p.Title == ep.Title, "The page title should be "+ep.Title) + ex(p.Body == ep.Body, "The page body should be "+ep.Body) + } + test(1, ipage) - opage, err := c.Pages.Get(1) - expectNilErr(t, err) - opage.Name = "t" - opage.Title = "T" - opage.Body = "testing" - expectNilErr(t, opage.Commit()) + opage, err := c.Pages.Get(1) + expectNilErr(t, err) + opage.Name = "t" + opage.Title = "T" + opage.Body = "testing" + expectNilErr(t, opage.Commit()) - test(1, opage) + test(1, opage) - expectNilErr(t, c.Pages.Delete(1)) - ex(c.Pages.Count() == 0, "Page count should be 0") - _, e = c.Pages.Get(1) - recordMustNotExist(t, e, "Page 1 should not exist") - //e = c.Pages.Reload(1) - //recordMustNotExist(t,e,"Page 1 should not exist") + expectNilErr(t, c.Pages.Delete(1)) + ex(c.Pages.Count() == 0, "Page count should be 0") + _, e = c.Pages.Get(1) + recordMustNotExist(t, e, "Page 1 should not exist") + //e = c.Pages.Reload(1) + //recordMustNotExist(t,e,"Page 1 should not exist") - // TODO: More tests + // TODO: More tests } func TestWordFilters(t *testing.T) { - ex, exf := exp(t), expf(t) - // TODO: Test the word filters and their store - ex(c.WordFilters.Length() == 0, "Word filter list should be empty") - ex(c.WordFilters.EstCount() == 0, "Word filter list should be empty") - ex(c.WordFilters.Count() == 0, "Word filter list should be empty") - filters, err := c.WordFilters.GetAll() - expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here - ex(len(filters) == 0, "Word filter map should be empty") - // TODO: Add a test for ParseMessage relating to word filters - _, err = c.WordFilters.Get(1) - recordMustNotExist(t, err, "filter 1 should not exist") + ex, exf := exp(t), expf(t) + // TODO: Test the word filters and their store + ex(c.WordFilters.Length() == 0, "Word filter list should be empty") + ex(c.WordFilters.EstCount() == 0, "Word filter list should be empty") + ex(c.WordFilters.Count() == 0, "Word filter list should be empty") + filters, err := c.WordFilters.GetAll() + expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here + ex(len(filters) == 0, "Word filter map should be empty") + // TODO: Add a test for ParseMessage relating to word filters + _, err = c.WordFilters.Get(1) + recordMustNotExist(t, err, "filter 1 should not exist") - wfid, err := c.WordFilters.Create("imbecile", "lovely") - expectNilErr(t, err) - ex(wfid == 1, "The first word filter should have an ID of 1") - ex(c.WordFilters.Length() == 1, "Word filter list should not be empty") - ex(c.WordFilters.EstCount() == 1, "Word filter list should not be empty") - ex(c.WordFilters.Count() == 1, "Word filter list should not be empty") + wfid, err := c.WordFilters.Create("imbecile", "lovely") + expectNilErr(t, err) + ex(wfid == 1, "The first word filter should have an ID of 1") + ex(c.WordFilters.Length() == 1, "Word filter list should not be empty") + ex(c.WordFilters.EstCount() == 1, "Word filter list should not be empty") + ex(c.WordFilters.Count() == 1, "Word filter list should not be empty") - ftest := func(f *c.WordFilter, id int, find, replace string) { - exf(f.ID == id, "Word filter ID should be %d, not %d", id, f.ID) - exf(f.Find == find, "Word filter needle should be '%s', not '%s'", find, f.Find) - exf(f.Replace == replace, "Word filter replacement should be '%s', not '%s'", replace, f.Replace) - } + ftest := func(f *c.WordFilter, id int, find, replace string) { + exf(f.ID == id, "Word filter ID should be %d, not %d", id, f.ID) + exf(f.Find == find, "Word filter needle should be '%s', not '%s'", find, f.Find) + exf(f.Replace == replace, "Word filter replacement should be '%s', not '%s'", replace, f.Replace) + } - filters, err = c.WordFilters.GetAll() - expectNilErr(t, err) - ex(len(filters) == 1, "Word filter map should not be empty") - ftest(filters[1], 1, "imbecile", "lovely") + filters, err = c.WordFilters.GetAll() + expectNilErr(t, err) + ex(len(filters) == 1, "Word filter map should not be empty") + ftest(filters[1], 1, "imbecile", "lovely") - filter, err := c.WordFilters.Get(1) - expectNilErr(t, err) - ftest(filter, 1, "imbecile", "lovely") + filter, err := c.WordFilters.Get(1) + expectNilErr(t, err) + ftest(filter, 1, "imbecile", "lovely") - // Update - expectNilErr(t, c.WordFilters.Update(1, "b", "a")) + // Update + expectNilErr(t, c.WordFilters.Update(1, "b", "a")) - ex(c.WordFilters.Length() == 1, "Word filter list should not be empty") - ex(c.WordFilters.EstCount() == 1, "Word filter list should not be empty") - ex(c.WordFilters.Count() == 1, "Word filter list should not be empty") + ex(c.WordFilters.Length() == 1, "Word filter list should not be empty") + ex(c.WordFilters.EstCount() == 1, "Word filter list should not be empty") + ex(c.WordFilters.Count() == 1, "Word filter list should not be empty") - filters, err = c.WordFilters.GetAll() - expectNilErr(t, err) - ex(len(filters) == 1, "Word filter map should not be empty") - ftest(filters[1], 1, "b", "a") + filters, err = c.WordFilters.GetAll() + expectNilErr(t, err) + ex(len(filters) == 1, "Word filter map should not be empty") + ftest(filters[1], 1, "b", "a") - filter, err = c.WordFilters.Get(1) - expectNilErr(t, err) - ftest(filter, 1, "b", "a") + filter, err = c.WordFilters.Get(1) + expectNilErr(t, err) + ftest(filter, 1, "b", "a") - // TODO: Add a test for ParseMessage relating to word filters + // TODO: Add a test for ParseMessage relating to word filters - expectNilErr(t, c.WordFilters.Delete(1)) + expectNilErr(t, c.WordFilters.Delete(1)) - ex(c.WordFilters.Length() == 0, "Word filter list should be empty") - ex(c.WordFilters.EstCount() == 0, "Word filter list should be empty") - ex(c.WordFilters.Count() == 0, "Word filter list should be empty") - filters, err = c.WordFilters.GetAll() - expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here - ex(len(filters) == 0, "Word filter map should be empty") - _, err = c.WordFilters.Get(1) - recordMustNotExist(t, err, "filter 1 should not exist") + ex(c.WordFilters.Length() == 0, "Word filter list should be empty") + ex(c.WordFilters.EstCount() == 0, "Word filter list should be empty") + ex(c.WordFilters.Count() == 0, "Word filter list should be empty") + filters, err = c.WordFilters.GetAll() + expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here + ex(len(filters) == 0, "Word filter map should be empty") + _, err = c.WordFilters.Get(1) + recordMustNotExist(t, err, "filter 1 should not exist") - // TODO: Any more tests we could do? + // TODO: Any more tests we could do? } func TestMFAStore(t *testing.T) { - exf := expf(t) + exf := expf(t) - mustNone := func() { - _, e := c.MFAstore.Get(-1) - recordMustNotExist(t, e, "mfa uid -1 should not exist") - _, e = c.MFAstore.Get(0) - recordMustNotExist(t, e, "mfa uid 0 should not exist") - _, e = c.MFAstore.Get(1) - recordMustNotExist(t, e, "mfa uid 1 should not exist") - } - mustNone() + mustNone := func() { + _, e := c.MFAstore.Get(-1) + recordMustNotExist(t, e, "mfa uid -1 should not exist") + _, e = c.MFAstore.Get(0) + recordMustNotExist(t, e, "mfa uid 0 should not exist") + _, e = c.MFAstore.Get(1) + recordMustNotExist(t, e, "mfa uid 1 should not exist") + } + mustNone() - secret, e := c.GenerateGAuthSecret() - expectNilErr(t, e) - expectNilErr(t, c.MFAstore.Create(secret, 1)) - _, e = c.MFAstore.Get(0) - recordMustNotExist(t, e, "mfa uid 0 should not exist") - var scratches []string - it, e := c.MFAstore.Get(1) - test := func(j int) { - expectNilErr(t, e) - exf(it.UID == 1, "UID should be 1 not %d", it.UID) - exf(it.Secret == secret, "Secret should be '%s' not %s", secret, it.Secret) - exf(len(it.Scratch) == 8, "Scratch should be 8 not %d", len(it.Scratch)) - for i, scratch := range it.Scratch { - exf(scratch != "", "scratch %d should not be empty", i) - if scratches != nil { - if j == i { - exf(scratches[i] != scratch, "scratches[%d] should not be %s", i, scratches[i]) - } else { - exf(scratches[i] == scratch, "scratches[%d] should be %s not %s", i, scratches[i], scratch) - } - } - } - scratches = make([]string, 8) - copy(scratches, it.Scratch) - } - test(0) - for i := 0; i < len(scratches); i++ { - expectNilErr(t, it.BurnScratch(i)) - it, e = c.MFAstore.Get(1) - test(i) - } - token, e := gauth.GetTOTPToken(secret) - expectNilErr(t, e) - expectNilErr(t, c.Auth.ValidateMFAToken(token, 1)) - expectNilErr(t, it.Delete()) - mustNone() + secret, e := c.GenerateGAuthSecret() + expectNilErr(t, e) + expectNilErr(t, c.MFAstore.Create(secret, 1)) + _, e = c.MFAstore.Get(0) + recordMustNotExist(t, e, "mfa uid 0 should not exist") + var scratches []string + it, e := c.MFAstore.Get(1) + test := func(j int) { + expectNilErr(t, e) + exf(it.UID == 1, "UID should be 1 not %d", it.UID) + exf(it.Secret == secret, "Secret should be '%s' not %s", secret, it.Secret) + exf(len(it.Scratch) == 8, "Scratch should be 8 not %d", len(it.Scratch)) + for i, scratch := range it.Scratch { + exf(scratch != "", "scratch %d should not be empty", i) + if scratches != nil { + if j == i { + exf(scratches[i] != scratch, "scratches[%d] should not be %s", i, scratches[i]) + } else { + exf(scratches[i] == scratch, "scratches[%d] should be %s not %s", i, scratches[i], scratch) + } + } + } + scratches = make([]string, 8) + copy(scratches, it.Scratch) + } + test(0) + for i := 0; i < len(scratches); i++ { + expectNilErr(t, it.BurnScratch(i)) + it, e = c.MFAstore.Get(1) + test(i) + } + token, e := gauth.GetTOTPToken(secret) + expectNilErr(t, e) + expectNilErr(t, c.Auth.ValidateMFAToken(token, 1)) + expectNilErr(t, it.Delete()) + mustNone() } // TODO: Expand upon the valid characters which can go in URLs? func TestSlugs(t *testing.T) { - l := &MEPairList{nil} - c.Config.BuildSlugs = true // Flip this switch, otherwise all the tests will fail + l := &MEPairList{nil} + c.Config.BuildSlugs = true // Flip this switch, otherwise all the tests will fail - l.Add("Unknown", "unknown") - l.Add("Unknown2", "unknown2") - l.Add("Unknown ", "unknown") - l.Add("Unknown 2", "unknown-2") - l.Add("Unknown 2", "unknown-2") - l.Add("Admin Alice", "admin-alice") - l.Add("Admin_Alice", "adminalice") - l.Add("Admin_Alice-", "adminalice") - l.Add("-Admin_Alice-", "adminalice") - l.Add("-Admin@Alice-", "adminalice") - l.Add("-Admin😀Alice-", "adminalice") - l.Add("u", "u") - l.Add("", "untitled") - l.Add(" ", "untitled") - l.Add("-", "untitled") - l.Add("--", "untitled") - l.Add("é", "é") - l.Add("-é-", "é") - l.Add("-你好-", "untitled") - l.Add("-こにちは-", "untitled") + l.Add("Unknown", "unknown") + l.Add("Unknown2", "unknown2") + l.Add("Unknown ", "unknown") + l.Add("Unknown 2", "unknown-2") + l.Add("Unknown 2", "unknown-2") + l.Add("Admin Alice", "admin-alice") + l.Add("Admin_Alice", "adminalice") + l.Add("Admin_Alice-", "adminalice") + l.Add("-Admin_Alice-", "adminalice") + l.Add("-Admin@Alice-", "adminalice") + l.Add("-Admin😀Alice-", "adminalice") + l.Add("u", "u") + l.Add("", "untitled") + l.Add(" ", "untitled") + l.Add("-", "untitled") + l.Add("--", "untitled") + l.Add("é", "é") + l.Add("-é-", "é") + l.Add("-你好-", "untitled") + l.Add("-こにちは-", "untitled") - for _, item := range l.Items { - t.Log("Testing string '" + item.Msg + "'") - res := c.NameToSlug(item.Msg) - if res != item.Expects { - t.Error("Bad output:", "'"+res+"'") - t.Error("Expected:", item.Expects) - } - } + for _, item := range l.Items { + t.Log("Testing string '" + item.Msg + "'") + res := c.NameToSlug(item.Msg) + if res != item.Expects { + t.Error("Bad output:", "'"+res+"'") + t.Error("Expected:", item.Expects) + } + } } func TestWidgets(t *testing.T) { - ex, exf := exp(t), expf(t) - _, e := c.Widgets.Get(1) - recordMustNotExist(t, e, "There shouldn't be any widgets by default") - widgets := c.Docks.RightSidebar.Items - exf(len(widgets) == 0, "RightSidebar should have 0 items, not %d", len(widgets)) + ex, exf := exp(t), expf(t) + _, e := c.Widgets.Get(1) + recordMustNotExist(t, e, "There shouldn't be any widgets by default") + widgets := c.Docks.RightSidebar.Items + exf(len(widgets) == 0, "RightSidebar should have 0 items, not %d", len(widgets)) - widget := &c.Widget{Position: 0, Side: "rightSidebar", Type: "simple", Enabled: true, Location: "global"} - ewidget := &c.WidgetEdit{widget, map[string]string{"Name": "Test", "Text": "Testing"}} - wid, e := ewidget.Create() - expectNilErr(t, e) - ex(wid == 1, "wid should be 1") + widget := &c.Widget{Position: 0, Side: "rightSidebar", Type: "simple", Enabled: true, Location: "global"} + ewidget := &c.WidgetEdit{widget, map[string]string{"Name": "Test", "Text": "Testing"}} + wid, e := ewidget.Create() + expectNilErr(t, e) + ex(wid == 1, "wid should be 1") - wtest := func(w, w2 *c.Widget) { - ex(w.Position == w2.Position, "wrong position") - ex(w.Side == w2.Side, "wrong side") - ex(w.Type == w2.Type, "wrong type") - ex(w.Enabled == w2.Enabled, "wrong enabled") - ex(w.Location == w2.Location, "wrong location") - } + wtest := func(w, w2 *c.Widget) { + ex(w.Position == w2.Position, "wrong position") + ex(w.Side == w2.Side, "wrong side") + ex(w.Type == w2.Type, "wrong type") + ex(w.Enabled == w2.Enabled, "wrong enabled") + ex(w.Location == w2.Location, "wrong location") + } - // TODO: Do a test for the widget body - widget2, e := c.Widgets.Get(1) - expectNilErr(t, e) - wtest(widget, widget2) + // TODO: Do a test for the widget body + widget2, e := c.Widgets.Get(1) + expectNilErr(t, e) + wtest(widget, widget2) - widgets = c.Docks.RightSidebar.Items - exf(len(widgets) == 1, "RightSidebar should have 1 item, not %d", len(widgets)) - wtest(widget, widgets[0]) + widgets = c.Docks.RightSidebar.Items + exf(len(widgets) == 1, "RightSidebar should have 1 item, not %d", len(widgets)) + wtest(widget, widgets[0]) - widget2.Enabled = false - ewidget = &c.WidgetEdit{widget2, map[string]string{"Name": "Test", "Text": "Testing"}} - expectNilErr(t, ewidget.Commit()) + widget2.Enabled = false + ewidget = &c.WidgetEdit{widget2, map[string]string{"Name": "Test", "Text": "Testing"}} + expectNilErr(t, ewidget.Commit()) - widget2, e = c.Widgets.Get(1) - expectNilErr(t, e) - widget.Enabled = false - wtest(widget, widget2) + widget2, e = c.Widgets.Get(1) + expectNilErr(t, e) + widget.Enabled = false + wtest(widget, widget2) - widgets = c.Docks.RightSidebar.Items - exf(len(widgets) == 1, "RightSidebar should have 1 item, not %d", len(widgets)) - widget.Enabled = false - wtest(widget, widgets[0]) + widgets = c.Docks.RightSidebar.Items + exf(len(widgets) == 1, "RightSidebar should have 1 item, not %d", len(widgets)) + widget.Enabled = false + wtest(widget, widgets[0]) - expectNilErr(t, widget2.Delete()) + expectNilErr(t, widget2.Delete()) - _, e = c.Widgets.Get(1) - recordMustNotExist(t, e, "There shouldn't be any widgets anymore") - widgets = c.Docks.RightSidebar.Items - exf(len(widgets) == 0, "RightSidebar should have 0 items, not %d", len(widgets)) + _, e = c.Widgets.Get(1) + recordMustNotExist(t, e, "There shouldn't be any widgets anymore") + widgets = c.Docks.RightSidebar.Items + exf(len(widgets) == 0, "RightSidebar should have 0 items, not %d", len(widgets)) } /*type ForumActionStoreInt interface { - Get(faid int) (*ForumAction, error) - GetInForum(fid int) ([]*ForumAction, error) - GetAll() ([]*ForumAction, error) - GetNewTopicActions(fid int) ([]*ForumAction, error) + Get(faid int) (*ForumAction, error) + GetInForum(fid int) ([]*ForumAction, error) + GetAll() ([]*ForumAction, error) + GetNewTopicActions(fid int) ([]*ForumAction, error) - Add(fa *ForumAction) (int, error) - Delete(faid int) error - Exists(faid int) bool - Count() int - CountInForum(fid int) int + Add(fa *ForumAction) (int, error) + Delete(faid int) error + Exists(faid int) bool + Count() int + CountInForum(fid int) int - DailyTick() error + DailyTick() error }*/ func TestForumActions(t *testing.T) { - ex, exf, s := exp(t), expf(t), c.ForumActionStore + ex, exf, s := exp(t), expf(t), c.ForumActionStore - count := s.CountInForum(-1) - exf(count == 0, "count should be %d not %d", 0, count) - count = s.CountInForum(0) - exf(count == 0, "count in 0 should be %d not %d", 0, count) - ex(!s.Exists(-1), "faid -1 should not exist") - ex(!s.Exists(0), "faid 0 should not exist") - _, e := s.Get(-1) - recordMustNotExist(t, e, "faid -1 should not exist") - _, e = s.Get(0) - recordMustNotExist(t, e, "faid 0 should not exist") + count := s.CountInForum(-1) + exf(count == 0, "count should be %d not %d", 0, count) + count = s.CountInForum(0) + exf(count == 0, "count in 0 should be %d not %d", 0, count) + ex(!s.Exists(-1), "faid -1 should not exist") + ex(!s.Exists(0), "faid 0 should not exist") + _, e := s.Get(-1) + recordMustNotExist(t, e, "faid -1 should not exist") + _, e = s.Get(0) + recordMustNotExist(t, e, "faid 0 should not exist") - noActions := func(fid, faid int) { - /*sfid, */ sfaid := /*strconv.Itoa(fid), */ strconv.Itoa(faid) - count := s.Count() - exf(count == 0, "count should be %d not %d", 0, count) - count = s.CountInForum(fid) - exf(count == 0, "count in %d should be %d not %d", fid, 0, count) - exf(!s.Exists(faid), "faid %d should not exist", faid) - _, e := s.Get(faid) - recordMustNotExist(t, e, "faid "+sfaid+" should not exist") - //exf(fa == nil, "fa should be nil not %+v", fa) - fas, e := s.GetInForum(fid) - //recordMustNotExist(t, e, "fid "+sfid+" should not have any actions") - expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? - exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) - fas, e = s.GetAll() - //recordMustNotExist(t, e, "there should not be any actions") - expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? - exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) - fas, e = s.GetNewTopicActions(fid) - //recordMustNotExist(t, e, "fid "+sfid+" should not have any new topic actions") - expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? - exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) - } - noActions(1, 1) + noActions := func(fid, faid int) { + /*sfid, */ sfaid := /*strconv.Itoa(fid), */ strconv.Itoa(faid) + count := s.Count() + exf(count == 0, "count should be %d not %d", 0, count) + count = s.CountInForum(fid) + exf(count == 0, "count in %d should be %d not %d", fid, 0, count) + exf(!s.Exists(faid), "faid %d should not exist", faid) + _, e := s.Get(faid) + recordMustNotExist(t, e, "faid "+sfaid+" should not exist") + //exf(fa == nil, "fa should be nil not %+v", fa) + fas, e := s.GetInForum(fid) + //recordMustNotExist(t, e, "fid "+sfid+" should not have any actions") + expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? + exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) + fas, e = s.GetAll() + //recordMustNotExist(t, e, "there should not be any actions") + expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? + exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) + fas, e = s.GetNewTopicActions(fid) + //recordMustNotExist(t, e, "fid "+sfid+" should not have any new topic actions") + expectNilErr(t, e) // TODO: Why does this not return ErrNoRows? + exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas)) + } + noActions(1, 1) - fid, e := c.Forums.Create("Forum Action Test", "Forum Action Test", true, "") - expectNilErr(t, e) - noActions(fid, 1) + fid, e := c.Forums.Create("Forum Action Test", "Forum Action Test", true, "") + expectNilErr(t, e) + noActions(fid, 1) - faid, e := c.ForumActionStore.Add(&c.ForumAction{ - Forum: fid, - RunOnTopicCreation: false, - RunDaysAfterTopicCreation: 1, - RunDaysAfterTopicLastReply: 0, - Action: c.ForumActionLock, - Extra: "", - }) - expectNilErr(t, e) - exf(faid == 1, "faid should be %d not %d", 1, faid) - count = s.Count() - exf(count == 1, "count should be %d not %d", 1, count) - count = s.CountInForum(fid) - exf(count == 1, "count in %d should be %d not %d", fid, 1, count) - exf(s.Exists(faid), "faid %d should exist", faid) + faid, e := c.ForumActionStore.Add(&c.ForumAction{ + Forum: fid, + RunOnTopicCreation: false, + RunDaysAfterTopicCreation: 1, + RunDaysAfterTopicLastReply: 0, + Action: c.ForumActionLock, + Extra: "", + }) + expectNilErr(t, e) + exf(faid == 1, "faid should be %d not %d", 1, faid) + count = s.Count() + exf(count == 1, "count should be %d not %d", 1, count) + count = s.CountInForum(fid) + exf(count == 1, "count in %d should be %d not %d", fid, 1, count) + exf(s.Exists(faid), "faid %d should exist", faid) - fa, e := s.Get(faid) - expectNilErr(t, e) - exf(fa.ID == faid, "fa.ID should be %d not %d", faid, fa.ID) - exf(fa.Forum == fid, "fa.Forum should be %d not %d", fid, fa.Forum) - exf(fa.RunOnTopicCreation == false, "fa.RunOnTopicCreation should be false") - exf(fa.RunDaysAfterTopicCreation == 1, "fa.RunDaysAfterTopicCreation should be %d not %d", 1, fa.RunDaysAfterTopicCreation) - exf(fa.RunDaysAfterTopicLastReply == 0, "fa.RunDaysAfterTopicLastReply should be %d not %d", 0, fa.RunDaysAfterTopicLastReply) - exf(fa.Action == c.ForumActionLock, "fa.Action should be %d not %d", c.ForumActionLock, fa.Action) - exf(fa.Extra == "", "fa.Extra should be '%s' not '%s'", "", fa.Extra) + fa, e := s.Get(faid) + expectNilErr(t, e) + exf(fa.ID == faid, "fa.ID should be %d not %d", faid, fa.ID) + exf(fa.Forum == fid, "fa.Forum should be %d not %d", fid, fa.Forum) + exf(fa.RunOnTopicCreation == false, "fa.RunOnTopicCreation should be false") + exf(fa.RunDaysAfterTopicCreation == 1, "fa.RunDaysAfterTopicCreation should be %d not %d", 1, fa.RunDaysAfterTopicCreation) + exf(fa.RunDaysAfterTopicLastReply == 0, "fa.RunDaysAfterTopicLastReply should be %d not %d", 0, fa.RunDaysAfterTopicLastReply) + exf(fa.Action == c.ForumActionLock, "fa.Action should be %d not %d", c.ForumActionLock, fa.Action) + exf(fa.Extra == "", "fa.Extra should be '%s' not '%s'", "", fa.Extra) - tid, e := c.Topics.Create(fid, "Forum Action Topic", "Forum Action Topic", 1, "") - expectNilErr(t, e) - topic, e := c.Topics.Get(tid) - expectNilErr(t, e) - ex(!topic.IsClosed, "topic.IsClosed should be false") - dayAgo := time.Now().AddDate(0, 0, -5) - expectNilErr(t, topic.TestSetCreatedAt(dayAgo)) - expectNilErr(t, fa.Run()) - topic, e = c.Topics.Get(tid) - expectNilErr(t, e) - ex(topic.IsClosed, "topic.IsClosed should be true") - /*_, e = c.Rstore.Create(topic, "Forum Action Reply", "", 1) - expectNilErr(t, e)*/ + tid, e := c.Topics.Create(fid, "Forum Action Topic", "Forum Action Topic", 1, "") + expectNilErr(t, e) + topic, e := c.Topics.Get(tid) + expectNilErr(t, e) + ex(!topic.IsClosed, "topic.IsClosed should be false") + dayAgo := time.Now().AddDate(0, 0, -5) + expectNilErr(t, topic.TestSetCreatedAt(dayAgo)) + expectNilErr(t, fa.Run()) + topic, e = c.Topics.Get(tid) + expectNilErr(t, e) + ex(topic.IsClosed, "topic.IsClosed should be true") + /*_, e = c.Rstore.Create(topic, "Forum Action Reply", "", 1) + expectNilErr(t, e)*/ - tid, e = c.Topics.Create(fid, "Forum Action Topic 2", "Forum Action Topic 2", 1, "") - expectNilErr(t, e) - topic, e = c.Topics.Get(tid) - expectNilErr(t, e) - ex(!topic.IsClosed, "topic.IsClosed should be false") - expectNilErr(t, fa.Run()) - topic, e = c.Topics.Get(tid) - expectNilErr(t, e) - ex(!topic.IsClosed, "topic.IsClosed should be false") + tid, e = c.Topics.Create(fid, "Forum Action Topic 2", "Forum Action Topic 2", 1, "") + expectNilErr(t, e) + topic, e = c.Topics.Get(tid) + expectNilErr(t, e) + ex(!topic.IsClosed, "topic.IsClosed should be false") + expectNilErr(t, fa.Run()) + topic, e = c.Topics.Get(tid) + expectNilErr(t, e) + ex(!topic.IsClosed, "topic.IsClosed should be false") - _ = tid + _ = tid - expectNilErr(t, s.Delete(faid)) - noActions(fid, faid) + expectNilErr(t, s.Delete(faid)) + noActions(fid, faid) - // TODO: Bulk lock tests - faid, e = c.ForumActionStore.Add(&c.ForumAction{ - Forum: fid, - RunOnTopicCreation: false, - RunDaysAfterTopicCreation: 2, - RunDaysAfterTopicLastReply: 0, - Action: c.ForumActionLock, - Extra: "", - }) - expectNilErr(t, e) + // TODO: Bulk lock tests + faid, e = c.ForumActionStore.Add(&c.ForumAction{ + Forum: fid, + RunOnTopicCreation: false, + RunDaysAfterTopicCreation: 2, + RunDaysAfterTopicLastReply: 0, + Action: c.ForumActionLock, + Extra: "", + }) + expectNilErr(t, e) - var l []int - addTopic := func() { - tid, e = c.Topics.Create(fid, "Forum Action Topic 2", "Forum Action Topic 2", 1, "") - expectNilErr(t, e) - topic, e := c.Topics.Get(tid) - expectNilErr(t, e) - ex(!topic.IsClosed, "topic.IsClosed should be false") - dayAgo := time.Now().AddDate(0, 0, -5) - expectNilErr(t, topic.TestSetCreatedAt(dayAgo)) - l = append(l, tid) - } - lTest := func() { - for _, ll := range l { - to, e := c.Topics.Get(ll) - expectNilErr(t, e) - ex(to.IsClosed, "to.IsClosed should be true") - } - l = nil - } + var l []int + addTopic := func() { + tid, e = c.Topics.Create(fid, "Forum Action Topic 2", "Forum Action Topic 2", 1, "") + expectNilErr(t, e) + topic, e := c.Topics.Get(tid) + expectNilErr(t, e) + ex(!topic.IsClosed, "topic.IsClosed should be false") + dayAgo := time.Now().AddDate(0, 0, -5) + expectNilErr(t, topic.TestSetCreatedAt(dayAgo)) + l = append(l, tid) + } + lTest := func() { + for _, ll := range l { + to, e := c.Topics.Get(ll) + expectNilErr(t, e) + ex(to.IsClosed, "to.IsClosed should be true") + } + l = nil + } - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - expectNilErr(t, fa.Run()) - lTest() - // TODO: Create a method on the *ForumAction to get the count of topics which it could be run on and add a test to verify the count is as expected. + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + expectNilErr(t, fa.Run()) + lTest() + // TODO: Create a method on the *ForumAction to get the count of topics which it could be run on and add a test to verify the count is as expected. - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - addTopic() - expectNilErr(t, fa.Run()) - lTest() - // TODO: Create a method on the *ForumAction to get the count of topics which it could be run on and add a test to verify the count is as expected. + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + addTopic() + expectNilErr(t, fa.Run()) + lTest() + // TODO: Create a method on the *ForumAction to get the count of topics which it could be run on and add a test to verify the count is as expected. } func TestTopicList(t *testing.T) { - ex, exf := exp(t), expf(t) - fid, err := c.Forums.Create("Test Forum", "Desc for test forum", true, "") - expectNilErr(t, err) - tint := c.TopicList.(c.TopicListIntTest) + ex, exf := exp(t), expf(t) + fid, err := c.Forums.Create("Test Forum", "Desc for test forum", true, "") + expectNilErr(t, err) + tint := c.TopicList.(c.TopicListIntTest) - testPagi := func(p c.Paginator, pageList []int, page, lastPage int) { - exf(len(p.PageList) == len(pageList), "len(pagi.PageList) should be %d not %d", len(pageList), len(p.PageList)) - for i, page := range pageList { - exf(p.PageList[i] == page, "pagi.PageList[%d] should be %d not %d", i, page, p.PageList[i]) - } - exf(p.Page == page, "pagi.Page should be %d not %d", page, p.Page) - exf(p.LastPage == lastPage, "pagi.LastPage should be %d not %d", lastPage, p.LastPage) - } - test := func(topicList []*c.TopicsRow, pagi c.Paginator, listLen int, pagi2 c.Paginator, tid1 int) { - exf(len(topicList) == listLen, "len(topicList) should be %d not %d", listLen, len(topicList)) - if len(topicList) > 0 { - topic := topicList[0] - exf(topic.ID == tid1, "topic.ID should be %d not %d", tid1, topic.ID) - } - testPagi(pagi, pagi2.PageList, pagi2.Page, pagi2.LastPage) - } - noTopics := func(topicList []*c.TopicsRow, pagi c.Paginator) { - exf(len(topicList) == 0, "len(topicList) should be 0 not %d", len(topicList)) - testPagi(pagi, []int{}, 1, 1) - } - noTopicsOnPage2 := func(topicList []*c.TopicsRow, pagi c.Paginator) { - exf(len(topicList) == 0, "len(topicList) should be 0 not %d", len(topicList)) - testPagi(pagi, []int{1}, 2, 1) - } + testPagi := func(p c.Paginator, pageList []int, page, lastPage int) { + exf(len(p.PageList) == len(pageList), "len(pagi.PageList) should be %d not %d", len(pageList), len(p.PageList)) + for i, page := range pageList { + exf(p.PageList[i] == page, "pagi.PageList[%d] should be %d not %d", i, page, p.PageList[i]) + } + exf(p.Page == page, "pagi.Page should be %d not %d", page, p.Page) + exf(p.LastPage == lastPage, "pagi.LastPage should be %d not %d", lastPage, p.LastPage) + } + test := func(topicList []*c.TopicsRow, pagi c.Paginator, listLen int, pagi2 c.Paginator, tid1 int) { + exf(len(topicList) == listLen, "len(topicList) should be %d not %d", listLen, len(topicList)) + if len(topicList) > 0 { + topic := topicList[0] + exf(topic.ID == tid1, "topic.ID should be %d not %d", tid1, topic.ID) + } + testPagi(pagi, pagi2.PageList, pagi2.Page, pagi2.LastPage) + } + noTopics := func(topicList []*c.TopicsRow, pagi c.Paginator) { + exf(len(topicList) == 0, "len(topicList) should be 0 not %d", len(topicList)) + testPagi(pagi, []int{}, 1, 1) + } + noTopicsOnPage2 := func(topicList []*c.TopicsRow, pagi c.Paginator) { + exf(len(topicList) == 0, "len(topicList) should be 0 not %d", len(topicList)) + testPagi(pagi, []int{1}, 2, 1) + } - forum, err := c.Forums.Get(fid) - expectNilErr(t, err) + forum, err := c.Forums.Get(fid) + expectNilErr(t, err) - isAdmin, isMod, isBanned := false, false, false - gid, err := c.Groups.Create("Topic List Test", "Test", isAdmin, isMod, isBanned) - expectNilErr(t, err) - ex(c.Groups.Exists(gid), "The group we just made doesn't exist") + isAdmin, isMod, isBanned := false, false, false + gid, err := c.Groups.Create("Topic List Test", "Test", isAdmin, isMod, isBanned) + expectNilErr(t, err) + ex(c.Groups.Exists(gid), "The group we just made doesn't exist") - fp, err := c.FPStore.GetCopy(fid, gid) - if err == sql.ErrNoRows { - fp = *c.BlankForumPerms() - } else if err != nil { - expectNilErr(t, err) - } - fp.ViewTopic = true + fp, err := c.FPStore.GetCopy(fid, gid) + if err == sql.ErrNoRows { + fp = *c.BlankForumPerms() + } else if err != nil { + expectNilErr(t, err) + } + fp.ViewTopic = true - forum, err = c.Forums.Get(fid) - expectNilErr(t, err) - expectNilErr(t, forum.SetPerms(&fp, "custom", gid)) + forum, err = c.Forums.Get(fid) + expectNilErr(t, err) + expectNilErr(t, forum.SetPerms(&fp, "custom", gid)) - g, err := c.Groups.Get(gid) - expectNilErr(t, err) + g, err := c.Groups.Get(gid) + expectNilErr(t, err) - noTopicsTests := func() { - rr := func(page, orderby int) { - topicList, forumList, pagi, err := c.TopicList.GetListByGroup(g, page, orderby, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - } - rr(1, 0) - rr(2, 0) - rr(1, 1) - rr(2, 1) + noTopicsTests := func() { + rr := func(page, orderby int) { + topicList, forumList, pagi, err := c.TopicList.GetListByGroup(g, page, orderby, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + } + rr(1, 0) + rr(2, 0) + rr(1, 1) + rr(2, 1) - topicList, pagi, err := c.TopicList.GetListByForum(forum, 1, 0) - expectNilErr(t, err) - noTopics(topicList, pagi) + topicList, pagi, err := c.TopicList.GetListByForum(forum, 1, 0) + expectNilErr(t, err) + noTopics(topicList, pagi) - topicList, pagi, err = tint.RawGetListByForum(forum, 1, 0) - expectNilErr(t, err) - noTopics(topicList, pagi) + topicList, pagi, err = tint.RawGetListByForum(forum, 1, 0) + expectNilErr(t, err) + noTopics(topicList, pagi) - topicList, forumList, pagi, err := c.TopicList.GetList(1, 0, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err := c.TopicList.GetList(1, 0, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{fid}, 1, 0, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{fid}, 1, 0, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{}, 1, 0, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - // TODO: Why is there a discrepency between this and GetList()? - exf(len(forumList) == 0, "len(forumList) should be 0 not %d", len(forumList)) - } - noTopicsTests() + topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{}, 1, 0, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + // TODO: Why is there a discrepency between this and GetList()? + exf(len(forumList) == 0, "len(forumList) should be 0 not %d", len(forumList)) + } + noTopicsTests() - tid, err := c.Topics.Create(fid, "New Topic", "New Topic Body", 1, "") - expectNilErr(t, err) + tid, err := c.Topics.Create(fid, "New Topic", "New Topic Body", 1, "") + expectNilErr(t, err) - topicList, forumList, pagi, err := c.TopicList.GetListByGroup(g, 1, 0, []int{fid}) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err := c.TopicList.GetListByGroup(g, 1, 0, []int{fid}) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 2, 0, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 2, 0, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 1, 1, []int{fid}) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 1, 1, []int{fid}) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 2, 1, []int{fid}) - expectNilErr(t, err) - noTopicsOnPage2(topicList, pagi) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByGroup(g, 2, 1, []int{fid}) + expectNilErr(t, err) + noTopicsOnPage2(topicList, pagi) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, pagi, err = tint.RawGetListByForum(forum, 1, 0) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + topicList, pagi, err = tint.RawGetListByForum(forum, 1, 0) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - topicList, pagi, err = tint.RawGetListByForum(forum, 0, 0) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + topicList, pagi, err = tint.RawGetListByForum(forum, 0, 0) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - expectNilErr(t, tint.Tick()) - forum, err = c.Forums.Get(fid) - expectNilErr(t, err) - topicList, pagi, err = c.TopicList.GetListByForum(forum, 1, 0) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + expectNilErr(t, tint.Tick()) + forum, err = c.Forums.Get(fid) + expectNilErr(t, err) + topicList, pagi, err = c.TopicList.GetListByForum(forum, 1, 0) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - topicList, forumList, pagi, err = c.TopicList.GetList(1, 0, []int{fid}) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetList(1, 0, []int{fid}) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{fid}, 1, 0, []int{fid}) - expectNilErr(t, err) - test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) - exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{fid}, 1, 0, []int{fid}) + expectNilErr(t, err) + test(topicList, pagi, 1, c.Paginator{[]int{1}, 1, 1}, tid) + exf(len(forumList) == 1, "len(forumList) should be 1 not %d", len(forumList)) - topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{}, 1, 0, []int{fid}) - expectNilErr(t, err) - noTopics(topicList, pagi) - exf(len(forumList) == 0, "len(forumList) should be 0 not %d", len(forumList)) + topicList, forumList, pagi, err = c.TopicList.GetListByCanSee([]int{}, 1, 0, []int{fid}) + expectNilErr(t, err) + noTopics(topicList, pagi) + exf(len(forumList) == 0, "len(forumList) should be 0 not %d", len(forumList)) - topic, err := c.Topics.Get(tid) - expectNilErr(t, err) - expectNilErr(t, topic.Delete()) + topic, err := c.Topics.Get(tid) + expectNilErr(t, err) + expectNilErr(t, topic.Delete()) - forum, err = c.Forums.Get(fid) - expectNilErr(t, err) - noTopicsTests() + forum, err = c.Forums.Get(fid) + expectNilErr(t, err) + noTopicsTests() - // TODO: More tests + // TODO: More tests - _ = ex + _ = ex } func TestUtils(t *testing.T) { - ee := func(email, eemail string) { - cemail := c.CanonEmail(email) - expectf(t, cemail == eemail, "%s should be %s", cemail, eemail) - } - ee("test@example.com", "test@example.com") - ee("test.test@example.com", "test.test@example.com") - ee("", "") - ee("ddd", "ddd") - ee("test.test@gmail.com", "testtest@gmail.com") - ee("TEST.test@gmail.com", "testtest@gmail.com") - ee("test.TEST.test@gmail.com", "testtesttest@gmail.com") - ee("test..TEST.test@gmail.com", "testtesttest@gmail.com") - ee("TEST.test@example.com", "test.test@example.com") - ee("test.TEST.test@example.com", "test.test.test@example.com") - // TODO: Exotic unicode email types? Are there those? + ee := func(email, eemail string) { + cemail := c.CanonEmail(email) + expectf(t, cemail == eemail, "%s should be %s", cemail, eemail) + } + ee("test@example.com", "test@example.com") + ee("test.test@example.com", "test.test@example.com") + ee("", "") + ee("ddd", "ddd") + ee("test.test@gmail.com", "testtest@gmail.com") + ee("TEST.test@gmail.com", "testtest@gmail.com") + ee("test.TEST.test@gmail.com", "testtesttest@gmail.com") + ee("test..TEST.test@gmail.com", "testtesttest@gmail.com") + ee("TEST.test@example.com", "test.test@example.com") + ee("test.TEST.test@example.com", "test.test.test@example.com") + // TODO: Exotic unicode email types? Are there those? - // TODO: More utils.go tests + // TODO: More utils.go tests } func TestWeakPassword(t *testing.T) { - ex := exp(t) - /*weakPass := func(password, name, email string) func(error,string,...interface{}) { - err := c.WeakPassword(password, name, email) - return func(expectErr error, m string, p ...interface{}) { - m = fmt.Sprintf("pass=%s, user=%s, email=%s ", password, name, email) + m - expect(t, err == expectErr, fmt.Sprintf(m,p...)) - } - }*/ - nilErrStr := func(e error) error { - if e == nil { - e = errors.New("nil") - } - return e - } - weakPass := func(password, name, email string) func(error) { - err := c.WeakPassword(password, name, email) - e := nilErrStr(err) - m := fmt.Sprintf("pass=%s, user=%s, email=%s ", password, name, email) - return func(expectErr error) { - ee := nilErrStr(expectErr) - ex(err == expectErr, m+fmt.Sprintf("err should be '%s' not '%s'", ee, e)) - } - } + ex := exp(t) + /*weakPass := func(password, name, email string) func(error,string,...interface{}) { + err := c.WeakPassword(password, name, email) + return func(expectErr error, m string, p ...interface{}) { + m = fmt.Sprintf("pass=%s, user=%s, email=%s ", password, name, email) + m + expect(t, err == expectErr, fmt.Sprintf(m,p...)) + } + }*/ + nilErrStr := func(e error) error { + if e == nil { + e = errors.New("nil") + } + return e + } + weakPass := func(password, name, email string) func(error) { + err := c.WeakPassword(password, name, email) + e := nilErrStr(err) + m := fmt.Sprintf("pass=%s, user=%s, email=%s ", password, name, email) + return func(expectErr error) { + ee := nilErrStr(expectErr) + ex(err == expectErr, m+fmt.Sprintf("err should be '%s' not '%s'", ee, e)) + } + } - //weakPass("test", "test", "test@example.com")(c.ErrWeakPasswordContains,"err should be ErrWeakPasswordContains not '%s'") - weakPass("", "draw", "test@example.com")(c.ErrWeakPasswordNone) - weakPass("test", "draw", "test@example.com")(c.ErrWeakPasswordShort) - weakPass("testtest", "draw", "test@example.com")(c.ErrWeakPasswordContains) - weakPass("testdraw", "draw", "test@example.com")(c.ErrWeakPasswordNameInPass) - weakPass("test@example.com", "draw", "test@example.com")(c.ErrWeakPasswordEmailInPass) - weakPass("meet@example.com2", "draw", "")(c.ErrWeakPasswordNoUpper) - weakPass("Meet@example.com2", "draw", "")(nil) - weakPass("test2", "draw", "test@example.com")(c.ErrWeakPasswordShort) - weakPass("test22222222", "draw", "test@example.com")(c.ErrWeakPasswordContains) - weakPass("superman", "draw", "test@example.com")(c.ErrWeakPasswordCommon) - weakPass("Superman", "draw", "test@example.com")(c.ErrWeakPasswordCommon) - weakPass("Superma2", "draw", "test@example.com")(nil) - weakPass("superman2", "draw", "test@example.com")(c.ErrWeakPasswordCommon) - weakPass("Superman2", "draw", "test@example.com")(c.ErrWeakPasswordCommon) - weakPass("superman22", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) - weakPass("K\\@<^s}1", "draw", "test@example.com")(nil) - weakPass("K\\@<^s}r", "draw", "test@example.com")(c.ErrWeakPasswordNoNumbers) - weakPass("k\\@<^s}1", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) - weakPass("aaaaaaaa", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) - weakPass("aA1aA1aA1", "draw", "test@example.com")(c.ErrWeakPasswordUniqueChars) - weakPass("abababab", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) - weakPass("11111111111111111111", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) - weakPass("aaaaaaaaaaAAAAAAAAAA", "draw", "test@example.com")(c.ErrWeakPasswordUniqueChars) - weakPass("-:u/nMxb,A!n=B;H\\sjM", "draw", "test@example.com")(nil) + //weakPass("test", "test", "test@example.com")(c.ErrWeakPasswordContains,"err should be ErrWeakPasswordContains not '%s'") + weakPass("", "draw", "test@example.com")(c.ErrWeakPasswordNone) + weakPass("test", "draw", "test@example.com")(c.ErrWeakPasswordShort) + weakPass("testtest", "draw", "test@example.com")(c.ErrWeakPasswordContains) + weakPass("testdraw", "draw", "test@example.com")(c.ErrWeakPasswordNameInPass) + weakPass("test@example.com", "draw", "test@example.com")(c.ErrWeakPasswordEmailInPass) + weakPass("meet@example.com2", "draw", "")(c.ErrWeakPasswordNoUpper) + weakPass("Meet@example.com2", "draw", "")(nil) + weakPass("test2", "draw", "test@example.com")(c.ErrWeakPasswordShort) + weakPass("test22222222", "draw", "test@example.com")(c.ErrWeakPasswordContains) + weakPass("superman", "draw", "test@example.com")(c.ErrWeakPasswordCommon) + weakPass("Superman", "draw", "test@example.com")(c.ErrWeakPasswordCommon) + weakPass("Superma2", "draw", "test@example.com")(nil) + weakPass("superman2", "draw", "test@example.com")(c.ErrWeakPasswordCommon) + weakPass("Superman2", "draw", "test@example.com")(c.ErrWeakPasswordCommon) + weakPass("superman22", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) + weakPass("K\\@<^s}1", "draw", "test@example.com")(nil) + weakPass("K\\@<^s}r", "draw", "test@example.com")(c.ErrWeakPasswordNoNumbers) + weakPass("k\\@<^s}1", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) + weakPass("aaaaaaaa", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) + weakPass("aA1aA1aA1", "draw", "test@example.com")(c.ErrWeakPasswordUniqueChars) + weakPass("abababab", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) + weakPass("11111111111111111111", "draw", "test@example.com")(c.ErrWeakPasswordNoUpper) + weakPass("aaaaaaaaaaAAAAAAAAAA", "draw", "test@example.com")(c.ErrWeakPasswordUniqueChars) + weakPass("-:u/nMxb,A!n=B;H\\sjM", "draw", "test@example.com")(nil) } func TestAuth(t *testing.T) { - ex := exp(t) - // bcrypt likes doing stupid things, so this test will probably fail - realPassword := "Madame Cassandra's Mystic Orb" - t.Logf("Set realPassword to '%s'", realPassword) - t.Log("Hashing the real password with bcrypt") - hashedPassword, _, err := c.BcryptGeneratePassword(realPassword) - if err != nil { - t.Error(err) - } - passwordTest(t, realPassword, hashedPassword) - // TODO: Peek at the prefix to verify this is a bcrypt hash + ex := exp(t) + // bcrypt likes doing stupid things, so this test will probably fail + realPassword := "Madame Cassandra's Mystic Orb" + t.Logf("Set realPassword to '%s'", realPassword) + t.Log("Hashing the real password with bcrypt") + hashedPassword, _, err := c.BcryptGeneratePassword(realPassword) + if err != nil { + t.Error(err) + } + passwordTest(t, realPassword, hashedPassword) + // TODO: Peek at the prefix to verify this is a bcrypt hash - t.Log("Hashing the real password") - hashedPassword2, _, err := c.GeneratePassword(realPassword) - if err != nil { - t.Error(err) - } - passwordTest(t, realPassword, hashedPassword2) - // TODO: Peek at the prefix to verify this is a bcrypt hash + t.Log("Hashing the real password") + hashedPassword2, _, err := c.GeneratePassword(realPassword) + if err != nil { + t.Error(err) + } + passwordTest(t, realPassword, hashedPassword2) + // TODO: Peek at the prefix to verify this is a bcrypt hash - _, err, _ = c.Auth.Authenticate("None", "password") - errmsg := "Name None shouldn't exist" - if err != nil { - errmsg += "\n" + err.Error() - } - ex(err == c.ErrNoUserByName, errmsg) + _, err, _ = c.Auth.Authenticate("None", "password") + errmsg := "Name None shouldn't exist" + if err != nil { + errmsg += "\n" + err.Error() + } + ex(err == c.ErrNoUserByName, errmsg) - uid, err, _ := c.Auth.Authenticate("Admin", "password") - expectNilErr(t, err) - expectf(t, uid == 1, "Default admin uid should be 1 not %d", uid) + uid, err, _ := c.Auth.Authenticate("Admin", "password") + expectNilErr(t, err) + expectf(t, uid == 1, "Default admin uid should be 1 not %d", uid) - _, err, _ = c.Auth.Authenticate("Sam", "ReallyBadPassword") - errmsg = "Name Sam shouldn't exist" - if err != nil { - errmsg += "\n" + err.Error() - } - ex(err == c.ErrNoUserByName, errmsg) + _, err, _ = c.Auth.Authenticate("Sam", "ReallyBadPassword") + errmsg = "Name Sam shouldn't exist" + if err != nil { + errmsg += "\n" + err.Error() + } + ex(err == c.ErrNoUserByName, errmsg) - admin, err := c.Users.Get(1) - expectNilErr(t, err) - // TODO: Move this into the user store tests to provide better coverage? E.g. To see if the installer and the user creator initialise the field differently - ex(admin.Session == "", "Admin session should be blank") + admin, err := c.Users.Get(1) + expectNilErr(t, err) + // TODO: Move this into the user store tests to provide better coverage? E.g. To see if the installer and the user creator initialise the field differently + ex(admin.Session == "", "Admin session should be blank") - session, err := c.Auth.CreateSession(1) - expectNilErr(t, err) - ex(session != "", "Admin session shouldn't be blank") - // TODO: Test the actual length set in the setting in addition to this "too short" test - // TODO: We might be able to push up this minimum requirement - ex(len(session) > 10, "Admin session shouldn't be too short") - ex(admin.Session != session, "Old session should not match new one") - admin, err = c.Users.Get(1) - expectNilErr(t, err) - ex(admin.Session == session, "Sessions should match") + session, err := c.Auth.CreateSession(1) + expectNilErr(t, err) + ex(session != "", "Admin session shouldn't be blank") + // TODO: Test the actual length set in the setting in addition to this "too short" test + // TODO: We might be able to push up this minimum requirement + ex(len(session) > 10, "Admin session shouldn't be too short") + ex(admin.Session != session, "Old session should not match new one") + admin, err = c.Users.Get(1) + expectNilErr(t, err) + ex(admin.Session == session, "Sessions should match") - // TODO: Create a user with a unicode password and see if we can login as them - // TODO: Tests for SessionCheck, GetCookies, and ForceLogout - // TODO: Tests for MFA Verification + // TODO: Create a user with a unicode password and see if we can login as them + // TODO: Tests for SessionCheck, GetCookies, and ForceLogout + // TODO: Tests for MFA Verification } // TODO: Vary the salts? Keep in mind that some algorithms store the salt in the hash therefore the salt string may be blank func passwordTest(t *testing.T, realPassword, hashedPassword string) { - if len(hashedPassword) < 10 { - t.Error("Hash too short") - } - salt := "" - password := realPassword - t.Logf("Testing password '%s'", password) - t.Logf("Testing salt '%s'", salt) - err := c.CheckPassword(hashedPassword, password, salt) - if err == c.ErrMismatchedHashAndPassword { - t.Error("The two don't match") - } else if err == c.ErrPasswordTooLong { - t.Error("CheckPassword thinks the password is too long") - } else if err != nil { - t.Error(err) - } + if len(hashedPassword) < 10 { + t.Error("Hash too short") + } + salt := "" + password := realPassword + t.Logf("Testing password '%s'", password) + t.Logf("Testing salt '%s'", salt) + err := c.CheckPassword(hashedPassword, password, salt) + if err == c.ErrMismatchedHashAndPassword { + t.Error("The two don't match") + } else if err == c.ErrPasswordTooLong { + t.Error("CheckPassword thinks the password is too long") + } else if err != nil { + t.Error(err) + } - password = "hahaha" - t.Logf("Testing password '%s'", password) - t.Logf("Testing salt '%s'", salt) - err = c.CheckPassword(hashedPassword, password, salt) - if err == c.ErrPasswordTooLong { - t.Error("CheckPassword thinks the password is too long") - } else if err == nil { - t.Error("The two shouldn't match!") - } + password = "hahaha" + t.Logf("Testing password '%s'", password) + t.Logf("Testing salt '%s'", salt) + err = c.CheckPassword(hashedPassword, password, salt) + if err == c.ErrPasswordTooLong { + t.Error("CheckPassword thinks the password is too long") + } else if err == nil { + t.Error("The two shouldn't match!") + } - password = "Madame Cassandra's Mystic" - t.Logf("Testing password '%s'", password) - t.Logf("Testing salt '%s'", salt) - err = c.CheckPassword(hashedPassword, password, salt) - expect(t, err != c.ErrPasswordTooLong, "CheckPassword thinks the password is too long") - expect(t, err != nil, "The two shouldn't match!") + password = "Madame Cassandra's Mystic" + t.Logf("Testing password '%s'", password) + t.Logf("Testing salt '%s'", salt) + err = c.CheckPassword(hashedPassword, password, salt) + expect(t, err != c.ErrPasswordTooLong, "CheckPassword thinks the password is too long") + expect(t, err != nil, "The two shouldn't match!") } func TestUserPrivacy(t *testing.T) { - pu, u := c.BlankUser(), &c.GuestUser - pu.ID = 1 - ex, exf := exp(t), expf(t) - ex(!pu.Privacy.NoPresence, "pu.Privacy.NoPresence should be false") - ex(!u.Privacy.NoPresence, "u.Privacy.NoPresence should be false") + pu, u := c.BlankUser(), &c.GuestUser + pu.ID = 1 + ex, exf := exp(t), expf(t) + ex(!pu.Privacy.NoPresence, "pu.Privacy.NoPresence should be false") + ex(!u.Privacy.NoPresence, "u.Privacy.NoPresence should be false") - var msg string - test := func(expects bool, level int) { - pu.Privacy.ShowComments = level - val := c.PrivacyCommentsShow(pu, u) - var bit string - if !expects { - bit = " not" - val = !val - } - exf(val, "%s should%s be able to see comments on level %d", msg, bit, level) - } - // 0 = default, 1 = public, 2 = registered, 3 = friends, 4 = self, 5 = disabled + var msg string + test := func(expects bool, level int) { + pu.Privacy.ShowComments = level + val := c.PrivacyCommentsShow(pu, u) + var bit string + if !expects { + bit = " not" + val = !val + } + exf(val, "%s should%s be able to see comments on level %d", msg, bit, level) + } + // 0 = default, 1 = public, 2 = registered, 3 = friends, 4 = self, 5 = disabled - msg = "guest users" - test(true, 0) - test(true, 1) - test(false, 2) - test(false, 3) - test(false, 4) - test(false, 5) + msg = "guest users" + test(true, 0) + test(true, 1) + test(false, 2) + test(false, 3) + test(false, 4) + test(false, 5) - u = c.BlankUser() - msg = "blank users" - test(true, 0) - test(true, 1) - test(false, 2) - //test(false,3) - test(false, 4) - test(false, 5) + u = c.BlankUser() + msg = "blank users" + test(true, 0) + test(true, 1) + test(false, 2) + //test(false,3) + test(false, 4) + test(false, 5) - u.Loggedin = true - msg = "registered users" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) + u.Loggedin = true + msg = "registered users" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) - u.IsBanned = true - msg = "banned users" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) - u.IsBanned = false + u.IsBanned = true + msg = "banned users" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) + u.IsBanned = false - u.IsMod = true - msg = "mods" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) - u.IsMod = false + u.IsMod = true + msg = "mods" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) + u.IsMod = false - u.IsSuperMod = true - msg = "super mods" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) - u.IsSuperMod = false + u.IsSuperMod = true + msg = "super mods" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) + u.IsSuperMod = false - u.IsAdmin = true - msg = "admins" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) - u.IsAdmin = false + u.IsAdmin = true + msg = "admins" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) + u.IsAdmin = false - u.IsSuperAdmin = true - msg = "super admins" - test(true, 0) - test(true, 1) - test(true, 2) - test(false, 3) - test(false, 4) - test(false, 5) - u.IsSuperAdmin = false + u.IsSuperAdmin = true + msg = "super admins" + test(true, 0) + test(true, 1) + test(true, 2) + test(false, 3) + test(false, 4) + test(false, 5) + u.IsSuperAdmin = false - u.ID = 1 - test(true, 0) - test(true, 1) - test(true, 2) - test(true, 3) - test(true, 4) - test(false, 5) + u.ID = 1 + test(true, 0) + test(true, 1) + test(true, 2) + test(true, 3) + test(true, 4) + test(false, 5) } type METri struct { - Name string // Optional, this is here for tests involving invisible characters so we know what's going in - Msg string - Expects string + Name string // Optional, this is here for tests involving invisible characters so we know what's going in + Msg string + Expects string } type METriList struct { - Items []METri + Items []METri } func (l *METriList) Add(args ...string) { - if len(args) < 2 { - panic("need 2 or more args") - } - if len(args) > 2 { - l.Items = append(l.Items, METri{args[0], args[1], args[2]}) - } else { - l.Items = append(l.Items, METri{"", args[0], args[1]}) - } + if len(args) < 2 { + panic("need 2 or more args") + } + if len(args) > 2 { + l.Items = append(l.Items, METri{args[0], args[1], args[2]}) + } else { + l.Items = append(l.Items, METri{"", args[0], args[1]}) + } } type CountTest struct { - Name string - Msg string - Expects int + Name string + Msg string + Expects int } type CountTestList struct { - Items []CountTest + Items []CountTest } func (l *CountTestList) Add(name, msg string, expects int) { - l.Items = append(l.Items, CountTest{name, msg, expects}) + l.Items = append(l.Items, CountTest{name, msg, expects}) } func TestWordCount(t *testing.T) { - l := &CountTestList{nil} - l.Add("blank", "", 0) - l.Add("single-letter", "h", 1) - l.Add("single-kana", "お", 1) - l.Add("single-letter-words", "h h", 2) - l.Add("two-letter", "h", 1) - l.Add("two-kana", "おは", 1) - l.Add("two-letter-words", "hh hh", 2) - l.Add("", "h,h", 2) - l.Add("", "h,,h", 2) - l.Add("", "h, h", 2) - l.Add("", " h, h", 2) - l.Add("", "h, h ", 2) - l.Add("", " h, h ", 2) - l.Add("", "h, h", 2) - l.Add("", "h\nh", 2) - l.Add("", "h\"h", 2) - l.Add("", "h[r]h", 3) - l.Add("", "お,お", 2) - l.Add("", "お、お", 2) - l.Add("", "お\nお", 2) - l.Add("", "お”お", 2) - l.Add("", "お「あ」お", 3) + l := &CountTestList{nil} + l.Add("blank", "", 0) + l.Add("single-letter", "h", 1) + l.Add("single-kana", "お", 1) + l.Add("single-letter-words", "h h", 2) + l.Add("two-letter", "h", 1) + l.Add("two-kana", "おは", 1) + l.Add("two-letter-words", "hh hh", 2) + l.Add("", "h,h", 2) + l.Add("", "h,,h", 2) + l.Add("", "h, h", 2) + l.Add("", " h, h", 2) + l.Add("", "h, h ", 2) + l.Add("", " h, h ", 2) + l.Add("", "h, h", 2) + l.Add("", "h\nh", 2) + l.Add("", "h\"h", 2) + l.Add("", "h[r]h", 3) + l.Add("", "お,お", 2) + l.Add("", "お、お", 2) + l.Add("", "お\nお", 2) + l.Add("", "お”お", 2) + l.Add("", "お「あ」お", 3) - for _, item := range l.Items { - res := c.WordCount(item.Msg) - if res != item.Expects { - if item.Name != "" { - t.Error("Name: ", item.Name) - } - t.Error("Testing string '" + item.Msg + "'") - t.Error("Bad output:", res) - t.Error("Expected:", item.Expects) - } - } + for _, item := range l.Items { + res := c.WordCount(item.Msg) + if res != item.Expects { + if item.Name != "" { + t.Error("Name: ", item.Name) + } + t.Error("Testing string '" + item.Msg + "'") + t.Error("Bad output:", res) + t.Error("Expected:", item.Expects) + } + } } func TestTick(t *testing.T) { - expectNilErr(t, c.StartupTasks()) - expectNilErr(t, c.Dailies()) + expectNilErr(t, c.StartupTasks()) + expectNilErr(t, c.Dailies()) - expectNilErr(t, c.Tasks.HalfSec.Run()) - expectNilErr(t, c.Tasks.Sec.Run()) - expectNilErr(t, c.Tasks.FifteenMin.Run()) - expectNilErr(t, c.Tasks.Hour.Run()) - expectNilErr(t, c.Tasks.Day.Run()) + expectNilErr(t, c.Tasks.HalfSec.Run()) + expectNilErr(t, c.Tasks.Sec.Run()) + expectNilErr(t, c.Tasks.FifteenMin.Run()) + expectNilErr(t, c.Tasks.Hour.Run()) + expectNilErr(t, c.Tasks.Day.Run()) - thumbChan := make(chan bool) - expectNilErr(t, tickLoop(thumbChan)) - expectNilErr(t, c.CTickLoop.HalfSecf()) - expectNilErr(t, c.CTickLoop.Secf()) - expectNilErr(t, c.CTickLoop.FifteenMinf()) - expectNilErr(t, c.CTickLoop.Hourf()) - expectNilErr(t, c.CTickLoop.Dayf()) + thumbChan := make(chan bool) + expectNilErr(t, tickLoop(thumbChan)) + expectNilErr(t, c.CTickLoop.HalfSecf()) + expectNilErr(t, c.CTickLoop.Secf()) + expectNilErr(t, c.CTickLoop.FifteenMinf()) + expectNilErr(t, c.CTickLoop.Hourf()) + expectNilErr(t, c.CTickLoop.Dayf()) } func TestWSHub(t *testing.T) { - ex, exf, h := exp(t), expf(t), &c.WsHub - exf(h.GuestCount() == 0, "GuestCount should be %d not %d", 0, h.GuestCount()) - exf(h.UserCount() == 0, "UserCount should be %d not %d", 0, h.UserCount()) - ex(!h.HasUser(-1), "HasUser(-1) should be false") - ex(!h.HasUser(0), "HasUser(0) should be false") - ex(!h.HasUser(1), "HasUser(1) should be false") + ex, exf, h := exp(t), expf(t), &c.WsHub + exf(h.GuestCount() == 0, "GuestCount should be %d not %d", 0, h.GuestCount()) + exf(h.UserCount() == 0, "UserCount should be %d not %d", 0, h.UserCount()) + ex(!h.HasUser(-1), "HasUser(-1) should be false") + ex(!h.HasUser(0), "HasUser(0) should be false") + ex(!h.HasUser(1), "HasUser(1) should be false") - uid, e := c.Users.Create("WsHub Test", "WsHub Test", "", 1, true) - expectNilErr(t, e) - exf(!h.HasUser(uid), "HasUser(%d) should be false", uid) - exf(len(h.AllUsers()) == 0, "len(AllUsers()) should be %d not %d", 0, len(h.AllUsers())) + uid, e := c.Users.Create("WsHub Test", "WsHub Test", "", 1, true) + expectNilErr(t, e) + exf(!h.HasUser(uid), "HasUser(%d) should be false", uid) + exf(len(h.AllUsers()) == 0, "len(AllUsers()) should be %d not %d", 0, len(h.AllUsers())) - f := func(uid, guestCount, userCount, allUserListLen int, hasUser bool) { - exf(h.GuestCount() == guestCount, "GuestCount should be %d not %d", guestCount, h.GuestCount()) - exf(h.UserCount() == userCount, "UserCount should be %d not %d", userCount, h.UserCount()) - exf(len(h.AllUsers()) == allUserListLen, "len(AllUsers()) should be %d not %d", allUserListLen, len(h.AllUsers())) - if hasUser { - exf(h.HasUser(uid), "HasUser(%d) should be true", uid) - } else { - exf(!h.HasUser(uid), "HasUser(%d) should be false", uid) - } - } + f := func(uid, guestCount, userCount, allUserListLen int, hasUser bool) { + exf(h.GuestCount() == guestCount, "GuestCount should be %d not %d", guestCount, h.GuestCount()) + exf(h.UserCount() == userCount, "UserCount should be %d not %d", userCount, h.UserCount()) + exf(len(h.AllUsers()) == allUserListLen, "len(AllUsers()) should be %d not %d", allUserListLen, len(h.AllUsers())) + if hasUser { + exf(h.HasUser(uid), "HasUser(%d) should be true", uid) + } else { + exf(!h.HasUser(uid), "HasUser(%d) should be false", uid) + } + } - u, e := c.Users.Get(uid) - expectNilErr(t, e) - wsUser, e := h.AddConn(u, nil) - expectNilErr(t, e) - f(uid, 0, 1, 1, true) + u, e := c.Users.Get(uid) + expectNilErr(t, e) + wsUser, e := h.AddConn(u, nil) + expectNilErr(t, e) + f(uid, 0, 1, 1, true) - uid, e = c.Users.Create("WsHub Test 2", "WsHub Test 2", "", 1, true) - expectNilErr(t, e) - u2, e := c.Users.Get(uid) - expectNilErr(t, e) - wsUser2, e := h.AddConn(u2, nil) - expectNilErr(t, e) - f(uid, 0, 2, 2, true) + uid, e = c.Users.Create("WsHub Test 2", "WsHub Test 2", "", 1, true) + expectNilErr(t, e) + u2, e := c.Users.Get(uid) + expectNilErr(t, e) + wsUser2, e := h.AddConn(u2, nil) + expectNilErr(t, e) + f(uid, 0, 2, 2, true) - h.RemoveConn(wsUser2, nil) - f(uid, 0, 1, 1, false) - h.RemoveConn(wsUser2, nil) - f(uid, 0, 1, 1, false) - h.RemoveConn(wsUser, nil) - f(uid, 0, 0, 0, false) + h.RemoveConn(wsUser2, nil) + f(uid, 0, 1, 1, false) + h.RemoveConn(wsUser2, nil) + f(uid, 0, 1, 1, false) + h.RemoveConn(wsUser, nil) + f(uid, 0, 0, 0, false) - countSockets := func(wsUser *c.WSUser, expect int) { - exf(wsUser.CountSockets() == expect, "CountSockets() should be %d not %d", expect, wsUser.CountSockets()) - } - wsUser2, e = h.AddConn(u2, nil) - expectNilErr(t, e) - f(uid, 0, 1, 1, true) - countSockets(wsUser2, 1) - wsUser2.RemoveSocket(nil) - f(uid, 0, 1, 1, true) - countSockets(wsUser2, 0) - h.RemoveConn(wsUser2, nil) - f(uid, 0, 0, 0, false) - countSockets(wsUser2, 0) + countSockets := func(wsUser *c.WSUser, expect int) { + exf(wsUser.CountSockets() == expect, "CountSockets() should be %d not %d", expect, wsUser.CountSockets()) + } + wsUser2, e = h.AddConn(u2, nil) + expectNilErr(t, e) + f(uid, 0, 1, 1, true) + countSockets(wsUser2, 1) + wsUser2.RemoveSocket(nil) + f(uid, 0, 1, 1, true) + countSockets(wsUser2, 0) + h.RemoveConn(wsUser2, nil) + f(uid, 0, 0, 0, false) + countSockets(wsUser2, 0) - wsUser2, e = h.AddConn(u2, nil) - expectNilErr(t, e) - f(uid, 0, 1, 1, true) - countSockets(wsUser2, 1) - expectNilErr(t, wsUser2.Ping()) - f(uid, 0, 1, 1, true) - countSockets(wsUser2, 0) - h.RemoveConn(wsUser2, nil) - f(uid, 0, 0, 0, false) - countSockets(wsUser2, 0) + wsUser2, e = h.AddConn(u2, nil) + expectNilErr(t, e) + f(uid, 0, 1, 1, true) + countSockets(wsUser2, 1) + expectNilErr(t, wsUser2.Ping()) + f(uid, 0, 1, 1, true) + countSockets(wsUser2, 0) + h.RemoveConn(wsUser2, nil) + f(uid, 0, 0, 0, false) + countSockets(wsUser2, 0) - // TODO: Add more tests + // TODO: Add more tests } diff --git a/mysql.go b/mysql.go index 8d9b3d1e..bc074360 100644 --- a/mysql.go +++ b/mysql.go @@ -2,81 +2,81 @@ /* * -* Gosora MySQL Interface -* Copyright Azareal 2016 - 2020 +* Gosora MySQL Interface +* Copyright Azareal 2016 - 2020 * */ package main import ( - "log" + "log" - c "github.com/Azareal/Gosora/common" - qgen "github.com/Azareal/Gosora/query_gen" - _ "github.com/go-sql-driver/mysql" - "github.com/pkg/errors" + c "github.com/Azareal/Gosora/common" + qgen "github.com/Azareal/Gosora/query_gen" + _ "github.com/go-sql-driver/mysql" + "github.com/pkg/errors" ) var dbCollation = "utf8mb4_general_ci" func init() { - dbAdapter = "mysql" - _initDatabase = initMySQL + dbAdapter = "mysql" + _initDatabase = initMySQL } func initMySQL() (err error) { - err = qgen.Builder.Init("mysql", map[string]string{ - "host": c.DbConfig.Host, - "port": c.DbConfig.Port, - "name": c.DbConfig.Dbname, - "username": c.DbConfig.Username, - "password": c.DbConfig.Password, - "collation": dbCollation, - }) - if err != nil { - return errors.WithStack(err) - } + err = qgen.Builder.Init("mysql", map[string]string{ + "host": c.DbConfig.Host, + "port": c.DbConfig.Port, + "name": c.DbConfig.Dbname, + "username": c.DbConfig.Username, + "password": c.DbConfig.Password, + "collation": dbCollation, + }) + if err != nil { + return errors.WithStack(err) + } - // Set the number of max open connections - db = qgen.Builder.GetConn() - db.SetMaxOpenConns(64) - db.SetMaxIdleConns(32) - //db.SetConnMaxLifetime(time.Second * 60 * 5) // Just in case we accumulate some bad connections due to the MySQL driver being stupid + // Set the number of max open connections + db = qgen.Builder.GetConn() + db.SetMaxOpenConns(64) + db.SetMaxIdleConns(32) + //db.SetConnMaxLifetime(time.Second * 60 * 5) // Just in case we accumulate some bad connections due to the MySQL driver being stupid - // Only hold connections open for five seconds to avoid accumulating a large number of stale connections - //db.SetConnMaxLifetime(5 * time.Second) - db.SetConnMaxLifetime(c.DBTimeout()) + // Only hold connections open for five seconds to avoid accumulating a large number of stale connections + //db.SetConnMaxLifetime(5 * time.Second) + db.SetConnMaxLifetime(c.DBTimeout()) - // Build the generated prepared statements, we are going to slowly move the queries over to the query generator rather than writing them all by hand, this'll make it easier for us to implement database adapters for other databases like PostgreSQL, MSSQL, SQlite, etc. - err = _gen_mysql() - if err != nil { - return errors.WithStack(err) - } + // Build the generated prepared statements, we are going to slowly move the queries over to the query generator rather than writing them all by hand, this'll make it easier for us to implement database adapters for other databases like PostgreSQL, MSSQL, SQlite, etc. + err = _gen_mysql() + if err != nil { + return errors.WithStack(err) + } - // TODO: Is there a less noisy way of doing this for tests? - /*log.Print("Preparing getActivityFeedByWatcher statement.") - stmts.getActivityFeedByWatcher, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid DESC LIMIT 16") - if err != nil { - return errors.WithStack(err) - }*/ + // TODO: Is there a less noisy way of doing this for tests? + /*log.Print("Preparing getActivityFeedByWatcher statement.") + stmts.getActivityFeedByWatcher, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid DESC LIMIT 16") + if err != nil { + return errors.WithStack(err) + }*/ - log.Print("Preparing getActivityFeedByWatcher statement.") - stmts.getActivityFeedByWatcher, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=? ORDER BY activity_stream.asid DESC LIMIT ?") - if err != nil { - return errors.WithStack(err) - } + log.Print("Preparing getActivityFeedByWatcher statement.") + stmts.getActivityFeedByWatcher, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=? ORDER BY activity_stream.asid DESC LIMIT ?") + if err != nil { + return errors.WithStack(err) + } - /*log.Print("Preparing getActivityFeedByWatcherAfter statement.") - stmts.getActivityFeedByWatcherAfter, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=? AND asid => ? ORDER BY activity_stream.asid DESC LIMIT ?") - if err != nil { - return errors.WithStack(err) - }*/ + /*log.Print("Preparing getActivityFeedByWatcherAfter statement.") + stmts.getActivityFeedByWatcherAfter, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID, activity_stream.createdAt FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=? AND asid => ? ORDER BY activity_stream.asid DESC LIMIT ?") + if err != nil { + return errors.WithStack(err) + }*/ - log.Print("Preparing getActivityCountByWatcher statement.") - stmts.getActivityCountByWatcher, err = db.Prepare("SELECT count(*) FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=?") - if err != nil { - return errors.WithStack(err) - } + log.Print("Preparing getActivityCountByWatcher statement.") + stmts.getActivityCountByWatcher, err = db.Prepare("SELECT count(*) FROM activity_stream_matches INNER JOIN activity_stream ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE watcher=?") + if err != nil { + return errors.WithStack(err) + } - return nil + return nil } diff --git a/public/Sortable-1.4.0/.jshintrc b/public/Sortable-1.4.0/.jshintrc index 3f67a098..bcb45c45 100644 --- a/public/Sortable-1.4.0/.jshintrc +++ b/public/Sortable-1.4.0/.jshintrc @@ -1,24 +1,24 @@ { - "strict": true, - "newcap": false, - "node": true, - "expr": true, - "supernew": true, - "laxbreak": true, - "white": true, - "globals": { - "define": true, - "test": true, - "expect": true, - "module": true, - "asyncTest": true, - "start": true, - "ok": true, - "equal": true, - "notEqual": true, - "deepEqual": true, - "window": true, - "document": true, - "performance": true - } + "strict": true, + "newcap": false, + "node": true, + "expr": true, + "supernew": true, + "laxbreak": true, + "white": true, + "globals": { + "define": true, + "test": true, + "expect": true, + "module": true, + "asyncTest": true, + "start": true, + "ok": true, + "equal": true, + "notEqual": true, + "deepEqual": true, + "window": true, + "document": true, + "performance": true + } } diff --git a/public/Sortable-1.4.0/README.md b/public/Sortable-1.4.0/README.md index 41d4751c..43f61519 100644 --- a/public/Sortable-1.4.0/README.md +++ b/public/Sortable-1.4.0/README.md @@ -33,9 +33,9 @@ Demo: http://rubaxa.github.io/Sortable/ ### Usage ```html ``` @@ -53,79 +53,79 @@ You can use any element for the list and its elements, not just `ul`/`li`. Here ### Options ```js var sortable = new Sortable(el, { - group: "name", // or { name: "...", pull: [true, false, clone], put: [true, false, array] } - sort: true, // sorting inside list - delay: 0, // time in milliseconds to define when the sorting should start - disabled: false, // Disables the sortable if set to true. - store: null, // @see Store - animation: 150, // ms, animation speed moving items when sorting, `0` — without animation - handle: ".my-handle", // Drag handle selector within list items - filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function) - draggable: ".item", // Specifies which items inside the element should be sortable - ghostClass: "sortable-ghost", // Class name for the drop placeholder - chosenClass: "sortable-chosen", // Class name for the chosen item - dataIdAttr: 'data-id', - - forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in - fallbackClass: "sortable-fallback" // Class name for the cloned DOM Element when using forceFallback - fallbackOnBody: false // Appends the cloned DOM Element into the Document's Body - - scroll: true, // or HTMLElement - scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling. - scrollSpeed: 10, // px - - setData: function (dataTransfer, dragEl) { - dataTransfer.setData('Text', dragEl.textContent); - }, + group: "name", // or { name: "...", pull: [true, false, clone], put: [true, false, array] } + sort: true, // sorting inside list + delay: 0, // time in milliseconds to define when the sorting should start + disabled: false, // Disables the sortable if set to true. + store: null, // @see Store + animation: 150, // ms, animation speed moving items when sorting, `0` — without animation + handle: ".my-handle", // Drag handle selector within list items + filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function) + draggable: ".item", // Specifies which items inside the element should be sortable + ghostClass: "sortable-ghost", // Class name for the drop placeholder + chosenClass: "sortable-chosen", // Class name for the chosen item + dataIdAttr: 'data-id', + + forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in + fallbackClass: "sortable-fallback" // Class name for the cloned DOM Element when using forceFallback + fallbackOnBody: false // Appends the cloned DOM Element into the Document's Body + + scroll: true, // or HTMLElement + scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling. + scrollSpeed: 10, // px + + setData: function (dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, - // dragging started - onStart: function (/**Event*/evt) { - evt.oldIndex; // element index within parent - }, - - // dragging ended - onEnd: function (/**Event*/evt) { - evt.oldIndex; // element's old index within parent - evt.newIndex; // element's new index within parent - }, + // dragging started + onStart: function (/**Event*/evt) { + evt.oldIndex; // element index within parent + }, + + // dragging ended + onEnd: function (/**Event*/evt) { + evt.oldIndex; // element's old index within parent + evt.newIndex; // element's new index within parent + }, - // Element is dropped into the list from another list - onAdd: function (/**Event*/evt) { - var itemEl = evt.item; // dragged HTMLElement - evt.from; // previous list - // + indexes from onEnd - }, + // Element is dropped into the list from another list + onAdd: function (/**Event*/evt) { + var itemEl = evt.item; // dragged HTMLElement + evt.from; // previous list + // + indexes from onEnd + }, - // Changed sorting within list - onUpdate: function (/**Event*/evt) { - var itemEl = evt.item; // dragged HTMLElement - // + indexes from onEnd - }, + // Changed sorting within list + onUpdate: function (/**Event*/evt) { + var itemEl = evt.item; // dragged HTMLElement + // + indexes from onEnd + }, - // Called by any change to the list (add / update / remove) - onSort: function (/**Event*/evt) { - // same properties as onUpdate - }, + // Called by any change to the list (add / update / remove) + onSort: function (/**Event*/evt) { + // same properties as onUpdate + }, - // Element is removed from the list into another list - onRemove: function (/**Event*/evt) { - // same properties as onUpdate - }, + // Element is removed from the list into another list + onRemove: function (/**Event*/evt) { + // same properties as onUpdate + }, - // Attempt to drag a filtered element - onFilter: function (/**Event*/evt) { - var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event. - }, - - // Event when you move an item in the list or between lists - onMove: function (/**Event*/evt) { - // Example: http://jsbin.com/tuyafe/1/edit?js,output - evt.dragged; // dragged HTMLElement - evt.draggedRect; // TextRectangle {left, top, right и bottom} - evt.related; // HTMLElement on which have guided - evt.relatedRect; // TextRectangle - // return false; — for cancel - } + // Attempt to drag a filtered element + onFilter: function (/**Event*/evt) { + var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event. + }, + + // Event when you move an item in the list or between lists + onMove: function (/**Event*/evt) { + // Example: http://jsbin.com/tuyafe/1/edit?js,output + evt.dragged; // dragged HTMLElement + evt.draggedRect; // TextRectangle {left, top, right и bottom} + evt.related; // HTMLElement on which have guided + evt.relatedRect; // TextRectangle + // return false; — for cancel + } }); ``` @@ -172,9 +172,9 @@ Demo: http://jsbin.com/xiloqu/1/edit?html,js,output var sortable = Sortable.create(list); document.getElementById("switcher").onclick = function () { - var state = sortable.option("disabled"); // get + var state = sortable.option("disabled"); // get - sortable.option("disabled", !state); // set + sortable.option("disabled", !state); // set }; ``` @@ -191,21 +191,21 @@ Demo: http://jsbin.com/newize/1/edit?html,js,output ```js Sortable.create(el, { - handle: ".my-handle" + handle: ".my-handle" }); ``` ```html ``` ```css .my-handle { - cursor: move; - cursor: -webkit-grabbing; + cursor: move; + cursor: -webkit-grabbing; } ``` @@ -218,18 +218,18 @@ Sortable.create(el, { ```js Sortable.create(list, { - filter: ".js-remove, .js-edit", - onFilter: function (evt) { - var item = evt.item, - ctrl = evt.target; + filter: ".js-remove, .js-edit", + onFilter: function (evt) { + var item = evt.item, + ctrl = evt.target; - if (Sortable.utils.is(ctrl, ".js-remove")) { // Click on remove button - item.parentNode.removeChild(item); // remove sortable item - } - else if (Sortable.utils.is(ctrl, ".js-edit")) { // Click on edit link - // ... - } - } + if (Sortable.utils.is(ctrl, ".js-remove")) { // Click on remove button + item.parentNode.removeChild(item); // remove sortable item + } + else if (Sortable.utils.is(ctrl, ".js-edit")) { // Click on edit link + // ... + } + } }) ``` @@ -326,35 +326,35 @@ Demo: http://jsbin.com/naduvo/1/edit?html,js,output ```html
- + - + - +
``` ```js angular.module('myApp', ['ng-sortable']) - .controller('demo', ['$scope', function ($scope) { - $scope.items = ['item 1', 'item 2']; - $scope.foo = ['foo 1', '..']; - $scope.bar = ['bar 1', '..']; - $scope.barConfig = { - group: 'foobar', - animation: 150, - onSort: function (/** ngSortEvent */evt){ - // @see https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js#L18-L24 - } - }; - }]); + .controller('demo', ['$scope', function ($scope) { + $scope.items = ['item 1', 'item 2']; + $scope.foo = ['foo 1', '..']; + $scope.bar = ['bar 1', '..']; + $scope.barConfig = { + group: 'foobar', + animation: 150, + onSort: function (/** ngSortEvent */evt){ + // @see https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js#L18-L24 + } + }; + }]); ``` @@ -369,23 +369,23 @@ See [more options](react-sortable-mixin.js#L26). ```jsx var SortableList = React.createClass({ - mixins: [SortableMixin], + mixins: [SortableMixin], - getInitialState: function() { - return { - items: ['Mixin', 'Sortable'] - }; - }, + getInitialState: function() { + return { + items: ['Mixin', 'Sortable'] + }; + }, - handleSort: function (/** Event */evt) { /*..*/ }, + handleSort: function (/** Event */evt) { /*..*/ }, - render: function() { - return - } + render: function() { + return + } }); React.render(, document.body); @@ -395,51 +395,51 @@ React.render(, document.body); // Groups // var AllUsers = React.createClass({ - mixins: [SortableMixin], + mixins: [SortableMixin], - sortableOptions: { - ref: "user", - group: "shared", - model: "users" - }, + sortableOptions: { + ref: "user", + group: "shared", + model: "users" + }, - getInitialState: function() { - return { users: ['Abbi', 'Adela', 'Bud', 'Cate', 'Davis', 'Eric']; }; - }, + getInitialState: function() { + return { users: ['Abbi', 'Adela', 'Bud', 'Cate', 'Davis', 'Eric']; }; + }, - render: function() { - return ( -

Users

-
    { - this.state.users.map(function (text) { - return
  • {text}
  • - }) - }
- ); - } + render: function() { + return ( +

Users

+
    { + this.state.users.map(function (text) { + return
  • {text}
  • + }) + }
+ ); + } }); var ApprovedUsers = React.createClass({ - mixins: [SortableMixin], - sortableOptions: { group: "shared" }, + mixins: [SortableMixin], + sortableOptions: { group: "shared" }, - getInitialState: function() { - return { items: ['Hal', 'Judy']; }; - }, + getInitialState: function() { + return { items: ['Hal', 'Judy']; }; + }, - render: function() { - return
    { - this.state.items.map(function (text) { - return
  • {text}
  • - }) - }
- } + render: function() { + return
    { + this.state.items.map(function (text) { + return
  • {text}
  • + }) + }
+ } }); React.render(
- -
- + +
+
, document.body); ``` @@ -453,11 +453,11 @@ Include [knockout-sortable.js](knockout-sortable.js) ```html
- +
- +
``` @@ -466,8 +466,8 @@ Using this bindingHandler sorts the observableArray when the user sorts the HTML The sortable/draggable bindingHandlers supports the same syntax as Knockouts built in [template](http://knockoutjs.com/documentation/template-binding.html) binding except for the `data` option, meaning that you could supply the name of a template or specify a separate templateEngine. The difference between the sortable and draggable handlers is that the draggable has the sortable `group` option set to `{pull:'clone',put: false}` and the `sort` option set to false by default (overridable). Other attributes are: -* options: an object that contains settings for the underlaying sortable, ie `group`,`handle`, events etc. -* collection: if your `foreach` array is a computed then you would supply the underlaying observableArray that you would like to sort here. +* options: an object that contains settings for the underlaying sortable, ie `group`,`handle`, events etc. +* collection: if your `foreach` array is a computed then you would supply the underlaying observableArray that you would like to sort here. --- @@ -527,35 +527,35 @@ Saving and restoring of the sort. ```html
    -
  • order
  • -
  • save
  • -
  • restore
  • +
  • order
  • +
  • save
  • +
  • restore
``` ```js Sortable.create(el, { - group: "localStorage-example", - store: { - /** - * Get the order of elements. Called once during initialization. - * @param {Sortable} sortable - * @returns {Array} - */ - get: function (sortable) { - var order = localStorage.getItem(sortable.options.group); - return order ? order.split('|') : []; - }, + group: "localStorage-example", + store: { + /** + * Get the order of elements. Called once during initialization. + * @param {Sortable} sortable + * @returns {Array} + */ + get: function (sortable) { + var order = localStorage.getItem(sortable.options.group); + return order ? order.split('|') : []; + }, - /** - * Save the order of elements. Called onEnd (when the item is dropped). - * @param {Sortable} sortable - */ - set: function (sortable) { - var order = sortable.toArray(); - localStorage.setItem(sortable.options.group, order.join('|')); - } - } + /** + * Save the order of elements. Called onEnd (when the item is dropped). + * @param {Sortable} sortable + */ + set: function (sortable) { + var order = sortable.toArray(); + localStorage.setItem(sortable.options.group, order.join('|')); + } + } }) ``` @@ -578,11 +578,11 @@ Demo: http://jsbin.com/luxero/2/edit?html,js,output
    -
  • This is Sortable
  • -
  • It works with Bootstrap...
  • -
  • ...out of the box.
  • -
  • It has support for touch devices.
  • -
  • Just drag some elements around.
  • +
  • This is Sortable
  • +
  • It works with Bootstrap...
  • +
  • ...out of the box.
  • +
  • It has support for touch devices.
  • +
  • Just drag some elements around.