mirror of
https://github.com/mozilla-services/syncstorage-rs.git
synced 2025-08-06 20:06:57 +02:00
test(e2e): run integration and e2e tests with pytest (#1697)
Some checks are pending
Glean probe-scraper / glean-probe-scraper (push) Waiting to run
Some checks are pending
Glean probe-scraper / glean-probe-scraper (push) Waiting to run
- Update docker compose steps for mysql and spanner to use pytest - Add infra and configuration for pytest to run tests - Remove old "run.py" test setup Closes STOR-235
This commit is contained in:
parent
4ba7b32dc8
commit
6f15ad546d
@ -101,7 +101,7 @@ commands:
|
|||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: Install test dependencies
|
name: Install test dependencies
|
||||||
command: cargo install cargo-nextest cargo-llvm-cov
|
command: cargo install --locked cargo-nextest cargo-llvm-cov
|
||||||
|
|
||||||
make-test-dir:
|
make-test-dir:
|
||||||
steps:
|
steps:
|
||||||
@ -145,32 +145,16 @@ commands:
|
|||||||
make run_token_server_integration_tests
|
make run_token_server_integration_tests
|
||||||
environment:
|
environment:
|
||||||
SYNCSTORAGE_RS_IMAGE: app:build
|
SYNCSTORAGE_RS_IMAGE: app:build
|
||||||
run-e2e-mysql-tests:
|
run-e2e-tests:
|
||||||
|
parameters:
|
||||||
|
db:
|
||||||
|
type: enum
|
||||||
|
enum: ["mysql", "spanner"]
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: e2e tests (syncstorage mysql)
|
name: e2e tests (syncstorage << parameters.db >>)
|
||||||
command: >
|
command: >
|
||||||
/usr/local/bin/docker-compose
|
make docker_run_<< parameters.db >>_e2e_tests
|
||||||
-f docker-compose.mysql.yaml
|
|
||||||
-f docker-compose.e2e.mysql.yaml
|
|
||||||
up
|
|
||||||
--exit-code-from mysql-e2e-tests
|
|
||||||
--abort-on-container-exit
|
|
||||||
environment:
|
|
||||||
SYNCSTORAGE_RS_IMAGE: app:build
|
|
||||||
|
|
||||||
|
|
||||||
run-e2e-spanner-tests:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: e2e tests (syncstorage spanner)
|
|
||||||
command: >
|
|
||||||
/usr/local/bin/docker-compose
|
|
||||||
-f docker-compose.spanner.yaml
|
|
||||||
-f docker-compose.e2e.spanner.yaml
|
|
||||||
up
|
|
||||||
--exit-code-from spanner-e2e-tests
|
|
||||||
--abort-on-container-exit
|
|
||||||
environment:
|
environment:
|
||||||
SYNCSTORAGE_RS_IMAGE: app:build
|
SYNCSTORAGE_RS_IMAGE: app:build
|
||||||
|
|
||||||
@ -283,7 +267,6 @@ jobs:
|
|||||||
# if the above tests don't run tokenserver-db tests (i.e. using --workspace)
|
# if the above tests don't run tokenserver-db tests (i.e. using --workspace)
|
||||||
# then run-tokenserver-scripts-tests will fail. These tests expect the db to be
|
# then run-tokenserver-scripts-tests will fail. These tests expect the db to be
|
||||||
# configured already, and it appears unit-tests modify the db to the expected state
|
# configured already, and it appears unit-tests modify the db to the expected state
|
||||||
- run-tokenserver-integration-tests
|
|
||||||
- store-test-results
|
- store-test-results
|
||||||
- upload-to-gcs:
|
- upload-to-gcs:
|
||||||
source: workflow/test-results
|
source: workflow/test-results
|
||||||
@ -324,11 +307,13 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Save docker-compose config
|
name: Save docker-compose config
|
||||||
command: cp docker-compose*mysql.yaml /home/circleci/cache
|
command: cp docker-compose*mysql.yaml /home/circleci/cache
|
||||||
|
- run:
|
||||||
|
name: Save Makefile to cache
|
||||||
|
command: cp Makefile /home/circleci/cache
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: mysql-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}-{{ epoch }}
|
key: mysql-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}-{{ epoch }}
|
||||||
paths:
|
paths:
|
||||||
- /home/circleci/cache
|
- /home/circleci/cache
|
||||||
|
|
||||||
build-spanner-image:
|
build-spanner-image:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/rust:1.86 # RUST_VER
|
- image: cimg/rust:1.86 # RUST_VER
|
||||||
@ -361,6 +346,9 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Save docker-compose config
|
name: Save docker-compose config
|
||||||
command: cp docker-compose*spanner.yaml /home/circleci/cache
|
command: cp docker-compose*spanner.yaml /home/circleci/cache
|
||||||
|
- run:
|
||||||
|
name: Save Makefile to cache
|
||||||
|
command: cp Makefile /home/circleci/cache
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: spanner-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}-{{ epoch }}
|
key: spanner-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}-{{ epoch }}
|
||||||
paths:
|
paths:
|
||||||
@ -422,7 +410,7 @@ jobs:
|
|||||||
|
|
||||||
mysql-e2e-tests:
|
mysql-e2e-tests:
|
||||||
docker:
|
docker:
|
||||||
- image: docker/compose:1.24.0
|
- image: cimg/base:2025.04
|
||||||
auth:
|
auth:
|
||||||
username: $DOCKER_USER
|
username: $DOCKER_USER
|
||||||
password: $DOCKER_PASS
|
password: $DOCKER_PASS
|
||||||
@ -434,14 +422,24 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Restore Docker image cache
|
name: Restore Docker image cache
|
||||||
command: docker load -i /home/circleci/cache/docker.tar
|
command: docker load -i /home/circleci/cache/docker.tar
|
||||||
|
- run:
|
||||||
|
name: Restore Makefile from save_cache
|
||||||
|
command: cp /home/circleci/cache/Makefile .
|
||||||
- run:
|
- run:
|
||||||
name: Restore docker-compose config
|
name: Restore docker-compose config
|
||||||
command: cp /home/circleci/cache/docker-compose*.yaml .
|
command: cp /home/circleci/cache/docker-compose*.yaml .
|
||||||
- run-e2e-mysql-tests
|
- make-test-dir
|
||||||
|
- run-e2e-tests:
|
||||||
|
db: mysql
|
||||||
|
- store-test-results
|
||||||
|
- upload-to-gcs:
|
||||||
|
source: workflow/test-results
|
||||||
|
destination: gs://ecosystem-test-eng-metrics/syncstorage-rs/junit
|
||||||
|
extension: xml
|
||||||
|
|
||||||
spanner-e2e-tests:
|
spanner-e2e-tests:
|
||||||
docker:
|
docker:
|
||||||
- image: docker/compose:1.24.0
|
- image: cimg/base:2025.04
|
||||||
auth:
|
auth:
|
||||||
username: $DOCKER_USER
|
username: $DOCKER_USER
|
||||||
password: $DOCKER_PASS
|
password: $DOCKER_PASS
|
||||||
@ -453,10 +451,20 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Restore Docker image cache
|
name: Restore Docker image cache
|
||||||
command: docker load -i /home/circleci/cache/docker.tar
|
command: docker load -i /home/circleci/cache/docker.tar
|
||||||
|
- run:
|
||||||
|
name: Restore Makefile from save_cache
|
||||||
|
command: cp /home/circleci/cache/Makefile .
|
||||||
- run:
|
- run:
|
||||||
name: Restore docker-compose config
|
name: Restore docker-compose config
|
||||||
command: cp /home/circleci/cache/docker-compose*.yaml .
|
command: cp /home/circleci/cache/docker-compose*.yaml .
|
||||||
- run-e2e-spanner-tests
|
- make-test-dir
|
||||||
|
- run-e2e-tests:
|
||||||
|
db: spanner
|
||||||
|
- store-test-results
|
||||||
|
- upload-to-gcs:
|
||||||
|
source: workflow/test-results
|
||||||
|
destination: gs://ecosystem-test-eng-metrics/syncstorage-rs/junit
|
||||||
|
extension: xml
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
docker:
|
docker:
|
||||||
|
46
Makefile
46
Makefile
@ -20,7 +20,13 @@ TEST_PROFILE := $(if $(CIRCLECI),ci,default)
|
|||||||
TEST_FILE_PREFIX := $(if $(CIRCLECI),$(CIRCLE_BUILD_NUM)__$(EPOCH_TIME)__$(CIRCLE_PROJECT_REPONAME)__$(WORKFLOW)__)
|
TEST_FILE_PREFIX := $(if $(CIRCLECI),$(CIRCLE_BUILD_NUM)__$(EPOCH_TIME)__$(CIRCLE_PROJECT_REPONAME)__$(WORKFLOW)__)
|
||||||
UNIT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)unit__results.xml
|
UNIT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)unit__results.xml
|
||||||
UNIT_COVERAGE_JSON := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)unit__coverage.json
|
UNIT_COVERAGE_JSON := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)unit__coverage.json
|
||||||
INTEGRATION_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)integration__results.xml
|
|
||||||
|
SPANNER_INT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)spanner_integration__results.xml
|
||||||
|
SPANNER_NO_JWK_INT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)spanner_no_oauth_integration__results.xml
|
||||||
|
MYSQL_INT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)mysql_integration__results.xml
|
||||||
|
MYSQL_NO_JWK_INT_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)mysql_no_oauth_integration__results.xml
|
||||||
|
|
||||||
|
LOCAL_INTEGRATION_JUNIT_XML := $(TEST_RESULTS_DIR)/$(TEST_FILE_PREFIX)local_integration__results.xml
|
||||||
SYNC_SYNCSTORAGE__DATABASE_URL ?= mysql://sample_user:sample_password@localhost/syncstorage_rs
|
SYNC_SYNCSTORAGE__DATABASE_URL ?= mysql://sample_user:sample_password@localhost/syncstorage_rs
|
||||||
SYNC_TOKENSERVER__DATABASE_URL ?= mysql://sample_user:sample_password@localhost/tokenserver_rs
|
SYNC_TOKENSERVER__DATABASE_URL ?= mysql://sample_user:sample_password@localhost/tokenserver_rs
|
||||||
|
|
||||||
@ -40,22 +46,48 @@ clean:
|
|||||||
rm -r venv
|
rm -r venv
|
||||||
|
|
||||||
docker_start_mysql:
|
docker_start_mysql:
|
||||||
docker-compose -f docker-compose.mysql.yaml up -d
|
docker compose -f docker-compose.mysql.yaml up -d
|
||||||
|
|
||||||
docker_start_mysql_rebuild:
|
docker_start_mysql_rebuild:
|
||||||
docker-compose -f docker-compose.mysql.yaml up --build -d
|
docker compose -f docker-compose.mysql.yaml up --build -d
|
||||||
|
|
||||||
docker_stop_mysql:
|
docker_stop_mysql:
|
||||||
docker-compose -f docker-compose.mysql.yaml down
|
docker compose -f docker-compose.mysql.yaml down
|
||||||
|
|
||||||
docker_start_spanner:
|
docker_start_spanner:
|
||||||
docker-compose -f docker-compose.spanner.yaml up -d
|
docker compose -f docker-compose.spanner.yaml up -d
|
||||||
|
|
||||||
docker_start_spanner_rebuild:
|
docker_start_spanner_rebuild:
|
||||||
docker-compose -f docker-compose.spanner.yaml up --build -d
|
docker compose -f docker-compose.spanner.yaml up --build -d
|
||||||
|
|
||||||
docker_stop_spanner:
|
docker_stop_spanner:
|
||||||
docker-compose -f docker-compose.spanner.yaml down
|
docker compose -f docker-compose.spanner.yaml down
|
||||||
|
|
||||||
|
.ONESHELL:
|
||||||
|
docker_run_mysql_e2e_tests:
|
||||||
|
docker compose \
|
||||||
|
-f docker-compose.mysql.yaml \
|
||||||
|
-f docker-compose.e2e.mysql.yaml \
|
||||||
|
up \
|
||||||
|
--exit-code-from mysql-e2e-tests \
|
||||||
|
--abort-on-container-exit;
|
||||||
|
exit_code=$$?;
|
||||||
|
docker cp mysql-e2e-tests:/mysql_integration_results.xml ${MYSQL_INT_JUNIT_XML};
|
||||||
|
docker cp mysql-e2e-tests:/mysql_no_jwk_integration_results.xml ${MYSQL_NO_JWK_INT_JUNIT_XML};
|
||||||
|
exit $$exit_code;
|
||||||
|
|
||||||
|
.ONESHELL:
|
||||||
|
docker_run_spanner_e2e_tests:
|
||||||
|
docker compose \
|
||||||
|
-f docker-compose.spanner.yaml \
|
||||||
|
-f docker-compose.e2e.spanner.yaml \
|
||||||
|
up \
|
||||||
|
--exit-code-from spanner-e2e-tests \
|
||||||
|
--abort-on-container-exit;
|
||||||
|
exit_code=$$?;
|
||||||
|
docker cp spanner-e2e-tests:/spanner_integration_results.xml ${SPANNER_INT_JUNIT_XML};
|
||||||
|
docker cp spanner-e2e-tests:/spanner_no_jwk_integration_results.xml ${SPANNER_NO_JWK_INT_JUNIT_XML};
|
||||||
|
exit $$exit_code;
|
||||||
|
|
||||||
python:
|
python:
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
|
@ -1,27 +1,24 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
sync-db:
|
|
||||||
tokenserver-db:
|
|
||||||
syncserver:
|
|
||||||
depends_on:
|
|
||||||
- sync-db
|
|
||||||
- tokenserver-db
|
|
||||||
# TODO: either syncserver should retry the db connection
|
|
||||||
# itself a few times or should include a wait-for-it.sh script
|
|
||||||
# inside its docker that would do this for us.
|
|
||||||
entrypoint: >
|
|
||||||
/bin/sh -c "
|
|
||||||
sleep 15;
|
|
||||||
/app/bin/syncserver;
|
|
||||||
"
|
|
||||||
mysql-e2e-tests:
|
mysql-e2e-tests:
|
||||||
|
container_name: mysql-e2e-tests
|
||||||
depends_on:
|
depends_on:
|
||||||
- mock-fxa-server
|
sync-db:
|
||||||
- syncserver
|
condition: service_healthy
|
||||||
|
mock-fxa-server:
|
||||||
|
condition: service_started
|
||||||
|
tokenserver-db:
|
||||||
|
condition: service_healthy
|
||||||
|
# this depend is to avoid migration collisions.
|
||||||
|
# the syncserver isn't actually used for the tests,
|
||||||
|
# but collisions can happen particularly in CI.
|
||||||
|
syncserver:
|
||||||
|
condition: service_started
|
||||||
image: app:build
|
image: app:build
|
||||||
privileged: true
|
privileged: true
|
||||||
user: root
|
user: root
|
||||||
environment:
|
environment:
|
||||||
|
JWK_CACHE_DISABLED: false
|
||||||
MOCK_FXA_SERVER_URL: http://mock-fxa-server:6000
|
MOCK_FXA_SERVER_URL: http://mock-fxa-server:6000
|
||||||
SYNC_HOST: 0.0.0.0
|
SYNC_HOST: 0.0.0.0
|
||||||
SYNC_MASTER_SECRET: secret0
|
SYNC_MASTER_SECRET: secret0
|
||||||
@ -43,5 +40,9 @@ services:
|
|||||||
TOKENSERVER_HOST: http://localhost:8000
|
TOKENSERVER_HOST: http://localhost:8000
|
||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/sh -c "
|
/bin/sh -c "
|
||||||
sleep 28; python3 /app/tools/integration_tests/run.py 'http://localhost:8000#secret0'
|
exit_code=0;
|
||||||
|
pytest /app/tools/integration_tests/ --junit-xml=/mysql_integration_results.xml || exit_code=$$?;
|
||||||
|
export JWK_CACHE_DISABLED=true;
|
||||||
|
pytest /app/tools/integration_tests/ --junit-xml=/mysql_no_jwk_integration_results.xml || exit_code=$$?;
|
||||||
|
exit $$exit_code;
|
||||||
"
|
"
|
||||||
|
@ -1,27 +1,23 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
sync-db:
|
|
||||||
sync-db-setup:
|
|
||||||
tokenserver-db:
|
|
||||||
syncserver:
|
|
||||||
depends_on:
|
|
||||||
- sync-db-setup
|
|
||||||
# TODO: either syncserver should retry the db connection
|
|
||||||
# itself a few times or should include a wait-for-it.sh script
|
|
||||||
# inside its docker that would do this for us.
|
|
||||||
entrypoint: >
|
|
||||||
/bin/sh -c "
|
|
||||||
sleep 15;
|
|
||||||
/app/bin/syncserver;
|
|
||||||
"
|
|
||||||
spanner-e2e-tests:
|
spanner-e2e-tests:
|
||||||
|
container_name: spanner-e2e-tests
|
||||||
depends_on:
|
depends_on:
|
||||||
- mock-fxa-server
|
mock-fxa-server:
|
||||||
- syncserver
|
condition: service_started
|
||||||
|
syncserver:
|
||||||
|
condition: service_started
|
||||||
|
tokenserver-db:
|
||||||
|
condition: service_healthy
|
||||||
image: app:build
|
image: app:build
|
||||||
privileged: true
|
privileged: true
|
||||||
user: root
|
user: root
|
||||||
environment:
|
environment:
|
||||||
|
# Some tests can run without the `FXA_OAUTH...` vars.
|
||||||
|
# Setting this to false will delete any of those keys before starting
|
||||||
|
# the syncserver and startging the test. This can be set/passed
|
||||||
|
# in from CircleCI when calling `docker-compose -f docker-compose.e2e.spanner.yaml`
|
||||||
|
JWK_CACHE_DISABLED: false
|
||||||
MOCK_FXA_SERVER_URL: http://mock-fxa-server:6000
|
MOCK_FXA_SERVER_URL: http://mock-fxa-server:6000
|
||||||
SYNC_HOST: 0.0.0.0
|
SYNC_HOST: 0.0.0.0
|
||||||
SYNC_MASTER_SECRET: secret0
|
SYNC_MASTER_SECRET: secret0
|
||||||
@ -44,5 +40,9 @@ services:
|
|||||||
TOKENSERVER_HOST: http://localhost:8000
|
TOKENSERVER_HOST: http://localhost:8000
|
||||||
entrypoint: >
|
entrypoint: >
|
||||||
/bin/sh -c "
|
/bin/sh -c "
|
||||||
sleep 28; python3 /app/tools/integration_tests/run.py 'http://localhost:8000#secret0'
|
exit_code=0;
|
||||||
|
pytest /app/tools/integration_tests/ --junit-xml=/spanner_integration_results.xml || exit_code=$$?;
|
||||||
|
export JWK_CACHE_DISABLED=true;
|
||||||
|
pytest /app/tools/integration_tests/ --junit-xml=/spanner_no_jwk_integration_results.xml || exit_code=$$?;
|
||||||
|
exit $$exit_code;
|
||||||
"
|
"
|
||||||
|
@ -24,6 +24,12 @@ services:
|
|||||||
MYSQL_DATABASE: syncstorage
|
MYSQL_DATABASE: syncstorage
|
||||||
MYSQL_USER: test
|
MYSQL_USER: test
|
||||||
MYSQL_PASSWORD: test
|
MYSQL_PASSWORD: test
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "mysqladmin -uroot -p$${MYSQL_ROOT_PASSWORD} version"]
|
||||||
|
interval: 2s
|
||||||
|
retries: 10
|
||||||
|
start_period: 20s
|
||||||
|
timeout: 2s
|
||||||
|
|
||||||
tokenserver-db:
|
tokenserver-db:
|
||||||
image: docker.io/library/mysql:5.7
|
image: docker.io/library/mysql:5.7
|
||||||
@ -39,6 +45,12 @@ services:
|
|||||||
MYSQL_DATABASE: tokenserver
|
MYSQL_DATABASE: tokenserver
|
||||||
MYSQL_USER: test
|
MYSQL_USER: test
|
||||||
MYSQL_PASSWORD: test
|
MYSQL_PASSWORD: test
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "mysqladmin -uroot -p$${MYSQL_ROOT_PASSWORD} version"]
|
||||||
|
interval: 2s
|
||||||
|
retries: 10
|
||||||
|
start_period: 20s
|
||||||
|
timeout: 2s
|
||||||
|
|
||||||
mock-fxa-server:
|
mock-fxa-server:
|
||||||
image: app:build
|
image: app:build
|
||||||
@ -58,8 +70,10 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- sync-db
|
sync-db:
|
||||||
- tokenserver-db
|
condition: service_healthy
|
||||||
|
tokenserver-db:
|
||||||
|
condition: service_healthy
|
||||||
environment:
|
environment:
|
||||||
SYNC_HOST: 0.0.0.0
|
SYNC_HOST: 0.0.0.0
|
||||||
SYNC_MASTER_SECRET: secret0
|
SYNC_MASTER_SECRET: secret0
|
||||||
|
@ -35,6 +35,12 @@ services:
|
|||||||
MYSQL_DATABASE: tokenserver
|
MYSQL_DATABASE: tokenserver
|
||||||
MYSQL_USER: test
|
MYSQL_USER: test
|
||||||
MYSQL_PASSWORD: test
|
MYSQL_PASSWORD: test
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "mysqladmin -uroot -p$${MYSQL_ROOT_PASSWORD} version"]
|
||||||
|
interval: 2s
|
||||||
|
retries: 10
|
||||||
|
start_period: 20s
|
||||||
|
timeout: 2s
|
||||||
mock-fxa-server:
|
mock-fxa-server:
|
||||||
image: app:build
|
image: app:build
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
162
tools/integration_tests/conftest.py
Normal file
162
tools/integration_tests/conftest.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import os
|
||||||
|
import psutil
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
|
||||||
|
DEBUG_BUILD = "target/debug/syncserver"
|
||||||
|
RELEASE_BUILD = "/app/bin/syncserver"
|
||||||
|
# max number of attempts to check server heartbeat
|
||||||
|
SYNC_SERVER_STARTUP_MAX_ATTEMPTS = 30
|
||||||
|
JWK_CACHE_DISABLED = os.environ.get("JWK_CACHE_DISABLED")
|
||||||
|
|
||||||
|
logger = logging.getLogger("tokenserver.scripts.conftest")
|
||||||
|
|
||||||
|
# Local setup for fixtures
|
||||||
|
|
||||||
|
|
||||||
|
def _terminate_process(process):
|
||||||
|
"""
|
||||||
|
Gracefully terminate the process and its children.
|
||||||
|
"""
|
||||||
|
proc = psutil.Process(pid=process.pid)
|
||||||
|
child_proc = proc.children(recursive=True)
|
||||||
|
for p in [proc] + child_proc:
|
||||||
|
os.kill(p.pid, signal.SIGTERM)
|
||||||
|
process.wait()
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_server_startup(max_attempts=SYNC_SERVER_STARTUP_MAX_ATTEMPTS):
|
||||||
|
"""
|
||||||
|
Waits for the __heartbeat__ endpoint to return a 200, pausing for 1 second
|
||||||
|
between attempts. Raises a RuntimeError if the server does not start after
|
||||||
|
the specific number of attempts.
|
||||||
|
"""
|
||||||
|
itter = 0
|
||||||
|
while True:
|
||||||
|
if itter >= max_attempts:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Server failed to start within the timeout period."
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
req = requests.get("http://localhost:8000/__heartbeat__",
|
||||||
|
timeout=2)
|
||||||
|
if req.status_code == 200:
|
||||||
|
break
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.warning("Connection failed: %s", e)
|
||||||
|
time.sleep(1)
|
||||||
|
itter += 1
|
||||||
|
|
||||||
|
|
||||||
|
def _start_server():
|
||||||
|
"""
|
||||||
|
Starts the syncserver process, waits for it to be running,
|
||||||
|
and return the process handle.
|
||||||
|
"""
|
||||||
|
target_binary = None
|
||||||
|
if os.path.exists(DEBUG_BUILD):
|
||||||
|
target_binary = DEBUG_BUILD
|
||||||
|
elif os.path.exists(RELEASE_BUILD):
|
||||||
|
target_binary = RELEASE_BUILD
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Neither {DEBUG_BUILD} nor {RELEASE_BUILD} were found."
|
||||||
|
)
|
||||||
|
|
||||||
|
server_proc = subprocess.Popen(
|
||||||
|
target_binary,
|
||||||
|
shell=True,
|
||||||
|
text=True,
|
||||||
|
env=os.environ,
|
||||||
|
)
|
||||||
|
|
||||||
|
_wait_for_server_startup()
|
||||||
|
|
||||||
|
return server_proc
|
||||||
|
|
||||||
|
|
||||||
|
def _server_manager():
|
||||||
|
"""
|
||||||
|
Context manager to gracefully start and stop the server.
|
||||||
|
"""
|
||||||
|
server_process = _start_server()
|
||||||
|
try:
|
||||||
|
yield server_process
|
||||||
|
finally:
|
||||||
|
_terminate_process(server_process)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_local_test_env_vars():
|
||||||
|
"""
|
||||||
|
Set environment variables for local testing.
|
||||||
|
This function sets the necessary environment variables for the syncserver.
|
||||||
|
"""
|
||||||
|
os.environ.setdefault("SYNC_MASTER_SECRET", "secret0")
|
||||||
|
os.environ.setdefault("SYNC_CORS_MAX_AGE", "555")
|
||||||
|
os.environ.setdefault("SYNC_CORS_ALLOWED_ORIGIN", "*")
|
||||||
|
os.environ["MOZSVC_TEST_REMOTE"] = "localhost"
|
||||||
|
os.environ["SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL"] = \
|
||||||
|
os.environ["MOCK_FXA_SERVER_URL"]
|
||||||
|
|
||||||
|
# Fixtures
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def setup_server_local_testing():
|
||||||
|
"""
|
||||||
|
Fixture to set up the server for local testing.
|
||||||
|
This fixture sets the necessary environment variables and
|
||||||
|
starts the server.
|
||||||
|
"""
|
||||||
|
_set_local_test_env_vars()
|
||||||
|
yield from _server_manager()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def setup_server_local_testing_with_oauth():
|
||||||
|
"""
|
||||||
|
Fixture to set up the server for local testing with OAuth.
|
||||||
|
This fixture sets the necessary environment variables and
|
||||||
|
starts the server.
|
||||||
|
"""
|
||||||
|
_set_local_test_env_vars()
|
||||||
|
|
||||||
|
# Set OAuth-specific environment variables
|
||||||
|
os.environ["TOKENSERVER_AUTH_METHOD"] = "oauth"
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
yield from _server_manager()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def setup_server_end_to_end_testing():
|
||||||
|
"""
|
||||||
|
Fixture to set up the server for end-to-end testing.
|
||||||
|
This fixture sets the necessary environment variables and
|
||||||
|
starts the server.
|
||||||
|
"""
|
||||||
|
_set_local_test_env_vars()
|
||||||
|
# debatable if this should ONLY be here since it was only
|
||||||
|
# done against the "run_end_to_end_tests" prior, of if we
|
||||||
|
# just do it in _set_local_test_env_vars...
|
||||||
|
if JWK_CACHE_DISABLED:
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__KTY"]
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__ALG"]
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__KID"]
|
||||||
|
del os.environ[
|
||||||
|
"SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__FXA_CREATED_AT"
|
||||||
|
]
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__USE"]
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__N"]
|
||||||
|
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__E"]
|
||||||
|
|
||||||
|
# Set OAuth-specific environment variables
|
||||||
|
os.environ["SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL"] = \
|
||||||
|
"https://oauth.stage.mozaws.net"
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
yield from _server_manager()
|
@ -1,89 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
import psutil
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from test_storage import TestStorage
|
|
||||||
from test_support import run_live_functional_tests
|
|
||||||
import time
|
|
||||||
from tokenserver.run import (run_end_to_end_tests, run_local_tests)
|
|
||||||
|
|
||||||
DEBUG_BUILD = "target/debug/syncserver"
|
|
||||||
RELEASE_BUILD = "/app/bin/syncserver"
|
|
||||||
|
|
||||||
|
|
||||||
def terminate_process(process):
|
|
||||||
proc = psutil.Process(pid=process.pid)
|
|
||||||
child_proc = proc.children(recursive=True)
|
|
||||||
for p in [proc] + child_proc:
|
|
||||||
os.kill(p.pid, signal.SIGTERM)
|
|
||||||
process.wait()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# When run as a script, this file will execute the
|
|
||||||
# functional tests against a live webserver.
|
|
||||||
target_binary = None
|
|
||||||
if os.path.exists(DEBUG_BUILD):
|
|
||||||
target_binary = DEBUG_BUILD
|
|
||||||
elif os.path.exists(RELEASE_BUILD):
|
|
||||||
target_binary = RELEASE_BUILD
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Neither target/debug/syncserver \
|
|
||||||
nor /app/bin/syncserver were found."
|
|
||||||
)
|
|
||||||
|
|
||||||
def start_server():
|
|
||||||
the_server_subprocess = subprocess.Popen(
|
|
||||||
target_binary, shell=True, env=os.environ
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO we should change this to watch for a log message on startup
|
|
||||||
# to know when to continue instead of sleeping for a fixed amount
|
|
||||||
time.sleep(20)
|
|
||||||
|
|
||||||
return the_server_subprocess
|
|
||||||
|
|
||||||
os.environ.setdefault("SYNC_MASTER_SECRET", "secret0")
|
|
||||||
os.environ.setdefault("SYNC_CORS_MAX_AGE", "555")
|
|
||||||
os.environ.setdefault("SYNC_CORS_ALLOWED_ORIGIN", "*")
|
|
||||||
mock_fxa_server_url = os.environ["MOCK_FXA_SERVER_URL"]
|
|
||||||
url = "%s/v2" % mock_fxa_server_url
|
|
||||||
os.environ["SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL"] = mock_fxa_server_url
|
|
||||||
the_server_subprocess = start_server()
|
|
||||||
try:
|
|
||||||
res = 0
|
|
||||||
res |= run_live_functional_tests(TestStorage, sys.argv)
|
|
||||||
os.environ["TOKENSERVER_AUTH_METHOD"] = "oauth"
|
|
||||||
res |= run_local_tests()
|
|
||||||
finally:
|
|
||||||
terminate_process(the_server_subprocess)
|
|
||||||
|
|
||||||
os.environ["SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL"] = \
|
|
||||||
"https://oauth.stage.mozaws.net"
|
|
||||||
the_server_subprocess = start_server()
|
|
||||||
try:
|
|
||||||
res |= run_end_to_end_tests()
|
|
||||||
finally:
|
|
||||||
terminate_process(the_server_subprocess)
|
|
||||||
|
|
||||||
# Run the Tokenserver end-to-end tests without the JWK cached
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__KTY"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__ALG"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__KID"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__FXA_CREATED_AT"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__USE"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__N"]
|
|
||||||
del os.environ["SYNC_TOKENSERVER__FXA_OAUTH_PRIMARY_JWK__E"]
|
|
||||||
|
|
||||||
the_server_subprocess = start_server()
|
|
||||||
try:
|
|
||||||
verbosity = int(os.environ.get("VERBOSITY", "1"))
|
|
||||||
res |= run_end_to_end_tests(verbosity=verbosity)
|
|
||||||
finally:
|
|
||||||
terminate_process(the_server_subprocess)
|
|
||||||
|
|
||||||
sys.exit(res)
|
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,6 @@ import functools
|
|||||||
from konfig import Config, SettingsDict
|
from konfig import Config, SettingsDict
|
||||||
import hawkauthlib
|
import hawkauthlib
|
||||||
import os
|
import os
|
||||||
import optparse
|
|
||||||
from pyramid.authorization import ACLAuthorizationPolicy
|
from pyramid.authorization import ACLAuthorizationPolicy
|
||||||
from pyramid.config import Configurator
|
from pyramid.config import Configurator
|
||||||
from pyramid.interfaces import IAuthenticationPolicy
|
from pyramid.interfaces import IAuthenticationPolicy
|
||||||
@ -32,7 +31,6 @@ from webtest import TestApp
|
|||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
|
||||||
|
|
||||||
global_secret = None
|
|
||||||
VALID_FXA_ID_REGEX = re.compile("^[A-Za-z0-9=\\-_]{1,64}$")
|
VALID_FXA_ID_REGEX = re.compile("^[A-Za-z0-9=\\-_]{1,64}$")
|
||||||
|
|
||||||
|
|
||||||
@ -357,7 +355,6 @@ class FunctionalTestCase(TestCase):
|
|||||||
# This call implicitly commits the configurator. We probably still
|
# This call implicitly commits the configurator. We probably still
|
||||||
# want it for the side effects.
|
# want it for the side effects.
|
||||||
self.config.make_wsgi_app()
|
self.config.make_wsgi_app()
|
||||||
|
|
||||||
host_url = urlparse.urlparse(self.host_url)
|
host_url = urlparse.urlparse(self.host_url)
|
||||||
self.app = TestApp(
|
self.app = TestApp(
|
||||||
self.host_url,
|
self.host_url,
|
||||||
@ -397,6 +394,7 @@ class StorageFunctionalTestCase(FunctionalTestCase, StorageTestCase):
|
|||||||
|
|
||||||
def _authenticate(self):
|
def _authenticate(self):
|
||||||
policy = self.config.registry.getUtility(IAuthenticationPolicy)
|
policy = self.config.registry.getUtility(IAuthenticationPolicy)
|
||||||
|
global_secret = os.environ.get("SYNC_MASTER_SECRET")
|
||||||
if global_secret is not None:
|
if global_secret is not None:
|
||||||
policy.secrets._secrets = [global_secret]
|
policy.secrets._secrets = [global_secret]
|
||||||
self.user_id = random.randint(1, 100000)
|
self.user_id = random.randint(1, 100000)
|
||||||
@ -801,90 +799,3 @@ class SyncStorageAuthenticationPolicy(TokenServerAuthenticationPolicy):
|
|||||||
raise ValueError("invalid device_id in token data")
|
raise ValueError("invalid device_id in token data")
|
||||||
"""
|
"""
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def run_live_functional_tests(TestCaseClass, argv=None):
|
|
||||||
"""Execute the given suite of testcases against a live server."""
|
|
||||||
if argv is None:
|
|
||||||
argv = sys.argv
|
|
||||||
|
|
||||||
# This will only work using a StorageFunctionalTestCase subclass,
|
|
||||||
# since we override the _authenticate() method.
|
|
||||||
assert issubclass(TestCaseClass, StorageFunctionalTestCase)
|
|
||||||
|
|
||||||
usage = "Usage: %prog [options] <server-url>"
|
|
||||||
parser = optparse.OptionParser(usage=usage)
|
|
||||||
parser.add_option(
|
|
||||||
"-x",
|
|
||||||
"--failfast",
|
|
||||||
action="store_true",
|
|
||||||
help="stop after the first failed test",
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"",
|
|
||||||
"--config-file",
|
|
||||||
help="name of the config file in use by the server",
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"",
|
|
||||||
"--use-token-server",
|
|
||||||
action="store_true",
|
|
||||||
help="the given URL is a tokenserver, not an endpoint",
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"", "--email", help="email address to use for tokenserver tests"
|
|
||||||
)
|
|
||||||
parser.add_option(
|
|
||||||
"",
|
|
||||||
"--audience",
|
|
||||||
help="assertion audience to use for tokenserver tests",
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
opts, args = parser.parse_args(argv)
|
|
||||||
except SystemExit as e:
|
|
||||||
return e.args[0]
|
|
||||||
if len(args) != 2:
|
|
||||||
parser.print_usage()
|
|
||||||
return 2
|
|
||||||
|
|
||||||
url = args[1]
|
|
||||||
if opts.config_file is not None:
|
|
||||||
os.environ["MOZSVC_TEST_INI_FILE"] = opts.config_file
|
|
||||||
|
|
||||||
# If we're not using the tokenserver, the default implementation of
|
|
||||||
# _authenticate will do just fine. We optionally accept the token
|
|
||||||
# signing secret in the url hash fragement.
|
|
||||||
if opts.email is not None:
|
|
||||||
msg = "cant specify email address unless using live tokenserver"
|
|
||||||
raise ValueError(msg)
|
|
||||||
if opts.audience is not None:
|
|
||||||
msg = "cant specify audience unless using live tokenserver"
|
|
||||||
raise ValueError(msg)
|
|
||||||
host_url = urlparse.urlparse(url)
|
|
||||||
if host_url.fragment:
|
|
||||||
global global_secret
|
|
||||||
global_secret = host_url.fragment
|
|
||||||
host_url = host_url._replace(fragment="")
|
|
||||||
os.environ["MOZSVC_TEST_REMOTE"] = "localhost"
|
|
||||||
|
|
||||||
# Now use the unittest2 runner to execute them.
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
import test_storage
|
|
||||||
|
|
||||||
test_prefix = os.environ.get("SYNC_TEST_PREFIX", "test")
|
|
||||||
suite.addTest(unittest.findTestCases(test_storage, test_prefix))
|
|
||||||
# suite.addTest(unittest.makeSuite(LiveTestCases, prefix=test_prefix))
|
|
||||||
runner = unittest.TextTestRunner(
|
|
||||||
stream=sys.stderr,
|
|
||||||
failfast=opts.failfast,
|
|
||||||
verbosity=2,
|
|
||||||
)
|
|
||||||
res = runner.run(suite)
|
|
||||||
if not res.wasSuccessful():
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
# Tell over-zealous test discovery frameworks that this isn't a real test.
|
|
||||||
run_live_functional_tests.__test__ = False
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from tokenserver.test_authorization import TestAuthorization
|
|
||||||
from tokenserver.test_e2e import TestE2e
|
|
||||||
from tokenserver.test_misc import TestMisc
|
|
||||||
from tokenserver.test_node_assignment import TestNodeAssignment
|
|
||||||
|
|
||||||
|
|
||||||
def run_local_tests():
|
|
||||||
test_classes = [TestAuthorization, TestMisc, TestNodeAssignment]
|
|
||||||
|
|
||||||
return run_tests(test_classes)
|
|
||||||
|
|
||||||
|
|
||||||
def run_end_to_end_tests(verbosity=1):
|
|
||||||
return run_tests([TestE2e], verbosity=verbosity)
|
|
||||||
|
|
||||||
|
|
||||||
def run_tests(test_cases, verbosity=1):
|
|
||||||
loader = unittest.TestLoader()
|
|
||||||
success = True
|
|
||||||
|
|
||||||
for test_case in test_cases:
|
|
||||||
suite = loader.loadTestsFromTestCase(test_case)
|
|
||||||
runner = unittest.TextTestRunner(verbosity=verbosity)
|
|
||||||
res = runner.run(suite)
|
|
||||||
success = success and res.wasSuccessful()
|
|
||||||
|
|
||||||
if success:
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
return 1
|
|
@ -1,10 +1,12 @@
|
|||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
from tokenserver.test_support import TestCase
|
from tokenserver.test_support import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('setup_server_local_testing_with_oauth')
|
||||||
class TestAuthorization(TestCase, unittest.TestCase):
|
class TestAuthorization(TestCase, unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestAuthorization, self).setUp()
|
super(TestAuthorization, self).setUp()
|
||||||
@ -370,15 +372,15 @@ class TestAuthorization(TestCase, unittest.TestCase):
|
|||||||
client_state='aaaa')
|
client_state='aaaa')
|
||||||
# It's ok to request a shorter-duration token.
|
# It's ok to request a shorter-duration token.
|
||||||
res = self.app.get('/1.0/sync/1.5?duration=12', headers=headers)
|
res = self.app.get('/1.0/sync/1.5?duration=12', headers=headers)
|
||||||
self.assertEquals(res.json['duration'], 12)
|
self.assertEqual(res.json['duration'], 12)
|
||||||
# But you can't exceed the server's default value.
|
# But you can't exceed the server's default value.
|
||||||
res = self.app.get('/1.0/sync/1.5?duration=4000', headers=headers)
|
res = self.app.get('/1.0/sync/1.5?duration=4000', headers=headers)
|
||||||
self.assertEquals(res.json['duration'], 3600)
|
self.assertEqual(res.json['duration'], 3600)
|
||||||
# And nonsense values are ignored.
|
# And nonsense values are ignored.
|
||||||
res = self.app.get('/1.0/sync/1.5?duration=lolwut', headers=headers)
|
res = self.app.get('/1.0/sync/1.5?duration=lolwut', headers=headers)
|
||||||
self.assertEquals(res.json['duration'], 3600)
|
self.assertEqual(res.json['duration'], 3600)
|
||||||
res = self.app.get('/1.0/sync/1.5?duration=-1', headers=headers)
|
res = self.app.get('/1.0/sync/1.5?duration=-1', headers=headers)
|
||||||
self.assertEquals(res.json['duration'], 3600)
|
self.assertEqual(res.json['duration'], 3600)
|
||||||
|
|
||||||
# Although all servers are now writing keys_changed_at, we still need this
|
# Although all servers are now writing keys_changed_at, we still need this
|
||||||
# case to be handled. See this PR for more information:
|
# case to be handled. See this PR for more information:
|
||||||
|
@ -5,6 +5,7 @@ from base64 import urlsafe_b64decode
|
|||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import jwt
|
import jwt
|
||||||
|
import pytest
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
@ -33,6 +34,7 @@ PASSWORD_LENGTH = 32
|
|||||||
SCOPE = 'https://identity.mozilla.com/apps/oldsync'
|
SCOPE = 'https://identity.mozilla.com/apps/oldsync'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('setup_server_end_to_end_testing')
|
||||||
class TestE2e(TestCase, unittest.TestCase):
|
class TestE2e(TestCase, unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from tokenserver.test_support import TestCase
|
from tokenserver.test_support import TestCase
|
||||||
@ -8,6 +9,7 @@ from tokenserver.test_support import TestCase
|
|||||||
MAX_GENERATION = 9223372036854775807
|
MAX_GENERATION = 9223372036854775807
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('setup_server_local_testing_with_oauth')
|
||||||
class TestMisc(TestCase, unittest.TestCase):
|
class TestMisc(TestCase, unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestMisc, self).setUp()
|
super(TestMisc, self).setUp()
|
||||||
@ -57,7 +59,7 @@ class TestMisc(TestCase, unittest.TestCase):
|
|||||||
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
res = self.app.get('/1.0/sync/1.5', headers=headers)
|
||||||
self.assertIn('https://example.com/1.5', res.json['api_endpoint'])
|
self.assertIn('https://example.com/1.5', res.json['api_endpoint'])
|
||||||
self.assertIn('duration', res.json)
|
self.assertIn('duration', res.json)
|
||||||
self.assertEquals(res.json['duration'], 3600)
|
self.assertEqual(res.json['duration'], 3600)
|
||||||
|
|
||||||
def test_current_user_is_the_most_up_to_date(self):
|
def test_current_user_is_the_most_up_to_date(self):
|
||||||
# Add some users
|
# Add some users
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from tokenserver.test_support import TestCase
|
from tokenserver.test_support import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('setup_server_local_testing_with_oauth')
|
||||||
class TestNodeAssignment(TestCase, unittest.TestCase):
|
class TestNodeAssignment(TestCase, unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestNodeAssignment, self).setUp()
|
super(TestNodeAssignment, self).setUp()
|
||||||
|
Loading…
Reference in New Issue
Block a user