2024-06-25 12:39:04 +02:00
|
|
|
{ pkgs, self }:
|
|
|
|
let
|
2024-07-18 12:44:13 +02:00
|
|
|
lib = pkgs.lib;
|
2024-11-11 17:28:35 +01:00
|
|
|
|
|
|
|
## FIXME: this binding was not used, but maybe we want a side-effect or something?
|
|
|
|
# rebuildableTest = import ./rebuildableTest.nix pkgs;
|
|
|
|
|
2024-11-11 17:25:42 +01:00
|
|
|
seleniumScript =
|
|
|
|
pkgs.writers.writePython3Bin "selenium-script"
|
|
|
|
{
|
|
|
|
libraries = with pkgs.python3Packages; [ selenium ];
|
|
|
|
}
|
|
|
|
''
|
|
|
|
from selenium import webdriver
|
|
|
|
from selenium.webdriver.common.by import By
|
|
|
|
from selenium.webdriver.firefox.options import Options
|
|
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
|
|
|
|
|
|
print(1)
|
|
|
|
|
|
|
|
options = Options()
|
|
|
|
options.add_argument("--headless")
|
|
|
|
# devtools don't show up in headless screenshots
|
|
|
|
# options.add_argument("-devtools")
|
|
|
|
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
|
|
|
|
|
|
|
|
driver = webdriver.Firefox(options=options, service=service)
|
|
|
|
driver.get("http://mastodon.localhost:55001/public/local")
|
|
|
|
|
|
|
|
# wait until the statuses load
|
|
|
|
WebDriverWait(driver, 90).until(
|
|
|
|
lambda x: x.find_element(By.CLASS_NAME, "status"))
|
|
|
|
|
|
|
|
driver.save_screenshot("/mastodon-screenshot.png")
|
|
|
|
|
|
|
|
driver.close()
|
|
|
|
'';
|
2024-06-25 12:39:04 +02:00
|
|
|
in
|
2024-08-28 14:39:36 +02:00
|
|
|
pkgs.nixosTest {
|
2024-06-25 12:39:04 +02:00
|
|
|
name = "test-mastodon-garage";
|
|
|
|
|
|
|
|
nodes = {
|
2024-11-11 17:25:42 +01:00
|
|
|
server =
|
|
|
|
{ config, ... }:
|
|
|
|
{
|
|
|
|
virtualisation.memorySize = lib.mkVMOverride 4096;
|
|
|
|
imports = with self.nixosModules; [
|
|
|
|
bleedingFediverse
|
|
|
|
fediversity
|
|
|
|
garage-vm
|
|
|
|
mastodon-vm
|
|
|
|
];
|
|
|
|
# TODO: pair down
|
|
|
|
environment.systemPackages = with pkgs; [
|
|
|
|
python3
|
|
|
|
firefox-unwrapped
|
|
|
|
geckodriver
|
|
|
|
toot
|
|
|
|
xh
|
|
|
|
seleniumScript
|
|
|
|
helix
|
|
|
|
imagemagick
|
|
|
|
];
|
|
|
|
environment.variables = {
|
|
|
|
POST_MEDIA = ./green.png;
|
|
|
|
AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.mastodon.id;
|
|
|
|
AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.mastodon.secret;
|
|
|
|
};
|
2024-07-18 12:44:13 +02:00
|
|
|
};
|
2024-06-25 12:39:04 +02:00
|
|
|
};
|
|
|
|
|
2024-11-11 17:25:42 +01:00
|
|
|
testScript =
|
|
|
|
{ nodes, ... }:
|
|
|
|
''
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
|
|
|
|
server.start()
|
|
|
|
|
|
|
|
with subtest("Mastodon starts"):
|
|
|
|
server.wait_for_unit("mastodon-web.service")
|
|
|
|
|
|
|
|
# make sure mastodon is fully up and running before we interact with it
|
|
|
|
# TODO: is there a way to test for this?
|
|
|
|
time.sleep(180)
|
|
|
|
|
|
|
|
with subtest("Account creation"):
|
|
|
|
account_creation_output = server.succeed("mastodon-tootctl accounts create test --email test@test.com --confirmed --approve")
|
|
|
|
password_match = re.match('.*New password: ([^\n]*).*', account_creation_output, re.S)
|
|
|
|
if password_match is None:
|
|
|
|
raise Exception(f"account creation did not generate a password.\n{account_creation_output}")
|
|
|
|
password = password_match.group(1)
|
|
|
|
|
|
|
|
with subtest("TTY Login"):
|
|
|
|
server.wait_until_tty_matches("1", "login: ")
|
|
|
|
server.send_chars("root\n");
|
|
|
|
|
|
|
|
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
|
|
|
|
server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n")
|
|
|
|
server.wait_until_tty_matches("1", "Password: ")
|
|
|
|
server.send_chars(password + "\n")
|
|
|
|
server.wait_until_tty_matches("1", "Successfully logged in.")
|
|
|
|
|
|
|
|
with subtest("post text"):
|
|
|
|
server.succeed("echo 'hello mastodon' | toot post")
|
|
|
|
|
|
|
|
with subtest("post image"):
|
|
|
|
server.succeed("toot post --media $POST_MEDIA")
|
|
|
|
|
|
|
|
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 ls garage/mastodon")
|
|
|
|
|
|
|
|
with subtest("access image in garage"):
|
|
|
|
image = server.succeed("mc find garage --regex original")
|
|
|
|
image = image.rstrip()
|
|
|
|
if image == "":
|
|
|
|
raise Exception("image posted to mastodon did not get stored in garage")
|
|
|
|
server.succeed(f"mc cat {image} >/garage-image.webp")
|
|
|
|
garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp")
|
|
|
|
image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA")
|
|
|
|
if garage_image_hash != image_hash:
|
|
|
|
raise Exception("image stored in garage did not match image uploaded")
|
|
|
|
|
|
|
|
with subtest("Content security policy allows garage images"):
|
|
|
|
headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local")
|
|
|
|
csp_match = None
|
|
|
|
# I can't figure out re.MULTILINE
|
|
|
|
for header in headers.split("\n"):
|
|
|
|
csp_match = re.match('^Content-Security-Policy: (.*)$', header)
|
|
|
|
if csp_match is not None:
|
|
|
|
break
|
|
|
|
if csp_match is None:
|
|
|
|
raise Exception("mastodon did not send a content security policy header")
|
|
|
|
csp = csp_match.group(1)
|
|
|
|
# the img-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.
|
|
|
|
garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost.*", csp)
|
|
|
|
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.")
|
|
|
|
|
|
|
|
# this could in theory give a false positive if mastodon changes it's colorscheme to include pure green.
|
|
|
|
with subtest("image displays"):
|
|
|
|
server.succeed("selenium-script")
|
|
|
|
server.copy_from_vm("/mastodon-screenshot.png", "")
|
|
|
|
displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:")
|
|
|
|
# check that the green image displayed somewhere
|
|
|
|
green_check = re.match(".*#00FF00.*", displayed_colors, re.S)
|
|
|
|
if green_check is None:
|
|
|
|
raise Exception("cannot detect the uploaded image on mastodon page.")
|
|
|
|
'';
|
2024-06-25 12:39:04 +02:00
|
|
|
}
|