Merge remote-tracking branch 'origin/monolith' into vue
This commit is contained in:
commit
165a5bf374
3
Pipfile
3
Pipfile
@ -17,18 +17,17 @@ flask = "*"
|
|||||||
flask-sqlalchemy = "*"
|
flask-sqlalchemy = "*"
|
||||||
flask-assets = "*"
|
flask-assets = "*"
|
||||||
flask-session = "*"
|
flask-session = "*"
|
||||||
flask-wtf = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
bandit = "*"
|
bandit = "*"
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
pytest-tornado = "*"
|
|
||||||
ipython = "*"
|
ipython = "*"
|
||||||
ipdb = "*"
|
ipdb = "*"
|
||||||
pylint = "*"
|
pylint = "*"
|
||||||
black = "*"
|
black = "*"
|
||||||
pytest-watch = "*"
|
pytest-watch = "*"
|
||||||
factory-boy = "*"
|
factory-boy = "*"
|
||||||
|
pytest-flask = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.6"
|
python_version = "3.6"
|
||||||
|
76
Pipfile.lock
generated
76
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "e04e11d9bd5c1dcc725de48b20902f5c416417e73774e557e45af7bd0c147ff5"
|
"sha256": "9f17530cb96833c424369b9cac305cb43a817cdf19605aaedeb2d98566302857"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -62,14 +62,6 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.3.2"
|
"version": "==2.3.2"
|
||||||
},
|
},
|
||||||
"flask-wtf": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36",
|
|
||||||
"sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.14.2"
|
|
||||||
},
|
|
||||||
"itsdangerous": {
|
"itsdangerous": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||||
@ -168,7 +160,6 @@
|
|||||||
"sha256:1d936da41ee06216d89fdc7ead1ee9a5da2811a8787515a976b646e110c3f622",
|
"sha256:1d936da41ee06216d89fdc7ead1ee9a5da2811a8787515a976b646e110c3f622",
|
||||||
"sha256:e4ef42e82b0b493c5849eed98b5ab49d6767caf982127e9a33167f1153b36cc5"
|
"sha256:e4ef42e82b0b493c5849eed98b5ab49d6767caf982127e9a33167f1153b36cc5"
|
||||||
],
|
],
|
||||||
"markers": "python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.3.*'",
|
|
||||||
"version": "==2018.5"
|
"version": "==2018.5"
|
||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
@ -350,9 +341,16 @@
|
|||||||
"sha256:0e9a1227a3a0f3297a485715e72ee6eb77081b17b629367042b586e38c03c867",
|
"sha256:0e9a1227a3a0f3297a485715e72ee6eb77081b17b629367042b586e38c03c867",
|
||||||
"sha256:b4840807a94a3bad0217d6ed3f9b65a1cc6e1db1c99e1184673056ae2c0a4c4d"
|
"sha256:b4840807a94a3bad0217d6ed3f9b65a1cc6e1db1c99e1184673056ae2c0a4c4d"
|
||||||
],
|
],
|
||||||
"markers": "python_version != '3.1.*' and python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.0.*'",
|
|
||||||
"version": "==0.8.17"
|
"version": "==0.8.17"
|
||||||
},
|
},
|
||||||
|
"flask": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
|
||||||
|
"sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.0.2"
|
||||||
|
},
|
||||||
"gitdb2": {
|
"gitdb2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:87783b7f4a8f6b71c7fe81d32179b3c8781c1a7d6fa0c69bff2f315b00aff4f8",
|
"sha256:87783b7f4a8f6b71c7fe81d32179b3c8781c1a7d6fa0c69bff2f315b00aff4f8",
|
||||||
@ -395,9 +393,14 @@
|
|||||||
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
|
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
|
||||||
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
|
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
|
||||||
],
|
],
|
||||||
"markers": "python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'",
|
|
||||||
"version": "==4.3.4"
|
"version": "==4.3.4"
|
||||||
},
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||||
|
],
|
||||||
|
"version": "==0.24"
|
||||||
|
},
|
||||||
"jedi": {
|
"jedi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b409ed0f6913a701ed474a614a3bb46e6953639033e31f769ca7581da5bd1ec1",
|
"sha256:b409ed0f6913a701ed474a614a3bb46e6953639033e31f769ca7581da5bd1ec1",
|
||||||
@ -405,6 +408,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.12.1"
|
"version": "==0.12.1"
|
||||||
},
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||||
|
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||||
|
],
|
||||||
|
"version": "==2.10"
|
||||||
|
},
|
||||||
"lazy-object-proxy": {
|
"lazy-object-proxy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
|
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
|
||||||
@ -439,6 +449,12 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.3.1"
|
"version": "==1.3.1"
|
||||||
},
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
|
||||||
|
],
|
||||||
|
"version": "==1.0"
|
||||||
|
},
|
||||||
"mccabe": {
|
"mccabe": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||||
@ -494,7 +510,6 @@
|
|||||||
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
|
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
|
||||||
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
|
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
|
||||||
],
|
],
|
||||||
"markers": "python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'",
|
|
||||||
"version": "==0.7.1"
|
"version": "==0.7.1"
|
||||||
},
|
},
|
||||||
"prompt-toolkit": {
|
"prompt-toolkit": {
|
||||||
@ -517,7 +532,6 @@
|
|||||||
"sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
|
"sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
|
||||||
"sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
|
"sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
|
||||||
],
|
],
|
||||||
"markers": "python_version != '3.3.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'",
|
|
||||||
"version": "==1.5.4"
|
"version": "==1.5.4"
|
||||||
},
|
},
|
||||||
"pygments": {
|
"pygments": {
|
||||||
@ -543,13 +557,13 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==3.7.0"
|
"version": "==3.7.0"
|
||||||
},
|
},
|
||||||
"pytest-tornado": {
|
"pytest-flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:214fc59d06fb81696fce3028b56dff522168ac1cfc784cfc0077b7b1e425b4cd",
|
"sha256:2c5a36f9033ef8b6f85ddbefaebdd4f89197fc283f94b20dfe1a1beba4b77f03",
|
||||||
"sha256:687c1f9c0f5bda7808c1e53c14bbebfe4fb9452e34cc95b440e598d4724265e0"
|
"sha256:657c7de386215ab0230bee4d76ace0339ae82fcbb34e134e17a29f65032eef03"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.5.0"
|
"version": "==0.10.0"
|
||||||
},
|
},
|
||||||
"pytest-watch": {
|
"pytest-watch": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -567,11 +581,15 @@
|
|||||||
},
|
},
|
||||||
"pyyaml": {
|
"pyyaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
"sha256:1cbc199009e78f92d9edf554be4fe40fb7b0bef71ba688602a00e97a51909110",
|
||||||
"sha256:254bf6fda2b7c651837acb2c718e213df29d531eebf00edb54743d10bcb694eb",
|
"sha256:254bf6fda2b7c651837acb2c718e213df29d531eebf00edb54743d10bcb694eb",
|
||||||
"sha256:3108529b78577327d15eec243f0ff348a0640b0c3478d67ad7f5648f93bac3e2",
|
"sha256:3108529b78577327d15eec243f0ff348a0640b0c3478d67ad7f5648f93bac3e2",
|
||||||
"sha256:3c17fb92c8ba2f525e4b5f7941d850e7a48c3a59b32d331e2502a3cdc6648e76",
|
"sha256:3c17fb92c8ba2f525e4b5f7941d850e7a48c3a59b32d331e2502a3cdc6648e76",
|
||||||
|
"sha256:6f89b5c95e93945b597776163403d47af72d243f366bf4622ff08bdfd1c950b7",
|
||||||
"sha256:8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b",
|
"sha256:8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b",
|
||||||
"sha256:c8a88edd93ee29ede719080b2be6cb2333dfee1dccba213b422a9c8e97f2967b"
|
"sha256:be622cc81696e24d0836ba71f6272a2b5767669b0d79fdcf0295d51ac2e156c8",
|
||||||
|
"sha256:c8a88edd93ee29ede719080b2be6cb2333dfee1dccba213b422a9c8e97f2967b",
|
||||||
|
"sha256:f39411e380e2182ad33be039e8ee5770a5d9efe01a2bfb7ae58d9ba31c4a2a9d"
|
||||||
],
|
],
|
||||||
"version": "==4.2b4"
|
"version": "==4.2b4"
|
||||||
},
|
},
|
||||||
@ -615,19 +633,6 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.9.4"
|
"version": "==0.9.4"
|
||||||
},
|
},
|
||||||
"tornado": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1c0816fc32b7d31b98781bd8ebc7a9726d7dce67407dc353a2e66e697e138448",
|
|
||||||
"sha256:4f66a2172cb947387193ca4c2c3e19131f1c70fa8be470ddbbd9317fd0801582",
|
|
||||||
"sha256:5327ba1a6c694e0149e7d9126426b3704b1d9d520852a3e4aa9fc8fe989e4046",
|
|
||||||
"sha256:6a7e8657618268bb007646b9eae7661d0b57f13efc94faa33cd2588eae5912c9",
|
|
||||||
"sha256:a9b14804783a1d77c0bd6c66f7a9b1196cbddfbdf8bceb64683c5ae60bd1ec6f",
|
|
||||||
"sha256:c58757e37c4a3172949c99099d4d5106e4d7b63aa0617f9bb24bfbff712c7866",
|
|
||||||
"sha256:d8984742ce86c0855cccecd5c6f54a9f7532c983947cff06f3a0e2115b47f85c"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==5.1"
|
|
||||||
},
|
|
||||||
"traitlets": {
|
"traitlets": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
||||||
@ -684,6 +689,13 @@
|
|||||||
],
|
],
|
||||||
"version": "==0.1.7"
|
"version": "==0.1.7"
|
||||||
},
|
},
|
||||||
|
"werkzeug": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||||
|
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||||
|
],
|
||||||
|
"version": "==0.14.1"
|
||||||
|
},
|
||||||
"wrapt": {
|
"wrapt": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6"
|
"sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6"
|
||||||
|
@ -72,6 +72,12 @@ To log in as one of them, navigate to `/login-dev?username=<lowercase name>`. Fo
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
Tests require a test database:
|
||||||
|
|
||||||
|
```
|
||||||
|
createdb atat_test
|
||||||
|
```
|
||||||
|
|
||||||
To run lint, static analysis, and unit tests:
|
To run lint, static analysis, and unit tests:
|
||||||
|
|
||||||
script/test
|
script/test
|
||||||
|
@ -12,7 +12,7 @@ from atst.routes.workspaces import bp as workspace_routes
|
|||||||
from atst.routes.requests import requests_bp
|
from atst.routes.requests import requests_bp
|
||||||
|
|
||||||
|
|
||||||
ENV = os.getenv("TORNADO_ENV", "dev")
|
ENV = os.getenv("FLASK_ENV", "dev")
|
||||||
|
|
||||||
|
|
||||||
def make_app(config):
|
def make_app(config):
|
||||||
@ -40,7 +40,7 @@ def make_app(config):
|
|||||||
|
|
||||||
def make_flask_callbacks(app):
|
def make_flask_callbacks(app):
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def set_globals():
|
def _set_globals():
|
||||||
g.navigationContext = (
|
g.navigationContext = (
|
||||||
"workspace"
|
"workspace"
|
||||||
if re.match("\/workspaces\/[A-Za-z0-9]*", request.url)
|
if re.match("\/workspaces\/[A-Za-z0-9]*", request.url)
|
||||||
@ -84,6 +84,10 @@ def make_config():
|
|||||||
config = ConfigParser()
|
config = ConfigParser()
|
||||||
config.optionxform = str
|
config.optionxform = str
|
||||||
|
|
||||||
|
config_files = [BASE_CONFIG_FILENAME, ENV_CONFIG_FILENAME]
|
||||||
|
if OVERRIDE_CONFIG_FILENAME:
|
||||||
|
config_files.append(OVERRIDE_CONFIG_FILENAME)
|
||||||
|
|
||||||
config_files = [BASE_CONFIG_FILENAME, ENV_CONFIG_FILENAME]
|
config_files = [BASE_CONFIG_FILENAME, ENV_CONFIG_FILENAME]
|
||||||
if OVERRIDE_CONFIG_FILENAME:
|
if OVERRIDE_CONFIG_FILENAME:
|
||||||
config_files.append(OVERRIDE_CONFIG_FILENAME)
|
config_files.append(OVERRIDE_CONFIG_FILENAME)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from flask_assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
from atst.home import home
|
|
||||||
|
|
||||||
environment = Environment()
|
environment = Environment()
|
||||||
|
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
from sqlalchemy.dialects.postgresql import insert
|
from sqlalchemy.dialects.postgresql import insert
|
||||||
|
|
||||||
|
from atst.database import db
|
||||||
from atst.models.pe_number import PENumber
|
from atst.models.pe_number import PENumber
|
||||||
from .exceptions import NotFoundError
|
from .exceptions import NotFoundError
|
||||||
|
|
||||||
|
|
||||||
class PENumbers(object):
|
class PENumbers(object):
|
||||||
def __init__(self, db_session):
|
|
||||||
self.db_session = db_session
|
|
||||||
|
|
||||||
def get(self, number):
|
@classmethod
|
||||||
pe_number = self.db_session.query(PENumber).get(number)
|
def get(cls, number):
|
||||||
|
pe_number = db.session.query(PENumber).get(number)
|
||||||
if not pe_number:
|
if not pe_number:
|
||||||
raise NotFoundError("pe_number")
|
raise NotFoundError("pe_number")
|
||||||
|
|
||||||
return pe_number
|
return pe_number
|
||||||
|
|
||||||
def create_many(self, list_of_pe_numbers):
|
@classmethod
|
||||||
|
def create_many(cls, list_of_pe_numbers):
|
||||||
stmt = insert(PENumber).values(list_of_pe_numbers)
|
stmt = insert(PENumber).values(list_of_pe_numbers)
|
||||||
do_update = stmt.on_conflict_do_update(
|
do_update = stmt.on_conflict_do_update(
|
||||||
index_elements=["number"], set_=dict(description=stmt.excluded.description)
|
index_elements=["number"], set_=dict(description=stmt.excluded.description)
|
||||||
)
|
)
|
||||||
self.db_session.execute(do_update)
|
db.session.execute(do_update)
|
||||||
self.db_session.commit()
|
db.session.commit()
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import tornado.gen
|
|
||||||
from sqlalchemy import exists, and_
|
from sqlalchemy import exists, and_
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
from sqlalchemy.orm.attributes import flag_modified
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
import tornado
|
|
||||||
from tornado.gen import Return
|
|
||||||
from wtforms.fields.html5 import EmailField
|
from wtforms.fields.html5 import EmailField
|
||||||
from wtforms.fields import StringField, SelectField
|
from wtforms.fields import StringField, SelectField
|
||||||
from wtforms.form import Form
|
from wtforms.form import Form
|
||||||
from wtforms.validators import Required, Email
|
from wtforms.validators import Required, Email
|
||||||
|
|
||||||
from atst.domain.exceptions import NotFoundError
|
from atst.domain.exceptions import NotFoundError
|
||||||
|
from atst.domain.pe_numbers import PENumbers
|
||||||
|
|
||||||
from .fields import NewlineListField
|
from .fields import NewlineListField
|
||||||
from .forms import ValidatedForm
|
from .forms import ValidatedForm
|
||||||
@ -40,9 +39,9 @@ def suggest_pe_id(pe_id):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def validate_pe_id(field, existing_request, pe_numbers_repo):
|
def validate_pe_id(field, existing_request):
|
||||||
try:
|
try:
|
||||||
pe_number = pe_numbers_repo.get(field.data)
|
pe_number = PENumbers.get(field.data)
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
suggestion = suggest_pe_id(field.data)
|
suggestion = suggest_pe_id(field.data)
|
||||||
error_str = (
|
error_str = (
|
||||||
@ -50,17 +49,17 @@ def validate_pe_id(field, existing_request, pe_numbers_repo):
|
|||||||
"If you have double checked it you can submit anyway. "
|
"If you have double checked it you can submit anyway. "
|
||||||
"Your request will need to go through a manual review."
|
"Your request will need to go through a manual review."
|
||||||
).format('Did you mean "{}"? '.format(suggestion) if suggestion else "")
|
).format('Did you mean "{}"? '.format(suggestion) if suggestion else "")
|
||||||
field.errors.append(error_str)
|
field.errors += (error_str,)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class FinancialForm(ValidatedForm):
|
class FinancialForm(ValidatedForm):
|
||||||
def perform_extra_validation(self, existing_request, pe_numbers_repo):
|
def perform_extra_validation(self, existing_request):
|
||||||
valid = True
|
valid = True
|
||||||
if not existing_request or existing_request.get("pe_id") != self.pe_id.data:
|
if not existing_request or existing_request.get("pe_id") != self.pe_id.data:
|
||||||
valid = yield validate_pe_id(self.pe_id, existing_request, pe_numbers_repo)
|
valid = validate_pe_id(self.pe_id, existing_request)
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
task_order_id = StringField(
|
task_order_id = StringField(
|
||||||
|
@ -47,6 +47,7 @@ class RequestFinancialVerification(BaseHandler):
|
|||||||
|
|
||||||
if form.validate():
|
if form.validate():
|
||||||
yield self.update_request(request_id, form.data)
|
yield self.update_request(request_id, form.data)
|
||||||
|
# pylint: disable=E1121
|
||||||
valid = yield form.perform_extra_validation(
|
valid = yield form.perform_extra_validation(
|
||||||
existing_request.body.get("financial_verification"),
|
existing_request.body.get("financial_verification"),
|
||||||
self.pe_numbers_repo,
|
self.pe_numbers_repo,
|
||||||
|
@ -7,6 +7,11 @@ bp = Blueprint("atst", __name__)
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/")
|
@bp.route("/")
|
||||||
|
def root():
|
||||||
|
return render_template("root.html")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/home")
|
||||||
def home():
|
def home():
|
||||||
return render_template("home.html")
|
return render_template("home.html")
|
||||||
|
|
||||||
@ -14,3 +19,8 @@ def home():
|
|||||||
@bp.route("/styleguide")
|
@bp.route("/styleguide")
|
||||||
def styleguide():
|
def styleguide():
|
||||||
return render_template("styleguide.html")
|
return render_template("styleguide.html")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<path:path>')
|
||||||
|
def catch_all(path):
|
||||||
|
return render_template("{}.html".format(path))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from flask import render_template, redirect, url_for
|
from flask import render_template, redirect, url_for
|
||||||
|
from flask import request as http_request
|
||||||
|
|
||||||
from . import requests_bp
|
from . import requests_bp
|
||||||
from atst.domain.requests import Requests
|
from atst.domain.requests import Requests
|
||||||
@ -29,7 +30,7 @@ def update_financial_verification(request_id):
|
|||||||
existing_request.body.get("financial_verification")
|
existing_request.body.get("financial_verification")
|
||||||
)
|
)
|
||||||
if valid:
|
if valid:
|
||||||
redirect(url_for("requests.financial_verification_submitted"))
|
return redirect(url_for("requests.financial_verification_submitted"))
|
||||||
else:
|
else:
|
||||||
return render_template(
|
return render_template(
|
||||||
"requests/financial_verification.html", **rerender_args
|
"requests/financial_verification.html", **rerender_args
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from atst.domain.requests import Requests
|
from atst.domain.requests import Requests
|
||||||
from atst.forms.financial import FinancialForm
|
|
||||||
from atst.forms.request import RequestForm
|
from atst.forms.request import RequestForm
|
||||||
from atst.forms.org import OrgForm
|
from atst.forms.org import OrgForm
|
||||||
from atst.forms.poc import POCForm
|
from atst.forms.poc import POCForm
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from flask import Blueprint, render_template
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
from atst.domain.workspaces import Projects, Members
|
from atst.domain.workspaces import Projects, Members
|
||||||
from atst.database import db
|
|
||||||
|
|
||||||
bp = Blueprint("workspaces", __name__)
|
bp = Blueprint("workspaces", __name__)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ def navigationContext(self):
|
|||||||
|
|
||||||
|
|
||||||
def dev(self):
|
def dev(self):
|
||||||
return os.getenv("TORNADO_ENV", "dev") == "dev"
|
return os.getenv("FLASK_ENV", "dev") == "dev"
|
||||||
|
|
||||||
|
|
||||||
def matchesPath(self, href):
|
def matchesPath(self, href):
|
||||||
|
2
config/test.ini
Normal file
2
config/test.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[default]
|
||||||
|
PGDATABASE = atat_test
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
source "$(dirname "${0}")"/../script/include/global_header.inc.sh
|
source "$(dirname "${0}")"/../script/include/global_header.inc.sh
|
||||||
|
|
||||||
|
export FLASK_ENV=test
|
||||||
|
|
||||||
# Define all relevant python files and directories for this app
|
# Define all relevant python files and directories for this app
|
||||||
PYTHON_FILES="./app.py ./atst ./config"
|
PYTHON_FILES="./app.py ./atst ./config"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html.to" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% end %}
|
{% endblock %}
|
||||||
|
|
@ -16,40 +16,39 @@
|
|||||||
level="info"
|
level="info"
|
||||||
) %}
|
) %}
|
||||||
|
|
||||||
<div class='panel member-card'>
|
|
||||||
<div class='member-card__header'>
|
<div class='panel'>
|
||||||
<h1 class='member-card__heading'>{{ member_name }}</h1>
|
<div class='panel__heading'>
|
||||||
<dl><dt>Workspace Role</dt> <dd><span class='label label--info'>{{member_workspace_role}}</span></dd></dl>
|
<h1 class='h2'>
|
||||||
</div>
|
{% if is_new_member %}
|
||||||
<div class='member-card__details'>
|
Add new member
|
||||||
<dl>
|
{% else %}
|
||||||
<div>
|
{{ member_name }}
|
||||||
<dt>DOD ID:</dt>
|
{% end %}
|
||||||
<dd>{{ member_id }}</dd>
|
</h1>
|
||||||
</div>
|
<div>Workspace Role</span> <span class="label">{{member_workspace_role}}</div>
|
||||||
<div>
|
|
||||||
<dt>Email:</dt>
|
|
||||||
<dd>{{ member_email }}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
<a href='#' class='icon-link'>edit account details</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class='search-bar'>
|
|
||||||
<div class='usa-input search-input'>
|
<div class='panel panel__actions'>
|
||||||
<label for='project-search'>Search by project name</label>
|
<div class='row'>
|
||||||
<input type='search' id='project-search' name='project-search' placeholder="Search by project name"/>
|
<div class='col col--grow'>
|
||||||
|
<form class="usa-search usa-search-small">
|
||||||
|
<label class="usa-sr-only" for="search-field-small">Search small</label>
|
||||||
|
<input id="search-field-small" type="search" name="search" placeholder="Search by project name">
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<span class="hide">Search</span>
|
<span class="usa-sr-only">Search</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class='block-list project-list-item'>
|
<div class='block-list project-list-item'>
|
||||||
<header class='block-list__header'>
|
<header class='block-list__header'>
|
||||||
<h2 class='block-list__title'>{% module Icon('arrow-down') %} Code.mil</h2>
|
<h2 class='block-list__title'>Code.mil</h2>
|
||||||
<span><a href="#" class="icon-link icon-link--danger">revoke all access</a></span>
|
<a class="block-list__header__link icon-link icon-link--danger">revoke all access</a>
|
||||||
</header>
|
</header>
|
||||||
<ul>
|
<ul>
|
||||||
<li class='block-list__item project-list-item__environment'>
|
<li class='block-list__item project-list-item__environment'>
|
||||||
@ -57,15 +56,7 @@
|
|||||||
Development
|
Development
|
||||||
</span>
|
</span>
|
||||||
<div class='project-list-item__environment__actions'>
|
<div class='project-list-item__environment__actions'>
|
||||||
<span class="label">no access </span><a href="#" class="icon-link">set role</a>
|
<span>no access</span><a href="#" class="icon-link">set role</a>
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class='block-list__item project-list-item__environment'>
|
|
||||||
<span class='project-list-item__environment'>
|
|
||||||
Sandbox
|
|
||||||
</span>
|
|
||||||
<div class='project-list-item__environment__actions'>
|
|
||||||
<span class="label">no access</span><a href="#" class="icon-link">set role</a>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class='block-list__item project-list-item__environment'>
|
<li class='block-list__item project-list-item__environment'>
|
||||||
@ -73,17 +64,11 @@
|
|||||||
Production
|
Production
|
||||||
</span>
|
</span>
|
||||||
<div class='project-list-item__environment__actions'>
|
<div class='project-list-item__environment__actions'>
|
||||||
<span class="label label--success">Billing</span><a href="#" class="icon-link">set role</a>
|
<span>Billing</span><a href="#" class="icon-link">set role</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='block-list project-list-item'>
|
</ul>
|
||||||
<header class='block-list__header'>
|
|
||||||
<h2 class='block-list__title'>{% module Icon('arrow-right') %} Digital Dojo</h2>
|
|
||||||
<span class="label">no access</span>
|
|
||||||
</header>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='action-group'>
|
<div class='action-group'>
|
||||||
|
@ -1,44 +1,42 @@
|
|||||||
{% from "components.html" import SidenavItem %}
|
|
||||||
|
|
||||||
<nav class='sidenav workspace-navigation'>
|
<nav class='sidenav workspace-navigation'>
|
||||||
<ul>
|
<ul>
|
||||||
{{ SidenavItem(
|
{% module SidenavItem(
|
||||||
"Projects",
|
"Projects",
|
||||||
href=url_for("workspaces.workspace_projects", workspace_id="123456"),
|
href=reverse_url('workspace_projects', '123456'),
|
||||||
active=g.matchesPath('\/workspaces\/[A-Za-z0-9]*\/projects'),
|
active=matchesPath('\/workspaces\/[A-Za-z0-9]*\/projects'),
|
||||||
subnav=[
|
subnav=[
|
||||||
{
|
{
|
||||||
"label": "Add New Project",
|
"label": "Add New Project",
|
||||||
"href":"/",
|
"href":"/",
|
||||||
"active": g.matchesPath('workspaces/projects/new'),
|
"active": matchesPath('workspaces/projects/new'),
|
||||||
"icon": "plus"
|
"icon": "plus"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)}}
|
)%}
|
||||||
|
|
||||||
{{ SidenavItem(
|
{% module SidenavItem(
|
||||||
"Members",
|
"Members",
|
||||||
href="/workspaces/{}/members".format('123456'),
|
href=reverse_url('workspace_members', '123456'),
|
||||||
active=g.matchesPath('\/workspaces\/[A-Za-z0-9]*\/members'),
|
active=matchesPath('\/workspaces\/[A-Za-z0-9]*\/members'),
|
||||||
subnav=[
|
subnav=[
|
||||||
{
|
{
|
||||||
"label": "Add New Member",
|
"label": "Add New Member",
|
||||||
"href": "",
|
"href": "",
|
||||||
"active": g.matchesPath('/workspaces/members/new'),
|
"active": matchesPath('/workspaces/members/new'),
|
||||||
"icon": "plus"
|
"icon": "plus"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Editing Member",
|
"label": "Editing Member",
|
||||||
"href": "",
|
"href": "",
|
||||||
"active": g.matchesPath('/workspaces/123456/members/789/edit')
|
"active": matchesPath('/workspaces/123456/members/789/edit')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)}}
|
)%}
|
||||||
|
|
||||||
{{ SidenavItem(
|
{% module SidenavItem(
|
||||||
"Funding & Reports",
|
"Funding & Reports",
|
||||||
href=url_for("workspaces.workspace_projects", workspace_id="123456"),
|
href=reverse_url('workspace_projects', '123456'),
|
||||||
active=g.matchesPath('\/workspaces\/[A-Za-z0-9]*\/reports')
|
active=matchesPath('\/workspaces\/[A-Za-z0-9]*\/reports')
|
||||||
)}}
|
)%}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html.to" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% end %}
|
{% endblock %}
|
||||||
|
|
@ -3,10 +3,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>{% block title %}JEDI{% end %}</title>
|
<title>{% block title %}JEDI{% endblock %}</title>
|
||||||
{% for url in assets['css'].urls() %}
|
{% assets "css" %}
|
||||||
<link rel="stylesheet" href="{{ url }}" type="text/css">
|
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">
|
||||||
{% end %}
|
{% endassets %}
|
||||||
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -17,11 +17,11 @@
|
|||||||
|
|
||||||
<h1 class="usa-display">JEDI</h1>
|
<h1 class="usa-display">JEDI</h1>
|
||||||
|
|
||||||
<a class="usa-button" href='{{ config['default'].get('cac_url','https://cac.atat.codes') }}'><span>Sign In with CAC</span></a>
|
<a class="usa-button" href='{{ config.get('cac_url','https://cac.atat.codes') }}'><span>Sign In with CAC</span></a>
|
||||||
<button class="usa-button" disabled>Sign In via MFA</button>
|
<button class="usa-button" disabled>Sign In via MFA</button>
|
||||||
{% if dev() %}
|
{% if g.dev %}
|
||||||
<a class="usa-button usa-button-secondary" href='/login-dev'><span>DEV Login</span></a>
|
<a class="usa-button usa-button-secondary" href='/login-dev'><span>DEV Login</span></a>
|
||||||
{% end %}
|
{% endif %}
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html.to" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% end %}
|
{% endblock %}
|
||||||
|
|
@ -1,17 +1,15 @@
|
|||||||
{% from "components.html" import EmptyState %}
|
{% extends "base_workspace.html.to" %}
|
||||||
|
|
||||||
{% extends "base_workspace.html" %}
|
|
||||||
|
|
||||||
{% block workspace_content %}
|
{% block workspace_content %}
|
||||||
|
|
||||||
{% if not members %}
|
{% if not members %}
|
||||||
|
|
||||||
{{ EmptyState(
|
{% module EmptyState(
|
||||||
'There are currently no members in this Workspace.',
|
'There are currently no members in this Workspace.',
|
||||||
actionLabel='Invite a new Member',
|
actionLabel='Invite a new Member',
|
||||||
actionHref='/members/new',
|
actionHref='/members/new',
|
||||||
icon='avatar'
|
icon='avatar'
|
||||||
)}}
|
)%}
|
||||||
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -61,17 +59,17 @@
|
|||||||
{% for m in members %}
|
{% for m in members %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/workspaces/123456/members/789/edit" class="icon-link icon-link--large">{{ m['first_name'] }} {{ m['last_name'] }}</a></td>
|
<td><a href="/workspaces/123456/members/789/edit" class="icon-link icon-link--large">{{ m['first_name'] }} {{ m['last_name'] }}</a></td>
|
||||||
<td class='table-cell--shrink'>{% if m['num_projects'] == '0' %} <span class="label label--info">No Project Access</span> {% endif %}</td>
|
<td class='table-cell--shrink'>{% if m['num_projects'] == '0' %} <span class="label label--info">No Project Access</span> {% end %}</td>
|
||||||
<td>{{ m['status'] }}</a></td>
|
<td>{{ m['status'] }}</a></td>
|
||||||
<td>{{ m['workspace_role'] }}</a></td>
|
<td>{{ m['workspace_role'] }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% end %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% end %}
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% end %}
|
||||||
|
|
||||||
|
@ -1,44 +1,72 @@
|
|||||||
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
import alembic.config
|
||||||
|
import alembic.command
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
|
|
||||||
from atst.app import make_app, make_deps, make_config
|
from atst.app import make_app, make_config
|
||||||
from atst.database import make_db
|
|
||||||
from tests.mocks import MockApiClient
|
from tests.mocks import MockApiClient
|
||||||
from atst.sessions import DictSessions
|
from atst.sessions import DictSessions
|
||||||
|
from atst.models import Base
|
||||||
|
from atst.database import db as _db
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(scope='session')
|
||||||
def app(db):
|
def app(request):
|
||||||
TEST_DEPS = {
|
|
||||||
"authnid_client": MockApiClient("authnid"),
|
|
||||||
"sessions": DictSessions(),
|
|
||||||
"db_session": db
|
|
||||||
}
|
|
||||||
|
|
||||||
config = make_config()
|
config = make_config()
|
||||||
deps = make_deps(config)
|
|
||||||
deps.update(TEST_DEPS)
|
|
||||||
|
|
||||||
return make_app(config, deps)
|
_app = make_app(config)
|
||||||
|
|
||||||
|
ctx = _app.app_context()
|
||||||
|
ctx.push()
|
||||||
|
|
||||||
|
def teardown():
|
||||||
|
ctx.pop()
|
||||||
|
|
||||||
|
return _app
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
def apply_migrations():
|
||||||
def db():
|
"""Applies all alembic migrations."""
|
||||||
|
alembic_config = os.path.join(os.path.dirname(__file__), "../", "alembic.ini")
|
||||||
|
config = alembic.config.Config(alembic_config)
|
||||||
|
app_config = make_config()
|
||||||
|
config.set_main_option('sqlalchemy.url', app_config["DATABASE_URI"])
|
||||||
|
alembic.command.upgrade(config, 'head')
|
||||||
|
|
||||||
# Override db with a new SQLAlchemy session so that we can rollback
|
|
||||||
# each test's transaction.
|
@pytest.fixture(scope='session')
|
||||||
# Inspiration: https://docs.sqlalchemy.org/en/latest/orm/session_transaction.html#session-external-transaction
|
def db(app, request):
|
||||||
config = make_config()
|
|
||||||
database = make_db(config)
|
def teardown():
|
||||||
connection = database.get_bind().connect()
|
_db.drop_all()
|
||||||
|
|
||||||
|
_db.app = app
|
||||||
|
|
||||||
|
apply_migrations()
|
||||||
|
|
||||||
|
yield _db
|
||||||
|
|
||||||
|
_db.drop_all()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
|
def session(db, request):
|
||||||
|
"""Creates a new database session for a test."""
|
||||||
|
connection = db.engine.connect()
|
||||||
transaction = connection.begin()
|
transaction = connection.begin()
|
||||||
db = scoped_session(sessionmaker(bind=connection))
|
|
||||||
|
|
||||||
yield db
|
options = dict(bind=connection, binds={})
|
||||||
|
session = db.create_scoped_session(options=options)
|
||||||
|
|
||||||
|
db.session = session
|
||||||
|
|
||||||
|
yield session
|
||||||
|
|
||||||
db.close()
|
|
||||||
transaction.rollback()
|
transaction.rollback()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
session.remove()
|
||||||
|
|
||||||
|
|
||||||
class DummyForm(dict):
|
class DummyForm(dict):
|
||||||
|
@ -6,36 +6,32 @@ from atst.domain.pe_numbers import PENumbers
|
|||||||
from tests.factories import PENumberFactory
|
from tests.factories import PENumberFactory
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def pe_numbers(db):
|
|
||||||
return PENumbers(db)
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def new_pe_number(db):
|
def new_pe_number(session):
|
||||||
def make_pe_number(**kwargs):
|
def make_pe_number(**kwargs):
|
||||||
pen = PENumberFactory.create(**kwargs)
|
pen = PENumberFactory.create(**kwargs)
|
||||||
db.add(pen)
|
session.add(pen)
|
||||||
db.commit()
|
session.commit()
|
||||||
|
|
||||||
return pen
|
return pen
|
||||||
|
|
||||||
return make_pe_number
|
return make_pe_number
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_pe_number(pe_numbers, new_pe_number):
|
def test_can_get_pe_number(new_pe_number):
|
||||||
new_pen = new_pe_number(number="0701367F", description="Combat Support - Offensive")
|
new_pen = new_pe_number(number="0701367F", description="Combat Support - Offensive")
|
||||||
pen = pe_numbers.get(new_pen.number)
|
pen = PENumbers.get(new_pen.number)
|
||||||
|
|
||||||
assert pen.number == new_pen.number
|
assert pen.number == new_pen.number
|
||||||
|
|
||||||
|
|
||||||
def test_nonexistent_pe_number_raises(pe_numbers):
|
def test_nonexistent_pe_number_raises():
|
||||||
with pytest.raises(NotFoundError):
|
with pytest.raises(NotFoundError):
|
||||||
pe_numbers.get("some fake number")
|
PENumbers.get("some fake number")
|
||||||
|
|
||||||
def test_create_many(pe_numbers):
|
def test_create_many():
|
||||||
pen_list = [['123456', 'Land Speeder'], ['7891011', 'Lightsaber']]
|
pen_list = [['123456', 'Land Speeder'], ['7891011', 'Lightsaber']]
|
||||||
pe_numbers.create_many(pen_list)
|
PENumbers.create_many(pen_list)
|
||||||
|
|
||||||
assert pe_numbers.get(pen_list[0][0])
|
assert PENumbers.get(pen_list[0][0])
|
||||||
assert pe_numbers.get(pen_list[1][0])
|
assert PENumbers.get(pen_list[1][0])
|
||||||
|
@ -8,14 +8,14 @@ from tests.factories import RequestFactory
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def requests(db):
|
def requests(session):
|
||||||
return Requests(db)
|
return Requests()
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def new_request(db):
|
def new_request(session):
|
||||||
created_request = RequestFactory.create()
|
created_request = RequestFactory.create()
|
||||||
db.add(created_request)
|
session.add(created_request)
|
||||||
db.commit()
|
session.commit()
|
||||||
|
|
||||||
return created_request
|
return created_request
|
||||||
|
|
||||||
@ -31,25 +31,22 @@ def test_nonexistent_request_raises(requests):
|
|||||||
requests.get(uuid4())
|
requests.get(uuid4())
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_auto_approve_less_than_1m(requests, new_request):
|
def test_auto_approve_less_than_1m(requests, new_request):
|
||||||
new_request.body = {"details_of_use": {"dollar_value": 999999}}
|
new_request.body = {"details_of_use": {"dollar_value": 999999}}
|
||||||
request = yield requests.submit(new_request)
|
request = requests.submit(new_request)
|
||||||
|
|
||||||
assert request.status == 'approved'
|
assert request.status == 'approved'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_dont_auto_approve_if_dollar_value_is_1m_or_above(requests, new_request):
|
def test_dont_auto_approve_if_dollar_value_is_1m_or_above(requests, new_request):
|
||||||
new_request.body = {"details_of_use": {"dollar_value": 1000000}}
|
new_request.body = {"details_of_use": {"dollar_value": 1000000}}
|
||||||
request = yield requests.submit(new_request)
|
request = requests.submit(new_request)
|
||||||
|
|
||||||
assert request.status == 'submitted'
|
assert request.status == 'submitted'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_dont_auto_approve_if_no_dollar_value_specified(requests, new_request):
|
def test_dont_auto_approve_if_no_dollar_value_specified(requests, new_request):
|
||||||
new_request.body = {"details_of_use": {}}
|
new_request.body = {"details_of_use": {}}
|
||||||
request = yield requests.submit(new_request)
|
request = requests.submit(new_request)
|
||||||
|
|
||||||
assert request.status == 'submitted'
|
assert request.status == 'submitted'
|
||||||
|
@ -4,8 +4,8 @@ from atst.domain.exceptions import NotFoundError
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def roles_repo(db):
|
def roles_repo(session):
|
||||||
return Roles(db)
|
return Roles(session)
|
||||||
|
|
||||||
|
|
||||||
def test_get_all_roles(roles_repo):
|
def test_get_all_roles(roles_repo):
|
||||||
|
@ -7,15 +7,15 @@ from tests.factories import TaskOrderFactory
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def task_orders(db):
|
def task_orders(session):
|
||||||
return TaskOrders(db)
|
return TaskOrders(session)
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def new_task_order(db):
|
def new_task_order(session):
|
||||||
def make_task_order(**kwargs):
|
def make_task_order(**kwargs):
|
||||||
to = TaskOrderFactory.create(**kwargs)
|
to = TaskOrderFactory.create(**kwargs)
|
||||||
db.add(to)
|
session.add(to)
|
||||||
db.commit()
|
session.commit()
|
||||||
|
|
||||||
return to
|
return to
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ from atst.domain.exceptions import NotFoundError, AlreadyExistsError
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def users_repo(db):
|
def users_repo(session):
|
||||||
return Users(db)
|
return Users(session)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
|
@ -6,13 +6,13 @@ from atst.domain.users import Users
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def users_repo(db):
|
def users_repo(session):
|
||||||
return Users(db)
|
return Users(session)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def workspace_users_repo(db):
|
def workspace_users_repo(session):
|
||||||
return WorkspaceUsers(db)
|
return WorkspaceUsers(session)
|
||||||
|
|
||||||
|
|
||||||
def test_can_create_new_workspace_user(users_repo, workspace_users_repo):
|
def test_can_create_new_workspace_user(users_repo, workspace_users_repo):
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import re
|
|
||||||
import pytest
|
|
||||||
import tornado
|
|
||||||
import urllib
|
|
||||||
from tests.mocks import MOCK_REQUEST, MOCK_USER
|
|
||||||
from tests.factories import PENumberFactory
|
|
||||||
|
|
||||||
|
|
||||||
class TestPENumberInForm:
|
|
||||||
|
|
||||||
required_data = {
|
|
||||||
"pe_id": "123",
|
|
||||||
"task_order_id": "1234567899C0001",
|
|
||||||
"fname_co": "Contracting",
|
|
||||||
"lname_co": "Officer",
|
|
||||||
"email_co": "jane@mail.mil",
|
|
||||||
"office_co": "WHS",
|
|
||||||
"fname_cor": "Officer",
|
|
||||||
"lname_cor": "Representative",
|
|
||||||
"email_cor": "jane@mail.mil",
|
|
||||||
"office_cor": "WHS",
|
|
||||||
"funding_type": "RDTE",
|
|
||||||
"funding_type_other": "other",
|
|
||||||
"clin_0001": "50,000",
|
|
||||||
"clin_0003": "13,000",
|
|
||||||
"clin_1001": "30,000",
|
|
||||||
"clin_1003": "7,000",
|
|
||||||
"clin_2001": "30,000",
|
|
||||||
"clin_2003": "7,000",
|
|
||||||
}
|
|
||||||
|
|
||||||
def _set_monkeypatches(self, monkeypatch):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_financial_verification.RequestFinancialVerification.get_current_user", lambda s: MOCK_USER
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_financial_verification.RequestFinancialVerification.check_xsrf_cookie", lambda s: True
|
|
||||||
)
|
|
||||||
monkeypatch.setattr("atst.forms.request.RequestForm.validate", lambda s: True)
|
|
||||||
monkeypatch.setattr("atst.domain.requests.Requests.get", lambda s, i: MOCK_REQUEST)
|
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
|
||||||
def submit_data(self, http_client, base_url, data):
|
|
||||||
response = yield http_client.fetch(
|
|
||||||
base_url + "/requests/verify/{}".format(MOCK_REQUEST.id),
|
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
||||||
body=urllib.parse.urlencode(data),
|
|
||||||
follow_redirects=False,
|
|
||||||
raise_error=False,
|
|
||||||
)
|
|
||||||
return response
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_request_form_with_invalid_pe_id(self, monkeypatch, http_client, base_url):
|
|
||||||
self._set_monkeypatches(monkeypatch)
|
|
||||||
|
|
||||||
response = yield self.submit_data(http_client, base_url, self.required_data)
|
|
||||||
|
|
||||||
assert "We couldn\'t find that PE number" in response.body.decode()
|
|
||||||
assert response.code == 200
|
|
||||||
assert "/requests/verify" in response.effective_url
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_request_form_with_unchanged_pe_id(self, monkeypatch, http_client, base_url):
|
|
||||||
self._set_monkeypatches(monkeypatch)
|
|
||||||
|
|
||||||
data = dict(self.required_data)
|
|
||||||
data['pe_id'] = MOCK_REQUEST.body['financial_verification']['pe_id']
|
|
||||||
|
|
||||||
response = yield self.submit_data(http_client, base_url, data)
|
|
||||||
|
|
||||||
assert response.code == 302
|
|
||||||
assert response.headers.get("Location") == "/requests/financial_verification_submitted"
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_request_form_with_new_valid_pe_id(self, db, monkeypatch, http_client, base_url):
|
|
||||||
self._set_monkeypatches(monkeypatch)
|
|
||||||
pe = PENumberFactory.create(number="8675309U", description="sample PE number")
|
|
||||||
db.add(pe)
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
data = dict(self.required_data)
|
|
||||||
data['pe_id'] = pe.number
|
|
||||||
|
|
||||||
response = yield self.submit_data(http_client, base_url, data)
|
|
||||||
|
|
||||||
assert response.code == 302
|
|
||||||
assert response.headers.get("Location") == "/requests/financial_verification_submitted"
|
|
@ -1,54 +0,0 @@
|
|||||||
import re
|
|
||||||
import pytest
|
|
||||||
import tornado
|
|
||||||
import urllib
|
|
||||||
from tests.mocks import MOCK_USER
|
|
||||||
from tests.factories import RequestFactory
|
|
||||||
|
|
||||||
ERROR_CLASS = "alert--error"
|
|
||||||
MOCK_REQUEST = RequestFactory.create(
|
|
||||||
creator=MOCK_USER["id"],
|
|
||||||
body={
|
|
||||||
"financial_verification": {
|
|
||||||
"pe_id": "0203752A",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_invalid_request_form(monkeypatch, http_client, base_url):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_new.RequestNew.check_xsrf_cookie", lambda s: True
|
|
||||||
)
|
|
||||||
# this just needs to send a known invalid form value
|
|
||||||
response = yield http_client.fetch(
|
|
||||||
base_url + "/requests/new",
|
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
||||||
body="total_ram=5",
|
|
||||||
)
|
|
||||||
assert response.effective_url == base_url + "/requests/new"
|
|
||||||
assert re.search(ERROR_CLASS, response.body.decode())
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_valid_request_form(monkeypatch, http_client, base_url):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_new.RequestNew.check_xsrf_cookie", lambda s: True
|
|
||||||
)
|
|
||||||
monkeypatch.setattr("atst.forms.request.RequestForm.validate", lambda s: True)
|
|
||||||
|
|
||||||
# this just needs to send a known invalid form value
|
|
||||||
response = yield http_client.fetch(
|
|
||||||
base_url + "/requests/new",
|
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
||||||
body="meaning=42",
|
|
||||||
)
|
|
||||||
assert "/requests/new/2" in response.effective_url
|
|
@ -1,57 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import tornado
|
|
||||||
from tests.mocks import MOCK_USER
|
|
||||||
from tests.factories import RequestFactory
|
|
||||||
|
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
|
||||||
def _mock_func(*args, **kwargs):
|
|
||||||
return RequestFactory.create()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_reviewed_request(monkeypatch, http_client, base_url):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_submit.RequestsSubmit.get_current_user",
|
|
||||||
lambda s: MOCK_USER,
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_submit.RequestsSubmit.check_xsrf_cookie", lambda s: True
|
|
||||||
)
|
|
||||||
monkeypatch.setattr("atst.domain.requests.Requests.get", _mock_func)
|
|
||||||
monkeypatch.setattr("atst.domain.requests.Requests.submit", _mock_func)
|
|
||||||
monkeypatch.setattr("atst.models.request.Request.status", "pending")
|
|
||||||
# this just needs to send a known invalid form value
|
|
||||||
response = yield http_client.fetch(
|
|
||||||
base_url + "/requests/submit/1",
|
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
||||||
body="",
|
|
||||||
raise_error=False,
|
|
||||||
follow_redirects=False,
|
|
||||||
)
|
|
||||||
assert response.headers["Location"] == "/requests"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_submit_autoapproved_reviewed_request(monkeypatch, http_client, base_url):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_submit.RequestsSubmit.get_current_user",
|
|
||||||
lambda s: MOCK_USER,
|
|
||||||
)
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"atst.handlers.request_submit.RequestsSubmit.check_xsrf_cookie", lambda s: True
|
|
||||||
)
|
|
||||||
monkeypatch.setattr("atst.domain.requests.Requests.get", _mock_func)
|
|
||||||
monkeypatch.setattr("atst.domain.requests.Requests.submit", _mock_func)
|
|
||||||
monkeypatch.setattr("atst.models.request.Request.status", "approved")
|
|
||||||
# this just needs to send a known invalid form value
|
|
||||||
response = yield http_client.fetch(
|
|
||||||
base_url + "/requests/submit/1",
|
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
||||||
body="",
|
|
||||||
raise_error=False,
|
|
||||||
follow_redirects=False,
|
|
||||||
)
|
|
||||||
assert response.headers["Location"] == "/requests?modal=True"
|
|
76
tests/routes/test_financial_verification.py
Normal file
76
tests/routes/test_financial_verification.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import re
|
||||||
|
import pytest
|
||||||
|
import tornado
|
||||||
|
import urllib
|
||||||
|
from tests.mocks import MOCK_REQUEST, MOCK_USER
|
||||||
|
from tests.factories import PENumberFactory
|
||||||
|
|
||||||
|
|
||||||
|
class TestPENumberInForm:
|
||||||
|
|
||||||
|
required_data = {
|
||||||
|
"pe_id": "123",
|
||||||
|
"task_order_id": "1234567899C0001",
|
||||||
|
"fname_co": "Contracting",
|
||||||
|
"lname_co": "Officer",
|
||||||
|
"email_co": "jane@mail.mil",
|
||||||
|
"office_co": "WHS",
|
||||||
|
"fname_cor": "Officer",
|
||||||
|
"lname_cor": "Representative",
|
||||||
|
"email_cor": "jane@mail.mil",
|
||||||
|
"office_cor": "WHS",
|
||||||
|
"funding_type": "RDTE",
|
||||||
|
"funding_type_other": "other",
|
||||||
|
"clin_0001": "50,000",
|
||||||
|
"clin_0003": "13,000",
|
||||||
|
"clin_1001": "30,000",
|
||||||
|
"clin_1003": "7,000",
|
||||||
|
"clin_2001": "30,000",
|
||||||
|
"clin_2003": "7,000",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _set_monkeypatches(self, monkeypatch):
|
||||||
|
monkeypatch.setattr("atst.forms.financial.FinancialForm.validate", lambda s: True)
|
||||||
|
monkeypatch.setattr("atst.domain.requests.Requests.get", lambda i: MOCK_REQUEST)
|
||||||
|
|
||||||
|
def submit_data(self, client, data):
|
||||||
|
response = client.post(
|
||||||
|
"/requests/verify/{}".format(MOCK_REQUEST.id),
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
data=urllib.parse.urlencode(data),
|
||||||
|
follow_redirects=False,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def test_submit_request_form_with_invalid_pe_id(self, monkeypatch, client):
|
||||||
|
self._set_monkeypatches(monkeypatch)
|
||||||
|
|
||||||
|
response = self.submit_data(client, self.required_data)
|
||||||
|
|
||||||
|
assert "We couldn\'t find that PE number" in response.data.decode()
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
def test_submit_request_form_with_unchanged_pe_id(self, monkeypatch, client):
|
||||||
|
self._set_monkeypatches(monkeypatch)
|
||||||
|
|
||||||
|
data = dict(self.required_data)
|
||||||
|
data['pe_id'] = MOCK_REQUEST.body['financial_verification']['pe_id']
|
||||||
|
|
||||||
|
response = self.submit_data(client, data)
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert "/requests/financial_verification_submitted" in response.headers.get("Location")
|
||||||
|
|
||||||
|
def test_submit_request_form_with_new_valid_pe_id(self, session, monkeypatch, client):
|
||||||
|
self._set_monkeypatches(monkeypatch)
|
||||||
|
pe = PENumberFactory.create(number="8675309U", description="sample PE number")
|
||||||
|
session.add(pe)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
data = dict(self.required_data)
|
||||||
|
data['pe_id'] = pe.number
|
||||||
|
|
||||||
|
response = self.submit_data(client, data)
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert "/requests/financial_verification_submitted" in response.headers.get("Location")
|
35
tests/routes/test_request_new.py
Normal file
35
tests/routes/test_request_new.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import re
|
||||||
|
import pytest
|
||||||
|
import urllib
|
||||||
|
from tests.mocks import MOCK_USER
|
||||||
|
from tests.factories import RequestFactory
|
||||||
|
|
||||||
|
ERROR_CLASS = "alert--error"
|
||||||
|
MOCK_REQUEST = RequestFactory.create(
|
||||||
|
creator=MOCK_USER["id"],
|
||||||
|
body={
|
||||||
|
"financial_verification": {
|
||||||
|
"pe_id": "0203752A",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_invalid_request_form(monkeypatch, client):
|
||||||
|
response = client.post(
|
||||||
|
"/requests/new/1",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
data="total_ram=5",
|
||||||
|
)
|
||||||
|
assert re.search(ERROR_CLASS, response.data.decode())
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_valid_request_form(monkeypatch, client):
|
||||||
|
monkeypatch.setattr("atst.forms.request.RequestForm.validate", lambda s: True)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/requests/new/1",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
data="meaning=42",
|
||||||
|
)
|
||||||
|
assert "/requests/new/2" in response.headers.get("Location")
|
37
tests/routes/test_request_submit.py
Normal file
37
tests/routes/test_request_submit.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import pytest
|
||||||
|
import tornado
|
||||||
|
from tests.mocks import MOCK_USER
|
||||||
|
from tests.factories import RequestFactory
|
||||||
|
|
||||||
|
|
||||||
|
def _mock_func(*args, **kwargs):
|
||||||
|
return RequestFactory.create()
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_reviewed_request(monkeypatch, client):
|
||||||
|
monkeypatch.setattr("atst.domain.requests.Requests.get", _mock_func)
|
||||||
|
monkeypatch.setattr("atst.domain.requests.Requests.submit", _mock_func)
|
||||||
|
monkeypatch.setattr("atst.models.request.Request.status", "pending")
|
||||||
|
# this just needs to send a known invalid form value
|
||||||
|
response = client.post(
|
||||||
|
"/requests/submit/1",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
data="",
|
||||||
|
follow_redirects=False,
|
||||||
|
)
|
||||||
|
assert "/requests" in response.headers["Location"]
|
||||||
|
assert "modal" not in response.headers["Location"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_autoapproved_reviewed_request(monkeypatch, client):
|
||||||
|
monkeypatch.setattr("atst.domain.requests.Requests.get", _mock_func)
|
||||||
|
monkeypatch.setattr("atst.domain.requests.Requests.submit", _mock_func)
|
||||||
|
monkeypatch.setattr("atst.models.request.Request.status", "approved")
|
||||||
|
# this just needs to send a known invalid form value
|
||||||
|
response = client.post(
|
||||||
|
"/requests/submit/1",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
data="",
|
||||||
|
follow_redirects=False,
|
||||||
|
)
|
||||||
|
assert "/requests?modal=True" in response.headers["Location"]
|
@ -1,10 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from atst.api_client import ApiClient
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_api_client(http_client, base_url):
|
|
||||||
client = ApiClient(base_url)
|
|
||||||
response = yield client.get("")
|
|
||||||
assert response.code == 200
|
|
@ -1,81 +1,78 @@
|
|||||||
import re
|
import re
|
||||||
import pytest
|
import pytest
|
||||||
import tornado.web
|
|
||||||
import tornado.gen
|
|
||||||
|
|
||||||
MOCK_USER = {"id": "438567dd-25fa-4d83-a8cc-8aa8366cb24a"}
|
MOCK_USER = {"id": "438567dd-25fa-4d83-a8cc-8aa8366cb24a"}
|
||||||
@tornado.gen.coroutine
|
|
||||||
def _fetch_user_info(c, t):
|
def _fetch_user_info(c, t):
|
||||||
return MOCK_USER
|
return MOCK_USER
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
@pytest.mark.skip
|
||||||
def test_redirects_when_not_logged_in(http_client, base_url):
|
def test_redirects_when_not_logged_in():
|
||||||
response = yield http_client.fetch(
|
pass
|
||||||
base_url + "/home", raise_error=False, follow_redirects=False
|
# response = yield http_client.fetch(
|
||||||
)
|
# base_url + "/home", raise_error=False, follow_redirects=False
|
||||||
location = response.headers["Location"]
|
# )
|
||||||
assert response.code == 302
|
# location = response.headers["Location"]
|
||||||
assert response.error
|
# assert response.code == 302
|
||||||
assert re.match("/\??", location)
|
# assert response.error
|
||||||
|
# assert re.match("/\??", location)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
# @pytest.mark.skip
|
||||||
def test_redirects_when_session_does_not_exist(monkeypatch, http_client, base_url):
|
# def test_redirects_when_session_does_not_exist():
|
||||||
monkeypatch.setattr("atst.handlers.main.Main.get_secure_cookie", lambda s,c: 'stale cookie!')
|
# monkeypatch.setattr("atst.handlers.main.Main.get_secure_cookie", lambda s,c: 'stale cookie!')
|
||||||
response = yield http_client.fetch(
|
# response = yield http_client.fetch(
|
||||||
base_url + "/home", raise_error=False, follow_redirects=False
|
# base_url + "/home", raise_error=False, follow_redirects=False
|
||||||
)
|
# )
|
||||||
location = response.headers["Location"]
|
# location = response.headers["Location"]
|
||||||
cookie = response.headers._dict.get('Set-Cookie')
|
# cookie = response.headers._dict.get('Set-Cookie')
|
||||||
# should clear session cookie
|
# # should clear session cookie
|
||||||
assert 'atat=""' in cookie
|
# assert 'atat=""' in cookie
|
||||||
assert response.code == 302
|
# assert response.code == 302
|
||||||
assert response.error
|
# assert response.error
|
||||||
assert re.match("/\??", location)
|
# assert re.match("/\??", location)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
# @pytest.mark.skip
|
||||||
def test_login_with_valid_bearer_token(app, monkeypatch, http_client, base_url):
|
# def test_login_with_valid_bearer_token():
|
||||||
monkeypatch.setattr("atst.handlers.login_redirect.LoginRedirect._fetch_user_info", _fetch_user_info)
|
# monkeypatch.setattr("atst.handlers.login_redirect.LoginRedirect._fetch_user_info", _fetch_user_info)
|
||||||
response = yield http_client.fetch(
|
# response = client.fetch(
|
||||||
base_url + "/login-redirect?bearer-token=abc-123",
|
# base_url + "/login-redirect?bearer-token=abc-123",
|
||||||
follow_redirects=False,
|
# follow_redirects=False,
|
||||||
raise_error=False,
|
# raise_error=False,
|
||||||
)
|
# )
|
||||||
assert response.headers["Set-Cookie"].startswith("atat")
|
# assert response.headers["Set-Cookie"].startswith("atat")
|
||||||
assert response.headers["Location"] == "/home"
|
# assert response.headers["Location"] == "/home"
|
||||||
assert response.code == 302
|
# assert response.code == 302
|
||||||
|
#
|
||||||
|
#
|
||||||
@pytest.mark.gen_test
|
# @pytest.mark.skip
|
||||||
def test_login_via_dev_endpoint(app, http_client, base_url):
|
# def test_login_via_dev_endpoint():
|
||||||
response = yield http_client.fetch(
|
# response = yield http_client.fetch(
|
||||||
base_url + "/login-dev", raise_error=False, follow_redirects=False
|
# base_url + "/login-dev", raise_error=False, follow_redirects=False
|
||||||
)
|
# )
|
||||||
assert response.headers["Set-Cookie"].startswith("atat")
|
# assert response.headers["Set-Cookie"].startswith("atat")
|
||||||
assert response.code == 302
|
# assert response.code == 302
|
||||||
assert response.headers["Location"] == "/home"
|
# assert response.headers["Location"] == "/home"
|
||||||
|
#
|
||||||
|
#
|
||||||
@pytest.mark.gen_test
|
# @pytest.mark.skip
|
||||||
@pytest.mark.skip(reason="need to work out auth error user paths")
|
# def test_login_with_invalid_bearer_token():
|
||||||
def test_login_with_invalid_bearer_token(http_client, base_url):
|
# _response = yield http_client.fetch(
|
||||||
_response = yield http_client.fetch(
|
# base_url + "/home",
|
||||||
base_url + "/home",
|
# raise_error=False,
|
||||||
raise_error=False,
|
# headers={"Cookie": "bearer-token=anything"},
|
||||||
headers={"Cookie": "bearer-token=anything"},
|
# )
|
||||||
)
|
#
|
||||||
|
# @pytest.mark.skip
|
||||||
@pytest.mark.gen_test
|
# def test_valid_login_creates_session():
|
||||||
def test_valid_login_creates_session(app, monkeypatch, http_client, base_url):
|
# monkeypatch.setattr("atst.handlers.login_redirect.LoginRedirect._fetch_user_info", _fetch_user_info)
|
||||||
monkeypatch.setattr("atst.handlers.login_redirect.LoginRedirect._fetch_user_info", _fetch_user_info)
|
# assert len(app.sessions.sessions) == 0
|
||||||
assert len(app.sessions.sessions) == 0
|
# yield http_client.fetch(
|
||||||
yield http_client.fetch(
|
# base_url + "/login-redirect?bearer-token=abc-123",
|
||||||
base_url + "/login-redirect?bearer-token=abc-123",
|
# follow_redirects=False,
|
||||||
follow_redirects=False,
|
# raise_error=False,
|
||||||
raise_error=False,
|
# )
|
||||||
)
|
# assert len(app.sessions.sessions) == 1
|
||||||
assert len(app.sessions.sessions) == 1
|
# session = list(app.sessions.sessions.values())[0]
|
||||||
session = list(app.sessions.sessions.values())[0]
|
# assert "atat_permissions" in session["user"]
|
||||||
assert "atat_permissions" in session["user"]
|
# assert isinstance(session["user"]["atat_permissions"], list)
|
||||||
assert isinstance(session["user"]["atat_permissions"], list)
|
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import pytest
|
def test_hello_world(client):
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200
|
||||||
@pytest.mark.gen_test
|
|
||||||
def test_hello_world(http_client, base_url):
|
|
||||||
response = yield http_client.fetch(base_url)
|
|
||||||
assert response.code == 200
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import tornado
|
|
||||||
from tests.mocks import MOCK_USER
|
from tests.mocks import MOCK_USER
|
||||||
from atst.handlers.request_new import JEDIRequestFlow
|
from atst.routes.requests.jedi_request_flow import JEDIRequestFlow
|
||||||
|
|
||||||
SCREENS = JEDIRequestFlow(None, None, 3).screens
|
@pytest.fixture
|
||||||
|
def screens(app):
|
||||||
|
return JEDIRequestFlow(3).screens
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
@pytest.mark.skip()
|
||||||
def test_stepthrough_request_form(monkeypatch, http_client, base_url):
|
def test_stepthrough_request_form(monkeypatch, screens, client):
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER
|
"atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER
|
||||||
)
|
)
|
||||||
@ -18,29 +19,28 @@ def test_stepthrough_request_form(monkeypatch, http_client, base_url):
|
|||||||
"atst.handlers.request_new.JEDIRequestFlow.validate", lambda s: True
|
"atst.handlers.request_new.JEDIRequestFlow.validate", lambda s: True
|
||||||
)
|
)
|
||||||
|
|
||||||
@tornado.gen.coroutine
|
|
||||||
def take_a_step(inc, req=None):
|
def take_a_step(inc, req=None):
|
||||||
req_url = base_url + "/requests/new/{}".format(inc)
|
req_url = "/requests/new/{}".format(inc)
|
||||||
if req:
|
if req:
|
||||||
req_url += "/" + req
|
req_url += "/" + req
|
||||||
response = yield http_client.fetch(
|
response = client.post(
|
||||||
req_url,
|
req_url,
|
||||||
method="POST",
|
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
body="meaning=42",
|
data="meaning=42",
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# GET the initial form
|
# GET the initial form
|
||||||
response = yield http_client.fetch(base_url + "/requests/new", method="GET")
|
response = client.get("/requests/new")
|
||||||
assert SCREENS[0]["title"] in response.body.decode()
|
assert screens[0]["title"] in response.data.decode()
|
||||||
|
|
||||||
# POST to each of the form pages up until review and submit
|
# POST to each of the form pages up until review and submit
|
||||||
req_id = None
|
req_id = None
|
||||||
for i in range(1, len(SCREENS)):
|
for i in range(1, len(screens)):
|
||||||
resp = yield take_a_step(i, req=req_id)
|
resp = take_a_step(i, req=req_id)
|
||||||
|
__import__('ipdb').set_trace()
|
||||||
req_id = resp.effective_url.split("/")[-1]
|
req_id = resp.effective_url.split("/")[-1]
|
||||||
screen_title = SCREENS[i]["title"].replace("&", "&")
|
screen_title = screens[i]["title"].replace("&", "&")
|
||||||
|
|
||||||
assert "/requests/new/{}/{}".format(i + 1, req_id) in resp.effective_url
|
assert "/requests/new/{}/{}".format(i + 1, req_id) in resp.effective_url
|
||||||
assert screen_title in resp.body.decode()
|
assert screen_title in resp.data.decode()
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.gen_test
|
def test_routes(client):
|
||||||
def test_routes(http_client, base_url):
|
|
||||||
for path in (
|
for path in (
|
||||||
"/",
|
"/",
|
||||||
"/home",
|
"/home",
|
||||||
"/workspaces",
|
"/workspaces",
|
||||||
"/requests",
|
"/requests",
|
||||||
"/requests/new",
|
"/requests/new",
|
||||||
"/requests/new/1",
|
"/requests/new/2",
|
||||||
"/users",
|
"/users",
|
||||||
"/reports",
|
"/reports",
|
||||||
"/calculator",
|
"/calculator",
|
||||||
):
|
):
|
||||||
response = yield http_client.fetch(base_url + path)
|
response = client.get(path)
|
||||||
assert response.code == 200
|
if response.status_code == 404:
|
||||||
|
__import__('ipdb').set_trace()
|
||||||
|
assert response.status_code == 200
|
||||||
|
Loading…
x
Reference in New Issue
Block a user