Files
specklepy/tests/integration/client/current/test_model_ingestion_resource.py
T
Gergő Jedlicska 8249cd2184
Publish Python Package / test (push) Has been cancelled
Publish Python Package / Build and Publish Python Package (push) Has been cancelled
Jedd/cxpla 340 specklepy (#475)
* First pass

* add tests

* Add cancellation

* fix

* status changes

* fixes

* test fixes

* tests(subscriptions): fix model ingestion tests

* feat(modelingestion): rename resource and add some more tests

* feat(ifcimport): use new modelingestion api

* feat: wrap up new ingestion

* fix: model ingestion payload and test server url

* fix: test port was 3000

* fix: remove version message from model ingestion success input

* fix: test subs cancelled

* ci: signal public of private envv in ci

---------

Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-12-08 13:25:54 +01:00

269 lines
10 KiB
Python

from datetime import datetime
import pytest
from specklepy.api import operations
from specklepy.api.client import SpeckleClient
from specklepy.core.api.enums import ModelIngestionStatus
from specklepy.core.api.inputs.model_ingestion_inputs import (
ModelIngestionCancelledInput,
ModelIngestionCreateInput,
ModelIngestionFailedInput,
ModelIngestionRequeueInput,
ModelIngestionStartProcessingInput,
ModelIngestionSuccessInput,
ModelIngestionUpdateInput,
SourceDataInput,
)
from specklepy.core.api.inputs.model_inputs import CreateModelInput
from specklepy.core.api.inputs.project_inputs import ProjectCreateInput
from specklepy.core.api.models.current import (
Model,
ModelIngestion,
ModelIngestionStatusData,
Project,
ProjectVisibility,
Version,
)
from specklepy.logging.exceptions import GraphQLException
from specklepy.objects.base import Base
from specklepy.transports.server.server import ServerTransport
from tests.integration.conftest import is_public
@pytest.mark.run()
@pytest.mark.skipif(is_public(), reason="The public API does not support these tests")
class TestIngestionResource:
@pytest.fixture
def project(self, client: SpeckleClient):
return client.project.create(
ProjectCreateInput(
name="test", description=None, visibility=ProjectVisibility.PUBLIC
)
)
@pytest.fixture
def model(self, client: SpeckleClient, project: Project):
return client.model.create(
CreateModelInput(name="test", description=None, project_id=project.id)
)
@pytest.fixture()
def ingestion(
self, client: SpeckleClient, model: Model, project: Project
) -> ModelIngestion:
input = ModelIngestionCreateInput(
model_id=model.id,
project_id=project.id,
progress_message="Starting processing",
source_data=SourceDataInput(
source_application_slug="pytest",
source_application_version="0.0.0",
file_name=None,
file_size_bytes=None,
),
)
ingestion = client.model_ingestion.create(input)
assert isinstance(ingestion, ModelIngestion)
assert isinstance(ingestion.id, str)
assert isinstance(ingestion.created_at, datetime)
assert isinstance(ingestion.updated_at, datetime)
assert isinstance(ingestion.cancellation_requested, bool)
assert isinstance(ingestion.model_id, str)
assert isinstance(ingestion.status_data, ModelIngestionStatusData)
assert isinstance(ingestion.status_data.progress_message, str | None)
assert ingestion.status_data.status == ModelIngestionStatus.PROCESSING
assert not ingestion.cancellation_requested
assert ingestion.model_id == model.id
return ingestion
def test_update_progress(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
def update(progress: float | None, message: str):
input = ModelIngestionUpdateInput(
ingestion_id=ingestion.id,
project_id=project.id,
progress=progress,
progress_message=message,
)
res = client.model_ingestion.update_progress(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message == message
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.PROCESSING
update(None, "None")
update(0.1, "0.1")
update(0.5, "Whoa-oh! We're half way there!")
update(1, "Finished")
update(0.2, "Back to processing again")
def test_start_processing_and_requeue(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
# just setting the baseline state
assert ingestion.status_data.status == ModelIngestionStatus.PROCESSING
def requeue(message: str):
input = ModelIngestionRequeueInput(
project_id=project.id,
ingestion_id=ingestion.id,
progress_message=message,
)
res = client.model_ingestion.requeue(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message == message
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.QUEUED
def start_processing(message: str):
input = ModelIngestionStartProcessingInput(
ingestion_id=ingestion.id,
project_id=project.id,
progress_message=message,
source_data=SourceDataInput(
source_application_slug="test",
source_application_version="test",
file_name="test",
file_size_bytes=1,
),
)
res = client.model_ingestion.start_processing(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message == message
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.PROCESSING
requeue("put it back in there")
start_processing("go for it")
requeue("and again")
start_processing("run forest run")
def update(progress: float | None, message: str):
input = ModelIngestionUpdateInput(
ingestion_id=ingestion.id,
project_id=project.id,
progress=progress,
progress_message=message,
)
res = client.model_ingestion.update_progress(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message == message
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.PROCESSING
update(None, "None")
update(0.1, "0.1")
update(0.5, "Whoa-oh! We're half way there!")
update(1, "Finished")
update(0.2, "Back to processing again")
def test_error(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
input = ModelIngestionFailedInput(
ingestion_id=ingestion.id,
project_id=project.id,
error_reason="Failed to integration test an error",
error_stacktrace="over here in test_error",
)
res = client.model_ingestion.fail_with_error(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message is None
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.FAILED
# trying to fail for a second time should throw
# with pytest.raises(GraphQLException):
# _ = client.ingestion.fail_with_error(input)
def test_complete(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
remote = ServerTransport(project.id, client)
object_id = operations.send(
Base(applicationId="ASDFGHJKL"), [remote], use_default_cache=False
)
input = ModelIngestionSuccessInput(
ingestion_id=ingestion.id,
root_object_id=object_id,
project_id=project.id,
# version_message=None,
)
res = client.model_ingestion.complete(input)
assert isinstance(res, str)
version = client.version.get(res, project.id)
assert isinstance(version, Version)
# trying to complete for a second time should throw
# with pytest.raises(GraphQLException):
# _ = client.ingestion.complete(input)
def test_cancel(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
input = ModelIngestionCancelledInput(
ingestion_id=ingestion.id,
project_id=project.id,
cancellation_message="This was cancelled for testing purposes",
)
res = client.model_ingestion.fail_with_cancel(input)
assert isinstance(res, ModelIngestion)
assert res.status_data.progress_message is None
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.CANCELLED
# Cancel again, should be idempotent
res = client.model_ingestion.fail_with_cancel(input)
assert res.status_data.progress_message is None
assert not res.cancellation_requested
assert res.status_data.status == ModelIngestionStatus.CANCELLED
def test_error_non_existent_ingestion(
self, client: SpeckleClient, project: Project
):
input = ModelIngestionFailedInput(
ingestion_id="Non-existent-ingestion",
project_id=project.id,
error_reason="Failed to integration test an error",
error_stacktrace="over here in test_error",
)
with pytest.raises(GraphQLException):
_ = client.model_ingestion.fail_with_error(input)
def test_complete_failed_non_existent_ingestion(
self, client: SpeckleClient, project: Project
):
input = ModelIngestionFailedInput(
ingestion_id="Non-existent-ingestion",
project_id=project.id,
error_reason="Failed to integration test an error",
error_stacktrace="over here in test_error",
)
with pytest.raises(GraphQLException):
_ = client.model_ingestion.fail_with_error(input)
def test_complete_non_existent_root_object(
self, client: SpeckleClient, ingestion: ModelIngestion, project: Project
):
input = ModelIngestionSuccessInput(
ingestion_id=ingestion.id,
root_object_id="asdfasdfasdfasfd",
project_id=project.id,
# version_message=None,
)
with pytest.raises(GraphQLException):
_ = client.model_ingestion.complete(input)