interactive test is working
This commit is contained in:
		
							parent
							
								
									693e21b1a8
								
							
						
					
					
						commit
						dab12bc2b8
					
				
					 4 changed files with 110 additions and 35 deletions
				
			
		
							
								
								
									
										33
									
								
								garage.nix
									
										
									
									
									
								
							
							
						
						
									
										33
									
								
								garage.nix
									
										
									
									
									
								
							|  | @ -38,7 +38,8 @@ let | |||
| 
 | ||||
|       ${optionalString corsRules.enable '' | ||||
|         garage bucket allow --read --write --owner ${bucketArg} --key tmp | ||||
|         aws --endpoint http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} | ||||
|         # TODO: endpoin-url should not be hard-coded | ||||
|         aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} | ||||
|         garage bucket deny --read --write --owner ${bucketArg} --key tmp | ||||
|       ''} | ||||
|     ''; | ||||
|  | @ -124,21 +125,23 @@ in { | |||
|   }; | ||||
| 
 | ||||
|   config = { | ||||
|     virtualisation.diskSize = 2048; | ||||
|     virtualisation.forwardPorts = [ | ||||
|       { | ||||
|         from = "host"; | ||||
|         host.port = 3901; | ||||
|         guest.port = 3901; | ||||
|       } | ||||
|       { | ||||
|         from = "host"; | ||||
|         host.port = 3902; | ||||
|         guest.port = 3902; | ||||
|       } | ||||
|     ]; | ||||
|     virtualisation.vmVariant = { config, ... }: { | ||||
|       virtualisation.diskSize = 2048; | ||||
|       virtualisation.forwardPorts = [ | ||||
|         { | ||||
|           from = "host"; | ||||
|           host.port = 3901; | ||||
|           guest.port = 3901; | ||||
|         } | ||||
|         { | ||||
|           from = "host"; | ||||
|           host.port = 3902; | ||||
|           guest.port = 3902; | ||||
|         } | ||||
|       ]; | ||||
| 
 | ||||
|     environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; | ||||
|       environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; | ||||
|     }; | ||||
| 
 | ||||
|     networking.firewall.allowedTCPPorts = [ 3901 3902 ]; | ||||
|     services.garage = { | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 5.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								tests/green.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/green.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 692 B | 
|  | @ -1,7 +1,37 @@ | |||
| { pkgs, self }: | ||||
| let | ||||
|   lib = pkgs.lib; | ||||
|   # python = pkgs.python310.withPackages (ps: with ps; [ requests aiokafka ]); | ||||
|   rebuildableTest = import ./rebuildableTest.nix pkgs; | ||||
|   seleniumScript = pkgs.writers.writePython3Bin "selenium-script" | ||||
|     { | ||||
|       libraries = with pkgs.python3Packages; [ selenium ]; | ||||
|     } '' | ||||
|     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 | ||||
| 
 | ||||
|     print(1) | ||||
| 
 | ||||
|     options = Options() | ||||
|     options.add_argument("--headless") | ||||
|     # devtools don't show up in headless screenshots | ||||
|     # options.add_argument("-devtools") | ||||
|     service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}")  # noqa: E501 | ||||
| 
 | ||||
|     driver = webdriver.Firefox(options=options, service=service) | ||||
|     driver.get("http://mastodon.localhost:55001/public/local") | ||||
| 
 | ||||
|     # wait until the statuses load | ||||
|     WebDriverWait(driver, 90).until( | ||||
|         lambda x: x.find_element(By.CLASS_NAME, "status")) | ||||
| 
 | ||||
|     # XXX: how do I save this to the derivation output? | ||||
|     driver.save_screenshot("/mastodon-screenshot.png") | ||||
| 
 | ||||
|     driver.close() | ||||
|   ''; | ||||
| in | ||||
| rebuildableTest { | ||||
|   name = "test-mastodon-garage"; | ||||
|  | @ -10,13 +40,29 @@ rebuildableTest { | |||
|   # skipTypeCheck = true; | ||||
| 
 | ||||
|   nodes = { | ||||
|     server = { | ||||
|     server = {config, ...}: { | ||||
|       virtualisation.memorySize = lib.mkVMOverride 4096; | ||||
|       imports = [ self.nixosModules.garage self.nixosModules.mastodon ]; | ||||
|       environment.systemPackages = with pkgs; [ toot ]; | ||||
|       # TODO: pair down | ||||
|       environment.systemPackages = with pkgs; [ | ||||
|         python3 | ||||
|         firefox-unwrapped | ||||
|         geckodriver | ||||
|         toot | ||||
|         xh | ||||
|         seleniumScript | ||||
|         helix | ||||
|         imagemagick | ||||
|       ]; | ||||
|       environment.variables = { | ||||
|         POST_MEDIA = ./green.png; | ||||
|         AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.mastodon.id; | ||||
|         AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.mastodon.secret; | ||||
|       }; | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|   testScript = '' | ||||
|   testScript = {nodes, ...}: '' | ||||
|     import re | ||||
|     import time | ||||
| 
 | ||||
|  | @ -26,33 +72,59 @@ rebuildableTest { | |||
|       server.wait_for_unit("mastodon-web.service") | ||||
| 
 | ||||
|     # make sure mastodon is fully up and running before we interact with it | ||||
|     # TODO: is there a way to test for this? | ||||
|     time.sleep(180) | ||||
| 
 | ||||
|     with subtest("Account creation"): | ||||
|       account_creation_output = server.succeed("mastodon-tootctl accounts create test --email test@test.com --confirmed --approve") | ||||
|       password_re = re.compile('New password: (.*)') | ||||
|       password_match = password_re.match(account_creation_output) | ||||
|       password_match = re.match('.*New password: ([^\n]*).*', account_creation_output, re.S) | ||||
|       assert password_match is not None | ||||
|       password = password_match.groups()[0] | ||||
|       password = password_match.group(1) | ||||
| 
 | ||||
|     with subtest("TTY Login"): | ||||
|       server.wait_until_tty_matches("1", "login: ") | ||||
|       server.send_chars("root\n"); | ||||
| 
 | ||||
|     with subtest("Log in with toot"): | ||||
|       # toot doesn't provide a way to just specify our login details as arguments, so we have to pretend we're typing them in at the prompt | ||||
|       server.send_chars("toot login_ctl\n") | ||||
|       time.sleep(0.2) | ||||
|       # Enter instance URL | ||||
|       server.send_chars("http://mastodon.localhost:55001\n") | ||||
|       time.sleep(0.2) | ||||
|       # Email | ||||
|       server.send_chars("test@test.com\n") | ||||
|       time.sleep(0.2) | ||||
|       # Password | ||||
|       server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n") | ||||
|       server.wait_until_tty_matches("1", "Password: ") | ||||
|       server.send_chars(password + "\n") | ||||
|       server.wait_until_tty_matches("1", "Successfully logged in.") | ||||
| 
 | ||||
|     with subtest("Post an image"): | ||||
|       server.succeed("toot post --media ${./fediversity.png}") | ||||
|     with subtest("post text"): | ||||
|       server.succeed("echo 'hello mastodon' | toot post") | ||||
| 
 | ||||
|       # TODO: I don't think there's a good way to test for whether the image visually shows up.  | ||||
|       # we can test for CORS headers using curl / xh | ||||
|       # or **maybe** somehow read the javascript console? | ||||
|     with subtest("post image"): | ||||
|       server.succeed("toot post --media $POST_MEDIA") | ||||
| 
 | ||||
|     with subtest("access garage"): | ||||
|       server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") | ||||
|       server.succeed("mc ls garage/mastodon") | ||||
| 
 | ||||
|     with subtest("access image in garage"): | ||||
|       image = server.succeed("mc find garage --regex original") | ||||
|       image = image.rstrip() | ||||
|       assert image != "" | ||||
|       server.succeed(f"mc cat {image} >/garage-image.webp") | ||||
|       garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp") | ||||
|       image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA") | ||||
|       assert garage_image_hash == image_hash | ||||
| 
 | ||||
|     with subtest("Content security policy allows garage images"): | ||||
|       headers = server.succeed("xh -h http://masstodon.localhost:55001/public/local") | ||||
|       csp_match = re.match('^Content-Security-Policy: (.*)$', headers, re.M) | ||||
|       assert csp_match is not None | ||||
|       csp = csp_match.group(1) | ||||
|       # the content security policy should include the garage server | ||||
|       garage_csp = re.match(".*web\.garage\.localhost:3902.*", csp) | ||||
|       assert garage_csp is not None | ||||
| 
 | ||||
|     with subtest("image displays"): | ||||
|       server.succeed("selenium-script") | ||||
|       server.copy_from_vm("/mastodon-screenshot.png", "") | ||||
|       displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:") | ||||
|       # check that the green image displayed somewhere | ||||
|       re.match(".*#00FF00.*", displayed_colors, re.S) | ||||
|   ''; | ||||
| } | ||||
|  |  | |||
		Reference in a new issue
	
	 Taeer Bar-Yam
						Taeer Bar-Yam