Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BB2-2263] local dev docker cleanup and improve #1106

Merged
merged 8 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
more refactoring and optimize the selenium test using pytest instead …
…of Django.test
  • Loading branch information
JAMES FUQIAN committed Apr 19, 2023
commit bd1c2fc762e42192ef66774b7682884157fcd1a8
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
FROM python:3.7.13
ENV PYTHONUNBUFFERED 1
# ENV PYTHONDEVMODE 1
# RUN mkdir /code
RUN useradd -m -s /bin/bash DEV
USER DEV
ADD . /code
Expand All @@ -11,4 +10,5 @@ RUN . /tmp/venv/bin/activate
ENV PATH="/tmp/venv/bin:${PATH}"
RUN pip install --upgrade pip
RUN pip install pip-tools
RUN pip show setuptools
RUN pip install -r ./requirements/requirements.dev.txt --no-index --find-links ./vendor/
Copy link
Contributor

@ajshred ajshred Apr 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

James,

Could you please put the make call back instead of duplicating that call here? It made me go look and the makefile to see what changed and it appears that nothing changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, here the pip show is for trouble shooting - will remove it
and original intention is getting rid of make by pull out the make target and pip install directly...

but I can put the make call back.

18 changes: 10 additions & 8 deletions Dockerfile.selenium
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
FROM selenium/standalone-chrome-debug

ENV PYTHONUNBUFFERED 1
USER root
# libpq-dev: ubuntu dev lib for psypsycopg2 sdist build
RUN apt-get update && apt-get install -yq python3.8 python3-pip git libpq-dev libffi-dev
RUN mkdir /code
ADD . /code/
WORKDIR /code
USER root
RUN apt-get update ; apt-get install -yq python3.8 python3.8-venv python3-pip git libpq-dev libffi-dev
RUN ln -s /usr/bin/python3 /usr/local/bin/python
RUN useradd -m -s /bin/bash DEV
USER DEV
ADD . /code
WORKDIR /code
RUN python -m venv /tmp/venv
RUN . /tmp/venv/bin/activate
ENV PATH="/tmp/venv/bin:${PATH}"
RUN pip3 install --upgrade pip
RUN pip3 install selenium
RUN pip3 install pyyaml==6.0 pillow==9.3.0 newrelic==8.7.0
RUN pip3 install -r requirements/requirements.dev.txt --no-index --find-links ./vendor/
RUN pip3 install selenium pytest
Empty file.
8 changes: 4 additions & 4 deletions apps/integration_tests/logging_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from json.decoder import JSONDecodeError

from apps.integration_tests.common_utils import validate_json_schema
from apps.integration_tests.selenium_tests import SeleniumTests
from apps.integration_tests.log_event_schemas import (
from .common_utils import validate_json_schema
from .selenium_tests import TestBlueButtonAPI
from .log_event_schemas import (
LOG_MIDDLEWARE_FHIR_READ_EVENT_SCHEMA,
LOG_MIDDLEWARE_FHIR_SEARCH_EVENT_SCHEMA,
LOG_MIDDLEWARE_FHIR_NAVIGATION_EVENT_SCHEMA,
Expand Down Expand Up @@ -161,7 +161,7 @@
]


class LoggingTests(SeleniumTests):
class TestLoggings(TestBlueButtonAPI):
'''
Test loggings generated from authorization and fhir flow using the built in testclient as
the driver (selenium)
Expand Down
9 changes: 5 additions & 4 deletions apps/integration_tests/selenium_accounts_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from .selenium_cases import (
USER_ACCT_ACTIVATION_EMAIL_SUBJ,
USER_ACCT_ACTIVATION_KEY_PREFIX,
USER_ACTIVATION_URL_FMT,
USER_ACTIVATION_PATH_FMT,
ACCT_TESTS,
)


class SeleniumUserAndAppTests(SeleniumGenericTests):
class TestUserAndAppMgmt(SeleniumGenericTests):

def testAccountAndAppMgmt(self):
step = [0]
Expand All @@ -27,9 +27,10 @@ def testAccountAndAppMgmt(self):
activation_key = self._validate_events(USER_ACCT_ACTIVATION_EMAIL_SUBJ, USER_ACCT_ACTIVATION_KEY_PREFIX)
# good activation requests are idempotent - should not generate any extra email notifications
# activate once
self._load_page(USER_ACTIVATION_URL_FMT.format(activation_key))
usr_activate_url = USER_ACTIVATION_PATH_FMT.format(self.hostname_url, activation_key)
self._load_page(usr_activate_url)
# activate twice
self._load_page(USER_ACTIVATION_URL_FMT.format(activation_key))
self._load_page(usr_activate_url)
# there is still one activation email
activation_key = self._validate_events(USER_ACCT_ACTIVATION_EMAIL_SUBJ, USER_ACCT_ACTIVATION_KEY_PREFIX)
# now login to the account and do app stuff
Expand Down
28 changes: 15 additions & 13 deletions apps/integration_tests/selenium_cases.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from django.conf import settings
import os
from enum import Enum
from selenium.webdriver.common.by import By


HOSTNAME_URL = os.environ['HOSTNAME_URL']
PROD_URL = 'https://api.bluebutton.cms.gov'
USER_ACTIVATION_URL_FMT = settings.HOSTNAME_URL + "/v1/accounts/activation-verify/{}"
USER_ACTIVATION_PATH_FMT = "{}/v1/accounts/activation-verify/{}"


class Action(Enum):
Expand Down Expand Up @@ -162,7 +164,7 @@ class Action(Enum):
LOAD_TESTCLIENT_HOME = {
"display": "Load Test Client Home Page",
"action": Action.LOAD_PAGE,
"params": [settings.HOSTNAME_URL + "/testclient"]
"params": [HOSTNAME_URL + "/testclient"]
}

CLICK_RADIO_NOT_SHARE = {
Expand Down Expand Up @@ -233,9 +235,9 @@ class Action(Enum):
{
"display": "Load BB2 Landing Page ...",
"action": Action.LOAD_PAGE,
"params": [settings.HOSTNAME_URL]
"params": [HOSTNAME_URL]
},
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
{
"display": "Click link to get sample token v1/v2",
"action": Action.GET_SAMPLE_TOKEN_START,
Expand Down Expand Up @@ -264,9 +266,9 @@ class Action(Enum):
{
"display": "Load BB2 Landing Page ...",
"action": Action.LOAD_PAGE,
"params": [settings.HOSTNAME_URL]
"params": [HOSTNAME_URL]
},
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
{
"display": "Click link to get sample token v1/v2 with PKCE enabled",
"action": Action.GET_SAMPLE_TOKEN_PKCE_START,
Expand Down Expand Up @@ -310,7 +312,7 @@ class Action(Enum):
"action": Action.FIND_CLICK,
"params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
},
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
{
"display": "Click 'ExplanationOfBenefit' on FHIR resources page",
"action": Action.FIND_CLICK,
Expand All @@ -327,7 +329,7 @@ class Action(Enum):
"params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
},
WAIT_SECONDS,
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
WAIT_SECONDS,
{
"display": "Click 'Profile' on FHIR resources page",
Expand Down Expand Up @@ -393,7 +395,7 @@ class Action(Enum):
"action": Action.FIND_CLICK,
"params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
},
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
{
"display": "Click 'ExplanationOfBenefit' on FHIR resources page",
"action": Action.FIND_CLICK,
Expand All @@ -410,7 +412,7 @@ class Action(Enum):
"params": [20, By.LINK_TEXT, LNK_TXT_NAV_LAST]
},
WAIT_SECONDS,
CLICK_TESTCLIENT if not settings.HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
CLICK_TESTCLIENT if not HOSTNAME_URL.startswith(PROD_URL) else LOAD_TESTCLIENT_HOME,
WAIT_SECONDS,
{
"display": "Click 'Profile' on FHIR resources page",
Expand Down Expand Up @@ -480,7 +482,7 @@ class Action(Enum):
{
"display": "Load BB2 Landing Page ...",
"action": Action.LOAD_PAGE,
"params": [settings.HOSTNAME_URL]
"params": [HOSTNAME_URL]
},
{
"display": "Click link 'Signup' to create user account",
Expand Down Expand Up @@ -713,7 +715,7 @@ class Action(Enum):
USER_ACCT_ACTIVATION_W_BAD_KEY = {
"display": "Send activation request (with bad activation key)...",
"action": Action.LOAD_PAGE,
"params": [USER_ACTIVATION_URL_FMT.format("bad-key-470c74445228")]
"params": [USER_ACTIVATION_PATH_FMT.format(HOSTNAME_URL, "bad-key-470c74445228")]
}

SEE_ACCOUNT_HAS_ISSUE_MSG = {
Expand Down
45 changes: 22 additions & 23 deletions apps/integration_tests/selenium_generic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os
import time
from django.test import TestCase

from selenium import webdriver
from selenium.webdriver.common.by import By
Expand All @@ -27,27 +26,28 @@
USER_ACCOUNT_TESTS_LOGGING_FILE = "./docker-compose/tmp/bb2_account_tests.log"


class SeleniumGenericTests(TestCase):
# class SeleniumGenericTests(TestCase):
class SeleniumGenericTests():
'''
A super selenium tests to be extended by
A base selenium tests to be extended by
other selenium tests covering functional areas
'''
wait_completed = False
driver_ready = False

def setUp(self):
super(SeleniumGenericTests, self).setUp()
def setup_method(self, method):
# a bit waiting for selenium services ready for sure
if not SeleniumGenericTests.wait_completed:
if not SeleniumGenericTests.driver_ready:
time.sleep(20)
SeleniumGenericTests.wait_completed = True
print("set wait_completed={}".format(SeleniumGenericTests.wait_completed))
SeleniumGenericTests.driver_ready = True
print("set driver_ready={}".format(SeleniumGenericTests.driver_ready))
else:
print("wait_completed={}".format(SeleniumGenericTests.wait_completed))
print("driver_ready={}".format(SeleniumGenericTests.driver_ready))

self.hostname_url = os.environ['HOSTNAME_URL']
self.use_mslsx = os.environ['USE_MSLSX']
self.use_debug = os.environ['USE_DEBUG']
self.login_seq = SEQ_LOGIN_MSLSX if self.use_mslsx == 'true' else SEQ_LOGIN_SLSX
print("use_mslsx={}, use_debug={}".format(self.use_mslsx, self.use_debug))
print("use_mslsx={}, use_debug={}, hostname_url={}".format(self.use_mslsx, self.use_debug, self.hostname_url))

opt = webdriver.ChromeOptions()
opt.add_argument("--disable-dev-shm-usage")
Expand Down Expand Up @@ -86,9 +86,8 @@ def setUp(self):
Action.VALIDATE_EVENTS: self._validate_events,
}

def tearDown(self):
def teardown_method(self, method):
self.driver.quit()
super(SeleniumGenericTests, self).tearDown()

def _validate_events(self, subj_line, key_line_prefix, **kwargs):
with open(USER_ACCOUNT_TESTS_LOGGING_FILE, 'r') as f:
Expand All @@ -110,15 +109,15 @@ def _validate_events(self, subj_line, key_line_prefix, **kwargs):
# print("NOT COUNTED: {}".format(r))
# assert one and only one expected email (subj line) found
# if key_line_prefix is not None - need to extract activation key
self.assertEqual(email_subj_cnt, 1)
assert email_subj_cnt == 1
if key_line_prefix is not None:
self.assertEqual(key_cnt, 1)
self.assertIsNotNone(ak)
assert key_cnt == 1
assert ak is not None
return ak

def _find_and_click(self, timeout_sec, by, by_expr, **kwargs):
elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
self.assertIsNotNone(elem)
assert elem is not None
elem.click()
return elem

Expand All @@ -127,7 +126,7 @@ def _testclient_home(self, **kwargs):

def _find_and_sendkey(self, timeout_sec, by, by_expr, txt, **kwargs):
elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
self.assertIsNotNone(elem)
assert elem is not None
elem.send_keys(txt)
return elem

Expand All @@ -142,7 +141,7 @@ def _click_get_sample_token_pkce(self, **kwargs):

def _find_and_return(self, timeout_sec, by, by_expr, **kwargs):
elem = WebDriverWait(self.driver, timeout_sec).until(EC.visibility_of_element_located((by, by_expr)))
self.assertIsNotNone(elem)
assert elem is not None
return elem

def _load_page(self, url, **kwargs):
Expand All @@ -155,18 +154,18 @@ def _check_page_title(self, timeout_sec, by, by_expr, fmt, resource_type, **kwar
elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs)
if not (elem.text == fmt.format(resource_type, kwargs.get("api_ver"))):
print("PAGE:{}".format(self.driver.page_source))
self.assertEqual(elem.text, fmt.format(resource_type, kwargs.get("api_ver")))
assert elem.text == fmt.format(resource_type, kwargs.get("api_ver"))

def _check_pkce_challenge(self, timeout_sec, by, by_expr, pkce, **kwargs):
elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs)
if pkce:
self.assertTrue(("code_challenge" in elem.text and "code_challenge_method" in elem.text))
assert (("code_challenge" in elem.text and "code_challenge_method" in elem.text))
else:
self.assertFalse(("code_challenge" in elem.text or "code_challenge_method" in elem.text))
assert not (("code_challenge" in elem.text or "code_challenge_method" in elem.text))

def _check_page_content(self, timeout_sec, by, by_expr, content_txt, **kwargs):
elem = self._find_and_return(timeout_sec, by, by_expr, **kwargs)
self.assertIn(content_txt, elem.text)
assert content_txt in elem.text

def _back(self, **kwargs):
self.driver.back()
Expand Down
2 changes: 1 addition & 1 deletion apps/integration_tests/selenium_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
)


class SeleniumTests(SeleniumGenericTests):
class TestBlueButtonAPI(SeleniumGenericTests):
'''
Test authorization and fhir flow through the built in testclient by
leveraging selenium web driver (chrome is used)
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.selenium.remote.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
build:
context: ./
dockerfile: Dockerfile.selenium
command: python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests
command: pytest ./apps/integration_tests/selenium_tests.py
env_file:
- docker-compose/selenium-env-vars.env
volumes:
Expand All @@ -15,7 +15,7 @@ services:
build:
context: ./
dockerfile: Dockerfile.selenium
command: python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests
command: pytest ./apps/integration_tests/selenium_tests.py
env_file:
- docker-compose/selenium-env-vars.env
volumes:
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.selenium.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
build:
context: ./
dockerfile: Dockerfile.selenium
command: python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests
command: pytest ./apps/integration_tests/selenium_tests.py
env_file:
- docker-compose/selenium-env-vars.env
volumes:
Expand All @@ -17,7 +17,7 @@ services:
build:
context: ./
dockerfile: Dockerfile.selenium
command: python runtests.py --selenium apps.integration_tests.selenium_tests.SeleniumTests
command: pytest ./apps/integration_tests/selenium_tests.py
env_file:
- docker-compose/selenium-env-vars.env
volumes:
Expand Down
8 changes: 4 additions & 4 deletions docker-compose/run_selenium_tests_local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ set -e -u -o pipefail
export USE_MSLSX=true
export USE_DEBUG=false
export SERVICE_NAME="selenium-tests"
export TESTS_LIST="apps.integration_tests.selenium_tests.SeleniumTests"
export TESTS_LIST="./apps/integration_tests/selenium_tests.py"
export DJANGO_MEDICARE_SLSX_REDIRECT_URI="http:https://bb2slsx:8000/mymedicare/sls-callback"
export DJANGO_MEDICARE_SLSX_LOGIN_URI="http:https://msls:8080/sso/authorize?client_id=bb2api"
export DJANGO_SLSX_HEALTH_CHECK_ENDPOINT="http:https://msls:8080/health"
Expand Down Expand Up @@ -111,15 +111,15 @@ else
rm -rf ./docker-compose/tmp/bb2_logging_test.log
mkdir ./docker-compose/tmp
export DJANGO_SETTINGS_MODULE="hhs_oauth_server.settings.logging_it"
export TESTS_LIST="apps.integration_tests.logging_tests.LoggingTests.test_auth_fhir_flows_logging"
export TESTS_LIST="./apps/integration_tests/logging_tests.py"
export DJANGO_LOG_JSON_FORMAT_PRETTY=False
fi
if [[ $1 == "account" ]]
then
# cleansing log file before run
rm -rf ./docker-compose/tmp/bb2_account_tests.log
mkdir -p ./docker-compose/tmp
export TESTS_LIST="apps.integration_tests.selenium_accounts_tests.SeleniumUserAndAppTests"
export TESTS_LIST="./apps/integration_tests/selenium_accounts_tests.py"
export DJANGO_LOG_JSON_FORMAT_PRETTY=False
export BB2_SERVER_STD2FILE="./docker-compose/tmp/bb2_account_tests.log"
fi
Expand Down Expand Up @@ -203,7 +203,7 @@ echo "MSLSX=" ${USE_MSLSX}
echo "DEBUG=" ${USE_DEBUG}
echo "SERVICE NAME=" ${SERVICE_NAME}

docker-compose -f docker-compose.selenium.yml run ${SERVICE_NAME} bash -c "python runtests.py --selenium ${TESTS_LIST}"
docker-compose -f docker-compose.selenium.yml run ${SERVICE_NAME} bash -c "pytest ${TESTS_LIST}"

#Stop containers after use
echo_msg
Expand Down
Loading