summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/config/cliparsing_test.go206
-rw-r--r--internal/config/flags.go14
-rw-r--r--internal/config/testdata/test.json60
-rw-r--r--internal/config/testdata/test.yaml411
-rw-r--r--internal/config/testdata/test2.yaml2
5 files changed, 691 insertions, 2 deletions
diff --git a/internal/config/cliparsing_test.go b/internal/config/cliparsing_test.go
new file mode 100644
index 000000000..5f41cbe08
--- /dev/null
+++ b/internal/config/cliparsing_test.go
@@ -0,0 +1,206 @@
+/*
+ GoToSocial
+ Copyright (C) 2022 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package config_test
+
+import (
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+ "gopkg.in/yaml.v3"
+)
+
+func expectedKV(kvpairs ...string) map[string]interface{} {
+ ret := make(map[string]interface{}, len(kvpairs)/2)
+
+ for i := 0; i < len(kvpairs)-1; i += 2 {
+ ret[kvpairs[i]] = kvpairs[i+1]
+ }
+
+ return ret
+}
+
+func expectedFile(t *testing.T, file string) map[string]interface{} {
+ expectedConfig, err := os.ReadFile(file)
+ if err != nil {
+ t.Errorf("error reading expected config from file %q: %v", file, err)
+ }
+
+ var ret map[string]interface{}
+ if err := yaml.Unmarshal(expectedConfig, &ret); err != nil {
+ t.Errorf("error parsing expected config from file %q: %v", file, err)
+ }
+
+ return ret
+}
+
+func TestCLIParsing(t *testing.T) {
+ type testcase struct {
+ cli []string
+ env []string
+ expected map[string]interface{}
+ }
+
+ defaults, _ := config.Defaults.MarshalMap()
+
+ testcases := map[string]testcase{
+ "Make sure defaults are set correctly": {
+ expected: defaults,
+ },
+
+ "Override db-address from default using cli flag": {
+ cli: []string{
+ "--db-address", "some.db.address",
+ },
+ expected: expectedKV(
+ "db-address", "some.db.address",
+ ),
+ },
+
+ "Override db-address from default using env var": {
+ env: []string{
+ "GTS_DB_ADDRESS=some.db.address",
+ },
+ expected: expectedKV(
+ "db-address", "some.db.address",
+ ),
+ },
+
+ "Override db-address from default using both env var and cli flag. The cli flag should take priority": {
+ cli: []string{
+ "--db-address", "some.db.address",
+ },
+ env: []string{
+ "GTS_DB_ADDRESS=some.other.db.address",
+ },
+ expected: expectedKV(
+ "db-address", "some.db.address",
+ ),
+ },
+
+ "Loading a config file via env var": {
+ env: []string{
+ "GTS_CONFIG_PATH=testdata/test.yaml",
+ },
+ expected: expectedFile(t, "testdata/test.yaml"),
+ },
+
+ "Loading a config file via cli flag": {
+ cli: []string{
+ "--config-path", "testdata/test.yaml",
+ },
+ expected: expectedFile(t, "testdata/test.yaml"),
+ },
+
+ "Loading a config file and overriding one of the variables with a cli flag": {
+ cli: []string{
+ "--config-path", "testdata/test.yaml",
+ "--account-domain", "my.test.domain",
+ },
+ // only checking our overridden one and one non-default from the config file here instead of including all of test.yaml
+ expected: expectedKV(
+ "account-domain", "my.test.domain",
+ "host", "gts.example.org",
+ ),
+ },
+
+ "Loading a config file and overriding one of the variables with an env var": {
+ cli: []string{
+ "--config-path", "testdata/test.yaml",
+ },
+ env: []string{
+ "GTS_ACCOUNT_DOMAIN=my.test.domain",
+ },
+ // only checking our overridden one and one non-default from the config file here instead of including all of test.yaml
+ expected: expectedKV(
+ "account-domain", "my.test.domain",
+ "host", "gts.example.org",
+ ),
+ },
+
+ "Loading a config file and overriding one of the variables with both an env var and a cli flag. The cli flag should have priority": {
+ cli: []string{
+ "--config-path", "testdata/test.yaml",
+ "--account-domain", "my.test.domain",
+ },
+ env: []string{
+ "GTS_ACCOUNT_DOMAIN=my.wrong.test.domain",
+ },
+ // only checking our overridden one and one non-default from the config file here instead of including all of test.yaml
+ expected: expectedKV(
+ "account-domain", "my.test.domain",
+ "host", "gts.example.org",
+ ),
+ },
+
+ "Loading a config file from json": {
+ cli: []string{
+ "--config-path", "testdata/test.json",
+ },
+ expected: expectedFile(t, "testdata/test.json"),
+ },
+
+ "Loading a partial config file. Default values should be used apart from those set in the config file": {
+ cli: []string{
+ "--config-path", "testdata/test2.yaml",
+ },
+ expected: expectedKV(
+ "log-level", "trace",
+ "account-domain", "peepee.poopoo",
+ "application-name", "gotosocial",
+ ),
+ },
+ }
+
+ for desc, data := range testcases {
+ t.Run(desc, func(t *testing.T) {
+ os.Clearenv()
+
+ if data.env != nil {
+ for _, s := range data.env {
+ kv := strings.SplitN(s, "=", 2)
+ os.Setenv(kv[0], kv[1])
+ }
+ }
+
+ state := config.NewState()
+ cmd := cobra.Command{}
+ state.AddGlobalFlags(&cmd)
+ state.AddServerFlags(&cmd)
+
+ if data.cli != nil {
+ cmd.ParseFlags(data.cli)
+ }
+
+ state.BindFlags(&cmd)
+
+ state.Reload()
+
+ state.Viper(func(v *viper.Viper) {
+ for k, ev := range data.expected {
+ assert.EqualValues(t, ev, v.Get(k))
+ }
+ })
+ })
+ }
+}
diff --git a/internal/config/flags.go b/internal/config/flags.go
index bb3f67732..ddc3c60de 100644
--- a/internal/config/flags.go
+++ b/internal/config/flags.go
@@ -26,7 +26,12 @@ import (
// AddGlobalFlags will attach global configuration flags to given cobra command, loading defaults from global config.
func AddGlobalFlags(cmd *cobra.Command) {
- Config(func(cfg *Configuration) {
+ global.AddGlobalFlags(cmd)
+}
+
+// AddGlobalFlags will attach global configuration flags to given cobra command, loading defaults from State.
+func (s *ConfigState) AddGlobalFlags(cmd *cobra.Command) {
+ s.Config(func(cfg *Configuration) {
// General
cmd.PersistentFlags().String(ApplicationNameFlag(), cfg.ApplicationName, fieldtag("ApplicationName", "usage"))
cmd.PersistentFlags().String(LandingPageUserFlag(), cfg.LandingPageUser, fieldtag("LandingPageUser", "usage"))
@@ -51,7 +56,12 @@ func AddGlobalFlags(cmd *cobra.Command) {
// AddServerFlags will attach server configuration flags to given cobra command, loading defaults from global config.
func AddServerFlags(cmd *cobra.Command) {
- Config(func(cfg *Configuration) {
+ global.AddServerFlags(cmd)
+}
+
+// AddServerFlags will attach server configuration flags to given cobra command, loading defaults from State.
+func (s *ConfigState) AddServerFlags(cmd *cobra.Command) {
+ s.Config(func(cfg *Configuration) {
// Router
cmd.PersistentFlags().String(BindAddressFlag(), cfg.BindAddress, fieldtag("BindAddress", "usage"))
cmd.PersistentFlags().Int(PortFlag(), cfg.Port, fieldtag("Port", "usage"))
diff --git a/internal/config/testdata/test.json b/internal/config/testdata/test.json
new file mode 100644
index 000000000..9bbedd36a
--- /dev/null
+++ b/internal/config/testdata/test.json
@@ -0,0 +1,60 @@
+{
+ "account-domain": "example.org",
+ "accounts-approval-required": true,
+ "accounts-reason-required": true,
+ "accounts-registration-open": true,
+ "application-name": "gotosocial",
+ "bind-address": "0.0.0.0",
+ "config-path": "testdata/test.json",
+ "db-address": "127.0.0.1",
+ "db-database": "postgres",
+ "db-password": "postgres",
+ "db-port": 5432,
+ "db-tls-ca-cert": "",
+ "db-tls-mode": "disable",
+ "db-type": "postgres",
+ "db-user": "postgres",
+ "host": "gts.example.org",
+ "letsencrypt-cert-dir": "/gotosocial/storage/certs",
+ "letsencrypt-email-address": "",
+ "letsencrypt-enabled": true,
+ "letsencrypt-port": 80,
+ "log-level": "info",
+ "media-description-max-chars": 500,
+ "media-description-min-chars": 0,
+ "media-image-max-size": 10485760,
+ "media-video-max-size": 41943040,
+ "oidc-client-id": "",
+ "oidc-client-secret": "",
+ "oidc-enabled": false,
+ "oidc-idp-name": "",
+ "oidc-issuer": "",
+ "oidc-scopes": [
+ "openid",
+ "email",
+ "profile",
+ "groups"
+ ],
+ "oidc-skip-verification": false,
+ "port": 8080,
+ "protocol": "https",
+ "smtp-from": "someone@example.org",
+ "smtp-host": "verycoolemailhost.mail",
+ "smtp-password": "smtp-password",
+ "smtp-port": 8888,
+ "smtp-username": "smtp-username",
+ "software-version": "",
+ "statuses-cw-max-chars": 100,
+ "statuses-max-chars": 5000,
+ "statuses-media-max-files": 6,
+ "statuses-poll-max-options": 6,
+ "statuses-poll-option-max-chars": 50,
+ "storage-backend": "local",
+ "storage-local-base-path": "/gotosocial/storage",
+ "trusted-proxies": [
+ "127.0.0.1/32",
+ "0.0.0.0/0"
+ ],
+ "web-asset-base-dir": "./web/assets/",
+ "web-template-base-dir": "./web/template/"
+}
diff --git a/internal/config/testdata/test.yaml b/internal/config/testdata/test.yaml
new file mode 100644
index 000000000..9a57b589d
--- /dev/null
+++ b/internal/config/testdata/test.yaml
@@ -0,0 +1,411 @@
+# GoToSocial
+# Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+###########################
+##### GENERAL CONFIG ######
+###########################
+
+# String. Log level to use throughout the application. Must be lower-case.
+# Options: ["trace","debug","info","warn","error","fatal"]
+# Default: "info"
+log-level: "info"
+
+# Bool. Log database queries when log-level is set to debug or trace.
+# This setting produces verbose logs, so it's better to only enable it
+# when you're trying to track an issue down.
+# Options: [true, false]
+# Default: false
+log-db-queries: false
+
+# String. Application name to use internally.
+# Examples: ["My Application","gotosocial"]
+# Default: "gotosocial"
+application-name: "gotosocial"
+
+# String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
+# but you should *definitely* change this when running for real, or your server won't work at all.
+# DO NOT change this after your server has already run once, or you will break things!
+# Examples: ["gts.example.org","some.server.com"]
+# Default: "localhost"
+host: "gts.example.org"
+
+# String. Domain to use when federating profiles. This is useful when you want your server to be at
+# eg., "gts.example.org", but you want the domain on accounts to be "example.org" because it looks better
+# or is just shorter/easier to remember.
+# To make this setting work properly, you need to redirect requests at "example.org/.well-known/webfinger"
+# to "gts.example.org/.well-known/webfinger" so that GtS can handle them properly.
+# You should also redirect requests at "example.org/.well-known/nodeinfo" in the same way.
+# An empty string (ie., not set) means that the same value as 'host' will be used.
+# DO NOT change this after your server has already run once, or you will break things!
+# Examples: ["example.org","server.com"]
+# Default: ""
+account-domain: "example.org"
+
+# String. Protocol to use for the server. Only change to http for local testing!
+# This should be the protocol part of the URI that your server is actually reachable on. So even if you're
+# running GoToSocial behind a reverse proxy that handles SSL certificates for you, instead of using built-in
+# letsencrypt, it should still be https.
+# Options: ["http","https"]
+# Default: "https"
+protocol: "https"
+
+# String. Address to bind the GoToSocial server to.
+# This can be an IPv4 address or an IPv6 address (surrounded in square brackets), or a hostname.
+# Default value will bind to all interfaces.
+# You probably won't need to change this unless you're setting GoToSocial up in some fancy way or
+# you have specific networking requirements.
+# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
+# Default: "0.0.0.0"
+bind-address: "0.0.0.0"
+
+# Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker,
+# container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly.
+# If you are running with built-in letsencrypt enabled, and running GoToSocial directly on a host machine, you will
+# probably want to set this to 443 (standard https port), unless you have other services already using that port.
+# This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off.
+# Examples: [443, 6666, 8080]
+# Default: 8080
+port: 8080
+
+# Array of string. CIDRs or IP addresses of proxies that should be trusted when determining real client IP from behind a reverse proxy.
+# If you're running inside a Docker container behind Traefik or Nginx, for example, add the subnet of your docker network,
+# or the gateway of the docker network, and/or the address of the reverse proxy (if it's not running on the host network).
+# Example: ["127.0.0.1/32", "172.20.0.1"]
+# Default: ["127.0.0.1/32"] (localhost)
+trusted-proxies:
+ - "127.0.0.1/32"
+ - "0.0.0.0/0"
+
+############################
+##### DATABASE CONFIG ######
+############################
+
+# Config pertaining to the Gotosocial database connection
+
+# String. Database type.
+# Options: ["postgres","sqlite"]
+# Default: "postgres"
+db-type: "postgres"
+
+# String. Database address or parameters.
+# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:"]
+# Default: "localhost"
+db-address: "127.0.0.1"
+
+# Int. Port for database connection.
+# Examples: [5432, 1234, 6969]
+# Default: 5432
+db-port: 5432
+
+# String. Username for the database connection.
+# Examples: ["mydbuser","postgres","gotosocial"]
+# Default: "postgres"
+db-user: "postgres"
+
+# REQUIRED
+# String. Password to use for the database connection
+# Examples: ["password123","verysafepassword","postgres"]
+# Default: "postgres"
+db-password: "postgres"
+
+# String. Name of the database to use within the provided database type.
+# Examples: ["mydb","postgres","gotosocial"]
+# Default: "postgres"
+db-database: "postgres"
+
+# String. Disable, enable, or require SSL/TLS connection to the database.
+# If "disable" then no TLS connection will be attempted.
+# If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs).
+# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
+# Options: ["disable", "enable", "require"]
+# Default: "disable"
+db-tls-mode: "disable"
+
+# String. Path to a CA certificate on the host machine for db certificate validation.
+# If this is left empty, just the host certificates will be used.
+# If filled in, the certificate will be loaded and added to host certificates.
+# Examples: ["/path/to/some/cert.crt"]
+# Default: ""
+db-tls-ca-cert: ""
+
+######################
+##### WEB CONFIG #####
+######################
+
+# Config pertaining to templating and serving of web pages/email notifications and the like
+
+# String. Directory from which gotosocial will attempt to load html templates (.tmpl files).
+# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
+# Default: "./web/template/"
+web-template-base-dir: "./web/template/"
+
+# String. Directory from which gotosocial will attempt to serve static web assets (images, scripts).
+# Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"]
+# Default: "./web/assets/"
+web-asset-base-dir: "./web/assets/"
+
+###########################
+##### ACCOUNTS CONFIG #####
+###########################
+
+# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
+
+# Bool. Do we want people to be able to just submit sign up requests, or do we want invite only?
+# Options: [true, false]
+# Default: true
+accounts-registration-open: true
+
+# Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?
+# Options: [true, false]
+# Default: true
+accounts-approval-required: true
+
+# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
+# Options: [true, false]
+# Default: true
+accounts-reason-required: true
+
+########################
+##### MEDIA CONFIG #####
+########################
+
+# Config pertaining to user media uploads (videos, image, image descriptions).
+
+# Int. Maximum allowed image upload size in bytes.
+# Examples: [2097152, 10485760]
+# Default: 10485760 -- aka 10MB
+media-image-max-size: 10485760
+
+# Int. Maximum allowed video upload size in bytes.
+# Examples: [2097152, 10485760]
+# Default: 41943040 -- aka 40MB
+media-video-max-size: 41943040
+
+# Int. Minimum amount of characters required as an image or video description.
+# Examples: [500, 1000, 1500]
+# Default: 0 (not required)
+media-description-min-chars: 0
+
+# Int. Maximum amount of characters permitted in an image or video description.
+# Examples: [500, 1000, 1500]
+# Default: 500
+media-description-max-chars: 500
+
+##########################
+##### STORAGE CONFIG #####
+##########################
+
+# Config pertaining to storage of user-created uploads (videos, images, etc).
+
+# String. Type of storage backend to use.
+# Examples: ["local", "s3"]
+# Default: "local" (storage on local disk)
+# NOTE: s3 storage is not yet supported!
+storage-backend: "local"
+
+# String. Directory to use as a base path for storing files.
+# Make sure whatever user/group gotosocial is running as has permission to access
+# this directly, and create new subdirectories and files with in.
+# Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"]
+# Default: "/gotosocial/storage"
+storage-local-base-path: "/gotosocial/storage"
+
+###########################
+##### STATUSES CONFIG #####
+###########################
+
+# Config pertaining to the creation of statuses/posts, and permitted limits.
+
+# Int. Maximum amount of characters permitted for a new status.
+# Note that going way higher than the default might break federation.
+# Examples: [140, 500, 5000]
+# Default: 5000
+statuses-max-chars: 5000
+
+# Int. Maximum amount of characters allowed in the CW/subject header of a status.
+# Note that going way higher than the default might break federation.
+# Examples: [100, 200]
+# Default: 100
+statuses-cw-max-chars: 100
+
+# Int. Maximum amount of options to permit when creating a new poll.
+# Note that going way higher than the default might break federation.
+# Examples: [4, 6, 10]
+# Default: 6
+statuses-poll-max-options: 6
+
+# Int. Maximum amount of characters to permit per poll option when creating a new poll.
+# Note that going way higher than the default might break federation.
+# Examples: [50, 100, 150]
+# Default: 50
+statuses-poll-option-max-chars: 50
+
+# Int. Maximum amount of media files that can be attached to a new status.
+# Note that going way higher than the default might break federation.
+# Examples: [4, 6, 10]
+# Default: 6
+statuses-media-max-files: 6
+
+##############################
+##### LETSENCRYPT CONFIG #####
+##############################
+
+# Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates.
+
+# Bool. Whether or not letsencrypt should be enabled for the server.
+# If false, the rest of the settings here will be ignored.
+# You should only change this if you want to serve GoToSocial behind a reverse proxy
+# like Traefik, HAProxy, or Nginx.
+# Options: [true, false]
+# Default: true
+letsencrypt-enabled: true
+
+# Int. Port to listen for letsencrypt certificate challenges on.
+# If letsencrypt is enabled, this port must be reachable or you won't be able to obtain certs.
+# If letsencrypt is disabled, this port will not be used.
+# This *must not* be the same as the webserver/API port specified above.
+# Examples: [80, 8000, 1312]
+# Default: 80
+letsencrypt-port: 80
+
+# String. Directory in which to store LetsEncrypt certificates.
+# It is a good move to make this a sub-path within your storage directory, as it makes
+# backup easier, but you might wish to move them elsewhere if they're also accessed by other services.
+# In any case, make sure GoToSocial has permissions to write to / read from this directory.
+# Examples: ["/home/gotosocial/storage/certs", "/acmecerts"]
+# Default: "/gotosocial/storage/certs"
+letsencrypt-cert-dir: "/gotosocial/storage/certs"
+
+# String. Email address to use when registering LetsEncrypt certs.
+# Most likely, this will be the email address of the instance administrator.
+# LetsEncrypt will send notifications about expiring certificates etc to this address.
+# Examples: ["admin@example.org"]
+# Default: ""
+letsencrypt-email-address: ""
+
+#######################
+##### OIDC CONFIG #####
+#######################
+
+# Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc).
+
+# Bool. Enable authentication with external OIDC provider. If set to true, then
+# the other OIDC options must be set as well. If this is set to false, then the standard
+# internal oauth flow will be used, where users sign in to GtS with username/password.
+# Options: [true, false]
+# Default: false
+oidc-enabled: false
+
+# String. Name of the oidc idp (identity provider). This will be shown to users when
+# they log in.
+# Examples: ["Google", "Dex", "Auth0"]
+# Default: ""
+oidc-idp-name: ""
+
+# Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie.,
+# don't check the expiry or signature. This should only be used in debugging or testing,
+# never ever in a production environment as it's extremely unsafe!
+# Options: [true, false]
+# Default: false
+oidc-skip-verification: false
+
+# String. The OIDC issuer URI. This is where GtS will redirect users to for login.
+# Typically this will look like a standard web URL.
+# Examples: ["https://auth.example.org", "https://example.org/auth"]
+# Default: ""
+oidc-issuer: ""
+
+# String. The ID for this client as registered with the OIDC provider.
+# Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"]
+# Default: ""
+oidc-client-id: ""
+
+# String. The secret for this client as registered with the OIDC provider.
+# Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"]
+# Default: ""
+oidc-client-secret: ""
+
+# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
+# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
+# 'profile' is used to extract a username for the newly created user.
+# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
+# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
+# Default: ["openid", "email", "profile", "groups"]
+oidc-scopes:
+ - "openid"
+ - "email"
+ - "profile"
+ - "groups"
+
+#######################
+##### SMTP CONFIG #####
+#######################
+
+# Config for sending emails via an smtp server. See https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
+
+# String. The hostname of the smtp server you want to use.
+# If this is not set, smtp will not be used to send emails, and you can ignore the other settings.
+# Examples: ["mail.example.org", "localhost"]
+# Default: ""
+smtp-host: "verycoolemailhost.mail"
+
+# Int. Port to use to connect to the smtp server.
+# Examples: []
+# Default: 0
+smtp-port: 8888
+
+# String. Username to use when authenticating with the smtp server.
+# This should have been provided to you by your smtp host.
+# This is often, but not always, an email address.
+# Examples: ["maillord@example.org"]
+# Default: ""
+smtp-username: "smtp-username"
+
+# String. Password to use when authenticating with the smtp server.
+# This should have been provided to you by your smtp host.
+# Examples: ["1234", "password"]
+# Default: ""
+smtp-password: "smtp-password"
+
+# String. 'From' address for sent emails.
+# Examples: ["mail@example.org"]
+# Default: ""
+smtp-from: "someone@example.org"
+
+#########################
+##### SYSLOG CONFIG #####
+#########################
+
+# Config for additional syslog log hooks. See https://en.wikipedia.org/wiki/Syslog,
+# and https://github.com/sirupsen/logrus/tree/master/hooks/syslog.
+#
+# These settings are useful when one wants to daemonize GoToSocial and send logs
+# to a specific place, either a local location or a syslog server. Most users will
+# not need to touch these settings.
+
+# Bool. Enable the syslog logging hook. Logs will be mirrored to the configured destination.
+# Options: [true, false]
+# Default: false
+syslog-enabled: false
+
+# String. Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.
+# Options: ["udp", "tcp", ""]
+# Default: "tcp"
+syslog-protocol: "udp"
+
+# String. Address:port to send syslog logs to. Leave empty to connect to local syslog.
+# Default: "localhost:514"
+syslog-address: "localhost:514"
diff --git a/internal/config/testdata/test2.yaml b/internal/config/testdata/test2.yaml
new file mode 100644
index 000000000..f26ac7ffb
--- /dev/null
+++ b/internal/config/testdata/test2.yaml
@@ -0,0 +1,2 @@
+log-level: "trace"
+account-domain: "peepee.poopoo"