예제 #1
0
async def test_http_methods(client):
    async with respx.mock:
        url = "https://foo.bar"
        route = respx.get(url, path="/") % 404
        respx.post(url, path="/").respond(200)
        respx.post(url, path="/").respond(201)
        respx.put(url, path="/").respond(202)
        respx.patch(url, path="/").respond(500)
        respx.delete(url, path="/").respond(204)
        respx.head(url, path="/").respond(405)
        respx.options(url, path="/").respond(status_code=501)
        respx.request("GET", url, path="/baz/").respond(status_code=204)
        url += "/"

        response = httpx.get(url)
        assert response.status_code == 404
        response = await client.get(url)
        assert response.status_code == 404

        response = httpx.get(url + "baz/")
        assert response.status_code == 204
        response = await client.get(url + "baz/")
        assert response.status_code == 204

        response = httpx.post(url)
        assert response.status_code == 201
        response = await client.post(url)
        assert response.status_code == 201

        response = httpx.put(url)
        assert response.status_code == 202
        response = await client.put(url)
        assert response.status_code == 202

        response = httpx.patch(url)
        assert response.status_code == 500
        response = await client.patch(url)
        assert response.status_code == 500

        response = httpx.delete(url)
        assert response.status_code == 204
        response = await client.delete(url)
        assert response.status_code == 204

        response = httpx.head(url)
        assert response.status_code == 405
        response = await client.head(url)
        assert response.status_code == 405

        response = httpx.options(url)
        assert response.status_code == 501
        response = await client.options(url)
        assert response.status_code == 501

        assert route.called is True
        assert respx.calls.call_count == 8 * 2
예제 #2
0
def test_raise_on_request_error(client: Client) -> None:
    """If raise exception on a request error."""
    respx.put(
        re.compile(PLATFORM_URL + "/api/v1/requests/.*$"),
        content=httpcore.ConnectError(),
    )

    # TODO: do we really want to leak httpx to our clients?
    # We could catch all exception thrown by httpx, wrap it in a few library
    # exceptions and rethrow those.
    with pytest.raises(httpx.ConnectError):
        client.put(client.identity.public_identity)
예제 #3
0
def test_replace_namespaced(client: lightkube.Client):
    req = respx.put("https://localhost:9443/api/v1/namespaces/default/pods/xy").respond(json={'metadata': {'name': 'xy'}})
    pod = client.replace(Pod(metadata=ObjectMeta(name="xy")))
    assert req.calls[0][0].read() == b'{"metadata": {"name": "xy"}}'
    assert pod.metadata.name == 'xy'

    respx.put("https://localhost:9443/api/v1/namespaces/other/pods/xz").respond(json={'metadata': {'name': 'xz'}})
    pod = client.replace(Pod(metadata=ObjectMeta(name="xz")), namespace='other')
    assert pod.metadata.name == 'xz'

    # namespace inside object definition need to match with provided namespace parameter.
    with pytest.raises(ValueError):
        client.replace(Pod(metadata=ObjectMeta(name="xx", namespace='ns1')), namespace='ns2')
예제 #4
0
async def test_http_methods(client):
    async with respx.mock:
        url = "https://foo.bar/"
        m = respx.get(url, status_code=404)
        respx.post(url, status_code=201)
        respx.put(url, status_code=202)
        respx.patch(url, status_code=500)
        respx.delete(url, status_code=204)
        respx.head(url, status_code=405)
        respx.options(url, status_code=501)

        response = httpx.get(url)
        assert response.status_code == 404
        response = await client.get(url)
        assert response.status_code == 404

        response = httpx.post(url)
        assert response.status_code == 201
        response = await client.post(url)
        assert response.status_code == 201

        response = httpx.put(url)
        assert response.status_code == 202
        response = await client.put(url)
        assert response.status_code == 202

        response = httpx.patch(url)
        assert response.status_code == 500
        response = await client.patch(url)
        assert response.status_code == 500

        response = httpx.delete(url)
        assert response.status_code == 204
        response = await client.delete(url)
        assert response.status_code == 204

        response = httpx.head(url)
        assert response.status_code == 405
        response = await client.head(url)
        assert response.status_code == 405

        response = httpx.options(url)
        assert response.status_code == 501
        response = await client.options(url)
        assert response.status_code == 501

        assert m.called is True
        assert respx.stats.call_count == 7 * 2
예제 #5
0
 async def test_update_copyfactory_account(self):
     """Should update CopyFactory account via API."""
     account = {
         'name': 'Demo account',
         'connectionId': 'e8867baa-5ec2-45ae-9930-4d5cea18d0d6',
         'reservedMarginFraction': 0.25,
         'subscriptions': [{
             'strategyId': 'ABCD',
             'multiplier': 1
         }]
     }
     rsps = respx.put(
         f'{copy_factory_api_url}/users/current/configuration/accounts/' +
         '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
         json=account).mock(return_value=Response(200))
     await copy_factory_client.update_account(
         '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
         account)
     assert rsps.calls[0].request.url == f'{copy_factory_api_url}/users/current/configuration/accounts/' + \
                                         '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers[
         'auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.read() == json.dumps(account).encode(
         'utf-8')
예제 #6
0
 async def test_update_strategy(self):
     """Should update strategy via API."""
     strategy = {
         'name': 'Test strategy',
         'positionLifecycle': 'hedging',
         'connectionId': 'e8867baa-5ec2-45ae-9930-4d5cea18d0d6',
         'maxTradeRisk': 0.1,
         'stopOutRisk': {
             'value': 0.4,
             'startTime': '2020-08-24T00:00:00.000Z'
         },
         'timeSettings': {
             'lifetimeInHours': 192,
             'openingIntervalInMinutes': 5
         }
     }
     rsps = respx.put(f'{copy_factory_api_url}/users/current/configuration/strategies/ABCD', json=strategy)\
         .mock(return_value=Response(200))
     await copy_factory_client.update_strategy('ABCD', strategy)
     assert rsps.calls[
         0].request.url == f'{copy_factory_api_url}/users/current/configuration/strategies/ABCD'
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers[
         'auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.content == json.dumps(strategy).encode(
         'utf-8')
 async def test_update_strategy(self):
     """Should update strategy via API."""
     strategy = {
         'name': 'Test strategy',
         'description': 'Test description',
         'maxTradeRisk': 0.1,
         'stopOutRisk': {
             'relativeValue': 0.4,
             'startTime': date('2020-08-24T00:00:00.000Z')
         },
         'riskLimits': [{
             'type': 'monthly',
             'applyTo': 'balance',
             'maxRelativeRisk': 0.5,
             'closePositions': False,
             'startTime': date('2020-08-24T00:00:01.000Z')
         }],
         'timeSettings': {
             'lifetimeInHours': 192,
             'openingIntervalInMinutes': 5
         }
     }
     json_copy = deepcopy(strategy)
     json_copy['stopOutRisk']['startTime'] = format_date(json_copy['stopOutRisk']['startTime'])
     json_copy['riskLimits'][0]['startTime'] = format_date(json_copy['riskLimits'][0]['startTime'])
     rsps = respx.put(f'{copy_factory_api_url}/users/current/configuration/strategies/ABCD', json=json_copy)\
         .mock(return_value=Response(200))
     await copy_factory_client.update_strategy('ABCD', strategy)
     assert rsps.calls[0].request.url == f'{copy_factory_api_url}/users/current/configuration/strategies/ABCD'
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers['auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.content == json.dumps(json_copy).encode('utf-8')
예제 #8
0
    async def test_update_appearance(self, gl, is_gl_sync):
        new_title = "new-title"
        new_description = "new-description"

        request = respx.put(
            re.compile("^http://localhost/api/v4/application/appearance"),
            content={
                "title": new_title,
                "description": new_description,
                "logo": "/uploads/-/system/appearance/logo/1/logo.png",
                "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
                "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
                "new_project_guidelines": "Please read the FAQs for help.",
                "header_message": "",
                "footer_message": "",
                "message_background_color": "#e75e40",
                "message_font_color": "#ffffff",
                "email_header_and_footer_enabled": False,
            },
            status_code=codes.OK,
        )

        if is_gl_sync:
            gl.appearance.update(title=new_title, description=new_description)
        else:
            await gl.appearance.update(title=new_title, description=new_description)
예제 #9
0
async def test_failed_async_set_command_permission(dispike_object: Dispike, ):
    respx.put(
        "https://discord.com/api/v8/applications/APPID/guilds/1111/commands/1234/permissions"
    ).mock(return_value=Response(500, ))

    _set_commands = await dispike_object.async_set_command_permission(
        command_id=1234,
        guild_id=1111,
        new_permissions=NewApplicationPermission(permissions=[
            ApplicationCommandPermissions(
                permission=True,
                type=ApplicationCommandPermissionType.ROLE,
                id=1234)
        ]),
    )
    assert _set_commands == False
예제 #10
0
async def test_diagnostics_reset(
        monkeypatch, optimizer: servo.configuration.Optimizer) -> None:

    monkeypatch.setenv("POD_NAMESPACE", "test-namespace")

    servo_runner = servo.runner.ServoRunner(
        servo.Servo(
            config=servo.BaseServoConfiguration(name="archibald",
                                                optimizer=optimizer),
            connectors=[],  # Init empty servo
        ))

    put = respx.put(
        f"https://api.opsani.com/accounts/dev.opsani.com/applications/servox/{servo.telemetry.DIAGNOSTICS_CHECK_ENDPOINT}"
    ).mock(return_value=httpx.Response(200, text=f'{{"status": "ok"}}'))
    reset_state = servo.telemetry.DiagnosticStates.withhold
    response = await servo.telemetry.DiagnosticsHandler(
        servo_runner.servo)._diagnostics_api(
            method="PUT",
            endpoint=servo.telemetry.DIAGNOSTICS_CHECK_ENDPOINT,
            output_model=servo.api.Status,
            json=reset_state,
        )

    assert put.called
    assert response.status == servo.api.OptimizerStatuses.ok
예제 #11
0
def test_field_manager(kubeconfig):
    config = KubeConfig.from_file(str(kubeconfig))
    client = lightkube.Client(config=config, field_manager='lightkube')
    respx.patch("https://localhost:9443/api/v1/nodes/xx?fieldManager=lightkube").respond(json={'metadata': {'name': 'xx'}})
    client.patch(Node, "xx", [{"op": "add", "path": "/metadata/labels/x", "value": "y"}],
                       patch_type=types.PatchType.JSON)

    respx.post("https://localhost:9443/api/v1/namespaces/default/pods?fieldManager=lightkube").respond(json={'metadata': {'name': 'xx'}})
    client.create(Pod(metadata=ObjectMeta(name="xx", labels={'l': 'ok'})))

    respx.put("https://localhost:9443/api/v1/namespaces/default/pods/xy?fieldManager=lightkube").respond(
        json={'metadata': {'name': 'xy'}})
    client.replace(Pod(metadata=ObjectMeta(name="xy")))

    respx.put("https://localhost:9443/api/v1/namespaces/default/pods/xy?fieldManager=override").respond(
        json={'metadata': {'name': 'xy'}})
    client.replace(Pod(metadata=ObjectMeta(name="xy")), field_manager='override')
예제 #12
0
async def test_replace_global(client: lightkube.AsyncClient):
    req = respx.put("https://localhost:9443/api/v1/nodes/xx").respond(
        json={'metadata': {
            'name': 'xx'
        }})
    pod = await client.replace(Node(metadata=ObjectMeta(name="xx")))
    assert req.calls[0][0].read() == b'{"metadata": {"name": "xx"}}'
    assert pod.metadata.name == 'xx'
    await client.close()
예제 #13
0
    async def test_get_update_appearance(self, gl, gl_get_value, is_gl_sync):
        title = "GitLab Test Instance"
        new_title = "new-title"
        description = "gitlab-test.example.com"
        new_description = "new-description"

        request_get_appearance = respx.get(
            "http://localhost/api/v4/application/appearance",
            content={
                "title": title,
                "description": description,
                "logo": "/uploads/-/system/appearance/logo/1/logo.png",
                "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
                "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
                "new_project_guidelines": "Please read the FAQs for help.",
                "header_message": "",
                "footer_message": "",
                "message_background_color": "#e75e40",
                "message_font_color": "#ffffff",
                "email_header_and_footer_enabled": False,
            },
            status_code=codes.OK,
        )
        request_update_appearance = respx.put(
            "http://localhost/api/v4/application/appearance",
            content={
                "title": new_title,
                "description": new_description,
                "logo": "/uploads/-/system/appearance/logo/1/logo.png",
                "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
                "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
                "new_project_guidelines": "Please read the FAQs for help.",
                "header_message": "",
                "footer_message": "",
                "message_background_color": "#e75e40",
                "message_font_color": "#ffffff",
                "email_header_and_footer_enabled": False,
            },
            status_code=codes.OK,
        )

        appearance = gl.appearance.get()
        appearance = await gl_get_value(appearance)

        assert appearance.title == title
        assert appearance.description == description
        appearance.title = new_title
        appearance.description = new_description
        if is_gl_sync:
            appearance.save()
        else:
            await appearance.save()
        assert appearance.title == new_title
        assert appearance.description == new_description
예제 #14
0
    async def test_put_request(self, gl, gl_get_value):
        request = respx.put(
            "http://localhost/api/v4/projects",
            headers={"content-type": "application/json"},
            content='{"name": "project1"}',
            status_code=codes.OK,
        )
        result = gl.http_put("/projects")
        result = await gl_get_value(result)

        assert isinstance(result, dict)
        assert result["name"] == "project1"
예제 #15
0
def test_bulk_edit_command_guild_only(dispike_object: Dispike,
                                      example_edit_command: DiscordCommand):
    respx.put(
        "https://discord.com/api/v8/applications/APPID/guilds/EXAMPLE_GUILD/commands"
    ).mock(return_value=Response(
        200,
        json=[example_edit_command.dict(),
              example_edit_command.dict()],
    ))
    _edit_command = dispike_object.edit_command(
        new_command=[example_edit_command, example_edit_command],
        guild_only=True,
        bulk=True,
        guild_id_passed="EXAMPLE_GUILD",
    )
    assert isinstance(_edit_command, list) == True
    assert len(_edit_command) == 2
    for command in _edit_command:
        assert isinstance(command, DiscordCommand)
        assert command.id == example_edit_command.id
        assert command.name == example_edit_command.name
예제 #16
0
 async def test_upload_file(self):
     """Should upload file to a provisioning profile via API."""
     rsps = respx.put(f'{PROVISIONING_API_URL}/users/current/provisioning-profiles/id/servers.dat') \
         .mock(return_value=Response(204))
     with mock.patch('__main__.open',
                     new=mock.mock_open(read_data='test')) as file:
         file.return_value = 'test', 'test2'
         await provisioning_client.upload_provisioning_profile_file(
             'id', 'servers.dat', file())
         assert rsps.calls[0].request.url == \
             f'{PROVISIONING_API_URL}/users/current/provisioning-profiles/id/servers.dat'
         assert rsps.calls[0].request.headers[
             'auth-token'] == 'header.payload.sign'
예제 #17
0
 async def test_update(self):
     """Should update provisioning profile via API."""
     rsps = respx.put(f'{PROVISIONING_API_URL}/users/current/provisioning-profiles/id') \
         .mock(return_value=Response(200))
     await provisioning_client.update_provisioning_profile(
         'id', {'name': 'new name'})
     assert rsps.calls[0].request.url == \
            f'{PROVISIONING_API_URL}/users/current/provisioning-profiles/id'
     assert rsps.calls[0].request.headers[
         'auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.content == json.dumps({
         'name': 'new name'
     }).encode('utf-8')
    async def test_update_mixin_no_id(self, gl):
        class M(UpdateMixin, FakeManager):
            _create_attrs = (("foo",), ("bar", "baz"))
            _update_attrs = (("foo",), ("bam",))

        request = respx.put(
            "http://localhost/api/v4/tests",
            headers={"Content-Type": "application/json"},
            content='{"foo": "baz"}',
            status_code=codes.OK,
        )
        mgr = M(gl)
        server_data = await mgr.update(new_data={"foo": "baz"})
        assert isinstance(server_data, dict)
        assert server_data["foo"] == "baz"
    async def test_set_mixin(self, gl):
        class M(SetMixin, FakeManager):
            pass

        request = respx.put(
            "http://localhost/api/v4/tests/foo",
            headers={"Content-Type": "application/json"},
            content='{"key": "foo", "value": "bar"}',
            status_code=codes.OK,
        )

        mgr = M(gl)
        obj = await mgr.set("foo", "bar")
        assert isinstance(obj, FakeObject)
        assert obj.key == "foo"
        assert obj.value == "bar"
예제 #20
0
    async def test_create_update_project_snippets(self, gl, gl_get_value,
                                                  is_gl_sync):
        title = "Example Snippet Title"
        new_title = "new-title"
        visibility = "private"
        request_update = respx.put(
            "http://localhost/api/v4/projects/1/snippets",
            content={
                "title": new_title,
                "description": "More verbose snippet description",
                "file_name": "example.txt",
                "content": "source code with multiple lines",
                "visibility": visibility,
            },
            status_code=codes.OK,
        )

        request_create = respx.post(
            "http://localhost/api/v4/projects/1/snippets",
            content={
                "title": title,
                "description": "More verbose snippet description",
                "file_name": "example.txt",
                "content": "source code with multiple lines",
                "visibility": visibility,
            },
            status_code=codes.OK,
        )

        project = gl.projects.get(1, lazy=True)
        snippet = project.snippets.create({
            "title": title,
            "file_name": title,
            "content": title,
            "visibility": visibility,
        })
        snippet = await gl_get_value(snippet)
        assert snippet.title == title
        assert snippet.visibility == visibility

        snippet.title = new_title
        if is_gl_sync:
            snippet.save()
        else:
            await snippet.save()
        assert snippet.title == new_title
        assert snippet.visibility == visibility
 async def test_update(self):
     """Should update MetaTrader account via API."""
     update_account = {
         'name': 'new account name',
         'password': '******',
         'server': 'ICMarketsSC2-Demo',
         'tags': ['tag1']
     }
     rsps = respx.put(f'{PROVISIONING_API_URL}/users/current/accounts/id', json=update_account) \
         .mock(return_value=Response(204))
     await account_client.update_account('id', update_account)
     assert rsps.calls[
         0].request.url == f'{PROVISIONING_API_URL}/users/current/accounts/id'
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers[
         'auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.content == json.dumps(
         update_account).encode('utf-8')
예제 #22
0
 async def test_update_external_signal(self):
     """Should update external signal."""
     signal = {
         'symbol': 'EURUSD',
         'type': 'POSITION_TYPE_BUY',
         'time': date('2020-08-24T00:00:00.000Z'),
         'updateTime': date('2020-08-24T00:00:00.000Z'),
         'volume': 1
     }
     rsps = respx.put(f'{copy_factory_api_url}/users/current/strategies/ABCD/external-signals/0123456')\
         .mock(return_value=Response(200))
     await trading_client.update_external_signal('ABCD', '0123456', signal)
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers[
         'auth-token'] == 'header.payload.sign'
     signal['time'] = format_date(signal['time'])
     signal['updateTime'] = format_date(signal['updateTime'])
     assert rsps.calls[0].request.content == json.dumps(signal).encode(
         'utf-8')
예제 #23
0
    async def test_update_submodule(self, gl, gl_get_value):
        request_get_project = respx.get(
            "http://localhost/api/v4/projects/1",
            headers={"content-type": "application/json"},
            content='{"name": "name", "id": 1}'.encode("utf-8"),
            status_code=codes.OK,
        )
        request_update_submodule = respx.put(
            "http://localhost/api/v4/projects/1/repository/submodules/foo%2Fbar",
            headers={"content-type": "application/json"},
            content="""{
            "id": "ed899a2f4b50b4370feeea94676502b42383c746",
            "short_id": "ed899a2f4b5",
            "title": "Message",
            "author_name": "Author",
            "author_email": "*****@*****.**",
            "committer_name": "Author",
            "committer_email": "*****@*****.**",
            "created_at": "2018-09-20T09:26:24.000-07:00",
            "message": "Message",
            "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ],
            "committed_date": "2018-09-20T09:26:24.000-07:00",
            "authored_date": "2018-09-20T09:26:24.000-07:00",
            "status": null}""".encode("utf-8"),
            status_code=codes.OK,
        )
        project = gl.projects.get(1)
        project = await gl_get_value(project)
        assert isinstance(project, Project)
        assert project.name == "name"
        assert project.id == 1

        ret = project.update_submodule(
            submodule="foo/bar",
            branch="master",
            commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664",
            commit_message="Message",
        )
        ret = await gl_get_value(ret)
        assert isinstance(ret, dict)
        assert ret["message"] == "Message"
        assert ret["id"] == "ed899a2f4b50b4370feeea94676502b42383c746"
    async def test_save_mixin(self, gl):
        class M(UpdateMixin, FakeManager):
            pass

        class O(SaveMixin, RESTObject):
            pass

        request = respx.put(
            "http://localhost/api/v4/tests/42",
            headers={"Content-Type": "application/json"},
            content='{"id": 42, "foo": "baz"}',
            status_code=codes.OK,
        )

        mgr = M(gl)
        obj = O(mgr, {"id": 42, "foo": "bar"})
        obj.foo = "baz"
        await obj.save()
        assert obj._attrs["foo"] == "baz"
        assert obj._updated_attrs == {}
 async def test_update_portfolio_strategy(self):
     """Should update portfolio strategy via API."""
     strategy = {
         'name': 'Test strategy',
         'members': [{
             'strategyId': 'BCDE'
         }],
         'maxTradeRisk': 0.1,
         'stopOutRisk': {
             'relativeValue': 0.4,
             'startTime': '2020-08-24T00:00:00.000Z'
         }
     }
     rsps = respx.put(f'{copy_factory_api_url}/users/current/configuration/portfolio-strategies/ABCD',
                      json=strategy).mock(return_value=Response(200))
     await copy_factory_client.update_portfolio_strategy('ABCD', strategy)
     assert rsps.calls[0].request.url == f'{copy_factory_api_url}/users/current/configuration/' \
                                         f'portfolio-strategies/ABCD'
     assert rsps.calls[0].request.method == 'PUT'
     assert rsps.calls[0].request.headers['auth-token'] == 'header.payload.sign'
     assert rsps.calls[0].request.content == json.dumps(strategy).encode('utf-8')
예제 #26
0
    async def test_deployment(self, gl, gl_get_value, is_gl_sync):

        content = '{"id": 42, "status": "success", "ref": "master"}'
        json_content = json.loads(content)

        request_deployment_create = respx.post(
            "http://localhost/api/v4/projects/1/deployments",
            headers={"content-type": "application/json"},
            content=json_content,
            status_code=codes.OK,
        )

        project = gl.projects.get(1, lazy=True)
        deployment = project.deployments.create({
            "environment": "Test",
            "sha": "1agf4gs",
            "ref": "master",
            "tag": False,
            "status": "created",
        })
        deployment = await gl_get_value(deployment)
        assert deployment.id == 42
        assert deployment.status == "success"
        assert deployment.ref == "master"

        json_content["status"] = "failed"
        request_deployment_update = respx.put(
            "http://localhost/api/v4/projects/1/deployments/42",
            headers={"content-type": "application/json"},
            content=json_content,
            status_code=codes.OK,
        )
        deployment.status = "failed"

        if is_gl_sync:
            deployment.save()
        else:
            await deployment.save()

        assert deployment.status == "failed"