コード例 #1
0
def test_session_auth_token():
    s = SessionFactory()

    # Calling environment should generate an auth token for the creator
    assert s.auth_token is None

    _ = s.environment

    assert s.auth_token.user == s.creator
    assert s.auth_token.expiry == s.expires_at + timedelta(
        minutes=WORKSTATIONS_GRACE_MINUTES)

    # old tokens should be deleted
    old_pk = s.auth_token.pk

    _ = s.environment

    assert s.auth_token.pk != old_pk

    # expiry should stay in sync
    s.maximum_duration = timedelta(days=1)
    s.save()

    assert s.auth_token.expiry == s.expires_at + timedelta(
        minutes=WORKSTATIONS_GRACE_MINUTES)
コード例 #2
0
 def test_password_change_clears_sessions(self):
     SessionFactory(user=self.user)
     SessionFactory(user=self.user)
     SessionFactory(user=self.user)
     assert_equal(3, Session.find().count())
     self.user.set_password('killerqueen')
     assert_equal(0, Session.find().count())
コード例 #3
0
def test_session_cleanup(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=True)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    try:
        s1, s2 = (
            SessionFactory(workstation_image=wsi),
            SessionFactory(workstation_image=wsi,
                           maximum_duration=timedelta(seconds=0)),
        )

        assert s1.service.container
        assert s2.service.container

        stop_expired_services(app_label="workstations", model_name="session")

        assert s1.service.container
        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s2.service.container
    finally:
        stop_all_sessions()
コード例 #4
0
def test_session_api_patch_permissions(client, two_workstation_sets):
    tests = (
        (two_workstation_sets.ws1.editor, 200, True),
        (two_workstation_sets.ws1.user, 200, True),
        (two_workstation_sets.ws1.user1, 404, False),
        (two_workstation_sets.ws2.editor, 404, False),
        (two_workstation_sets.ws2.user, 404, False),
        (UserFactory(), 404, False),
        (UserFactory(is_staff=True), 404, False),
        (None, 401, False),
    )

    for test in tests:
        s = SessionFactory(
            workstation_image=two_workstation_sets.ws1.image,
            creator=two_workstation_sets.ws1.user,
        )

        response = get_view_for_user(
            viewname="api:session-keep-alive",
            client=client,
            method=client.patch,
            user=test[0],
            reverse_kwargs={"pk": s.pk},
            content_type="application/json",
        )
        assert response.status_code == test[1]

        # The maximum duration should have changed from the default
        s.refresh_from_db()
        assert s.status == s.QUEUED  # Read only, always unchanged
        assert (s.maximum_duration == timedelta(minutes=10)) is not test[2]
コード例 #5
0
def test_correct_session_stopped(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=True)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    try:
        s1, s2 = (
            SessionFactory(workstation_image=wsi),
            SessionFactory(workstation_image=wsi),
        )

        assert s1.service.container
        assert s2.service.container

        s2.user_finished = True
        s2.save()

        assert s1.service.container
        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s2.service.container
    finally:
        stop_all_sessions()
コード例 #6
0
ファイル: conftest.py プロジェクト: crgwilson/cabinet
def expired_token(session):
    admin_user_id = 1

    session_factory = SessionFactory()
    new_session = session_factory.new(admin_user_id, ttl=0)

    session.commit()

    token_factory = SessionTokenFactory()
    token = token_factory.new(new_session, test_config.CABINET_SECRET)

    yield token.decode("utf-8")
コード例 #7
0
def test_session_cleanup(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=True)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    default_region = "eu-nl-1"

    try:
        with capture_on_commit_callbacks(execute=True):
            s1, s2, s3 = (
                SessionFactory(workstation_image=wsi, region=default_region),
                SessionFactory(
                    workstation_image=wsi,
                    maximum_duration=timedelta(seconds=0),
                    region=default_region,
                ),
                # An expired service in a different region
                SessionFactory(
                    workstation_image=wsi,
                    maximum_duration=timedelta(seconds=0),
                    region="us-east-1",
                ),
            )

        assert s1.service.container
        assert s2.service.container
        assert s3.service.container

        # Stop expired services in the default region
        stop_expired_services(
            app_label="workstations",
            model_name="session",
            region=default_region,
        )

        assert s1.service.container
        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s2.service.container
        assert s3.service.container

    finally:
        stop_all_sessions()
コード例 #8
0
def test_workstation_ready(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=False)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    s = SessionFactory(workstation_image=wsi)
    s.refresh_from_db()

    assert s.status == s.FAILED
コード例 #9
0
ファイル: test_models.py プロジェクト: comic/comic-django
def test_workstation_ready(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(
        image__from_path=path, image_sha256=sha256, ready=False
    )

    # Execute the celery in place
    settings.task_eager_propagates = (True,)
    settings.task_always_eager = (True,)

    s = SessionFactory(workstation_image=wsi)
    s.refresh_from_db()

    assert s.status == s.FAILED
コード例 #10
0
def test_session_detail(client):
    s1, s2 = SessionFactory(), SessionFactory()

    response = get_view_for_user(
        client=client,
        viewname="workstations:session-detail",
        reverse_kwargs={
            "slug": s1.workstation_image.workstation.slug,
            "pk": s1.pk,
        },
        user=s1.creator,
    )

    assert response.status_code == 200
    assert str(s1.pk) in response.rendered_content
    assert str(s2.pk) not in response.rendered_content
コード例 #11
0
def test_workstations_staff_views(client, view):
    if view in [
        "workstations:update",
        "workstations:detail",
        "workstations:image-create",
        "workstations:session-create",
    ]:
        reverse_kwargs = {"slug": WorkstationFactory().slug}
    elif view in ["workstations:image-detail", "workstations:image-update"]:
        wsi = WorkstationImageFactory()
        reverse_kwargs = {"slug": wsi.workstation.slug, "pk": wsi.pk}
    elif view in [
        "workstations:session-detail",
        "workstations:session-update",
    ]:
        session = SessionFactory()
        reverse_kwargs = {
            "slug": session.workstation_image.workstation.slug,
            "pk": session.pk,
        }
    else:
        reverse_kwargs = {}

    validate_staff_only_view(
        client=client, viewname=view, reverse_kwargs=reverse_kwargs
    )
コード例 #12
0
def test_session_proxy_permissions(client, two_workstation_sets):
    tests = (
        (two_workstation_sets.ws1.editor, 403),
        (two_workstation_sets.ws1.user, 200),
        (two_workstation_sets.ws1.user1, 403),
        (two_workstation_sets.ws2.editor, 403),
        (two_workstation_sets.ws2.user, 403),
        (UserFactory(), 403),
        (UserFactory(is_staff=True), 403),
        (None, 403),
    )

    s = SessionFactory(
        workstation_image=two_workstation_sets.ws1.image,
        creator=two_workstation_sets.ws1.user,
    )

    for test in tests:
        response = get_view_for_user(
            viewname="session-proxy",
            client=client,
            user=test[0],
            reverse_kwargs={
                "slug": s.workstation_image.workstation.slug,
                "pk": s.pk,
                "path": "foo/bar/../../baz",
                "rendering_subdomain": s.region,
            },
        )
        assert response.status_code == test[1]
コード例 #13
0
def test_workstation_user_permissions(client, two_workstation_sets, viewname):
    tests = (
        (two_workstation_sets.ws1.editor, 200),
        (two_workstation_sets.ws1.user, 200),
        (two_workstation_sets.ws2.editor, 403),
        (two_workstation_sets.ws2.user, 403),
        (UserFactory(), 403),
        (UserFactory(is_staff=True), 403),
        (None, 302),
    )

    two_workstation_sets.ws1.image.ready = True
    two_workstation_sets.ws1.image.save()

    kwargs = {"slug": two_workstation_sets.ws1.workstation.slug}

    if viewname == "session-detail":
        s = SessionFactory(
            workstation_image=two_workstation_sets.ws1.image,
            creator=two_workstation_sets.ws1.user,
        )
        kwargs.update({"pk": s.pk, "rendering_subdomain": s.region})
        tests += ((two_workstation_sets.ws1.user1, 403), )

    for test in tests:
        response = get_view_for_user(
            viewname=viewname,
            client=client,
            user=test[0],
            reverse_kwargs=kwargs,
        )
        assert response.status_code == test[1]
コード例 #14
0
def test_workstation_proxy(client):
    u1, u2 = UserFactory(), UserFactory()
    session = SessionFactory(creator=u1)

    url = reverse(
        "workstations:session-proxy",
        kwargs={
            "slug": session.workstation_image.workstation.slug,
            "pk": session.pk,
            "path": "foo/bar/../baz/test",
        },
    )

    response = get_view_for_user(client=client, url=url, user=u1)

    assert response.status_code == 200
    assert response.has_header("X-Accel-Redirect")

    redirect_url = response.get("X-Accel-Redirect")

    assert redirect_url.endswith("foo/baz/test")
    assert redirect_url.startswith("/workstation-proxy/")
    assert session.hostname in redirect_url

    # try as another user
    response = get_view_for_user(client=client, url=url, user=u2)
    assert not response.has_header("X-Accel-Redirect")
    assert response.status_code == 403
コード例 #15
0
ファイル: test_api.py プロジェクト: comic/comic-django
def test_session_detail_api(client):
    user = UserFactory(is_staff=True)
    s = SessionFactory()

    response = get_view_for_user(
        client=client,
        viewname="api:session-detail",
        reverse_kwargs={"pk": s.pk},
        user=user,
        content_type="application/json",
    )

    # Status and pk are required by the js app
    assert response.status_code == 200
    assert all([k in response.json() for k in ["pk", "status"]])
    assert response.json()["pk"] == str(s.pk)
    assert response.json()["status"] == s.get_status_display()
コード例 #16
0
    def test_remove_session_for_user(self):
        SessionFactory(user=self.user)

        # sanity check
        assert_equal(1, Session.find().count())

        utils.remove_sessions_for_user(self.user)
        assert_equal(0, Session.find().count())

        SessionFactory()
        SessionFactory(user=self.user)

        # sanity check
        assert_equal(2, Session.find().count())

        utils.remove_sessions_for_user(self.user)
        assert_equal(1, Session.find().count())
コード例 #17
0
def test_session_detail_api(client):
    user = UserFactory()
    s = SessionFactory(creator=user)

    response = get_view_for_user(
        client=client,
        viewname="api:session-detail",
        reverse_kwargs={"pk": s.pk},
        user=user,
        content_type="application/json",
    )

    # Status and pk are required by the js app
    assert response.status_code == 200
    assert all([k in response.json() for k in ["pk", "status"]])
    assert response.json()["pk"] == str(s.pk)
    assert response.json()["status"] == s.get_status_display()
コード例 #18
0
def test_session_update_read_only_fails(client):
    user = UserFactory()
    s = SessionFactory(creator=user)

    response = get_view_for_user(
        client=client,
        method=client.patch,
        viewname="api:session-keep-alive",
        reverse_kwargs={"pk": s.pk},
        user=user,
        data={"status": "Stopped"},
        content_type="application/json",
    )

    assert response.status_code == 200

    s.refresh_from_db()
    assert s.status == s.QUEUED
コード例 #19
0
 def test_expired_cookie(self):
     self.session = SessionFactory(
         user=self.user1,
         date_created=(datetime.utcnow() -
                       timedelta(seconds=settings.OSF_SESSION_TIMEOUT)))
     cookie = self.user1.get_or_create_cookie()
     self.app.set_cookie(settings.COOKIE_NAME, str(cookie))
     res = self.app.get(self.reachable_url)
     assert_equal(res.status_code, 302)
     assert_in('login', res.location)
コード例 #20
0
def test_session_update_extends_timeout(client):
    user = UserFactory()
    s = SessionFactory(creator=user)

    assert s.maximum_duration == timedelta(minutes=10)

    response = get_view_for_user(
        client=client,
        method=client.patch,
        viewname="api:session-keep-alive",
        reverse_kwargs={"pk": s.pk},
        user=user,
        content_type="application/json",
    )

    assert response.status_code == 200

    s.refresh_from_db()
    # Just check that it changed from the default
    assert s.maximum_duration != timedelta(minutes=10)
コード例 #21
0
def test_correct_session_stopped(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=True)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    try:
        with capture_on_commit_callbacks(execute=True):
            s1, s2 = (
                SessionFactory(workstation_image=wsi),
                SessionFactory(workstation_image=wsi),
            )

        assert s1.service.container
        assert s2.service.container

        s2.refresh_from_db()
        auth_token_pk = s2.auth_token.pk

        with capture_on_commit_callbacks(execute=True):
            s2.user_finished = True
            s2.save()

        assert s1.service.container
        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s2.service.container

        with pytest.raises(ObjectDoesNotExist):
            # auth token should be deleted when the service is stopped
            AuthToken.objects.get(pk=auth_token_pk)

    finally:
        stop_all_sessions()
コード例 #22
0
def test_session_start(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(image__from_path=path,
                                  image_sha256=sha256,
                                  ready=True)

    # Execute the celery in place
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    with capture_on_commit_callbacks(execute=True):
        s = SessionFactory(workstation_image=wsi)

    try:
        assert s.service.container

        s.refresh_from_db()
        assert s.status == s.STARTED

        container = s.service.container

        assert container.labels["traefik.enable"] == "true"
        assert container.labels[
            f"traefik.http.services.{s.hostname}-http.loadbalancer.server.port"] == str(
                s.workstation_image.http_port)
        assert container.labels[
            f"traefik.http.services.{s.hostname}-websocket.loadbalancer.server.port"] == str(
                s.workstation_image.websocket_port)

        networks = container.attrs.get("NetworkSettings")["Networks"]
        assert len(networks) == 1
        assert settings.WORKSTATIONS_NETWORK_NAME in networks

        with capture_on_commit_callbacks(execute=True):
            s.user_finished = True
            s.save()

        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s.service.container
    finally:
        s.stop()
コード例 #23
0
ファイル: conftest.py プロジェクト: crgwilson/cabinet
def tokens(session, user) -> dict:
    admin_user_id = 1
    tokens_to_return = {}

    session_factory = SessionFactory()
    privileged_session = session_factory.new(admin_user_id)
    expired_session = session_factory.new(admin_user_id, ttl=0)
    unprivileged_session = session_factory.new(user.id)

    session.commit()

    token_factory = SessionTokenFactory()
    privileged_token = token_factory.new(privileged_session, test_config.CABINET_SECRET)
    expired_token = token_factory.new(expired_session, test_config.CABINET_SECRET)
    unprivileged_token = token_factory.new(
        unprivileged_session, test_config.CABINET_SECRET
    )

    tokens_to_return["privileged"] = privileged_token.decode("utf-8")
    tokens_to_return["expired"] = expired_token.decode("utf-8")
    tokens_to_return["unprivileged"] = unprivileged_token.decode("utf-8")

    yield tokens_to_return
コード例 #24
0
def test_session_update(client):
    session = SessionFactory()

    assert session.user_finished == False

    response = get_view_for_user(
        client=client,
        method=client.post,
        viewname="workstations:session-update",
        reverse_kwargs={
            "slug": session.workstation_image.workstation.slug,
            "pk": session.pk,
        },
        user=session.creator,
        data={"user_finished": True},
    )

    assert response.status_code == 302
    assert response.url == session.get_absolute_url()

    session.refresh_from_db()

    assert session.user_finished == True
コード例 #25
0
def test_session_list_api(client):
    user = UserFactory()

    response = get_view_for_user(
        client=client,
        viewname="api:session-list",
        user=user,
        content_type="application/json",
    )

    assert response.status_code == 200
    assert response.json()["count"] == 0

    s, _ = SessionFactory(creator=user), SessionFactory(creator=user)

    response = get_view_for_user(
        client=client,
        viewname="api:session-list",
        user=user,
        content_type="application/json",
    )

    assert response.status_code == 200
    assert response.json()["count"] == 2
コード例 #26
0
def test_session_environ(settings, debug):
    settings.DEBUG = debug

    s = SessionFactory()
    env = s.environment

    assert env["GRAND_CHALLENGE_API_ROOT"] == "https://testserver/api/v1/"
    assert "Bearer " in env["GRAND_CHALLENGE_AUTHORIZATION"]
    assert env["WORKSTATION_SESSION_ID"] == str(s.pk)
    assert "WORKSTATION_SENTRY_DSN" in env

    if debug:
        assert "GRAND_CHALLENGE_UNSAFE" in env
    else:
        assert "GRAND_CHALLENGE_UNSAFE" not in env
コード例 #27
0
ファイル: test_views.py プロジェクト: comic/comic-django
def test_session_update(client):
    user = UserFactory(is_staff=True)
    session = SessionFactory()

    assert session.user_finished == False

    response = get_view_for_user(
        client=client,
        method=client.post,
        viewname="workstations:session-update",
        reverse_kwargs={
            "slug": session.workstation_image.workstation.slug,
            "pk": session.pk,
        },
        user=user,
        data={"user_finished": True},
    )

    assert response.status_code == 302
    assert response.url == session.get_absolute_url()

    session.refresh_from_db()

    assert session.user_finished == True
コード例 #28
0
def test_session_only_patchable_by_creator(client):
    user = UserFactory()
    s = SessionFactory(creator=user)

    assert s.maximum_duration == timedelta(minutes=10)

    response = get_view_for_user(
        client=client,
        method=client.patch,
        viewname="api:session-keep-alive",
        reverse_kwargs={"pk": s.pk},
        user=UserFactory(),
        content_type="application/json",
    )

    assert response.status_code == 404
コード例 #29
0
def test_session_environ(settings, debug):
    settings.DEBUG = debug

    s = SessionFactory()
    env = s.environment

    assert env["GRAND_CHALLENGE_PROXY_URL_MAPPINGS"] == ""
    assert "{key}" in env["GRAND_CHALLENGE_QUERY_IMAGE_URL"]
    assert env["GRAND_CHALLENGE_QUERY_IMAGE_URL"].startswith(
        "https://testserver")
    assert (Token.objects.get(user=s.creator).key
            in env["GRAND_CHALLENGE_AUTHORIZATION"])

    if debug:
        assert "GRAND_CHALLENGE_UNSAFE" in env
    else:
        assert "GRAND_CHALLENGE_UNSAFE" not in env
コード例 #30
0
def test_workstation_url_session(settings):
    image, overlay = ImageFactory(), ImageFactory()
    context = {}

    url = workstation_url(context=context, image=image, overlay=overlay)
    assert url.startswith(settings.WORKSTATIONS_GLOBAL_APPLICATION)
    assert f"{settings.WORKSTATIONS_BASE_IMAGE_QUERY_PARAM}={image.pk}" in url
    assert f"{settings.WORKSTATIONS_OVERLAY_QUERY_PARAM}={overlay.pk}" in url

    session = SessionFactory()
    # This will be set by
    # grandchallenge.workstations.context_processors.workstation_session
    context = {"workstation_session": session}

    url = workstation_url(context=context, image=image, overlay=overlay)
    assert url.startswith(session.workstation_url)
    assert f"{settings.WORKSTATIONS_BASE_IMAGE_QUERY_PARAM}={image.pk}" in url
    assert f"{settings.WORKSTATIONS_OVERLAY_QUERY_PARAM}={overlay.pk}" in url
コード例 #31
0
ファイル: test_models.py プロジェクト: comic/comic-django
def test_session_start(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(
        image__from_path=path, image_sha256=sha256, ready=True
    )

    # Execute the celery in place
    settings.task_eager_propagates = (True,)
    settings.task_always_eager = (True,)

    s = SessionFactory(workstation_image=wsi)

    try:
        assert s.service.container

        s.refresh_from_db()
        assert s.status == s.STARTED

        container = s.service.container

        assert container.labels["traefik.enable"] == "true"
        assert container.labels["traefik.http.port"] == str(
            s.workstation_image.http_port
        )
        assert container.labels["traefik.websocket.port"] == str(
            s.workstation_image.websocket_port
        )

        networks = container.attrs.get("NetworkSettings")["Networks"]
        assert len(networks) == 1
        assert settings.WORKSTATIONS_NETWORK_NAME in networks

        s.user_finished = True
        s.save()

        with pytest.raises(NotFound):
            # noinspection PyStatementEffect
            s.service.container
    finally:
        s.stop()
コード例 #32
0
def test_session_auth_token():
    s = SessionFactory()

    # Calling environment should generate an auth token for the creator
    assert s.auth_token is None

    _ = s.environment

    expected_duration = (
        s.created + timedelta(minutes=settings.WORKSTATIONS_GRACE_MINUTES) +
        timedelta(seconds=settings.WORKSTATIONS_SESSION_DURATION_LIMIT))

    assert s.auth_token.user == s.creator
    assert abs(s.auth_token.expiry - expected_duration) < timedelta(seconds=10)

    # old tokens should be deleted
    old_pk = s.auth_token.pk

    _ = s.environment

    assert s.auth_token.pk != old_pk
コード例 #33
0
def test_session_api_permissions(client):
    tests = [(UserFactory(), 403), (UserFactory(is_staff=True), 200)]
    session = SessionFactory()

    for test in tests:
        response = get_view_for_user(
            client=client,
            viewname="api:session-list",
            user=test[0],
            content_type="application/json",
        )
        assert response.status_code == test[1]

        response = get_view_for_user(
            client=client,
            viewname="api:session-detail",
            reverse_kwargs={"pk": session.pk},
            user=test[0],
            content_type="application/json",
        )
        assert response.status_code == test[1]
コード例 #34
0
def test_session_api_permissions(client, two_workstation_sets, viewname):
    tests = (
        (two_workstation_sets.ws1.editor, 200),
        (two_workstation_sets.ws1.user, 200),
        (two_workstation_sets.ws1.user1, 404),
        (two_workstation_sets.ws2.editor, 404),
        (two_workstation_sets.ws2.user, 404),
        (UserFactory(), 404),
        (UserFactory(is_staff=True), 404),
        (None, 404),
    )

    s = SessionFactory(
        workstation_image=two_workstation_sets.ws1.image,
        creator=two_workstation_sets.ws1.user,
    )

    if viewname == "api:session-detail":
        kwargs = {"pk": s.pk}
    else:
        kwargs = {}

    for test in tests:
        response = get_view_for_user(
            viewname=viewname,
            client=client,
            user=test[0],
            reverse_kwargs=kwargs,
        )
        if viewname == "api:session-list":
            if test[1] == 200:
                assert response.json()["count"] == 1
            else:
                assert response.json()["count"] == 0
        else:
            assert response.status_code == test[1]
コード例 #35
0
ファイル: test_models.py プロジェクト: comic/comic-django
def test_session_limit(http_image, docker_client, settings):
    path, sha256 = http_image

    wsi = WorkstationImageFactory(
        image__from_path=path, image_sha256=sha256, ready=True
    )

    # Execute the celery in place
    settings.task_eager_propagates = (True,)
    settings.task_always_eager = (True,)
    settings.WORKSTATIONS_MAXIMUM_SESSIONS = 1

    try:
        s1 = SessionFactory(workstation_image=wsi)
        s1.refresh_from_db()
        assert s1.status == s1.STARTED

        s2 = SessionFactory(workstation_image=wsi)
        s2.refresh_from_db()
        assert s2.status == s2.FAILED

        s1.stop()

        s3 = SessionFactory(workstation_image=wsi)
        s3.refresh_from_db()
        assert s3.status == s3.STARTED
    finally:
        stop_all_sessions()