def test_sta_mini_import_output(tmp_path: Path, storage_service) -> None: params = RequestParameters(user=ADMIN) path_study_output = (storage_service.get_study_path("STA-mini", params) / "output" / "20201014-1422eco-hello") sta_mini_output_zip_filepath = shutil.make_archive(tmp_path, "zip", path_study_output) shutil.rmtree(path_study_output) sta_mini_output_zip_path = Path(sta_mini_output_zip_filepath) app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), user_service=Mock(), config=storage_service.study_service.config, ) client = app.test_client() study_output_data = io.BytesIO(sta_mini_output_zip_path.read_bytes()) result = client.post( "/studies/STA-mini/output", data={"output": (study_output_data, "output.zip")}, ) assert result.status_code == HTTPStatus.ACCEPTED.value
def test_create_study(tmp_path: str, storage_service_builder, project_path) -> None: path_studies = Path(tmp_path) path_study = path_studies / "study1" path_study.mkdir() (path_study / "study.antares").touch() storage_service = Mock() storage_service.create_study.return_value = "my-uuid" app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result_right = client.post("/studies/study2") assert result_right.status_code == HTTPStatus.CREATED.value assert json.loads(result_right.data) == "/studies/my-uuid" storage_service.create_study.assert_called_once_with("study2", [], PARAMS)
def test_server_with_parameters() -> None: mock_storage_service = Mock() mock_storage_service.get.return_value = {} app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/studies/study1?depth=4") parameters = RequestParameters(user=ADMIN) assert result.status_code == 200 mock_storage_service.get.assert_called_once_with("study1", 4, parameters) result = client.get("/studies/study2?depth=WRONG_TYPE") excepted_parameters = RequestParameters(user=ADMIN) assert result.status_code == 200 mock_storage_service.get.assert_called_with("study2", 3, excepted_parameters)
def test_export_params() -> None: mock_storage_service = Mock() mock_storage_service.export_study.return_value = BytesIO(b"Hello") app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/studies/name/export") assert result.data == b"Hello" client.get("/studies/name/export?no-output") client.get("/studies/name/export?no-output=true") client.get("/studies/name/export?no-output=false") mock_storage_service.export_study.assert_has_calls([ call(Markup("name"), PARAMS, True), call(Markup("name"), PARAMS, False), call(Markup("name"), PARAMS, False), ])
def test_list_studies(tmp_path: str, storage_service_builder) -> None: studies = { "study1": { "antares": { "caption": "" } }, "study2": { "antares": { "caption": "" } }, } storage_service = Mock() storage_service.get_studies_information.return_value = studies app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/studies") assert json.loads(result.data) == studies
def assert_url_content(storage_service: StorageService, url: str) -> bytes: app = Flask(__name__) build_storage( app, session=Mock(), user_service=Mock(), storage_service=storage_service, config=storage_service.study_service.config, ) client = app.test_client() res = client.get(url) return res.data
def test_server_health() -> None: app = Flask(__name__) build_storage( app, storage_service=Mock(), session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/health") assert result.data == b'{"status":"available"}\n'
def assert_url_content(storage_service: StorageService, url: str, expected_output: str) -> None: app = Flask(__name__) build_storage( app, session=Mock(), user_service=Mock(), storage_service=storage_service, config=storage_service.study_service.config, ) client = app.test_client() res = client.get(url) assert json.loads(res.data) == expected_output
def test_study_permission_management(tmp_path: Path, storage_service_builder) -> None: storage_service = Mock() app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), user_service=Mock(), config=CONFIG, ) client = app.test_client() result = client.put("/studies/existing-study/owner/2") storage_service.change_owner.assert_called_with( "existing-study", 2, PARAMS, ) assert result.status_code == HTTPStatus.OK.value result = client.put("/studies/existing-study/groups/group-a") storage_service.add_group.assert_called_with( "existing-study", "group-a", PARAMS, ) assert result.status_code == HTTPStatus.OK.value result = client.delete("/studies/existing-study/groups/group-b") storage_service.remove_group.assert_called_with( "existing-study", "group-b", PARAMS, ) assert result.status_code == HTTPStatus.OK.value result = client.put("/studies/existing-study/public_mode/FULL") storage_service.set_public_mode.assert_called_with( "existing-study", PublicMode.FULL, PARAMS, ) assert result.status_code == HTTPStatus.OK.value result = client.put("/studies/existing-study/public_mode/UNKNOWN") assert result.status_code == HTTPStatus.INTERNAL_SERVER_ERROR.value
def test_exporter_file(tmp_path: Path, sta_mini_zip_path: Path): path_studies = tmp_path / "studies" with ZipFile(sta_mini_zip_path) as zip_output: zip_output.extractall(path=path_studies) config = Config( resources_path=Path(), security=SecurityConfig(disabled=True), storage=StorageConfig( workspaces={ DEFAULT_WORKSPACE_NAME: WorkspaceConfig(path=path_studies) } ), ) md = RawStudy( id="STA-mini", workspace=DEFAULT_WORKSPACE_NAME, path=str(path_studies / "STA-mini"), ) repo = Mock() repo.get.return_value = md service = build_storage( application=Mock(), config=config, session=Mock(), user_service=Mock(), metadata_repository=repo, ) data = assert_url_content(service, url="/studies/STA-mini/export") assert_data(data)
def build_storage_service( study_factory=Mock(), exporter=Mock(), session=Mock(), path_studies=Path(), path_resources=Path(), user_service=Mock(), ) -> StorageService: config = Config( resources_path=path_resources, security=SecurityConfig(disabled=True), storage=StorageConfig( workspaces={ DEFAULT_WORKSPACE_NAME: WorkspaceConfig(path=path_studies) }), ) return build_storage( application=Mock(), config=config, session=session, user_service=user_service, study_factory=study_factory, exporter=exporter, )
def test_delete_study() -> None: mock_storage_service = Mock() app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() client.delete("/studies/name") mock_storage_service.delete_study.assert_called_once_with("name", PARAMS)
def test_server() -> None: mock_service = Mock() mock_service.get.return_value = {} app = Flask(__name__) build_storage( app, storage_service=mock_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() client.get("/studies/study1/settings/general/params") mock_service.get.assert_called_once_with("study1/settings/general/params", 3, PARAMS)
def test_404() -> None: mock_storage_service = Mock() mock_storage_service.get.side_effect = UrlNotMatchJsonDataError("Test") app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/studies/study1/settings/general/params") assert result.status_code == 404 result = client.get("/studies/WRONG_STUDY") assert result.status_code == 404
def test_sta_mini_copy(storage_service) -> None: source_study_name = "STA-mini" destination_study_name = "copy-STA-mini" app = Flask(__name__) build_storage( app, session=Mock(), user_service=Mock(), storage_service=storage_service, config=storage_service.study_service.config, ) client = app.test_client() result = client.post( f"/studies/{source_study_name}/copy?dest={destination_study_name}") assert result.status_code == HTTPStatus.CREATED.value uuid = result.data.decode("utf-8") parameters = RequestParameters(user=ADMIN) data_source = storage_service.get(source_study_name, -1, parameters) data_destination = storage_service.get(uuid, -1, parameters) link_url_source = data_source["input"]["links"]["de"]["fr"] assert link_url_source == input_link link_url_destination = data_destination["input"]["links"]["de"]["fr"] assert link_url_destination == input_link def replace_study_name(data: JSON) -> None: if isinstance(data, dict): for key, value in data.items(): if isinstance(value, str) and value.startswith("file/"): data[key] = value.replace(uuid, source_study_name) else: replace_study_name(value) replace_study_name(data_destination) del data_source["output"] data_source["study"] = {} data_destination["study"] = {} assert data_source == data_destination
def test_export_files() -> None: mock_storage_service = Mock() mock_storage_service.export_study.return_value = BytesIO(b"Hello") app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.get("/studies/name/export") assert result.data == b"Hello" mock_storage_service.export_study.assert_called_once_with( "name", PARAMS, True)
def test_edit_study() -> None: mock_storage_service = Mock() mock_storage_service.edit_study.return_value = {} data = json.dumps({"Hello": "World"}) app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() client.post("/studies/my-uuid/url/to/change", data=data) mock_storage_service.edit_study.assert_called_once_with( "my-uuid/url/to/change", {"Hello": "World"}, PARAMS)
def test_edit_study_fail() -> None: mock_storage_service = Mock() data = json.dumps({}) app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() res = client.post("/studies/my-uuid/url/to/change", data=data) assert res.status_code == 400 mock_storage_service.edit_study.assert_not_called()
def test_get_matrix() -> None: mock_storage_service = Mock() mock_storage_service.get_matrix.return_value = b"Hello World" app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() path = "/file/my-study/matrix.txt" result = client.get(path) assert result.status_code == HTTPStatus.OK.value assert result.data == b"Hello World"
def test_version() -> None: mock_storage_service = Mock() mock_storage_service.study_service.path_resources = Path("/") app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=Config(), user_service=Mock(), ) client = app.test_client() path = "/version" result = client.get(path) assert result.status_code == HTTPStatus.OK.value assert json.loads(result.data)["version"] == __version__
def test_sta_mini_import(tmp_path: Path, storage_service) -> None: params = RequestParameters(user=ADMIN) path_study = storage_service.get_study_path("STA-mini", params) sta_mini_zip_filepath = shutil.make_archive(tmp_path, "zip", path_study) sta_mini_zip_path = Path(sta_mini_zip_filepath) app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), user_service=Mock(), config=storage_service.study_service.config, ) client = app.test_client() study_data = io.BytesIO(sta_mini_zip_path.read_bytes()) result = client.post("/studies", data={"study": (study_data, "study.zip")}) assert result.status_code == HTTPStatus.CREATED.value
def test_import_matrix() -> None: mock_storage_service = Mock() app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() data = io.BytesIO(b"hello") path = "path/to/matrix.txt" result = client.post("/file/" + path, data={"matrix": (data, "matrix.txt")}) mock_storage_service.upload_matrix.assert_called_once_with( path, b"hello", PARAMS) assert result.status_code == HTTPStatus.NO_CONTENT.value
def test_import_matrix_with_wrong_path() -> None: mock_storage_service = Mock() mock_storage_service.upload_matrix = Mock( side_effect=IncorrectPathError("")) app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() data = io.BytesIO(b"hello") path = "path/to/matrix.txt" result = client.post("/file/" + path, data={"matrix": (data, "matrix.txt")}) assert result.status_code == HTTPStatus.NOT_FOUND.value
def test_import_study_zipped(tmp_path: Path, storage_service_builder, project_path) -> None: tmp_path /= "tmp" tmp_path.mkdir() study_name = "study1" path_study = tmp_path / study_name path_study.mkdir() path_file = path_study / "study.antares" path_file.write_text("[antares]") shutil.make_archive(path_study, "zip", path_study) path_zip = tmp_path / "study1.zip" mock_storage_service = Mock() mock_storage_service.import_study.return_value = study_name app = Flask(__name__) build_storage( app, storage_service=mock_storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.post("/studies") assert result.status_code == HTTPStatus.BAD_REQUEST.value study_data = io.BytesIO(path_zip.read_bytes()) result = client.post("/studies", data={"study": (study_data, "study.zip")}) print(result.data) assert json.loads(result.data) == "/studies/" + study_name assert result.status_code == HTTPStatus.CREATED.value mock_storage_service.import_study.assert_called_once()
def test_copy_study(tmp_path: Path, storage_service_builder) -> None: storage_service = Mock() storage_service.copy_study.return_value = "/studies/study-copied" app = Flask(__name__) build_storage( app, storage_service=storage_service, session=Mock(), config=CONFIG, user_service=Mock(), ) client = app.test_client() result = client.post("/studies/existing-study/copy?dest=study-copied") storage_service.copy_study.assert_called_with( src_uuid="existing-study", dest_study_name="study-copied", group_ids=[], params=PARAMS, ) assert result.status_code == HTTPStatus.CREATED.value
def storage_service(tmp_path: str, project_path: Path, sta_mini_zip_path: Path) -> StorageService: path_studies = Path(tmp_path) / "studies" path_resources = project_path / "resources" with ZipFile(sta_mini_zip_path) as zip_output: zip_output.extractall(path=path_studies) md = RawStudy( id="STA-mini", workspace=DEFAULT_WORKSPACE_NAME, path=str(path_studies / "STA-mini"), ) repo = Mock() repo.get.side_effect = lambda name: RawStudy( id=name, workspace=DEFAULT_WORKSPACE_NAME, path=str(path_studies / name), ) repo.get_all.return_value = [md] config = Config( resources_path=path_resources, security=SecurityConfig(disabled=True), storage=StorageConfig( workspaces={ DEFAULT_WORKSPACE_NAME: WorkspaceConfig(path=path_studies) }), ) storage_service = build_storage( application=Mock(), session=Mock(), user_service=Mock(), config=config, metadata_repository=repo, ) return storage_service
def flask_app(config_file: Path, resource_path: Optional[Path] = None) -> Flask: res = resource_path or get_local_path() / "resources" config = Config.from_yaml_file(res=res, file=config_file) configure_logger(config) # Database engine = create_engine(config.db_url, echo=config.debug) Base.metadata.create_all(engine) db_session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=engine)) application = Flask(__name__, static_url_path="/static", static_folder=str(res / "webapp")) application.wsgi_app = ReverseProxyMiddleware( application.wsgi_app) # type: ignore application.config["SECRET_KEY"] = config.security.jwt_key application.config["JWT_ACCESS_TOKEN_EXPIRES"] = Auth.ACCESS_TOKEN_DURATION application.config[ "JWT_REFRESH_TOKEN_EXPIRES"] = Auth.REFRESH_TOKEN_DURATION application.config["JWT_TOKEN_LOCATION"] = ["headers", "cookies"] @application.route("/", methods=["GET"]) def home() -> Any: """ Home ui --- responses: '200': content: application/html: {} description: html home page tags: - UI """ return render_template("index.html") @application.teardown_appcontext def shutdown_session(exception: Any = None) -> None: Auth.invalidate() db_session.remove() @application.errorhandler(HTTPException) def handle_exception(e: Any) -> Tuple[Any, Number]: """Return JSON instead of HTML for HTTP errors.""" # start with the correct headers and status code from the error response = e.get_response() # replace the body with JSON response.data = json.dumps({ "name": e.name, "description": e.description, }) response.content_type = "application/json" return response, e.code event_bus = build_eventbus(application, config) user_service = build_login(application, config, db_session, event_bus=event_bus) storage = build_storage( application, config, db_session, user_service=user_service, event_bus=event_bus, ) build_launcher( application, config, db_session, service_storage=storage, event_bus=event_bus, ) build_swagger(application) return application