Sync with imported parts now properly updates augmented parts and creates any new attachment points
This commit is contained in:
1 parent
d23f5b9e82
commit
13b64a2d17
6 files changed
+272
-8
No files matched your search
@@ -0,0 +1,167 @@
|
||||
"""Sync should refresh already-linked AugmentedParts' sync-owned fields when
|
||||
their backing ImportedPart's data changes, add missing attachment points,
|
||||
and never touch "extra" admin-owned fields or existing attachment points."""
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.crud import attachment_point as attachment_point_crud
|
||||
from app.crud import part_colour as part_colour_crud
|
||||
from app.models.augmented_part import AugmentedPart
|
||||
from app.models.imported_part import ImportedPart
|
||||
from app.models.manufacturer import Manufacturer
|
||||
from app.models.part_colour import PartColour
|
||||
from app.models.part_type import PartType
|
||||
from app.services import augmented_part_sync
|
||||
|
||||
|
||||
async def _seed_reference_data(db):
|
||||
manufacturer = Manufacturer(name="Acme", url="https://acme.test")
|
||||
colour = PartColour(name="Black")
|
||||
part_type = PartType(name="Scope")
|
||||
db.add_all([manufacturer, colour, part_type])
|
||||
await db.flush()
|
||||
return manufacturer, colour, part_type
|
||||
|
||||
|
||||
async def _get_ap_by_sku(db, sku):
|
||||
return (
|
||||
await db.execute(select(AugmentedPart).where(AugmentedPart.sku == sku))
|
||||
).scalar_one()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_refreshes_scalar_fields_on_already_linked_part(db_sessionmaker):
|
||||
async with db_sessionmaker() as db:
|
||||
await _seed_reference_data(db)
|
||||
db.add(
|
||||
ImportedPart(
|
||||
InventoryID="INV-REFRESH-1",
|
||||
Description="Old Scope description",
|
||||
TBXColour="Black",
|
||||
)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
result = await augmented_part_sync.sync(db)
|
||||
assert result.created == 1
|
||||
assert result.updated == 0
|
||||
|
||||
ap = await _get_ap_by_sku(db, "INV-REFRESH-1")
|
||||
assert ap.description == "Old Scope description"
|
||||
assert ap.uac_diameter_range_mm is None
|
||||
|
||||
# Simulate the source feed changing.
|
||||
ip = await db.get(ImportedPart, "INV-REFRESH-1")
|
||||
ip.Description = "New Scope description"
|
||||
ip.TBXColour = "Tan"
|
||||
ip.UACDiameterRangemm = "03505500"
|
||||
await db.commit()
|
||||
|
||||
result2 = await augmented_part_sync.sync(db)
|
||||
assert result2.created == 0
|
||||
assert result2.updated == 1
|
||||
|
||||
await db.refresh(ap)
|
||||
assert ap.description == "New Scope description"
|
||||
assert ap.uac_diameter_range_mm == "03.50-55.00"
|
||||
tan = await part_colour_crud.get_by_name(db, "Tan")
|
||||
assert ap.colour_id == tan.id
|
||||
|
||||
# Re-running sync with no further source changes is a no-op.
|
||||
result3 = await augmented_part_sync.sync(db)
|
||||
assert result3.updated == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_never_touches_extra_admin_owned_fields(db_sessionmaker):
|
||||
async with db_sessionmaker() as db:
|
||||
manufacturer, colour, part_type = await _seed_reference_data(db)
|
||||
other_manufacturer = Manufacturer(name="OtherCo", url="https://other.test")
|
||||
other_part_type = PartType(name="Flip Cover")
|
||||
db.add_all([other_manufacturer, other_part_type])
|
||||
await db.flush()
|
||||
|
||||
db.add(ImportedPart(InventoryID="INV-EXTRA-1", Description="A Scope widget"))
|
||||
await db.commit()
|
||||
|
||||
result = await augmented_part_sync.sync(db)
|
||||
assert result.created == 1
|
||||
|
||||
ap = await _get_ap_by_sku(db, "INV-EXTRA-1")
|
||||
|
||||
# Simulate an admin manually re-classifying the part after creation.
|
||||
ap.part_type_id = other_part_type.id
|
||||
ap.sub_part_type_id = None
|
||||
ap.manufacturer_id = other_manufacturer.id
|
||||
ap.archived = False
|
||||
await db.commit()
|
||||
admin_part_type_id = ap.part_type_id
|
||||
admin_manufacturer_id = ap.manufacturer_id
|
||||
|
||||
# Source feed also changes, to prove refresh runs at all.
|
||||
ip = await db.get(ImportedPart, "INV-EXTRA-1")
|
||||
ip.Description = "A Scope widget, renamed"
|
||||
await db.commit()
|
||||
|
||||
result2 = await augmented_part_sync.sync(db)
|
||||
assert result2.updated == 1
|
||||
|
||||
await db.refresh(ap)
|
||||
assert ap.description == "A Scope widget, renamed" # sync-owned: refreshed
|
||||
assert ap.part_type_id == admin_part_type_id # extra field: untouched
|
||||
assert ap.manufacturer_id == admin_manufacturer_id # extra field: untouched
|
||||
assert ap.sku == "INV-EXTRA-1" # extra field: untouched
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sync_adds_missing_attachment_point_without_touching_existing_one(
|
||||
db_sessionmaker,
|
||||
):
|
||||
async with db_sessionmaker() as db:
|
||||
await _seed_reference_data(db)
|
||||
db.add(
|
||||
ImportedPart(
|
||||
InventoryID="INV-AP-1",
|
||||
Description="A Scope widget",
|
||||
# No thread/OD/UAC data yet — no attachment points created.
|
||||
)
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
result = await augmented_part_sync.sync(db)
|
||||
assert result.created == 1
|
||||
|
||||
ap = await _get_ap_by_sku(db, "INV-AP-1")
|
||||
points = await attachment_point_crud.list_for_augmented_part(db, ap.id)
|
||||
assert points == []
|
||||
|
||||
# Source feed gains an OD value -> an "Ocular" point should appear.
|
||||
ip = await db.get(ImportedPart, "INV-AP-1")
|
||||
ip.ProductODmm = "40"
|
||||
ip.ProductODin = "1.575"
|
||||
await db.commit()
|
||||
|
||||
result2 = await augmented_part_sync.sync(db)
|
||||
assert result2.updated == 1
|
||||
|
||||
points = await attachment_point_crud.list_for_augmented_part(db, ap.id)
|
||||
assert len(points) == 1
|
||||
ocular = points[0]
|
||||
assert ocular.attachment_point_name.name == "Ocular"
|
||||
original_static_connection_id = ocular.static_connection_id
|
||||
|
||||
# Source OD changes again — the existing point must NOT be replaced.
|
||||
ip.ProductODmm = "56"
|
||||
ip.ProductODin = "2.205"
|
||||
await db.commit()
|
||||
|
||||
result3 = await augmented_part_sync.sync(db)
|
||||
# description/colour/uac unchanged, and the Ocular point already
|
||||
# exists, so nothing should be reported as updated.
|
||||
assert result3.updated == 0
|
||||
|
||||
points = await attachment_point_crud.list_for_augmented_part(db, ap.id)
|
||||
assert len(points) == 1
|
||||
assert points[0].id == ocular.id
|
||||
assert points[0].static_connection_id == original_static_connection_id
|
||||
Reference in new issue
Block a user