test_pipeline_stock_aliases_3386.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. """Run with pipeline maps hidden Standard refs for the right user (#3386).
  2. The preset listing hides a Standard preset behind its Bambu Cloud stock twin,
  3. and a pipeline run slices with that twin, as the Slice dialog does. The twin
  4. is resolved for the user who resolves the run's cloud presets, which is not
  5. the run's owner when a key without the cloud scope starts it.
  6. """
  7. from pathlib import Path
  8. from unittest.mock import AsyncMock, patch
  9. import pytest
  10. from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
  11. from backend.app.models.library import LibraryFile
  12. from backend.app.models.pipeline_run import PipelineRun
  13. from backend.app.models.slicer_pipeline import SlicerPipeline
  14. from backend.app.models.user import User
  15. from backend.app.schemas.slicer import PresetRef
  16. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  17. class _Stop(Exception):
  18. """Ends the run once the slice request has been captured."""
  19. async def _run_pipeline(db_session, test_engine, tmp_path, *, creator_user_id, created_by):
  20. from backend.app.api.routes.pipeline_runs import _make_orchestration_callable
  21. pipeline = SlicerPipeline(
  22. name="stock presets",
  23. printer_preset_source="standard",
  24. printer_preset_id="Bambu Lab X1 Carbon 0.4 nozzle",
  25. process_preset_source="standard",
  26. process_preset_id="0.20mm Standard @BBL X1C",
  27. filament_presets_json='[{"source": "standard", "id": "Bambu PLA Basic @BBL X1C"}]',
  28. bed_type="Textured PEI Plate",
  29. )
  30. source = LibraryFile(filename="cube.stl", file_path="library/cube.stl", file_type="stl", file_size=1)
  31. db_session.add_all([pipeline, source])
  32. await db_session.commit()
  33. run = PipelineRun(pipeline_id=pipeline.id, source_library_file_id=source.id, copies=1, created_by=created_by)
  34. db_session.add(run)
  35. await db_session.commit()
  36. model = tmp_path / "cube.stl"
  37. model.write_bytes(b"model")
  38. seen_users = []
  39. async def aliases_for(_db, user):
  40. # Only a signed-in user has a cloud list, so only they get aliases.
  41. seen_users.append(user.id if user is not None else None)
  42. if user is None:
  43. return {}
  44. return {
  45. "standard:Bambu Lab X1 Carbon 0.4 nozzle": PresetRef(source="cloud", id="GM001"),
  46. "standard:0.20mm Standard @BBL X1C": PresetRef(source="cloud", id="GP004"),
  47. "standard:Bambu PLA Basic @BBL X1C": PresetRef(source="cloud", id="GFSA00"),
  48. }
  49. sliced = AsyncMock(side_effect=_Stop())
  50. orchestrate = _make_orchestration_callable(
  51. run_id=run.id,
  52. pipeline_id=pipeline.id,
  53. src_kind="library_file",
  54. src_id=source.id,
  55. src_filename="cube.stl",
  56. src_path=Path(model),
  57. creator_user_id=creator_user_id,
  58. copies=1,
  59. )
  60. maker = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
  61. with (
  62. patch("backend.app.api.routes.pipeline_runs.async_session", maker),
  63. patch("backend.app.api.routes.pipeline_runs.load_stock_aliases", new=aliases_for),
  64. patch("backend.app.api.routes.library.slice_and_persist", new=sliced),
  65. patch("backend.app.api.routes.pipeline_runs._publish_run_event", new=AsyncMock()),
  66. pytest.raises(_Stop),
  67. ):
  68. await orchestrate(1)
  69. request = sliced.call_args.kwargs["request"]
  70. return request, seen_users
  71. async def _owner(db_session) -> User:
  72. owner = User(username="pipeline-owner", password_hash="x", role="user")
  73. db_session.add(owner)
  74. await db_session.commit()
  75. return owner
  76. async def test_a_run_without_a_cloud_user_slices_the_stored_standard_refs(db_session, test_engine, tmp_path):
  77. """A key without the cloud scope: the owner has a cloud list, but no one
  78. resolves cloud presets for this run, so mapping to the cloud twins would
  79. get the slice refused."""
  80. owner = await _owner(db_session)
  81. request, seen_users = await _run_pipeline(
  82. db_session, test_engine, tmp_path, creator_user_id=None, created_by=owner.id
  83. )
  84. assert seen_users == [None]
  85. assert request.printer_preset == PresetRef(source="standard", id="Bambu Lab X1 Carbon 0.4 nozzle")
  86. assert request.process_preset == PresetRef(source="standard", id="0.20mm Standard @BBL X1C")
  87. assert request.filament_presets == [PresetRef(source="standard", id="Bambu PLA Basic @BBL X1C")]
  88. async def test_a_run_with_a_cloud_user_slices_the_cloud_twins(db_session, test_engine, tmp_path):
  89. owner = await _owner(db_session)
  90. request, seen_users = await _run_pipeline(
  91. db_session, test_engine, tmp_path, creator_user_id=owner.id, created_by=owner.id
  92. )
  93. assert seen_users == [owner.id]
  94. assert request.printer_preset == PresetRef(source="cloud", id="GM001")
  95. assert request.process_preset == PresetRef(source="cloud", id="GP004")
  96. assert request.filament_presets == [PresetRef(source="cloud", id="GFSA00")]