예제 #1
0
def sample_context(root: Path) -> Context:
    class Branch(Artifact):
        a: bool
        b: int

    class Leaf1(Branch):
        class Spec(Protocol):
            arg: int

        def __init__(self, spec: Spec) -> None:
            self.a = bool(spec.arg)
            self.b = int(spec.arg)

    class Leaf2(Branch):
        class Spec(Protocol):
            arg: float

        def __init__(self, spec: Spec) -> None:
            self.a = bool(spec.arg - 1)
            self.b = int(spec.arg - 1)

    context = Context(root=root,
                      scope=dict(Branch=Branch, Leaf1=Leaf1, Leaf2=Leaf2))

    with using_context(context):
        Artifact(Namespace(_path_='@/x', type='Leaf1', arg=0))
        Artifact(Namespace(_path_='@/y', type='Leaf2', arg=1.2))

    return context
예제 #2
0
def test_using_contexts(arg_dicts: List[ContextArgDict], wrap: bool) -> None:
    '''
    Test `using_context`.
    '''
    context_blocks = [
        using_context(Context(**arg_dict)) if wrap else using_context(
            **arg_dict) for arg_dict in arg_dicts
    ]

    for block in context_blocks:
        block.__enter__()
    assert fields_match(get_context(), Context(**arg_dicts[-1]))

    for block in reversed(context_blocks):
        block.__exit__(None, None, None)
    assert fields_match(get_context(), Context())
예제 #3
0
def test_schema_requests(tmp_path: Path) -> None:
    '''
    Test requests in the form `GET /schema/{schema-name}`.
    '''
    with using_context(sample_context(tmp_path)):
        get = Client(API()).get
        assert get('/schemas/spec').json == get_spec_schema()
        assert get('/schemas/spec-list').json == get_spec_list_schema()
        assert get('/schemas/spec-dict').json == get_spec_dict_schema()
예제 #4
0
def test_artifact_delete_requests(tmp_path: Path) -> None:
    '''
    Test requests in the form `DELETE /artifacts{/path*}`.
    '''
    with using_context(sample_context(tmp_path)):
        delete = Client(API()).delete
        delete('/artifacts/x')
        assert listdir(tmp_path) == ['y']
        delete('/artifacts/y')
        assert listdir(tmp_path) == []
예제 #5
0
def test_artifact_get_requests(tmp_path: Path) -> None:
    '''
    Test requests in the form `GET /artifacts{/path*}`.
    '''
    with using_context(sample_context(tmp_path)):
        get = Client(API()).get
        x_meta = json.loads((tmp_path / 'x/_meta_.json').read_text())
        y_meta = json.loads((tmp_path / 'y/_meta_.json').read_text())
        for _ in range(3):  # To test caching
            root_res = get('/artifacts').body
            x_res = get('/artifacts/x').body
            y_res = get('/artifacts/y').body
            x_a_res = get('/artifacts/x/a').body
            x_b_res = get('/artifacts/x/b').body
            assert cbor2.loads(root_res) == dict(_meta_=None, x=None, y=None)
            assert cbor2.loads(x_res) == dict(_meta_=x_meta, a=None, b=None)
            assert cbor2.loads(y_res) == dict(_meta_=y_meta, a=None, b=None)
            assert x_a_res == (tmp_path / 'x/a.cbor').read_bytes()
            assert x_b_res == (tmp_path / 'x/b.cbor').read_bytes()
예제 #6
0
def test_artifact_post_requests(tmp_path: Path) -> None:
    '''
    Test requests in the form `POST /artifacts{/path*}`.
    '''
    with using_context(sample_context(tmp_path)):
        post = Client(API()).post

        z_spec = cbor2.dumps(dict(type='Leaf1', arg=2, _path_='@/z'))
        z_res = post('/artifacts', z_spec, status=201)
        assert z_res.location == '/artifacts/z'
        assert (Artifact @ '@/z')._meta_.spec.type == 'Leaf1'
        assert (Artifact @ '@/z')._meta_.events[-1].type == 'Success'

        anon_spec = cbor2.dumps(dict(type='Leaf2', arg=3.0))
        anon_res = post('/artifacts', anon_spec, status=201)
        anon_path = anon_res.location.replace('/artifacts', '@/')
        anon_meta = (Artifact @ anon_path)._meta_
        assert anon_meta.spec.type == 'Leaf2'
        assert anon_meta.events[-1].type == 'Success'

        post('/artifacts', z_spec, status=201)
        post('/artifacts', anon_spec, status=201)
예제 #7
0
def test_permissions(tmp_path: Path) -> None:
    '''
    Test API permissions.
    '''
    permissions = {
        'alice': ('read'),
        'bob': ('read', 'write'),
        'carl': ('read', 'write', 'delete')
    }
    headers = {
        pw: {
            'Authorization': 'Basic ' + b64encode(f':{pw}'.encode()).decode()
        }
        for pw in permissions
    }

    with using_context(sample_context(tmp_path)):
        client = Client(API(permissions=permissions))
        get, post, delete = client.get, client.post, client.delete

        get('/artifacts/x', headers={}, status=401)
        get('/artifacts/x', headers=headers['alice'], status=200)
        get('/artifacts/x', headers=headers['bob'], status=200)
        get('/artifacts/x', headers=headers['carl'], status=200)

        z0_spec = cbor2.dumps(dict(type='Leaf1', arg=2, _path_='@/z0'))
        z1_spec = cbor2.dumps(dict(type='Leaf1', arg=3, _path_='@/z1'))
        post('/artifacts/x', z0_spec, headers={}, status=401)
        post('/artifacts/x', z0_spec, headers=headers['alice'], status=401)
        post('/artifacts/x', z0_spec, headers=headers['bob'], status=201)
        post('/artifacts/x', z1_spec, headers=headers['carl'], status=201)

        delete('/artifacts/x', headers={}, status=401)
        delete('/artifacts/x', headers=headers['alice'], status=401)
        delete('/artifacts/x', headers=headers['bob'], status=401)
        delete('/artifacts/x', headers=headers['carl'], status=204)