{"id":39677852,"url":"https://github.com/cloudscale-ch/acceptance-tests","last_synced_at":"2026-01-18T09:48:54.059Z","repository":{"id":37798840,"uuid":"358574990","full_name":"cloudscale-ch/acceptance-tests","owner":"cloudscale-ch","description":"Automated testing of the cloudscale.ch IaaS offering","archived":false,"fork":false,"pushed_at":"2025-12-19T12:11:54.000Z","size":315,"stargazers_count":8,"open_issues_count":2,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-12-20T05:08:04.497Z","etag":null,"topics":["acceptance-tests","cloud","cloudscale-ch","switzerland"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudscale-ch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-04-16T11:21:01.000Z","updated_at":"2025-12-11T07:45:26.000Z","dependencies_parsed_at":"2025-11-29T17:10:57.802Z","dependency_job_id":null,"html_url":"https://github.com/cloudscale-ch/acceptance-tests","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cloudscale-ch/acceptance-tests","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudscale-ch%2Facceptance-tests","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudscale-ch%2Facceptance-tests/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudscale-ch%2Facceptance-tests/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudscale-ch%2Facceptance-tests/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudscale-ch","download_url":"https://codeload.github.com/cloudscale-ch/acceptance-tests/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudscale-ch%2Facceptance-tests/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534175,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acceptance-tests","cloud","cloudscale-ch","switzerland"],"created_at":"2026-01-18T09:48:53.448Z","updated_at":"2026-01-18T09:48:54.040Z","avatar_url":"https://github.com/cloudscale-ch.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Acceptance Tests for the cloudscale.ch IaaS Platform\n\nTo ensure that our cloud platform continues to meet our quality standards over time, we use a set of acceptance tests to validate various aspects of our offering:\n\n* Features work as documented.\n* Response times meet our expectations.\n* Regressions are avoided.\n\nThese tests are run regularly against our public infrastructure as well as our internal test environment where upgrades are staged prior to rollout.\n\n\u003ca href=\"https://github.com/cloudscale-ch/acceptance-tests/actions/workflows/acceptance-tests-in-lpg1.yml\"\u003e\u003cimg src=\"https://github.com/cloudscale-ch/acceptance-tests/actions/workflows/acceptance-tests-in-lpg1.yml/badge.svg\" title=\"Result of last acceptance test run in LPG1\"\u003e\u003c/a\u003e \u003ca href=\"https://github.com/cloudscale-ch/acceptance-tests/actions/workflows/acceptance-tests-in-rma1.yml\"\u003e\u003cimg src=\"https://github.com/cloudscale-ch/acceptance-tests/actions/workflows/acceptance-tests-in-rma1.yml/badge.svg\" title=\"Result of last acceptance test run in RMA1\"\u003e\u003c/a\u003e\n\n## Implemented Tests\n\n| Category                  | Test Name                                                                        | Images   |\n|---------------------------|----------------------------------------------------------------------------------|----------|\n| **API**                   | [test_duplicate_headers](./test_api.py#L17)                                      | -        |\n|                           | [test_invalid_duplicate_headers](./test_api.py#L33)                              | -        |\n|                           | [test_cors_headers](./test_api.py#L55)                                           | -        |\n|                           | [test_project_log](./test_api.py#L80)                                            | -        |\n| **Custom Image**          | [test_custom_image_with_slug](./test_custom_image.py#L11)                        | custom   |\n|                           | [test_custom_image_with_uuid](./test_custom_image.py#L22)                        | custom   |\n|                           | [test_custom_image_with_uefi](./test_custom_image.py#L33)                        | custom   |\n| **Floating IP**           | [test_floating_ip_connectivity](./test_floating_ip.py#L15)                       | default  |\n|                           | [test_multiple_floating_ips](./test_floating_ip.py#L33)                          | default  |\n|                           | [test_floating_ip_stability](./test_floating_ip.py#L55)                          | default  |\n|                           | [test_floating_ip_failover](./test_floating_ip.py#L98)                           | default  |\n|                           | [test_floating_ip_mass_failover](./test_floating_ip.py#L141)                     | default  |\n|                           | [test_floating_network](./test_floating_ip.py#L180)                              | default  |\n| **Load Balancer**         | [test_simple_tcp_load_balancer](./test_load_balancer.py#L25)                     | default  |\n|                           | [test_simple_udp_load_balancer](./test_load_balancer.py#L49)                     | default  |\n|                           | [test_load_balancer_end_to_end](./test_load_balancer.py#L79)                     | default  |\n|                           | [test_multiple_listeners](./test_load_balancer.py#L153)                          | default  |\n|                           | [test_multiple_listeners_multiple_pools](./test_load_balancer.py#L185)           | default  |\n|                           | [test_balancing_algorithm_round_robin](./test_load_balancer.py#L238)             | default  |\n|                           | [test_balancing_algorithm_source_ip](./test_load_balancer.py#L274)               | default  |\n|                           | [test_balancing_algorithm_least_connections](./test_load_balancer.py#L323)       | default  |\n|                           | [test_backend_health_monitors](./test_load_balancer.py#L364)                     | default  |\n|                           | [test_pool_member_change](./test_load_balancer.py#L444)                          | default  |\n|                           | [test_private_load_balancer_frontend](./test_load_balancer.py#L536)              | default  |\n|                           | [test_floating_ip](./test_load_balancer.py#L580)                                 | default  |\n|                           | [test_floating_ip_reassign](./test_load_balancer.py#L614)                        | default  |\n|                           | [test_frontend_allowed_cidr](./test_load_balancer.py#L737)                       | default  |\n|                           | [test_proxy_protocol](./test_load_balancer.py#L812)                              | default  |\n|                           | [test_ping](./test_load_balancer.py#L855)                                        | default  |\n| **Nested Virtualization** | [test_virtualization_support](./test_nested_virtualization.py#L14)               | default  |\n|                           | [test_run_nested_vm](./test_nested_virtualization.py#L40)                        | default  |\n| **Private Network**       | [test_private_ip_address_on_all_images](./test_private_network.py#L14)           | all      |\n|                           | [test_private_network_connectivity_on_all_images](./test_private_network.py#L35) | all      |\n|                           | [test_multiple_private_network_interfaces](./test_private_network.py#L88)        | default  |\n|                           | [test_no_private_network_port_security](./test_private_network.py#L145)          | default  |\n|                           | [test_private_network_without_dhcp](./test_private_network.py#L242)              | default  |\n|                           | [test_private_network_mtu](./test_private_network.py#L288)                       | default  |\n|                           | [test_private_network_only_on_all_images](./test_private_network.py#L349)        | all      |\n|                           | [test_private_network_attach_later](./test_private_network.py#L371)              | default  |\n|                           | [test_private_network_dhcp_dns_replies](./test_private_network.py#L408)          | default  |\n| **Public Network**        | [test_public_ip_address_on_all_images](./test_public_network.py#L22)             | all      |\n|                           | [test_public_network_connectivity_on_all_images](./test_public_network.py#L51)   | all      |\n|                           | [test_public_network_mtu](./test_public_network.py#L70)                          | default  |\n|                           | [test_public_network_port_security](./test_public_network.py#L102)               | default  |\n|                           | [test_public_network_ipv4_only_on_all_images](./test_public_network.py#L186)     | all      |\n|                           | [test_reverse_ptr_record_of_server](./test_public_network.py#L207)               | default  |\n|                           | [test_reverse_ptr_record_of_floating_ip](./test_public_network.py#L231)          | default  |\n| **Server**                | [test_change_flavor_from_flex_to_flex](./test_server.py#L21)                     | default  |\n|                           | [test_change_flavor_from_flex_to_plus](./test_server.py#L43)                     | default  |\n|                           | [test_change_flavor_from_plus_to_flex](./test_server.py#L65)                     | default  |\n|                           | [test_change_flavor_from_plus_to_plus](./test_server.py#L87)                     | default  |\n|                           | [test_hostname](./test_server.py#L109)                                           | default  |\n|                           | [test_rename_server](./test_server.py#L127)                                      | default  |\n|                           | [test_reboot_server](./test_server.py#L151)                                      | default  |\n|                           | [test_stop_and_start_server](./test_server.py#L179)                              | default  |\n|                           | [test_rename_server_group](./test_server.py#L208)                                | default  |\n|                           | [test_no_cpu_steal_on_plus_flavor](./test_server.py#L218)                        | default  |\n|                           | [test_random_number_generator](./test_server.py#L250)                            | default  |\n|                           | [test_metadata_on_all_images](./test_server.py#L265)                             | all      |\n|                           | [test_cloud_init_password_on_all_images](./test_server.py#L292)                  | all      |\n| **Volume**                | [test_attach_and_detach_volume_on_all_images](./test_volume.py#L23)              | all      |\n|                           | [test_expand_volume_online_on_all_images](./test_volume.py#L58)                  | all      |\n|                           | [test_expand_filesystem_online_on_common_images](./test_volume.py#L83)           | common   |\n|                           | [test_expand_filesystem_on_boot_on_common_images](./test_volume.py#L125)         | common   |\n|                           | [test_maximum_number_of_volumes](./test_volume.py#L153)                          | default  |\n|                           | [test_snapshot_volume_attached](./test_volume.py#L184)                           | default  |\n|                           | [test_snapshot_volume_detached](./test_volume.py#L251)                           | default  |\n|                           | [test_snapshot_root_volume](./test_volume.py#L324)                               | default  |\n|                           | [test_snapshots_in_multiple_steps](./test_volume.py#L384)                        | default  |\n|                           | [test_volume_from_snapshot](./test_volume.py#L480)                               | default  |\n|                           | [test_volume_from_root_snapshot](./test_volume.py#L524)                          | default  |\n\n## Warning\n\n\u003e ⚠️ Running these tests yourself may incur unexpected costs and may result in data loss if run against a production account with live systems. Therefore, we strongly advise you to use a separate account for these tests.\n\n## Installation\n\n\u003e ℹ︎ Note that you need at least Python 3.6.\n\nTo install the tests, you have to clone the repository:\n\n```console\ngit clone git@github.com:cloudscale-ch/acceptance-tests.git\n```\n\nNow, every time you want to run the tests in a new shell, use the following command first:\n\n```console\nsource acceptance-tests/pre-flight\n```\n\nYou will be automatically switched to the acceptance-tests directory, ready to run the tests as outlined below.\n\n## Running Tests\n\nTo run all tests, run py.test as follows:\n\n```console\npy.test .\n```\n\n### Running Individual Tests\n\nTo only run a specific test, run py.test as follows:\n\n```console\npy.test . -k \u003ctest-name\u003e\n```\n\n### Running Tests Against a Specific Image\n\nBy default, all tests are run against the default image, most tests are run against a set of common images, and some tests are run against all images provided by cloudscale.ch.\n\nTo run all tests against a specific image, use this image as the default:\n\n```console\npy.test --default-image ubuntu-20.04 --default-image-only\n```\n\nNote that our default image is Debian 10. If you pick a different default image your results may differ.\n\n### Running Tests Against a Custom Image\n\nCustom images can be used as the default image by specifying their slug, along with a username that can be used to connect via SSH. Note that custom images are less likely to pass all tests without prior modification, as the acceptance tests mainly focus on our common images.\n\n```console\npy.test --default-image custom:alpine --default-image-only --username alpine\n```\n\n### Running Tests Against a Specific Zone\n\nBy default, tests are run against a randomly selected zone.\n\nAlternatively, you can specify the zone to run the tests against:\n\n```console\npy.test --zone rma1\npy.test --zone lpg1\n```\n\n### Connect to Test Hosts\n\nDuring test development, it can be useful to manually connect to hosts created by the tests. In this case it is necessary to explicitly specify your own SSH key, since tests connect to hosts using temporary SSH keys only:\n\n```console\npy.test --ssh-key ~/.ssh/id_rsa.pub\n```\n\n## Running a Test Multiple Times\n\nSometimes it is useful to run a specific test multiple times in a row:\n\n```console\npy.test --count=10 test_floating_ip.py\n```\n\n## Events Log\n\nDuring execution, the acceptance tests generate a detailed log in the `events` directory (one file per test-run). Each line in such an event log is a structured JSON object.\n\nUsing a custom command, you can create a human-readable output of this log:\n\n```console\ninvoke pretty-print --file events/\u003cfile\u003e\n```\n\nYou can include filters as well:\n\n```console\ninvoke pretty-print --file events/\u003cfile\u003e --regex outcome=failed\n```\n\nOr, during test execution, you can follow the log in a separate terminal window while it is being written. This will tail all the event logs that are currently being written. No need to specify a single file.\n\n```console\ninvoke tail\n```\n\n## Cleanup\n\nDuring normal operation, all resources created by the acceptance tests are automatically cleaned up. However, if the process receives a `SIGKILL` signal, or if it crashes, there may be resources left afterwards.\n\nIf you want to be sure, you can clean up all resources created by any acceptance test using the cleanup command:\n\n```console\ninvoke cleanup\n```\n\nAll resources created by acceptance tests receive a unique tag, based on a securely hashed version of the API token, so using this command should be reasonably safe. However, we still strongly advise you to use a separate account for these tests as a precaution.\n\n## Developing New Tests\n\n### Create a New Branch\n\nIn order to review tests and to be able to develop multiple tests in parallel, they should be developed in a separate Git branch:\n\n```console\ngit branch \u003cyour_branch_name\u003e\n```\n\n### Writing Tests to be Run Against Specific Images\n\nIf you write a test with the `image` fixture, it will be called with the default image. This default image can be changed using the `--default-image` command line parameter.\n\nIf you want to ensure that a test runs against all common images, use the `image` fixture and include `all_images` in the name of your test:\n\n```python\ndef test_all_images_have_a_hosts_file(create_server, image):\n    server = create_server(image=image)\n```\n\nIf you use `common_images` in the name of your test, only common images will be tested:\n\n```python\ndef test_common_images_have_a_hosts_file(create_server, image):\n    server = create_server(image=image)\n```\n\n### Update README tests table\n\nIf you add new tests, update the tests table:\n\n```console\ninvoke implemented-tests-table\n```\n\n### Commit Your Test\n\n```console\ngit add \u003cnew_or_changed_files\u003e\ngit commit\n```\n\n### Push Your Branch and Create a Pull Request\n\n```console\ngit push origin \u003cyour_branch_name\u003e\n```\n\nTo create a pull request follow the link that will be displayed upon pushing a branch.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudscale-ch%2Facceptance-tests","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudscale-ch%2Facceptance-tests","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudscale-ch%2Facceptance-tests/lists"}