Skip to content

Commit

Permalink
style: Better steps
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Christie committed Jan 9, 2025
1 parent 1cccf9a commit 2624fe2
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 46 deletions.
17 changes: 9 additions & 8 deletions behaviour/features/api-errors-for-anonymous-users.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ Feature: Key GET methods need authentication

For all the tests here we need to start with a clean (empty) Fragalysis Stack.
We do this by ensuring that a new stack exists (creating one if necessary).
Stacks have a 'name', and we need specify an image tag like 'latest'.
Stacks have a 'name' that is obtained from the environment variable
BEHAVIOUR_STACK_USERNAME. We just need to specify an image tag like 'latest'.
The stack should also be functional, by responding correctly on the landing page.

Given an empty behaviour stack tagged latest
Given an empty stack using the image tag latest
Then the landing page response should be OK

Scenario Template: Some REST GET methods should return 'Not Authorized'

Here we do not login to the stack and therefore, for an un-authenticated user,
the chosen methods are expected to return 'Not Authorized' for a GET.

Given I do not login to the behaviour stack
When I do a GET at <method>
Then the API response should be FORBIDDEN
Given I do not login
When I do a GET request at <method>
Then the response should be FORBIDDEN

Examples: Restricted GET endpoints
| method |
Expand All @@ -34,9 +35,9 @@ Feature: Key GET methods need authentication
Here we do not login to the stack and therefore, for an un-authenticated user,
the chosen methods are expected to return 'Not Allowed'.

Given I do not login to the behaviour stack
When I do a GET at <method>
Then the API response should be METHOD_NOT_ALLOWED
Given I do not login
When I do a GET request at <method>
Then the response should be METHOD_NOT_ALLOWED

Examples: Endpoints without a GET
| method |
Expand Down
14 changes: 7 additions & 7 deletions behaviour/features/api-for-anonymous-users.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Feature: Empty stack public API operations
Stacks have a 'name', and we need specify an image tag like 'latest'.
The stack should also be functional, by responding correctly on the landing page.

Given an empty behaviour stack tagged latest
Given an empty stack using the image tag latest
Then the landing page response should be OK

Scenario Template: Check the main public API methods
Expand All @@ -22,9 +22,9 @@ Feature: Empty stack public API operations
An empty stack has no data so everything tested here should return
successfully and with an empty list of objects.

Given I do not login to the behaviour stack
When I do a GET at <method>
Then the API response should be OK
Given I do not login
When I do a GET request at <method>
Then the response should be OK
And the length of the list in the response should be 0

Examples: Public GET methods
Expand Down Expand Up @@ -79,7 +79,7 @@ Feature: Empty stack public API operations
A small number of methods return objects, even on an empty stack.
Here we check that an unauthenticated user sees this 'public' data.

Given I do not login to the behaviour stack
When I do a GET at /api/tag_category
Then the API response should be OK
Given I do not login
When I do a GET request at /api/tag_category
Then the response should be OK
And the length of the list in the response should be 9
22 changes: 11 additions & 11 deletions behaviour/features/public-target-loader.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Feature: Verify good Targets can be loaded against the public TAS
The variables it is expected to define will be passed to the
corresponding AWX Job Template when it's launched.

Given an empty behaviour stack tagged latest
Given an empty stack using the image tag latest
Then the landing page response should be OK

Scenario Template: Load public targets
Expand All @@ -28,19 +28,19 @@ Feature: Verify good Targets can be loaded against the public TAS
We introduce a timeout to allow for the upload to complete,
which is typically twice the expected processing time (after upload).

Given I can login to the behaviour stack
Given I can login
And I can access the fragalysis-stack-xchem-data bucket
When I get the TGZ encoded file <tgz> from the bucket
And load it against target access string lb18145-1
Then the API response should be ACCEPTED
And the API response should contain a task status endpoint
And the task status should have a value of SUCCESS within <timeout> minutes
When I do a GET at /api/target_experiment_uploads
Then the response should be ACCEPTED
And the response should contain a task status endpoint
And the task status should have a value of SUCCESS within <upload timeout> minutes
When I do a GET request at /api/target_experiment_uploads
Then the length of the list in the response should be 1
When I do a GET at /api/targets?title=<target>
When I do a GET request at /api/targets?title=<target>
Then the length of the list in the response should be 1

Examples: Public targets
| tgz | target | timeout |
| lb32627-66_v2.2_upload_1_2024-12_09 | A71EV2A | 7 |
| lb32633-6_v2.2_upload_1_2024-11-22 | CHIKV_Mac | 16 |
Examples: Experiment files and Targets
| tgz | target | upload timeout |
| lb32627-66_v2.2_upload_1_2024-12_09 | A71EV2A | 7 |
| lb32633-6_v2.2_upload_1_2024-11-22 | CHIKV_Mac | 16 |
11 changes: 10 additions & 1 deletion behaviour/features/steps/browser_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

from playwright.sync_api import expect, sync_playwright

# Fragalysis Stack hostname (i.e. example.com) and credentials for a CAS user
# Fragalysis Stack name (used to form the stack's URL), and credentials for a CAS user.
# The username is also used to form the stack's URL by the AWX Job Template.
_STACK_NAME: Optional[str] = os.environ.get("BEHAVIOUR_STACK_NAME")
_STACK_USERNAME: Optional[str] = os.environ.get("BEHAVIOUR_STACK_USERNAME")
_STACK_PASSWORD: Optional[str] = os.environ.get("BEHAVIOUR_STACK_PASSWORD")
_STACK_CLIENT_ID_SECRET: Optional[str] = os.environ.get(
Expand All @@ -26,6 +28,13 @@ def get_stack_client_id_secret() -> str:
return _STACK_CLIENT_ID_SECRET


def get_stack_name() -> str:
"""Returns the configured stack name (lower case)"""
if not _STACK_NAME:
raise ValueError("BEHAVIOUR_STACK_NAME is not set")
return _STACK_NAME.lower()


def login(host_url: str) -> str:
"""Login to a Fragalysis Stack (with assumed CAS authentication)
given a host url(i.e. https://example.com)"""
Expand Down
32 changes: 16 additions & 16 deletions behaviour/features/steps/steps_given.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from awx_utils import get_stack_url, get_stack_username, launch_awx_job_template
from behave import given
from browser_utils import get_stack_client_id_secret, login
from browser_utils import get_stack_client_id_secret, get_stack_name, login
from s3_utils import check_bucket

# To create a stack we need to know the names of templates (in the AWX server)
Expand All @@ -14,8 +14,8 @@
)


@given("an empty {stack_name} stack tagged {image_tag}") # pylint: disable=not-callable
def step_impl(context, stack_name, image_tag) -> None:
@given("an empty stack using the image tag {image_tag}") # pylint: disable=not-callable
def step_impl(context, image_tag) -> None:
"""Wipe any existing stack content and create a new (empty) one.
The user can pass in a JSON-encoded set of extra variables
via the context.text attribute. This appears as a string.
Expand All @@ -26,8 +26,8 @@ def step_impl(context, stack_name, image_tag) -> None:
"""
assert context.failed is False

lower_stack_name = stack_name.lower()
print(f"Creating stack '{lower_stack_name}'...")
stack_name = get_stack_name()
print(f"Creating stack '{stack_name}'...")

# The stack Client ID can be manufactured.
# For developer stacks it looks like this: -
Expand All @@ -37,11 +37,11 @@ def step_impl(context, stack_name, image_tag) -> None:
# "fragalysis-alan-behaviour-xchem-dev"

stack_oidc_rp_client_id: str = (
f"fragalysis-{get_stack_username().lower()}-{lower_stack_name}-xchem-dev"
f"fragalysis-{get_stack_username().lower()}-{stack_name}-xchem-dev"
)

extra_vars: Dict[str, str] = {
"stack_name": lower_stack_name,
"stack_name": stack_name,
"stack_image_tag": image_tag,
"stack_oidc_rp_client_id": stack_oidc_rp_client_id,
"stack_oidc_rp_client_secret": get_stack_client_id_secret(),
Expand All @@ -64,32 +64,32 @@ def step_impl(context, stack_name, image_tag) -> None:
}
launch_awx_job_template(create_jt, extra_vars=extra_vars)

context.stack_name = lower_stack_name
context.stack_name = stack_name
# The URL does not end with '/', so paths will need to start with '/'
context.stack_url = get_stack_url(lower_stack_name)
context.stack_url = get_stack_url(stack_name)

print(f"Created stack '{lower_stack_name}'")
print(f"Created stack '{stack_name}'")


@given("I can login to the {stack_name} stack") # pylint: disable=not-callable
def step_impl(context, stack_name) -> None: # pylint: disable=function-redefined
@given("I can login") # pylint: disable=not-callable
def step_impl(context) -> None: # pylint: disable=function-redefined
"""Sets the context members: -
- stack_name
- session_id"""
assert context.failed is False

context.stack_name = stack_name.lower()
context.stack_name = get_stack_name()
context.session_id = login(get_stack_url(context.stack_name))
assert context.session_id


@given("I do not login to the {stack_name} stack") # pylint: disable=not-callable
def step_impl(context, stack_name) -> None: # pylint: disable=function-redefined
@given("I do not login") # pylint: disable=not-callable
def step_impl(context) -> None: # pylint: disable=function-redefined
"""Sets the context members: -
- stack_name"""
assert context.failed is False

context.stack_name = stack_name.lower()
context.stack_name = get_stack_name()


@given("I can access the {bucket_name} bucket") # pylint: disable=not-callable
Expand Down
4 changes: 2 additions & 2 deletions behaviour/features/steps/steps_then.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def step_impl(context, count) -> None: # pylint: disable=function-redefined
assert context.response_count == count


@then("the API response should be {status_code_name}") # pylint: disable=not-callable
@then("the response should be {status_code_name}") # pylint: disable=not-callable
def step_impl(context, status_code_name) -> None: # pylint: disable=function-redefined
"""Relies on context members: -
- status_code"""
Expand All @@ -59,7 +59,7 @@ def step_impl(context, status_code_name) -> None: # pylint: disable=function-re


@then( # pylint: disable=not-callable
"the API response should contain a task status endpoint"
"the response should contain a task status endpoint"
)
def step_impl(context) -> None: # pylint: disable=function-redefined
"""Relies on context members: -
Expand Down
2 changes: 1 addition & 1 deletion behaviour/features/steps/steps_when.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
_REQUEST_TIMEOUT: int = 8


@when("I do a {method} at {endpoint}") # pylint: disable=not-callable
@when("I do a {method} request at {endpoint}") # pylint: disable=not-callable
def step_impl(context, method, endpoint) -> None:
"""Makes a REST request on an endpoint. Relies on context members: -
- stack_name
Expand Down

0 comments on commit 2624fe2

Please sign in to comment.