forked from Fediversity/Fediversity
Add a test for Peertube (#45)
This commit is contained in:
commit
e8b7c23366
|
@ -21,3 +21,9 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: cd website && nix-build -A tests
|
- run: cd website && nix-build -A tests
|
||||||
- run: cd website && nix-build -A build
|
- run: cd website && nix-build -A build
|
||||||
|
|
||||||
|
check-peertube:
|
||||||
|
runs-on: native
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: nix build .#checks.x86_64-linux.peertube -L
|
||||||
|
|
|
@ -836,11 +836,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_6": {
|
"nixpkgs_6": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1732350895,
|
"lastModified": 1734323986,
|
||||||
"narHash": "sha256-GcOQbOgmwlsRhpLGSwZJwLbo3pu9ochMETuRSS1xpz4=",
|
"narHash": "sha256-m/lh6hYMIWDYHCAsn81CDAiXoT3gmxXI9J987W5tZrE=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "0c582677378f2d9ffcb01490af2f2c678dcb29d3",
|
"rev": "394571358ce82dff7411395829aa6a3aad45b907",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -11,6 +11,10 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
80
|
80
|
||||||
443
|
443
|
||||||
|
|
||||||
|
## For Live streaming and Live streaming when RTMPS is enabled.
|
||||||
|
1935
|
||||||
|
1936
|
||||||
];
|
];
|
||||||
|
|
||||||
services.garage = {
|
services.garage = {
|
||||||
|
@ -70,6 +74,8 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
endpoint = config.fediversity.internal.garage.api.url;
|
endpoint = config.fediversity.internal.garage.api.url;
|
||||||
region = "garage";
|
region = "garage";
|
||||||
|
upload_acl.public = null; # Garage does not support ACL
|
||||||
|
upload_acl.private = null; # Garage does not support ACL
|
||||||
|
|
||||||
# not supported by garage
|
# not supported by garage
|
||||||
# SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube
|
# SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube
|
||||||
|
@ -101,7 +107,11 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
|
||||||
|
|
||||||
## Proxying through Nginx
|
## Proxying through Nginx
|
||||||
|
|
||||||
services.peertube.configureNginx = true;
|
services.peertube = {
|
||||||
|
configureNginx = true;
|
||||||
|
listenWeb = 443;
|
||||||
|
enableWebHttps = true;
|
||||||
|
};
|
||||||
services.nginx.virtualHosts.${config.services.peertube.localDomain} = {
|
services.nginx.virtualHosts.${config.services.peertube.localDomain} = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
checks = {
|
checks = {
|
||||||
mastodon = import ./tests/mastodon.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; };
|
||||||
|
peertube = import ./tests/peertube.nix { inherit self pkgs; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
BIN
services/tests/green.mp4
Normal file
BIN
services/tests/green.mp4
Normal file
Binary file not shown.
|
@ -43,7 +43,7 @@ let
|
||||||
in
|
in
|
||||||
|
|
||||||
pkgs.nixosTest {
|
pkgs.nixosTest {
|
||||||
name = "test-mastodon-garage";
|
name = "mastodon";
|
||||||
|
|
||||||
nodes = {
|
nodes = {
|
||||||
server =
|
server =
|
||||||
|
|
233
services/tests/peertube.nix
Normal file
233
services/tests/peertube.nix
Normal file
|
@ -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.")
|
||||||
|
'';
|
||||||
|
}
|
|
@ -1,11 +1,23 @@
|
||||||
{ modulesPath, ... }:
|
{
|
||||||
|
modulesPath,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ];
|
imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ];
|
||||||
|
|
||||||
|
fediversity = {
|
||||||
|
enable = true;
|
||||||
|
domain = "localhost";
|
||||||
|
peertube.enable = true;
|
||||||
|
|
||||||
|
temp.peertubeSecretsFile = pkgs.writeText "secret" ''
|
||||||
|
574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
services.peertube = {
|
services.peertube = {
|
||||||
enableWebHttps = false;
|
|
||||||
settings = {
|
settings = {
|
||||||
listen.hostname = "0.0.0.0";
|
listen.hostname = "0.0.0.0";
|
||||||
instance.name = "PeerTube Test VM";
|
instance.name = "PeerTube Test VM";
|
||||||
|
|
Loading…
Reference in a new issue