From 1864e20a8c0bd1ab7674bbb5d8c759ea3f3d064e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 17:15:11 +0100 Subject: [PATCH] Add Peertube test --- services/flake-part.nix | 1 + services/tests/green.mp4 | Bin 0 -> 1884 bytes services/tests/peertube.nix | 233 ++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 services/tests/green.mp4 create mode 100644 services/tests/peertube.nix diff --git a/services/flake-part.nix b/services/flake-part.nix index 5563878..485e261 100644 --- a/services/flake-part.nix +++ b/services/flake-part.nix @@ -9,6 +9,7 @@ checks = { mastodon = import ./tests/mastodon.nix { inherit self pkgs; }; pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit self pkgs; }; + peertube = import ./tests/peertube.nix { inherit self pkgs; }; }; }; } diff --git a/services/tests/green.mp4 b/services/tests/green.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..82ce4c110052a140fa8ec1044ca0ef0258d15e6a GIT binary patch literal 1884 zcmah~U1%It6uz5ijj?D7DHI#ws)4>_H#3toX@i5VNpOV=2*H55GTz302Ya4U3yvm&B98%`ucipEpTNLinx|!&1uS8^HmgrlcXw0;(UQS!WNFH^Gtf?40oj+CQi%L z7qkV9i&%u6T$);YWB~0HS8bqqp5`5dqPH^F)N2}o(D5i}OV^@|IwGu0 z#7$}HbKLQ>BoY=vb*?fQhCvQ=qJ6@36o9i5nHrWL+_On6O+%m3b?%8=S!v!1Qeu<| zR#GeRyj&`?YH;Oeh{#QYhe^^BJ_c6brt>@u9IT~p(0H5`b2vdH6s;qOmCQl`66|)k znu(6(BvC39#tx21Wg>`?0>n%Z$IC<{a~zg!r5)hF#oV?fT#?c)Y|9QrPLl+gEcUf7 zgQl-+_>xq{Rx?TAEsZHcTe5@Prg2>BK`RPkQ#W|-$XGh7%AD0`nVFy?&1CK)JaeoY z+a-VIm`>*L$i}(SwlFY_nVQB|Xfvi(n*&TKaiK-qoI43au4HP?@*qW#a3N4b`4b7uI@xBlXPeDv}w=U-}E{cPj>t3Uku-O-(Pv;*pB z{la8eziaosCx4{NUf+7SeXe`kbA&5fLM=WxGE!paS>W4&$LbgSN8hdcubmoszPo$z zjpMM5*0;_rADWuK5&1Sn5*u0Gp-9St$oCN$BC^@t-KzdxMt-?`cp&m>k3IZ(lq73l zgwdMsLJ!FB6?~9aScTDVPyRE2dEfYhLSi#hj5iVfbq4cNIJOeNw@S(xVWzr3?cu))hy&Cu6&a#I|TkTS9!l6 z#QBh3OK6h8oTbIUwi|%dNGgXSaI2CcZc2G zg#KWA>Db1`r6n8{yM?-E9%yWysjvh7FF$>C-=%-9U#IA?ROBf%Eri-QzOLRWAH@== zu_L)EDgan+l2Jl%u`#L)Nk86q_PBSH_K}`{yr=tP8d6-53;O^3W5ma;EBF(b+aUv_ zKli~NUV`sz;VC=VhrR~96OB0iMiUO&2FKW&lfX|x`RN*oRaXhg9!fh;mHih8#GzpT literal 0 HcmV?d00001 diff --git a/services/tests/peertube.nix b/services/tests/peertube.nix new file mode 100644 index 0000000..6a5161b --- /dev/null +++ b/services/tests/peertube.nix @@ -0,0 +1,233 @@ +## 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.") + ''; +}