e2e: add apply_default_data to load data from migrations after tables have been truncated
This commit is contained in:
parent
aa440c17b7
commit
fc2eb003ea
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
|
@ -130,6 +130,23 @@ jobs:
|
|||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
e2e:
|
||||
needs:
|
||||
- pylint
|
||||
- black
|
||||
- prospector
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup test containers
|
||||
run: |
|
||||
cd e2e
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
docker-compose exec passbook pip install -r /app/requirements-dev.txt
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
docker-compose exec passbook ./manage.py test e2e
|
||||
# Build
|
||||
build-server:
|
||||
needs:
|
||||
|
|
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
|
@ -82,7 +82,6 @@ jobs:
|
|||
- uses: actions/checkout@v1
|
||||
- name: Run test suite in final docker images
|
||||
run: |
|
||||
export PASSBOOK_DOMAIN=localhost
|
||||
docker-compose pull
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
|
|
1
.github/workflows/tag.yml
vendored
1
.github/workflows/tag.yml
vendored
|
@ -13,7 +13,6 @@ jobs:
|
|||
- uses: actions/checkout@master
|
||||
- name: Pre-release test
|
||||
run: |
|
||||
export PASSBOOK_DOMAIN=localhost
|
||||
docker-compose pull
|
||||
docker build \
|
||||
--no-cache \
|
||||
|
|
9
docker.env.yml
Normal file
9
docker.env.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
debug: true
|
||||
postgresql:
|
||||
user: postgres
|
||||
host: postgresql
|
||||
|
||||
redis:
|
||||
host: redis
|
||||
|
||||
log_level: debug
|
|
@ -11,12 +11,6 @@ This installation Method is for test-setups and small-scale productive setups.
|
|||
|
||||
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/BeryJu/passbook/master/docker-compose.yml). Place it in a directory of your choice.
|
||||
|
||||
passbook needs to know it's primary URL to create links in E-Mails and set cookies, so you have to run the following command:
|
||||
|
||||
```
|
||||
export PASSBOOK_DOMAIN=domain.tld # this can be any domain or IP, it just needs to point to passbook.
|
||||
```
|
||||
|
||||
The compose file references the current latest version, which can be overridden with the `SERVER_TAG` Environment variable.
|
||||
|
||||
If you plan to use this setup for production, it is also advised to change the PostgreSQL Password by setting `PG_PASS` to a password of your choice.
|
||||
|
|
|
@ -12,3 +12,21 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /tmp/videos:/home/seluser/videos
|
||||
privileged: true
|
||||
postgresql:
|
||||
image: postgres:11
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
POSTGRES_DB: passbook
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
passbook:
|
||||
image: beryju/passbook
|
||||
command: /bin/bash -c "sleep infinity"
|
||||
volumes:
|
||||
- ../:/testing
|
||||
environment:
|
||||
PASSBOOK_ENV: docker
|
||||
user: root
|
||||
working_dir: /testing
|
||||
|
|
|
@ -10,20 +10,21 @@ from passbook.policies.models import PolicyBinding
|
|||
from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||
from passbook.stages.user_login.models import UserLoginStage
|
||||
from passbook.stages.user_write.models import UserWriteStage
|
||||
|
||||
from passbook.stages.identification.models import IdentificationStage
|
||||
from e2e.utils import apply_default_data
|
||||
|
||||
class TestEnroll2Step(StaticLiveServerTestCase):
|
||||
"""Test 2-step enroll flow"""
|
||||
|
||||
host = "0.0.0.0"
|
||||
port = 8001
|
||||
host = "passbook"
|
||||
|
||||
def setUp(self):
|
||||
self.driver = webdriver.Remote(
|
||||
command_executor="http://localhost:4444/wd/hub",
|
||||
command_executor="http://hub:4444/wd/hub",
|
||||
desired_capabilities=DesiredCapabilities.CHROME,
|
||||
)
|
||||
self.driver.implicitly_wait(2)
|
||||
self.driver.implicitly_wait(5)
|
||||
apply_default_data()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
@ -66,7 +67,7 @@ class TestEnroll2Step(StaticLiveServerTestCase):
|
|||
# Password checking policy
|
||||
password_policy = ExpressionPolicy.objects.create(
|
||||
name="policy-enrollment-password-equals",
|
||||
expression="{{ request.context.password == request.context.password_repeat }}",
|
||||
expression="return request.context['password'] == request.context['password_repeat']",
|
||||
)
|
||||
PolicyBinding.objects.create(
|
||||
target=first_stage, policy=password_policy, order=0
|
||||
|
@ -78,11 +79,16 @@ class TestEnroll2Step(StaticLiveServerTestCase):
|
|||
designation=FlowDesignation.ENROLLMENT,
|
||||
)
|
||||
|
||||
# Attach enrollment flow to identification stage
|
||||
ident_stage: IdentificationStage = IdentificationStage.objects.first()
|
||||
ident_stage.enrollment_flow = flow
|
||||
ident_stage.save()
|
||||
|
||||
FlowStageBinding.objects.create(flow=flow, stage=first_stage, order=0)
|
||||
FlowStageBinding.objects.create(flow=flow, stage=second_stage, order=1)
|
||||
FlowStageBinding.objects.create(flow=flow, stage=user_write, order=2)
|
||||
FlowStageBinding.objects.create(flow=flow, stage=user_login, order=3)
|
||||
self.driver.get(f"http://host.docker.internal:{self.port}")
|
||||
self.driver.get(self.live_server_url)
|
||||
self.driver.find_element(By.CSS_SELECTOR, "[role=enroll]").click()
|
||||
self.driver.find_element(By.ID, "id_username").send_keys("foo")
|
||||
self.driver.find_element(By.ID, "id_password").send_keys("pbadmin")
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
"""test default login flow"""
|
||||
import string
|
||||
from random import SystemRandom
|
||||
|
||||
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||
from django.core.management import call_command
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
from passbook.core.models import User
|
||||
from e2e.utils import apply_default_data
|
||||
|
||||
|
||||
class TestLogin(StaticLiveServerTestCase):
|
||||
"""test default login flow"""
|
||||
|
||||
host = "0.0.0.0"
|
||||
port = 8000
|
||||
host = "passbook"
|
||||
|
||||
def setUp(self):
|
||||
self.driver = webdriver.Remote(
|
||||
command_executor="http://localhost:4444/wd/hub",
|
||||
command_executor="http://hub:4444/wd/hub",
|
||||
desired_capabilities=DesiredCapabilities.CHROME,
|
||||
)
|
||||
self.driver.implicitly_wait(2)
|
||||
self.password = "".join(
|
||||
SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(8)
|
||||
)
|
||||
User.objects.create_superuser(
|
||||
username="pbadmin", email="admin@example.tld", password=self.password
|
||||
)
|
||||
call_command("migrate", "--fake", "passbook_flows", "0001_initial")
|
||||
call_command("migrate", "passbook_flows", "0002_default_flows")
|
||||
self.driver.implicitly_wait(5)
|
||||
apply_default_data()
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
@ -41,12 +27,12 @@ class TestLogin(StaticLiveServerTestCase):
|
|||
def test_login(self):
|
||||
"""test default login flow"""
|
||||
self.driver.get(
|
||||
f"http://host.docker.internal:{self.port}/flows/default-authentication-flow/?next=%2F"
|
||||
f"{self.live_server_url}/flows/default-authentication-flow/"
|
||||
)
|
||||
self.driver.find_element(By.ID, "id_uid_field").click()
|
||||
self.driver.find_element(By.ID, "id_uid_field").send_keys("admin@example.tld")
|
||||
self.driver.find_element(By.ID, "id_uid_field").send_keys("pbadmin")
|
||||
self.driver.find_element(By.ID, "id_uid_field").send_keys(Keys.ENTER)
|
||||
self.driver.find_element(By.ID, "id_password").send_keys(self.password)
|
||||
self.driver.find_element(By.ID, "id_password").send_keys("pbadmin")
|
||||
self.driver.find_element(By.ID, "id_password").send_keys(Keys.ENTER)
|
||||
self.assertEqual(
|
||||
self.driver.find_element(By.XPATH, "//a[contains(@href, '/-/user/')]").text,
|
||||
|
|
35
e2e/utils.py
Normal file
35
e2e/utils.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
"""passbook e2e testing utilities"""
|
||||
|
||||
from glob import glob
|
||||
from inspect import getmembers, isfunction
|
||||
from importlib.util import spec_from_file_location, module_from_spec
|
||||
from django.apps import apps
|
||||
from django.db import connection, transaction
|
||||
from django.db.utils import IntegrityError
|
||||
|
||||
def apply_default_data():
|
||||
"""apply objects created by migrations after tables have been truncated"""
|
||||
# Find all migration files
|
||||
# load all functions
|
||||
migration_files = glob("**/migrations/*.py", recursive=True)
|
||||
matches = []
|
||||
for migration in migration_files:
|
||||
with open(migration, 'r+') as migration_file:
|
||||
# Check if they have a `RunPython`
|
||||
if "RunPython" in migration_file.read():
|
||||
matches.append(migration)
|
||||
|
||||
with connection.schema_editor() as schema_editor:
|
||||
for match in matches:
|
||||
# Load module from file path
|
||||
spec = spec_from_file_location("", match)
|
||||
migration_module = module_from_spec(spec)
|
||||
# pyright: reportGeneralTypeIssues=false
|
||||
spec.loader.exec_module(migration_module)
|
||||
# Call all functions from module
|
||||
for _, func in getmembers(migration_module, isfunction):
|
||||
with transaction.atomic():
|
||||
try:
|
||||
func(apps, schema_editor)
|
||||
except IntegrityError:
|
||||
pass
|
|
@ -7,15 +7,10 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|||
from passbook.flows.models import FlowDesignation
|
||||
from passbook.stages.prompt.models import FieldTypes
|
||||
|
||||
FLOW_POLICY_EXPRESSION = """{{ pb_is_sso_flow }}"""
|
||||
|
||||
PROMPT_POLICY_EXPRESSION = """
|
||||
{% if pb_flow_plan.context.prompt_data.username %}
|
||||
False
|
||||
{% else %}
|
||||
True
|
||||
{% endif %}
|
||||
"""
|
||||
FLOW_POLICY_EXPRESSION = """return pb_is_sso_flow"""
|
||||
PROMPT_POLICY_EXPRESSION = (
|
||||
"""return 'username' in pb_flow_plan.context['prompt_data']"""
|
||||
)
|
||||
|
||||
|
||||
def create_default_source_enrollment_flow(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash -xe
|
||||
coverage run --concurrency=multiprocessing manage.py test --failfast
|
||||
coverage run --concurrency=multiprocessing manage.py test passbook --failfast
|
||||
coverage combine
|
||||
coverage html
|
||||
coverage report
|
||||
|
|
Reference in a new issue