Improve on the Mastodon test (#35)

This commit is contained in:
Nicolas Jeannerod 2024-11-27 14:38:27 +01:00
commit 5134bab2d2
Signed by untrusted user: Niols
GPG key ID: 35DB9EC8886E1CB8
9 changed files with 51 additions and 80 deletions

View file

@ -40,7 +40,7 @@ NOTE: it sometimes takes a while for the services to start up, and in the meanti
``` ```
- Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account. - Creating other accounts has to be enabled via the admin interface. `Administration > Configuration > Basic > Enable Signup` or just add an account directly from `Administration > Create user`. But functionality can also be tested from the root account.
- Pixelfed: <http://pixelfed.localhost:8000> - Pixelfed: through the reverse proxy at <http://pixelfed.localhost:8080>
- Account creation via the web interface won't work until we figure out email - Account creation via the web interface won't work until we figure out email
- For now, they can be created on the VM command line - For now, they can be created on the VM command line
```bash ```bash

View file

@ -216,6 +216,10 @@ in
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Disable buffering to a temporary file. # Disable buffering to a temporary file.
proxy_max_temp_file_size 0; proxy_max_temp_file_size 0;
## NOTE: This page suggests many more options for the object storage
## proxy. We should take a look.
## https://docs.joinmastodon.org/admin/optional/object-storage-proxy/
''; '';
}; };
}; };

View file

@ -46,9 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_ACCESS_KEY_ID = snakeoil_key.id;
AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret;
S3_PROTOCOL = "http"; S3_PROTOCOL = "http";
S3_HOSTNAME = config.fediversity.internal.garage.web.rootDomain; S3_ALIAS_HOST = "${S3_BUCKET}.${config.fediversity.internal.garage.web.rootDomain}";
# by default it tries to use "<S3_HOSTNAME>/<S3_BUCKET>"
S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}";
# SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/
# TODO: can we set up ACLs with garage? # TODO: can we set up ACLs with garage?
S3_PERMISSION = ""; S3_PERMISSION = "";
@ -78,9 +76,6 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
fromAddress = "noreply@${config.fediversity.internal.mastodon.domain}"; fromAddress = "noreply@${config.fediversity.internal.mastodon.domain}";
createLocally = false; createLocally = false;
}; };
# TODO: this is hardware-dependent. let's figure it out when we have hardware
# streamingProcesses = 1;
}; };
security.acme = { security.acme = {

View file

@ -7,7 +7,7 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
checks = { checks = {
mastodon-garage = import ./tests/mastodon-garage.nix { inherit self pkgs; }; mastodon = import ./tests/mastodon.nix { inherit self pkgs; };
pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit self pkgs; }; pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit self pkgs; };
}; };
}; };

View file

@ -1,10 +1,19 @@
## This file is a basic test of Mastodon functionalities.
##
## NOTE: This test will fail for Mastodon < 4.3 because of
## https://github.com/mastodon/mastodon/issues/31145
{ pkgs, self }: { pkgs, self }:
let let
lib = pkgs.lib; lib = pkgs.lib;
## FIXME: this binding was not used, but maybe we want a side-effect or something? ## FIXME: this binding was not used, but maybe we want a side-effect or something?
# rebuildableTest = import ./rebuildableTest.nix pkgs; # rebuildableTest = import ./rebuildableTest.nix pkgs;
testImage = pkgs.copyPathToStore ./green.png;
testImageColour = "#00FF00";
seleniumScript = seleniumScript =
pkgs.writers.writePython3Bin "selenium-script" pkgs.writers.writePython3Bin "selenium-script"
{ libraries = with pkgs.python3Packages; [ selenium ]; } { libraries = with pkgs.python3Packages; [ selenium ]; }
@ -14,8 +23,6 @@ let
from selenium.webdriver.firefox.options import Options from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
print(1)
options = Options() options = Options()
options.add_argument("--headless") options.add_argument("--headless")
# devtools don't show up in headless screenshots # devtools don't show up in headless screenshots
@ -23,7 +30,7 @@ let
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501 service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
driver = webdriver.Firefox(options=options, service=service) driver = webdriver.Firefox(options=options, service=service)
driver.get("http://mastodon.localhost:55001/public/local") driver.get("http://mastodon.localhost/public/local")
# wait until the statuses load # wait until the statuses load
WebDriverWait(driver, 90).until( WebDriverWait(driver, 90).until(
@ -34,6 +41,7 @@ let
driver.close() driver.close()
''; '';
in in
pkgs.nixosTest { pkgs.nixosTest {
name = "test-mastodon-garage"; name = "test-mastodon-garage";
@ -46,6 +54,7 @@ pkgs.nixosTest {
fediversity fediversity
../vm/garage-vm.nix ../vm/garage-vm.nix
../vm/mastodon-vm.nix ../vm/mastodon-vm.nix
../vm/interactive-vm.nix
]; ];
# TODO: pair down # TODO: pair down
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
@ -57,9 +66,9 @@ pkgs.nixosTest {
seleniumScript seleniumScript
helix helix
imagemagick imagemagick
expect
]; ];
environment.variables = { environment.variables = {
POST_MEDIA = ./green.png;
AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.mastodon.id; AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.mastodon.id;
AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.mastodon.secret; AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.mastodon.secret;
}; };
@ -87,64 +96,67 @@ pkgs.nixosTest {
if password_match is None: if password_match is None:
raise Exception(f"account creation did not generate a password.\n{account_creation_output}") raise Exception(f"account creation did not generate a password.\n{account_creation_output}")
password = password_match.group(1) password = password_match.group(1)
# print(f"Test user (test@test.com)'s password is: {password}")
with subtest("TTY Login"):
server.wait_until_tty_matches("1", "login: ")
server.send_chars("root\n");
with subtest("Log in with toot"): with subtest("Log in with toot"):
# toot doesn't provide a way to just specify our login details as arguments, so we have to pretend we're typing them in at the prompt # toot doesn't provide a way to just specify our login details as
server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n") # arguments, so we have to pretend we're typing them in at the prompt;
server.wait_until_tty_matches("1", "Password: ") # we use 'expect' for this purpose.
server.send_chars(password + "\n") server.succeed(f"""
server.wait_until_tty_matches("1", "Successfully logged in.") expect -c '
spawn toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com
expect "Password: "
send "{password}\\n"
interact
' >&2
""")
with subtest("post text"): with subtest("Post a text"):
server.succeed("echo 'hello mastodon' | toot post") server.succeed("echo 'hello mastodon' | toot post")
with subtest("post image"): with subtest("Post an image"):
server.succeed("toot post --media $POST_MEDIA") server.succeed("toot post --media ${testImage}")
with subtest("access garage"): with subtest("Access garage"):
server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY")
server.succeed("mc ls garage/mastodon") server.succeed("mc ls garage/mastodon")
with subtest("access image in garage"): with subtest("Access image in garage"):
image = server.succeed("mc find garage --regex original") image = server.succeed("mc find garage --regex original")
image = image.rstrip() image = image.rstrip()
if image == "": if image == "":
raise Exception("image posted to mastodon did not get stored in garage") raise Exception("image posted to mastodon did not get stored in garage")
server.succeed(f"mc cat {image} >/garage-image.webp") server.succeed(f"mc cat {image} >/garage-image.webp")
garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp")
image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") image_hash = server.succeed("identify -quiet -format '%#' ${testImage}")
if garage_image_hash != image_hash: if garage_image_hash != image_hash:
raise Exception("image stored in garage did not match image uploaded") raise Exception("image stored in garage did not match image uploaded")
with subtest("Content security policy allows garage images"): with subtest("Content-Security-Policy allows garage content"):
headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local") headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local")
csp_match = None csp_match = None
# I can't figure out re.MULTILINE # I can't figure out re.MULTILINE
for header in headers.split("\n"): for header in headers.split("\n"):
csp_match = re.match('^Content-Security-Policy: (.*)$', header) csp_match = re.match('^Content-Security-Policy: (.*)$', header)
if csp_match is not None: if csp_match is not None:
break break
if csp_match is None: if csp_match is None:
raise Exception("mastodon did not send a content security policy header") raise Exception("mastodon did not send a content security policy header")
csp = csp_match.group(1) csp = csp_match.group(1)
# the img-src content security policy should include the garage server # the connect-src content security policy should include the garage server
## TODO: use `nodes.server.fediversity.internal.garage.api.url` same as above, but beware of escaping the regex. Be careful with port 80 though. ## TODO: use `nodes.server.fediversity.internal.garage.api.url` same as above, but beware of escaping the regex. Be careful with port 80 though.
garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost.*", csp) garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost.*", csp)
if garage_csp is None: if garage_csp is None:
raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") raise Exception("Mastodon's Content-Security-Policy does not include Garage.")
# this could in theory give a false positive if mastodon changes it's colorscheme to include pure green. # this could in theory give a false positive if mastodon changes it's colorscheme to include ${testImageColour}.
with subtest("image displays"): with subtest("Image displays"):
server.succeed("selenium-script") server.succeed("selenium-script")
server.copy_from_vm("/mastodon-screenshot.png", "") server.copy_from_vm("/mastodon-screenshot.png", "")
displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:")
# check that the green image displayed somewhere # check that the image displayed somewhere
green_check = re.match(".*#00FF00.*", displayed_colors, re.S) image_check = re.match(".*${testImageColour}.*", displayed_colors, re.S)
if green_check is None: if image_check is None:
raise Exception("cannot detect the uploaded image on mastodon page.") raise Exception("cannot detect the uploaded image on mastodon page.")
''; '';
} }

View file

@ -186,7 +186,7 @@ pkgs.nixosTest {
server.succeed("pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1") server.succeed("pixelfed-manage user:create --name=test --username=test --email=${email} --password=${password} --confirm_email=1")
# NOTE: This could in theory give a false positive if pixelfed changes it's # NOTE: This could in theory give a false positive if pixelfed changes it's
# colorscheme to include pure green. (see same problem in pixelfed-garage.nix). # colorscheme to include pure green. (see same problem in mastodon-garage.nix).
# TODO: For instance: post a red image and check that the green pixel IS NOT # TODO: For instance: post a red image and check that the green pixel IS NOT
# there, then post a green image and check that the green pixel IS there. # there, then post a green image and check that the green pixel IS there.

View file

@ -32,29 +32,8 @@
extra-experimental-features = nix-command flakes extra-experimental-features = nix-command flakes
''; '';
# no graphics. see nixos-shell virtualisation.memorySize = 2048;
virtualisation = {
graphics = false;
qemu.consoles = [
"tty0"
"hvc0"
];
qemu.options = [
"-serial null"
"-device virtio-serial"
"-chardev stdio,mux=on,id=char0,signal=off"
"-mon chardev=char0,mode=readline"
"-device virtconsole,chardev=char0,nr=0"
];
};
# we can't forward port 80 or 443, so let's run nginx on a different port
networking.firewall.allowedTCPPorts = [
8443
8080
];
services.nginx.defaultSSLListenPort = 8443;
services.nginx.defaultHTTPListenPort = 8080;
virtualisation.forwardPorts = [ virtualisation.forwardPorts = [
{ {
from = "host"; from = "host";
@ -64,12 +43,12 @@
{ {
from = "host"; from = "host";
host.port = 8080; host.port = 8080;
guest.port = 8080; guest.port = 80;
} }
{ {
from = "host"; from = "host";
host.port = 8443; host.port = 8443;
guest.port = 8443; guest.port = 443;
} }
]; ];
} }

View file

@ -33,15 +33,6 @@
email = "none"; email = "none";
}; };
}; };
virtualisation.memorySize = 2048;
virtualisation.forwardPorts = [
{
from = "host";
host.port = 44443;
guest.port = 443;
}
];
} }
#### run mastodon as development environment #### run mastodon as development environment
@ -58,7 +49,6 @@
BIND = "0.0.0.0"; BIND = "0.0.0.0";
# for letter_opener (still doesn't work though) # for letter_opener (still doesn't work though)
REMOTE_DEV = "true"; REMOTE_DEV = "true";
LOCAL_DOMAIN = "${config.fediversity.internal.mastodon.domain}:8443";
}; };
}; };

View file

@ -23,13 +23,4 @@ in
enableACME = mkVMOverride false; enableACME = mkVMOverride false;
}; };
}; };
virtualisation.memorySize = 2048;
virtualisation.forwardPorts = [
{
from = "host";
host.port = 8000;
guest.port = 80;
}
];
} }