at 25.11-pre 9.5 kB view raw
1import ./make-test-python.nix ( 2 { pkgs, lib, ... }: 3 { 4 name = "castopod"; 5 meta = with lib.maintainers; { 6 maintainers = [ alexoundos ]; 7 }; 8 9 nodes.castopod = 10 { nodes, ... }: 11 { 12 # otherwise 500 MiB file upload fails! 13 virtualisation.diskSize = 512 + 3 * 512; 14 15 networking.firewall.allowedTCPPorts = [ 80 ]; 16 networking.extraHosts = lib.strings.concatStringsSep "\n" ( 17 lib.attrsets.mapAttrsToList ( 18 name: _: "127.0.0.1 ${name}" 19 ) nodes.castopod.services.nginx.virtualHosts 20 ); 21 22 services.castopod = { 23 enable = true; 24 database.createLocally = true; 25 localDomain = "castopod.example.com"; 26 maxUploadSize = "512M"; 27 }; 28 }; 29 30 nodes.client = 31 { 32 nodes, 33 pkgs, 34 lib, 35 ... 36 }: 37 let 38 domain = nodes.castopod.services.castopod.localDomain; 39 40 getIP = node: (builtins.head node.networking.interfaces.eth1.ipv4.addresses).address; 41 42 targetPodcastSize = 500 * 1024 * 1024; 43 lameMp3Bitrate = 348300; 44 lameMp3FileAdjust = -800; 45 targetPodcastDuration = toString ((targetPodcastSize + lameMp3FileAdjust) / (lameMp3Bitrate / 8)); 46 bannerWidth = 3000; 47 banner = pkgs.runCommand "gen-castopod-cover.jpg" { } '' 48 ${pkgs.imagemagick}/bin/magick ` 49 `-background green -bordercolor white -gravity northwest xc:black ` 50 `-duplicate 99 ` 51 `-seed 1 -resize "%[fx:rand()*72+24]" ` 52 `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 16x36 ` 53 `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "150x50!" ` 54 `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append ` 55 `-resize ${toString bannerWidth} -quality 1 $out 56 ''; 57 58 coverWidth = toString 3000; 59 cover = pkgs.runCommand "gen-castopod-banner.jpg" { } '' 60 ${pkgs.imagemagick}/bin/magick ` 61 `-background white -bordercolor white -gravity northwest xc:black ` 62 `-duplicate 99 ` 63 `-seed 1 -resize "%[fx:rand()*72+24]" ` 64 `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 36x36 ` 65 `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "144x144!" ` 66 `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append ` 67 `-resize ${coverWidth} -quality 1 $out 68 ''; 69 in 70 { 71 networking.extraHosts = lib.strings.concatStringsSep "\n" ( 72 lib.attrsets.mapAttrsToList ( 73 name: _: "${getIP nodes.castopod} ${name}" 74 ) nodes.castopod.services.nginx.virtualHosts 75 ); 76 77 environment.systemPackages = 78 let 79 username = "admin"; 80 email = "admin@${domain}"; 81 password = "Abcd1234"; 82 podcastTitle = "Some Title"; 83 episodeTitle = "Episode Title"; 84 browser-test = 85 pkgs.writers.writePython3Bin "browser-test" 86 { 87 libraries = [ pkgs.python3Packages.selenium ]; 88 flakeIgnore = [ 89 "E124" 90 "E501" 91 ]; 92 } 93 '' 94 from selenium.webdriver.common.by import By 95 from selenium.webdriver import Firefox 96 from selenium.webdriver.firefox.options import Options 97 from selenium.webdriver.firefox.service import Service 98 from selenium.webdriver.support.ui import WebDriverWait 99 from selenium.webdriver.support import expected_conditions as EC 100 from subprocess import STDOUT 101 import logging 102 103 selenium_logger = logging.getLogger("selenium") 104 selenium_logger.setLevel(logging.DEBUG) 105 selenium_logger.addHandler(logging.StreamHandler()) 106 107 options = Options() 108 options.add_argument('--headless') 109 service = Service(log_output=STDOUT) 110 driver = Firefox(options=options, service=service) 111 driver = Firefox(options=options) 112 driver.implicitly_wait(30) 113 driver.set_page_load_timeout(60) 114 115 # install ########################################################## 116 117 driver.get('http://${domain}/cp-install') 118 119 wait = WebDriverWait(driver, 20) 120 121 wait.until(EC.title_contains("installer")) 122 123 driver.find_element(By.CSS_SELECTOR, '#username').send_keys( 124 '${username}' 125 ) 126 driver.find_element(By.CSS_SELECTOR, '#email').send_keys( 127 '${email}' 128 ) 129 driver.find_element(By.CSS_SELECTOR, '#password').send_keys( 130 '${password}' 131 ) 132 driver.find_element(By.XPATH, 133 "//button[contains(., 'Finish install')]" 134 ).click() 135 136 wait.until(EC.title_contains("Auth")) 137 138 driver.find_element(By.CSS_SELECTOR, '#email').send_keys( 139 '${email}' 140 ) 141 driver.find_element(By.CSS_SELECTOR, '#password').send_keys( 142 '${password}' 143 ) 144 driver.find_element(By.XPATH, 145 "//button[contains(., 'Login')]" 146 ).click() 147 148 wait.until(EC.title_contains("Admin dashboard")) 149 150 # create podcast ################################################### 151 152 driver.get('http://${domain}/admin/podcasts/new') 153 154 wait.until(EC.title_contains("Create podcast")) 155 156 driver.find_element(By.CSS_SELECTOR, '#cover').send_keys( 157 '${cover}' 158 ) 159 driver.find_element(By.CSS_SELECTOR, '#banner').send_keys( 160 '${banner}' 161 ) 162 driver.find_element(By.CSS_SELECTOR, '#title').send_keys( 163 '${podcastTitle}' 164 ) 165 driver.find_element(By.CSS_SELECTOR, '#handle').send_keys( 166 'some_handle' 167 ) 168 driver.find_element(By.CSS_SELECTOR, '#description').send_keys( 169 'Some description' 170 ) 171 driver.find_element(By.CSS_SELECTOR, '#owner_name').send_keys( 172 'Owner Name' 173 ) 174 driver.find_element(By.CSS_SELECTOR, '#owner_email').send_keys( 175 'owner@email.xyz' 176 ) 177 driver.find_element(By.XPATH, 178 "//button[contains(., 'Create podcast')]" 179 ).click() 180 181 wait.until(EC.title_contains("${podcastTitle}")) 182 183 driver.find_element(By.XPATH, 184 "//span[contains(., 'Add an episode')]" 185 ).click() 186 187 wait.until(EC.title_contains("Add an episode")) 188 189 # upload podcast ################################################### 190 191 driver.find_element(By.CSS_SELECTOR, '#audio_file').send_keys( 192 '/tmp/podcast.mp3' 193 ) 194 driver.find_element(By.CSS_SELECTOR, '#cover').send_keys( 195 '${cover}' 196 ) 197 driver.find_element(By.CSS_SELECTOR, '#description').send_keys( 198 'Episode description' 199 ) 200 driver.find_element(By.CSS_SELECTOR, '#title').send_keys( 201 '${episodeTitle}' 202 ) 203 driver.find_element(By.XPATH, 204 "//button[contains(., 'Create episode')]" 205 ).click() 206 207 wait.until(EC.title_contains("${episodeTitle}")) 208 209 driver.close() 210 driver.quit() 211 ''; 212 in 213 [ 214 pkgs.firefox-unwrapped 215 pkgs.geckodriver 216 browser-test 217 (pkgs.writeShellApplication { 218 name = "build-mp3"; 219 runtimeInputs = with pkgs; [ 220 sox 221 lame 222 ]; 223 text = '' 224 out=/tmp/podcast.mp3 225 sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 ` 226 `| lame --noreplaygain --cbr -q 9 -b 320 - $out 227 FILESIZE="$(stat -c%s $out)" 228 [ "$FILESIZE" -gt 0 ] 229 [ "$FILESIZE" -le "${toString targetPodcastSize}" ] 230 ''; 231 }) 232 ]; 233 }; 234 235 testScript = '' 236 start_all() 237 castopod.wait_for_unit("castopod-setup.service") 238 castopod.wait_for_file("/run/phpfpm/castopod.sock") 239 castopod.wait_for_unit("nginx.service") 240 castopod.wait_for_open_port(80) 241 castopod.wait_until_succeeds("curl -sS -f http://castopod.example.com") 242 243 client.succeed("build-mp3") 244 245 with subtest("Create superadmin, log in, create and upload a podcast"): 246 client.succeed(\ 247 "PYTHONUNBUFFERED=1 systemd-cat -t browser-test browser-test") 248 ''; 249 } 250)