def test_access_run_result_files_local(database, tmpdir): """Test accessing run result files.""" # -- Setup ---------------------------------------------------------------- env = Config().basedir(tmpdir).auth() fs = FS(env=env) workflow_id, group_id, run_id, user_id = success_run(database, fs, tmpdir) local_service = LocalAPIFactory(env=env, db=database, engine=StateEngine()) # -- Read result files ---------------------------------------------------- with local_service(user_id=user_id) as api: # Map file names to file handles. r = api.runs().get_run(run_id=run_id) files = dict() for fh in r['files']: files[fh['name']] = fh['id'] # Read content of result files. fh = api.runs().get_result_file(run_id=run_id, file_id=files['run/results/B.json']) results = util.read_object(fh.open()) assert results == {'B': 1} # -- Error when user 2 attempts to read file ------------------------------ with database.session() as session: user_2 = create_user(session, active=True) with local_service(user_id=user_2) as api: with pytest.raises(err.UnauthorizedAccessError): api.runs().get_result_file(run_id=run_id, file_id=files['run/results/B.json']) # -- With an open access policy user 2 can read the data file ------------- env = Config().basedir(tmpdir).open_access() local_service = LocalAPIFactory(env=env, db=database, engine=StateEngine()) with local_service(user_id=user_2) as api: api.runs().get_result_file(run_id=run_id, file_id=files['run/results/B.json'])
def test_update_workflow_name(database, tmpdir): """Test updating workflow names.""" # -- Setup ---------------------------------------------------------------- # # Create two workflow templates. Workflow 1 does not have a description # and instructions while workflow 2 has. fs = FileSystemStore(env=Config().basedir(tmpdir)) with database.session() as session: manager = WorkflowManager(session=session, fs=fs) # Initialize the repository wf = manager.create_workflow(name='A', source=BENCHMARK_DIR) workflow_1 = wf.workflow_id wf = manager.create_workflow(name='My benchmark', description='desc', instructions=INSTRUCTION_FILE, source=BENCHMARK_DIR) workflow_2 = wf.workflow_id # -- Test update workflow name -------------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.update_workflow(workflow_id=workflow_1, name='B') assert wf.name == 'B' # It is possible to change the name to an existing name only if it is # the same workflow. wf = manager.update_workflow(workflow_id=workflow_2, name='My benchmark') assert wf.name == 'My benchmark' # -- Error cases ---------------------------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) # Cannot change name to existing name. with pytest.raises(err.ConstraintViolationError): manager.update_workflow(workflow_id=workflow_2, name='B')
def test_run_remote_workflow_error(is_async, tmpdir): """Execute the remote workflow example synchronized and in asynchronous mode when execution results in an error state. """ # -- Setup ---------------------------------------------------------------- env = Config().volume(FStore(basedir=str(tmpdir))).auth() engine = RemoteWorkflowController(client=RemoteTestClient( runcount=3, error='some error'), poll_interval=0.1, is_async=is_async) service = LocalAPIFactory(env=env, engine=engine) # Need to set the association between the engine and the service explicitly # after the API is created. engine.service = service with service() as api: workflow_id = create_workflow(api, source=BENCHMARK_DIR) user_id = create_user(api) with service(user_id=user_id) as api: group_id = create_group(api, workflow_id) # -- Unit test ------------------------------------------------------------ # Start a new run with service(user_id=user_id) as api: run_id = start_run(api, group_id) # Poll workflow state every second. with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) watch_dog = 30 while run['state'] in st.ACTIVE_STATES and watch_dog: time.sleep(1) watch_dog -= 1 with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) serialize.validate_run_handle(run, state=st.STATE_ERROR) assert run['messages'] == ['some error']
def test_get_workflow(fscls, database, tmpdir): """Test retrieving workflows from the repository.""" # -- Setup ---------------------------------------------------------------- # # Create two workflows. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.create_workflow(name='A', source=BENCHMARK_DIR) workflow_1 = wf.workflow_id wf = manager.create_workflow(name='B', description='Workflow B', source=BENCHMARK_DIR, instructions=INSTRUCTION_FILE, specfile=TEMPLATE_WITHOUT_SCHEMA) workflow_2 = wf.workflow_id # -- Test getting workflow handles ---------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.get_workflow(workflow_1) assert wf.name == 'A' assert wf.description is None assert wf.instructions is None template = wf.get_template() assert template.result_schema is not None wf = manager.get_workflow(workflow_2) assert wf.name == 'B' assert wf.description == 'Workflow B' assert wf.instructions == '# Hello World' template = wf.get_template() assert template.result_schema is None
def test_delete_workflow(fscls, database, tmpdir): """Test deleting a workflows from the repository.""" # -- Setup ---------------------------------------------------------------- # # Create two workflows. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.create_workflow(name='A', source=BENCHMARK_DIR) workflow_1 = wf.workflow_id wf = manager.create_workflow(name='B', source=BENCHMARK_DIR) workflow_2 = wf.workflow_id # -- Test delete first workflow ------------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) manager.delete_workflow(workflow_1) with database.session() as session: # The second workflow still exists. manager = WorkflowManager(session=session, fs=fs) manager.get_workflow(workflow_2) is not None # -- Deleting the same repository multiple times raises an error ---------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) with pytest.raises(err.UnknownWorkflowError): manager.delete_workflow(workflow_id=workflow_1)
def test_run_remote_workflow_with_error(tmpdir): """Execute the remote workflow example that will end in an error state in asynchronous mode. """ # -- Setup ---------------------------------------------------------------- # # Start a new run for the workflow template. env = Config().basedir(tmpdir) engine = RemoteTestController(client=RemoteTestClient(runcount=3, error='some error'), poll_interval=1, is_async=True) service = LocalAPIFactory(env=env, engine=engine) engine.service = service with service() as api: workflow_id = create_workflow(api, source=TEMPLATE_DIR) user_id = create_user(api) with service(user_id=user_id) as api: group_id = create_group(api, workflow_id) run_id = start_run(api, group_id) # Poll workflow state every second. with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) while run['state'] in st.ACTIVE_STATES: time.sleep(1) with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) serialize.validate_run_handle(run, state=st.STATE_ERROR) assert run['messages'][0] == 'some error'
def test_delete_file(fscls, database, tmpdir): """Test deleting an uploaded file.""" # -- Setup ---------------------------------------------------------------- # # Create a database with two groups for a single workflow. Upload one file # for each group. file = io_file(data={'A': 1}) fn = 'data.json' fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_1 = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_1 = model.create_group(session, workflow_id, users=[user_1]) group_2 = model.create_group(session, workflow_id, users=[user_1]) manager = WorkflowGroupManager(session=session, fs=fs) fh = manager.upload_file(group_id=group_1, file=file, name=fn) file_1 = fh.file_id fh = manager.upload_file(group_id=group_2, file=file, name=fn) file_2 = fh.file_id # -- Test delete file ----------------------------------------------------- with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) fh = manager.get_uploaded_file(group_id=group_1, file_id=file_1) manager.delete_file(group_id=group_1, file_id=file_1) # File 1 can no longer be accessed while file 2 is still present. with pytest.raises(err.UnknownFileError): manager.get_uploaded_file(group_id=group_1, file_id=file_1).open() fh = manager.get_uploaded_file(group_id=group_2, file_id=file_2) # -- Error cases ---------------------------------------------------------- with database.session() as session: # - Delete unknown file manager = WorkflowGroupManager(session=session, fs=fs) with pytest.raises(err.UnknownFileError): manager.delete_file(group_id=group_1, file_id=file_1)
def test_workflow_leaderboard_serialization(database, tmpdir): """Test serialization of a workflow leaderboard.""" config = Config().basedir(tmpdir) schema = validator('WorkflowLeaderboard') view = WorkflowSerializer() with database.session() as session: manager = WorkflowManager(session=session, fs=FileSystemStore(config)) workflow = manager.create_workflow(source=BENCHMARK_DIR, name='Test', specfile=SPEC_FILE) ts = util.utc_now() ranking = [ RunResult(run_id='0', group_id='1', group_name='A', created_at=ts, started_at=ts, finished_at=ts, values={ 'len': 1, 'count': 10 }) ] doc = view.workflow_leaderboard(workflow, ranking=ranking) schema.validate(doc)
def test_list_files(fscls, database, tmpdir): """Test listing uploaded files.""" # -- Setup ---------------------------------------------------------------- # # Create a database with two groups for a single workflow. The first group # has one uploaded file and the second group has one file. file = io_file(data={'A': 1}) fn = 'data.json' fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_1 = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_1 = model.create_group(session, workflow_id, users=[user_1]) group_2 = model.create_group(session, workflow_id, users=[user_1]) manager = WorkflowGroupManager(session=session, fs=fs) manager.upload_file(group_id=group_1, file=file, name=fn) manager.upload_file(group_id=group_1, file=file, name=fn) manager.upload_file(group_id=group_2, file=file, name=fn) # -- Test list files for groups ------------------------------------------- with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) files = manager.list_uploaded_files(group_id=group_1) assert len(files) == 2 files = manager.list_uploaded_files(group_id=group_2) assert len(files) == 1
def test_cancel_remote_workflow(tmpdir): """Cancel the execution of a remote workflow.""" # -- Setup ---------------------------------------------------------------- # env = Config().basedir(tmpdir) engine = RemoteTestController(client=RemoteTestClient(runcount=100), poll_interval=1, is_async=True) service = LocalAPIFactory(env=env, engine=engine) engine.service = service # -- Start a new run for the workflow template. with service() as api: workflow_id = create_workflow(api, source=TEMPLATE_DIR) user_id = create_user(api) with service(user_id=user_id) as api: group_id = create_group(api, workflow_id) run_id = start_run(api, group_id) # -- Poll workflow state every second. with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) while run['state'] == st.STATE_PENDING: time.sleep(1) with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) serialize.validate_run_handle(run, state=st.STATE_RUNNING) with service(user_id=user_id) as api: api.runs().cancel_run(run_id=run_id, reason='test') # Sleep to ensure that the workflow monitor polls the state and makes an # attempt to update the run state. This should raise an error for the # monitor. The error is not propagated here or to the run. time.sleep(3) with service(user_id=user_id) as api: run = api.runs().get_run(run_id=run_id) serialize.validate_run_handle(run, state=st.STATE_CANCELED) assert run['messages'][0] == 'test'
def test_cancel_run(fscls, database, tmpdir): """Test setting run state to canceled.""" # -- Setup ---------------------------------------------------------------- fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_id = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_id = model.create_group(session, workflow_id, users=[user_id]) # -- Test set run to error state ------------------------------------------ with database.session() as session: groups = WorkflowGroupManager(session=session, fs=fs) runs = RunManager(session=session, fs=fs) run = runs.create_run(group=groups.get_group(group_id)) run_id = run.run_id state = run.state() runs.update_run(run_id=run_id, state=state.cancel()) with database.session() as session: runs = RunManager(session=session, fs=fs) run = runs.get_run(run_id) state = run.state() assert not state.is_active() assert not state.is_pending() assert not state.is_running() assert state.is_canceled() assert not state.is_error() assert not state.is_success() assert len(state.messages) == 1
def test_obsolete_runs(fscls, database, tmpdir): """Test deleting runs that were created before a given date.""" # -- Setup ---------------------------------------------------------------- fs = fscls(env=Config().basedir(tmpdir)) # Create two runs (one SUCCESS and one ERROR) before a timestamp t1 _, _, run_1, _ = success_run(database, fs, tmpdir) _, _, run_2 = error_run(database, fs, ['There were errors']) time.sleep(1) t1 = util.utc_now() # Create another SUCCESS run after timestamp t1 _, _, run_3, _ = success_run(database, fs, tmpdir) # -- Test delete run with state filter ------------------------------------ with database.session() as session: runs = RunManager(session=session, fs=fs) assert runs.delete_obsolete_runs(date=t1, state=st.STATE_ERROR) == 1 # After deleting the error run the two success runs still exist. runs.get_run(run_id=run_1) with pytest.raises(err.UnknownRunError): runs.get_run(run_id=run_2) runs.get_run(run_id=run_3) # -- Test delete all runs prior to a given date --------------------------- with database.session() as session: runs = RunManager(session=session, fs=fs) assert runs.delete_obsolete_runs(date=t1) == 1 # After deleting the run the only one success runs still exist. with pytest.raises(err.UnknownRunError): runs.get_run(run_id=run_1) runs.get_run(run_id=run_3)
def test_list_runs(fscls, database, tmpdir): """Test retrieving a list of run descriptors.""" # -- Setup ---------------------------------------------------------------- # # Create two runs: one in running state and one in error state. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_id = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_id = model.create_group(session, workflow_id, users=[user_id]) groups = WorkflowGroupManager(session=session, fs=fs) runs = RunManager(session=session, fs=fs) group = groups.get_group(group_id) # Run 1 in running state r = runs.create_run(group=group) run_1 = r.run_id runs.update_run(run_id=run_1, state=r.state().start()) r = runs.create_run(group=group) run_2 = r.run_id runs.update_run(run_id=run_2, state=r.state().error()) # -- Test get listing ----------------------------------------------------- with database.session() as session: runs = RunManager(session=session, fs=fs) run_index = dict() for run in runs.list_runs(group_id): run_index[run.run_id] = run assert len(run_index) == 2 assert run_index[run_1].state().is_running() assert run_index[run_2].state().is_error() # -- Test polling runs ---------------------------------------------------- with database.session() as session: runs = RunManager(session=session, fs=fs) assert len(runs.list_runs(group_id)) == 2 assert len(runs.list_runs(group_id, state=st.STATE_ERROR)) == 1 assert len(runs.list_runs(group_id, state=st.STATE_SUCCESS)) == 0
def test_invalid_state_transitions(fscls, database, tmpdir): """Test error cases for invalid state transitions.""" # -- Setup ---------------------------------------------------------------- fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_id = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_id = model.create_group(session, workflow_id, users=[user_id]) # -- Test set active run to pending --------------------------------------- with database.session() as session: groups = WorkflowGroupManager(session=session, fs=fs) runs = RunManager(session=session, fs=fs) run = runs.create_run(group=groups.get_group(group_id)) run_id = run.run_id state = run.state() runs.update_run(run_id=run_id, state=state.start()) with pytest.raises(err.ConstraintViolationError): runs.update_run(run_id=run_id, state=st.StatePending()) # Cancel run with database.session() as session: runs = RunManager(session=session, fs=fs) runs.update_run(run_id=run_id, state=state.cancel()) # -- Test cannot set run to any of the inactive states -------------------- with database.session() as session: groups = WorkflowGroupManager(session=session, fs=fs) runs = RunManager(session=session, fs=fs) assert runs.update_run(run_id=run_id, state=state.cancel()) is None with pytest.raises(err.ConstraintViolationError): runs.update_run(run_id=run_id, state=state.error()) with pytest.raises(err.ConstraintViolationError): runs.update_run(run_id=run_id, state=state.success())
def test_upload_file(fscls, database, tmpdir): """Test uploading files.""" # -- Setup ---------------------------------------------------------------- # # Create a database with two groups for a single workflow. Upload one file # for each group. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_1 = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_1 = model.create_group(session, workflow_id, users=[user_1]) # -- Test upload file ----------------------------------------------------- data = {'A': 1} with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) fh = manager.upload_file(group_id=group_1, file=io_file(data={'A': 1}), name='A.json') assert fh.name == 'A.json' assert fh.mime_type == 'application/json' fh = manager.get_uploaded_file(group_id=group_1, file_id=fh.file_id) assert json.load(fh.open()) == data # -- Test error case ------------------------------------------------------ data = {'A': 1} with database.session() as session: with pytest.raises(err.ConstraintViolationError): manager.upload_file(group_id=group_1, file=io_file(data={'A': 1}), name=' ') with pytest.raises(err.UnknownWorkflowGroupError): manager.upload_file(group_id='UNKNOWN', file=io_file(data={'A': 1}), name=' ')
def test_delete_group(fscls, database, tmpdir): """Test creating and deleting workflow groups.""" # -- Setup ---------------------------------------------------------------- # # Create a database with two groups for a single workflow. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_id = model.create_user(session, active=True) wf_id = model.create_workflow(session) manager = WorkflowGroupManager(session=session, fs=fs) group_1 = manager.create_group(workflow_id=wf_id, name='A', user_id=user_id, parameters=ParameterIndex(), workflow_spec=dict()).group_id group_2 = manager.create_group(workflow_id=wf_id, name='B', user_id=user_id, parameters=ParameterIndex(), workflow_spec=dict()).group_id # -- Delete group --------------------------------------------------------- with database.session() as session: # Ensure that group directores are deleted. manager = WorkflowGroupManager(session=session, fs=fs) manager.delete_group(group_1) # Access to group 1 raises error while group 2 is still accessible. with pytest.raises(err.UnknownWorkflowGroupError): manager.get_group(group_1) assert manager.get_group(group_2) is not None
def test_create_workflow_with_alt_spec(fscls, database, tmpdir): """Test creating workflows with alternative specification files.""" # -- Setup ---------------------------------------------------------------- fs = fscls(env=Config().basedir(tmpdir)) # -- Template without schema ---------------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.create_workflow(source=BENCHMARK_DIR, specfile=TEMPLATE_WITHOUT_SCHEMA) workflow_id = wf.workflow_id assert wf.name == 'Hello World' template = wf.get_template() assert template.result_schema is None with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.get_workflow(workflow_id=workflow_id) assert wf.name == 'Hello World' template = wf.get_template() assert template.result_schema is None # -- Template with post-processing step ----------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.create_workflow(name='Top Tagger', source=BENCHMARK_DIR, specfile=TEMPLATE_TOPTAGGER) workflow_id = wf.workflow_id assert wf.get_template().postproc_spec is not None with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.get_workflow(workflow_id=workflow_id) assert wf.get_template().postproc_spec is not None
def ClientAPI( env: Optional[Dict] = None, basedir: Optional[str] = None, database: Optional[str] = None, open_access: Optional[bool] = None, run_async: Optional[bool] = None, user_id: Optional[str] = None ) -> APIFactory: """Create an instance of the API factory that is responsible for generating API instances for a flowserv client. The main distinction here is whether a connection is made to a local instance of the service or to a remote instance. This distinction is made based on the value of the FLOWSERV_CLIENT environment variable that takes the values 'local' or 'remote'. The default is 'local'. Provides the option to alter the default settings of environment variables. Parameters ---------- env: dict, default=None Dictionary with configuration parameter values. basedir: string, default=None Base directory for all workflow files. If no directory is given or specified in the environment a temporary directory will be created. database: string, default=None Optional database connect url. open_access: bool, default=None Use an open access policy if set to True. run_async: bool, default=False Run workflows in asynchronous mode. user_id: string, default=None Optional identifier for the authenticated API user. Returns ------- flowserv.service.api.APIFactory """ # Get the base configuration settings from the environment if not given. env = env if env is not None else config.env() if not isinstance(env, Config): env = Config(env) # Update configuration based on the given optional arguments. if basedir is not None: env.basedir(basedir) if database is not None: env.database(database) if open_access is not None and open_access: env.open_access() # By default, the client runs all workflows synchronously. if run_async is not None and run_async: env.run_async() elif env.get(config.FLOWSERV_ASYNC) is None: env.run_sync() # Create local or remote API factory depending on the FLOWSERV_CLIENT value. client = env.get(config.FLOWSERV_CLIENT, config.LOCAL_CLIENT) if client == config.LOCAL_CLIENT: return LocalAPIFactory(env=env, user_id=user_id) elif client == config.REMOTE_CLIENT: # Not implemented yet. pass raise ValueError("inalid client type '{}'".format(client))
def test_empty_ranking(fscls, database, tmpdir): """The rankings for workflows without completed runs are empty.""" # -- Setup ---------------------------------------------------------------- workflows = init(database, tmpdir) fs = fscls(env=Config().basedir(tmpdir)) # -- Test empty listing with no successful runs --------------------------- with database.session() as session: wfrepo = WorkflowManager(session=session, fs=fs) rankings = RankingManager(session=session) for workflow_id, _ in workflows: wf = wfrepo.get_workflow(workflow_id) assert len(rankings.get_ranking(wf)) == 0
def test_update_groups(fscls, database, tmpdir): """Test updating group name and group members.""" # -- Setup ---------------------------------------------------------------- # # Create a database with two groups for one workflow. Group 1 has user 1 as # only member, group 2 has user 2 and 3 as member. fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: user_1 = model.create_user(session, active=True) user_2 = model.create_user(session, active=True) user_3 = model.create_user(session, active=True) workflow_id = model.create_workflow(session) members_1 = [user_1] group_1 = model.create_group(session, workflow_id, users=members_1) members_2 = [user_2, user_3] group_2 = model.create_group(session, workflow_id, users=members_2) # -- Test add member ------------------------------------------------------ with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) members_1 = [user_1, user_3] manager.update_group(group_1, members=members_1) members = [m.user_id for m in manager.get_group(group_1).members] assert set(members) == set(members_1) members = [m.user_id for m in manager.get_group(group_2).members] assert set(members) == set(members_2) # -- Test rename group ---------------------------------------------------- with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) manager.update_group(group_2, name='My Group') assert manager.get_group(group_1).name == group_1 assert manager.get_group(group_2).name == 'My Group' # -- Test rename group and change members --------------------------------- with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) members_2 = [user_1] + members_2 manager.update_group(group_2, name='The Group', members=members_2) members = [m.user_id for m in manager.get_group(group_1).members] assert set(members) == set(members_1) members = [m.user_id for m in manager.get_group(group_2).members] assert set(members) == set(members_2) assert manager.get_group(group_1).name == group_1 assert manager.get_group(group_2).name == 'The Group' # -- Test no changes ------------------------------------------------------ with database.session() as session: manager = WorkflowGroupManager(session=session, fs=fs) manager.update_group(group_2, name='The Group', members=members_2) members = [m.user_id for m in manager.get_group(group_1).members] assert set(members) == set(members_1) members = [m.user_id for m in manager.get_group(group_2).members] assert set(members) == set(members_2) assert manager.get_group(group_1).name == group_1 assert manager.get_group(group_2).name == 'The Group'
def test_workflow_listing_serialization(database, tmpdir): """Test serialization of workflow listings.""" config = Config().basedir(tmpdir) schema = validator('WorkflowListing') view = WorkflowSerializer() with database.session() as session: manager = WorkflowManager(session=session, fs=FileSystemStore(config)) model.create_workflow(session) model.create_workflow(session) workflows = manager.list_workflows() doc = view.workflow_listing(workflows) schema.validate(doc) assert len(doc[labels.WORKFLOW_LIST]) == 2
def test_postproc_workflow_errors(tmpdir): """Execute the modified helloworld example.""" # -- Setup ---------------------------------------------------------------- # # It is important here that we do not use the SQLite in-memory database # since this fails (for unknown reason; presumably due to different threads) # when the post-processing run is updated. # -- env = Config().basedir(tmpdir).run_async().auth() service = LocalAPIFactory(env=env) # Error during data preparation run_erroneous_workflow(service, SPEC_FILE_ERR_1) # Erroneous specification run_erroneous_workflow(service, SPEC_FILE_ERR_2)
def test_group_handle_serialization(database, tmpdir): """Test serialization of workflow group handles.""" config = Config().basedir(tmpdir) view = WorkflowGroupSerializer() with database.session() as session: manager = WorkflowGroupManager(session=session, fs=FileSystemStore(config)) user_id = model.create_user(session, active=True) workflow_id = model.create_workflow(session) group_id = model.create_group(session, workflow_id, users=[user_id]) manager.upload_file(group_id=group_id, file=io_file(data={'A': 1}), name='a.json') group = manager.get_group(group_id) doc = view.group_handle(group) validator('UserGroupHandle').validate(doc) assert len(doc[labels.GROUP_MEMBERS]) == 1
def test_delete_run(fscls, database, tmpdir): """Test deleting a run.""" # -- Setup ---------------------------------------------------------------- fs = fscls(env=Config().basedir(tmpdir)) _, _, run_id, _ = success_run(database, fs, tmpdir) # -- Test delete run ------------------------------------------------------ with database.session() as session: runs = RunManager(session=session, fs=fs) runs.delete_run(run_id) # -- Error cases ---------------------------------------------------------- with database.session() as session: # Error when deleting an unknown run. runs = RunManager(session=session, fs=fs) with pytest.raises(err.UnknownRunError): runs.delete_run(run_id)
def test_result_archive_local(database, tmpdir): """Test getting an archive of run results.""" # -- Setup ---------------------------------------------------------------- env = Config().basedir(tmpdir).auth() fs = FS(env=env) workflow_id, group_id, run_id, user_id = success_run(database, fs, tmpdir) local_service = LocalAPIFactory(env=env, db=database, engine=StateEngine()) # -- Get result archive --------------------------------------------------- with local_service(user_id=user_id) as api: archive = api.runs().get_result_archive(run_id=run_id) tar = tarfile.open(fileobj=archive.open(), mode='r:gz') members = [t.name for t in tar.getmembers()] assert len(members) == 2 assert 'A.json' in members assert 'run/results/B.json' in members
def test_create_workflow_with_alt_manifest(fscls, database, tmpdir): """Test creating 'Hello World' workflow with a different manifest file.""" fs = fscls(env=Config().basedir(tmpdir)) with database.session() as session: manager = WorkflowManager(session=session, fs=fs) wf = manager.create_workflow(source=BENCHMARK_DIR, manifestfile=ALT_MANIFEST) assert wf.name == 'Hello World' assert wf.description == 'Hello World Demo' assert wf.instructions == '# Hello World' template = wf.get_template() assert template.result_schema is not None staticdir = os.path.join(tmpdir, fs.workflow_staticdir(wf.workflow_id)) assert os.path.isfile(os.path.join(staticdir, 'code/helloworld.py')) assert not os.path.isfile(os.path.join(staticdir, 'data/names.txt'))
def test_initialize_filestore_from_env(tmpdir): """Test initializing the bucket store with a memory bucket from the envirnment variables. """ # -- Setup ---------------------------------------------------------------- env = Config().basedir(tmpdir) env[FLOWSERV_FILESTORE_MODULE] = 'flowserv.model.files.s3' env[FLOWSERV_FILESTORE_CLASS] = 'BucketStore' # -- Create bucket store instance ----------------------------------------- fs = FS(env=env) assert isinstance(fs, BucketStore) assert isinstance(fs.bucket, DiskBucket) # -- Error cases ---------------------------------------------------------- del env[FLOWSERV_FILESTORE_MODULE] with pytest.raises(err.MissingConfigurationError): FS(env=env) env[FLOWSERV_FILESTORE_MODULE] = 'flowserv.model.files.s3' del env[FLOWSERV_FILESTORE_CLASS] with pytest.raises(err.MissingConfigurationError): FS(env=env) # -- Default file store --------------------------------------------------- assert FS(env=Config().basedir(tmpdir)) is not None with pytest.raises(err.MissingConfigurationError): FS(env=Config())
def test_group_listing_serialization(database, tmpdir): """Test serialization of workflow group listing.""" config = Config().basedir(tmpdir) view = WorkflowGroupSerializer() with database.session() as session: manager = WorkflowGroupManager(session=session, fs=FileSystemStore(config)) user_id = model.create_user(session, active=True) workflow_id = model.create_workflow(session) model.create_group(session, workflow_id, users=[user_id]) model.create_group(session, workflow_id, users=[user_id]) groups = manager.list_groups(workflow_id=workflow_id, user_id=user_id) assert len(groups) == 2 doc = view.group_listing(groups) validator('UserGroupListing').validate(doc) assert len(doc[labels.GROUP_LIST]) == 2
def test_workflow_handle_serialization(database, tmpdir): """Test serialization of workflow handles.""" config = Config().basedir(tmpdir) schema = validator('WorkflowHandle') view = WorkflowSerializer() with database.session() as session: manager = WorkflowManager(session=session, fs=FileSystemStore(config)) workflow = manager.create_workflow(source=BENCHMARK_DIR, name='Test', specfile=SPEC_FILE) doc = view.workflow_handle(workflow) schema.validate(doc) workflow = manager.get_workflow(workflow.workflow_id) schema.validate(doc) assert doc[labels.WORKFLOW_NAME] == 'Test'
def test_list_workflow(database, tmpdir): """Test deleting a workflows from the repository.""" # -- Setup ---------------------------------------------------------------- # # Create two workflows. fs = FileSystemStore(env=Config().basedir(tmpdir)) with database.session() as session: manager = WorkflowManager(session=session, fs=fs) manager.create_workflow(source=BENCHMARK_DIR) manager.create_workflow(source=BENCHMARK_DIR) manager.create_workflow(source=BENCHMARK_DIR) # -- Test list workflows -------------------------------------------------- with database.session() as session: manager = WorkflowManager(session=session, fs=fs) workflows = manager.list_workflows() assert len(workflows) == 3