예제 #1
0
def test_assign_audit_info():
    """Test that audit_info can be injected with build but not set"""

    assert ProcessSpec("Spec with no audit info").audit_info is None,\
        "Audit info should be None by default"

    audit_info_dict = {'created_by': str(uuid4()), 'created_at': 1560033807392}
    audit_info_obj = AuditInfo.build(audit_info_dict)

    sample_object = ProcessSpec.build({
        'type': 'process_spec',
        'name': "A process spec",
        "audit_info": audit_info_dict
    })
    assert sample_object.audit_info == audit_info_obj, "Audit info should be built from a dict"

    another_object = ProcessSpec.build({
        'type': 'process_spec',
        'name': "A process spec",
        "audit_info": audit_info_obj
    })
    assert another_object.audit_info == audit_info_obj, "Audit info should be built from an obj"

    with pytest.raises(AttributeError, message="Audit info cannot be set"):
        sample_object.audit_info = None

    with pytest.raises(TypeError,
                       message="Audit info must be dict or obj valued"):
        ProcessSpec.build({
            'type': 'process_spec',
            'name': "A process spec",
            "audit_info": "Created by me, yesterday"
        })
예제 #2
0
def test_project_batch_delete_no_errors(project, session):
    job_resp = {
        'job_id': '1234'
    }

    # Actual response-like data - note there is no 'failures' array within 'output'
    successful_job_resp = {
        'job_type': 'batch_delete',
        'status': 'Success',
        'tasks': [
            {
                "id": "7b6bafd9-f32a-4567-b54c-7ce594edc018", "task_type": "batch_delete",
                "status": "Success", "dependencies": []
             }
            ],
        'output': {}
    }

    session.set_responses(job_resp, successful_job_resp)

    # When
    del_resp = project.gemd_batch_delete([uuid.UUID(
        '16fd2706-8baf-433b-82eb-8c7fada847da')])

    # Then
    assert len(del_resp) == 0

    # When trying with entities
    session.set_responses(job_resp, successful_job_resp)
    entity = ProcessSpec(name="proc spec", uids={'id': '16fd2706-8baf-433b-82eb-8c7fada847da'})
    del_resp = project.gemd_batch_delete([entity])

    # Then
    assert len(del_resp) == 0
예제 #3
0
def test_simple_deserialization(valid_data):
    """Ensure that a deserialized Process Spec looks sane."""
    process_spec: ProcessSpec = ProcessSpec.build(valid_data)
    assert process_spec.uids == {'id': valid_data['uids']['id']}
    assert process_spec.tags == ['baking::cakes', 'danger::low']
    assert process_spec.parameters[0] == Parameter(name='oven temp',
                                                   value=UniformReal(
                                                       195, 205, ''),
                                                   origin='specified')
    assert process_spec.conditions == []
    assert process_spec.template == \
           ProcessTemplate('the template', uids={'id': valid_data['template']['uids']['id']},
                           parameters=[
                               [ParameterTemplate('oven temp template',
                                                  bounds=RealBounds(175, 225, ''),
                                                  uids={'id': valid_data['template']['parameters'][0][0]['uids']['id']}),
                                RealBounds(175, 225, '')]
                           ],
                           description='a long description',
                           allowed_labels=['a', 'b'],
                           allowed_names=['a name'])
    assert process_spec.name == 'Process 1'
    assert process_spec.notes == 'make sure to use oven mitts'
    assert process_spec.file_links == [
        FileLink('cake_recipe.txt', 'www.baking.com')
    ]
    assert process_spec.typ == 'process_spec'
    assert process_spec.audit_info == AuditInfo(**valid_data['audit_info'])
예제 #4
0
def test_get_by_name_or_create_no_exist(fake_collection):
    # test when name doesn't exist
    default_provider = lambda: fake_collection.register(
        ProcessSpec("New Resource"))
    old_resource_count = len(list(fake_collection.list()))
    result = get_by_name_or_create(fake_collection, "New Resource",
                                   default_provider)
    new_resource_count = len(list(fake_collection.list()))
    assert result.name == "New Resource"
    assert new_resource_count == old_resource_count + 1
def test_both_present():
    objs = [
        ProcessSpec("ps"),
        PropertyTemplate("pt", bounds=CategoricalBounds()),
        MeasurementSpec("ms"),
        ConditionTemplate("ct", bounds=CategoricalBounds())
    ]
    templates, data_objects = split_templates_from_objects(objs)
    assert len(templates) == 2
    assert len(data_objects) == 2
예제 #6
0
def test_get_by_name_or_create_exist(fake_collection):
    # test when name exists
    resource_name = "resource 2"
    default_provider = lambda: fake_collection.register(
        ProcessSpec("New Resource"))
    old_resource_count = len(list(fake_collection.list()))
    result = get_by_name_or_create(fake_collection, resource_name,
                                   default_provider)
    new_resource_count = len(list(fake_collection.list()))
    assert result.name == resource_name
    assert new_resource_count == old_resource_count
예제 #7
0
def test_soft_process_ingredient_attachment():
    """Test that soft attachments are formed from process to ingredients"""
    vinegar = MaterialSpec("vinegar")
    baking_soda = MaterialSpec("baking soda")
    eruption = ProcessSpec("Volcano eruption")
    vinegar_sample = IngredientSpec("a bit of vinegar",
                                    material=vinegar,
                                    process=eruption)
    baking_soda_sample = IngredientSpec("a bit of NaOh", material=baking_soda)
    baking_soda_sample.process = eruption
    assert set(eruption.ingredients) == {vinegar_sample, baking_soda_sample}, \
        "Creating an ingredient for a process did not auto-populate that process's ingredient list"
예제 #8
0
def fake_collection() -> Collection:
    class FakeCollection(ProcessSpecCollection):
        resources = []

        def register(self, model: ProcessSpec, dry_run=False) -> ProcessSpec:
            self.resources.append(model)
            return model

        def list(self, page: Optional[int] = None, per_page: int = 100):
            if page is None:
                return self.resources
            else:
                return self.resources[(page - 1) * per_page:page * per_page]

    collection = FakeCollection(UUID('6b608f78-e341-422c-8076-35adc8828545'),
                                UUID('6b608f78-e341-422c-8076-35adc8828545'),
                                session)
    for i in range(0, 5):
        collection.register(ProcessSpec("resource " + str(i)))
    for i in range(0, 2):
        collection.register(ProcessSpec(duplicate_name))
    return collection
예제 #9
0
def test_template_assignment():
    """Test that an object and its attributes can both be assigned templates."""
    humidity_template = ConditionTemplate("Humidity",
                                          RealBounds(0.5, 0.75, ""))
    template = ProcessTemplate(
        "Dry", conditions=[[humidity_template,
                            RealBounds(0.5, 0.65, "")]])
    ProcessSpec("Dry a polymer",
                template=template,
                conditions=[
                    Condition("Humidity",
                              value=NominalReal(0.6, ""),
                              template=humidity_template)
                ])
def test_flatten():
    """Test that gemd utility methods can be applied to citrine-python objects.
    Citrine-python resources extend the gemd data model, so the gemd operations
    should work on them.
    """

    bounds = CategoricalBounds(categories=["foo", "bar"])
    template = ProcessTemplate(
        "spam",
        conditions=[(ConditionTemplate(name="eggs", bounds=bounds), bounds)]
    )
    spec = ProcessSpec(name="spec", template=template)

    flat = flatten(spec, scope='testing')
    assert len(flat) == 3, "Expected 3 flattened objects"
예제 #11
0
def test_make_resource_private(project, session):
    dataset_id = str(uuid.uuid4())
    dataset = project.datasets.build(dict(
        id=dataset_id,
        name="private dataset", summary="test", description="test"
    ))
    assert project.make_private(dataset)
    assert 1 == session.num_calls
    expected_call = FakeCall(
        method='POST',
        path='/projects/{}/make-private'.format(project.uid),
        json={
            'resource': {'type': 'DATASET', 'id': dataset_id}
        }
    )
    assert expected_call == session.last_call

    with pytest.raises(RuntimeError):
        project.make_private(ProcessSpec("dummy process"))
예제 #12
0
def test_register_data_concepts(dataset):
    """Check that register routes to the correct collections"""
    expected = {
        MaterialTemplateCollection:
        MaterialTemplate("foo"),
        MaterialSpecCollection:
        MaterialSpec("foo"),
        MaterialRunCollection:
        MaterialRun("foo"),
        ProcessTemplateCollection:
        ProcessTemplate("foo"),
        ProcessSpecCollection:
        ProcessSpec("foo"),
        ProcessRunCollection:
        ProcessRun("foo"),
        MeasurementTemplateCollection:
        MeasurementTemplate("foo"),
        MeasurementSpecCollection:
        MeasurementSpec("foo"),
        MeasurementRunCollection:
        MeasurementRun("foo"),
        IngredientSpecCollection:
        IngredientSpec("foo"),
        IngredientRunCollection:
        IngredientRun(),
        PropertyTemplateCollection:
        PropertyTemplate("bar", bounds=IntegerBounds(0, 1)),
        ParameterTemplateCollection:
        ParameterTemplate("bar", bounds=IntegerBounds(0, 1)),
        ConditionTemplateCollection:
        ConditionTemplate("bar", bounds=IntegerBounds(0, 1))
    }

    for collection, obj in expected.items():
        assert len(obj.uids) == 0
        registered = dataset.register(obj)
        assert len(obj.uids) == 1
        assert len(registered.uids) == 1
        assert basename(dataset.session.calls[-1].path) == basename(
            collection._path_template)
        for pair in obj.uids.items():
            assert pair[1] == registered.uids[pair[0]]
def test_simple_deserialization(valid_data):
    """Ensure that a deserialized Process Run looks sane."""
    process_run: ProcessRun = ProcessRun.build(valid_data)
    assert process_run.uids == {'id': valid_data['uids']['id'], 'my_id': 'process1-v1'}
    assert process_run.tags == ['baking::cakes', 'danger::low']
    assert process_run.conditions[0] == Condition(name='oven temp',
                                                         value=NominalReal(203.0, ''),
                                                         origin='measured')
    assert process_run.parameters == []
    assert process_run.file_links == []
    assert process_run.template is None
    assert process_run.output_material is None
    assert process_run.spec == \
           ProcessSpec(name="Spec for proc 1",
                       uids={'id': valid_data['spec']['uids']['id']},
                       conditions=[Condition(name='oven temp', value=UniformReal(175, 225, ''),
                                             origin='specified')]
                       )
    assert process_run.name == 'Process 1'
    assert process_run.notes == 'make sure to use oven mitts'
    assert process_run.typ == 'process_run'
예제 #14
0
def test_transfer_resource(project, session):

    dataset_id = str(uuid.uuid4())
    dataset = project.datasets.build(dict(
        id=dataset_id,
        name="dataset to transfer", summary="test", description="test"
    ))

    assert project.transfer_resource(dataset, project.uid)

    expected_call = FakeCall(
        method='POST',
        path='/projects/{}/transfer-resource'.format(project.uid),
        json={
            'to_project_id': str(project.uid),
            'resource': dataset.as_entity_dict()
        }
    )
    assert expected_call == session.last_call

    with pytest.raises(RuntimeError):
        project.transfer_resource(ProcessSpec("dummy process"), project.uid)
def test_batch_delete(dataset):
    job_resp = {'job_id': '1234'}

    import json
    failures_escaped_json = json.dumps([{
        "id": {
            'scope': 'somescope',
            'id': 'abcd-1234'
        },
        'cause': {
            "code":
            400,
            "message":
            "",
            "validation_errors": [{
                "failure_message": "fail msg",
                "failure_id": "identifier.coreid.missing"
            }]
        }
    }])

    failed_job_resp = {
        'job_type': 'batch_delete',
        'status': 'Success',
        'tasks': [],
        'output': {
            'failures': failures_escaped_json
        }
    }

    session = dataset.session
    session.set_responses(job_resp, failed_job_resp)

    # When
    del_resp = dataset.gemd_batch_delete(
        [uuid.UUID('16fd2706-8baf-433b-82eb-8c7fada847da')])

    # Then
    assert 2 == session.num_calls

    assert len(del_resp) == 1
    first_failure = del_resp[0]

    expected_api_error = ApiError(
        400,
        "",
        validation_errors=[
            ValidationError(failure_message="fail msg",
                            failure_id="identifier.coreid.missing")
        ])

    assert first_failure == (LinkByUID('somescope',
                                       'abcd-1234'), expected_api_error)

    # And again with tuples of (scope, id)
    session.set_responses(job_resp, failed_job_resp)
    del_resp = dataset.gemd_batch_delete(
        [LinkByUID('id', '16fd2706-8baf-433b-82eb-8c7fada847da')])
    assert len(del_resp) == 1
    first_failure = del_resp[0]

    assert first_failure == (LinkByUID('somescope',
                                       'abcd-1234'), expected_api_error)

    # And again with UUID-like strings
    session.set_responses(job_resp, failed_job_resp)
    del_resp = dataset.gemd_batch_delete(
        ['16fd2706-8baf-433b-82eb-8c7fada847da'])
    assert len(del_resp) == 1
    first_failure = del_resp[0]

    assert first_failure == (LinkByUID('somescope',
                                       'abcd-1234'), expected_api_error)

    # And again with a Base Entity
    session.set_responses(job_resp, failed_job_resp)
    del_resp = dataset.gemd_batch_delete(
        [ProcessSpec(name='PS', uids={"foof": "1"})])
    assert len(del_resp) == 1
    first_failure = del_resp[0]

    assert first_failure == (LinkByUID('somescope',
                                       'abcd-1234'), expected_api_error)
def test_no_templates():
    objs = [ProcessSpec("ps"), MeasurementSpec("ms")]
    templates, data_objects = split_templates_from_objects(objs)
    assert len(templates) == 0
    assert len(data_objects) == 2
예제 #17
0
def test_serialization(valid_data):
    """Ensure that a serialized Process Run looks sane."""
    process_spec: ProcessSpec = ProcessSpec.build(valid_data)
    serialized = process_spec.dump()
    valid_data.pop('audit_info')  # this field is not serialized
    assert serialized == valid_data
예제 #18
0
def test_serialization(valid_data):
    """Ensure that a serialized Process Run looks sane."""
    process_spec: ProcessSpec = ProcessSpec.build(valid_data)
    serialized = process_spec.dump()
    assert serialized == valid_data
예제 #19
0
def test_get_type():
    """Test that get_type works, even though its not used in DataConcepts.build"""

    assert DataConcepts.get_type({"type": "process_run"}) == ProcessRun
    assert DataConcepts.get_type(ProcessSpec("foo")) == ProcessSpec
예제 #20
0
def test_register_all_data_concepts(dataset):
    """Check that register_all registers everything and routes to all collections"""
    bounds = IntegerBounds(0, 1)
    property_template = PropertyTemplate("bar", bounds=bounds)
    parameter_template = ParameterTemplate("bar", bounds=bounds)
    condition_template = ConditionTemplate("bar", bounds=bounds)
    foo_process_template = ProcessTemplate(
        "foo",
        conditions=[[condition_template, bounds]],
        parameters=[[parameter_template, bounds]])
    foo_process_spec = ProcessSpec("foo", template=foo_process_template)
    foo_process_run = ProcessRun("foo", spec=foo_process_spec)
    foo_material_template = MaterialTemplate(
        "foo", properties=[[property_template, bounds]])
    foo_material_spec = MaterialSpec("foo",
                                     template=foo_material_template,
                                     process=foo_process_spec)
    foo_material_run = MaterialRun("foo",
                                   spec=foo_material_spec,
                                   process=foo_process_run)
    baz_template = MaterialTemplate("baz")
    foo_measurement_template = MeasurementTemplate(
        "foo",
        conditions=[[condition_template, bounds]],
        parameters=[[parameter_template, bounds]],
        properties=[[property_template, bounds]])
    foo_measurement_spec = MeasurementSpec("foo",
                                           template=foo_measurement_template)
    foo_measurement_run = MeasurementRun("foo",
                                         spec=foo_measurement_spec,
                                         material=foo_material_run)
    foo_ingredient_spec = IngredientSpec("foo",
                                         material=foo_material_spec,
                                         process=foo_process_spec)
    foo_ingredient_run = IngredientRun(spec=foo_ingredient_spec,
                                       material=foo_material_run,
                                       process=foo_process_run)
    baz_run = MeasurementRun("baz")

    # worst order possible
    expected = {
        foo_ingredient_run: IngredientRunCollection,
        foo_ingredient_spec: IngredientSpecCollection,
        foo_measurement_run: MeasurementRunCollection,
        foo_measurement_spec: MeasurementSpecCollection,
        foo_measurement_template: MeasurementTemplateCollection,
        foo_material_run: MaterialRunCollection,
        foo_material_spec: MaterialSpecCollection,
        foo_material_template: MaterialTemplateCollection,
        foo_process_run: ProcessRunCollection,
        foo_process_spec: ProcessSpecCollection,
        foo_process_template: ProcessTemplateCollection,
        baz_template: MaterialTemplateCollection,
        baz_run: MeasurementRunCollection,
        property_template: PropertyTemplateCollection,
        parameter_template: ParameterTemplateCollection,
        condition_template: ConditionTemplateCollection
    }
    for obj in expected:
        assert len(obj.uids) == 0  # All should be without ids
    registered = dataset.register_all(expected.keys())
    assert len(registered) == len(expected)

    seen_ids = set()
    for obj in expected:
        assert len(obj.uids) == 1  # All should now have exactly 1 id
        for pair in obj.uids.items():
            assert pair not in seen_ids  # All ids are different
            seen_ids.add(pair)
    for obj in registered:
        for pair in obj.uids.items():
            assert pair in seen_ids  # registered items have the same ids

    call_basenames = [
        call.path.split('/')[-2] for call in dataset.session.calls
    ]
    collection_basenames = [
        basename(collection._path_template)
        for collection in expected.values()
    ]
    assert set(call_basenames) == set(collection_basenames)
    assert len(set(call_basenames)) == len(
        call_basenames)  # calls are batched internally

    # spot check order. Does not check every constraint
    assert call_basenames.index(
        basename(
            IngredientRunCollection._path_template)) > call_basenames.index(
                basename(IngredientSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialRunCollection._path_template)) > call_basenames.index(
            basename(MaterialSpecCollection._path_template))
    assert call_basenames.index(
        basename(
            MeasurementRunCollection._path_template)) > call_basenames.index(
                basename(MeasurementSpecCollection._path_template))
    assert call_basenames.index(basename(
        ProcessRunCollection._path_template)) > call_basenames.index(
            basename(ProcessSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(MaterialTemplateCollection._path_template))
    assert call_basenames.index(
        basename(
            MeasurementSpecCollection._path_template)) > call_basenames.index(
                basename(MeasurementTemplateCollection._path_template))
    assert call_basenames.index(basename(
        ProcessSpecCollection._path_template)) > call_basenames.index(
            basename(ProcessTemplateCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(ProcessSpecCollection._path_template))
    assert call_basenames.index(basename(
        MaterialSpecCollection._path_template)) > call_basenames.index(
            basename(MeasurementSpecCollection._path_template))
    assert call_basenames.index(
        basename(MeasurementTemplateCollection._path_template)
    ) > call_basenames.index(
        basename(ConditionTemplateCollection._path_template))
    assert call_basenames.index(
        basename(MeasurementTemplateCollection._path_template)
    ) > call_basenames.index(
        basename(ParameterTemplateCollection._path_template))
    assert call_basenames.index(
        basename(
            MaterialTemplateCollection._path_template)) > call_basenames.index(
                basename(PropertyTemplateCollection._path_template))