## This file is a basic test of Peertube functionalities. { pkgs, self }: let lib = pkgs.lib; testVideo = pkgs.copyPathToStore ./green.mp4; testVideoColour = "#00FF00"; postVideoInBrowser = pkgs.writers.writePython3Bin "post-video-in-browser" { libraries = with pkgs.python3Packages; [ selenium ]; flakeIgnore = [ "E501" ]; # welcome to the 21st century } '' import sys from urllib.parse import urlparse 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 from selenium.common.exceptions import NoSuchElementException options = Options() print("########################################", file=sys.stderr) print("A", file=sys.stderr) options.add_argument("--headless") print("########################################", file=sys.stderr) print("B", file=sys.stderr) service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") print("########################################", file=sys.stderr) print("C", file=sys.stderr) driver = webdriver.Firefox(options=options, service=service) print("########################################", file=sys.stderr) print("D", file=sys.stderr) driver.set_window_size(4096, 2160) print("########################################", file=sys.stderr) print("E", file=sys.stderr) driver.implicitly_wait(360) print("########################################", file=sys.stderr) print("F", file=sys.stderr) wait = WebDriverWait(driver, timeout=360, poll_frequency=10) print("########################################", file=sys.stderr) ############################################################ # Login def load(driver, page): print(f"Loading page {page}...", file=sys.stderr) driver.get(page) print("Waiting until page is loaded...", file=sys.stderr) wait.until(lambda d: d.execute_script("return document.readyState") == "complete") if urlparse(driver.current_url).path == "/login": print("Hit a login page.", file=sys.stderr) print("Enter username...", file=sys.stderr) driver.find_element(By.ID, "username").send_keys("root") print("Enter password...", file=sys.stderr) driver.find_element(By.ID, "password").send_keys(sys.argv[1]) print("Click “Login” button...", file=sys.stderr) driver.find_element(By.XPATH, "//input[@value='Login']").click() print("Waiting until we are logged-in...", file=sys.stderr) wait.until(lambda d: urlparse(d.current_url).path == urlparse(page).path) print("Waiting until page is loaded...", file=sys.stderr) wait.until(lambda d: d.execute_script("return document.readyState") == "complete") print("Clicking the annoying setup wizard away...", file=sys.stderr) try: driver.find_element(By.XPATH, "//input[@value='Remind me later']").click() except NoSuchElementException: # Somehow, sometimes, the wizard just does not show up; then we # ignore the error and carry on like nothing happened. print("Setup wizard did not show up.", file=sys.stderr) print(f"Done loading page {page}.", file=sys.stderr) ############################################################ # Upload video and take a screenshot print("Go to the upload page...", file=sys.stderr) load(driver, "http://peertube.localhost/videos/upload") print("Submit video file...", file=sys.stderr) driver.find_element(By.XPATH, "//input[@type='file']").send_keys("${testVideo}") print("Wait for file to upload, then publish it...", file=sys.stderr) publish_button = driver.find_element(By.XPATH, "//button[.//span[normalize-space()='Publish']]") wait.until(lambda _d: "disabled" not in publish_button.get_attribute("class")) publish_button.click() print("Waiting until we are redirected...", file=sys.stderr) wait.until(lambda d: urlparse(d.current_url).path != "/videos/upload") wait.until(lambda d: d.execute_script("return document.readyState") == "complete") # FIXME: The video cannot play and we get “Failed to play video”. I # believe it is a codec problem, and it possibly has to do with video # codecs enabled in Firefox in headless mode -- after all, who would # want to play a video? The following is a list of things that I have # tried without success. Maybe one day we can manage? # # video = driver.find_element(By.XPATH, "//video") # wait.until(lambda _d: not video.get_attribute("src").startswith("blob:")) # wait.until(lambda d: d.execute_script("return arguments[0].readyState", video) == 4) # driver.find_element(By.XPATH, "//*[contains(text(), 'Failed to play video')]") # # def detect_image_in_screen(d): # print("Taking a screenshot...", file=sys.stderr) # d.save_screenshot("/screenshot.png") # print("Checking it...", file=sys.stderr) # displayed_colours = subprocess.run( # [ # "magick", # "/screenshot.png", # "-define", # "histogram:unique-colors=true", # "-format", # "%c", # "histogram:info:", # ], # capture_output=True, # text=True, # check=True, # ).stdout # return bool(re.match(".*#${testVideoColour}.*", displayed_colours, re.S)) # # print("Wait until the image shows in screen...", file=sys.stderr) # wait.until(detect_image_in_screen) ############################################################ print("Done; bye!", file=sys.stderr) driver.close() ''; acquireRootPassword = pkgs.writeShellScriptBin "acquire-root-password" '' readonly retry=30 readonly wait=60 for _ in $(seq $retry); do password=$(journalctl -u peertube | perl -ne '/password: (.*)/ && print $1') if [ -n "$password" ]; then echo "$password" exit 0 fi sleep $wait done echo "Could not acquire root password in $((retry * wait))s." exit 1 ''; in pkgs.nixosTest { name = "peertube"; nodes = { server = { config, ... }: { imports = with self.nixosModules; [ fediversity ../vm/garage-vm.nix ../vm/peertube-vm.nix ../vm/interactive-vm.nix ]; virtualisation = { memorySize = lib.mkVMOverride 8192; cores = 8; }; environment.systemPackages = with pkgs; [ python3 firefox-unwrapped geckodriver toot acquireRootPassword postVideoInBrowser imagemagick ffmpeg # to identify videos expect ]; ## FIXME: The CI is very slow, so the default timeout of 120s is not ## good enough. We bump it drastically. systemd.services.postgresql.serviceConfig.TimeoutSec = lib.mkForce 3600; environment.variables = { AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.peertube.id; AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.peertube.secret; PT_INITIAL_ROOT_PASSWORD = "testtest"; }; }; }; testScript = { nodes, ... }: '' server.start() # FIXME: I think this trick to look for a password can be replaced by # services.peertube.serviceEnvironmentFile.PT_INITIAL_ROOT_PASSWORD=testtest with subtest("Peertube starts"): server.wait_for_unit("peertube.service") root_password = server.succeed("acquire-root-password").rstrip() with subtest("Post a video in the browser"): server.succeed(f"post-video-in-browser {root_password}") with subtest("Find video in 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") video = server.succeed("mc find garage --regex '\\.mp4'").rstrip() if video == "": raise Exception("Could not find any .mp4 video stored in Garage") server.succeed(f"mc cat {video} > /video.mp4") garage_hash = server.succeed("identify -quiet -format '%#' /video.mp4") hash = server.succeed("identify -quiet -format '%#' ${testVideo}") if garage_hash != hash: raise Exception("The video stored in Garage does not correspond to the original one.") ''; }