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