e519d1804b
Root cause: sync_account_priorities_for_upstream() was doing a global priority re-rank across ALL imported accounts on a website whenever any upstream rate changed, triggering spurious account_priority_changed notifications for accounts in different target groups with no competition. Fix: - Add imported_target_group_id / imported_target_group_name to UpstreamGeneratedKey (nullable; old data falls back to group_id) - Writ imported_target_group_id on account import in websites.py - Rewrite sync_account_priorities_for_upstream(): * bucket accounts by competition_group = imported_target_group_id or group_id * only process buckets with count > 1 (genuine competition) * each competitive bucket independently sorted by rate; priority starts at 1 * single-account groups: completely skipped (no update_account, no notification) * no competitive groups at all: early return, no log, no notification - Remove auto priority update in re-import idempotency path (was also incorrect; now fully delegated to sync_account_priorities_for_upstream) - Fix Sub2ApiWebsiteClient local import in sync fn → use module-level name so monkeypatch works correctly in tests Tests: rewrite test_priority_sync.py - REMOVED: test_priority_sync_full_website_update (was asserting the buggy behavior) - NEW: test_no_update_when_different_groups_single_account_each - NEW: test_same_target_group_two_accounts_updated - NEW: test_two_target_groups_independent_priority - NEW: test_old_data_null_target_group_fallback - NEW: test_single_account_in_mixed_website - UPDATED: test_priority_sync_log_structure (now requires competitive group) - KEPT: test_priority_sync_cross_upstream_group, test_import_auto_priority_by_rate All 25 tests pass (8 priority_sync + 17 existing upstream tests).
37 lines
2.2 KiB
Python
37 lines
2.2 KiB
Python
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, Text, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.database import Base
|
|
|
|
|
|
class UpstreamGeneratedKey(Base):
|
|
__tablename__ = "upstream_generated_keys"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
upstream_id: Mapped[int] = mapped_column(Integer, ForeignKey("upstreams.id", ondelete="CASCADE"), index=True)
|
|
group_id: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
group_name: Mapped[str] = mapped_column(String(255), default="")
|
|
key_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
key_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
key_value: Mapped[str] = mapped_column(Text, nullable=False)
|
|
masked_key: Mapped[str] = mapped_column(String(255), default="")
|
|
raw_json: Mapped[str] = mapped_column(Text, default="{}")
|
|
managed_prefix: Mapped[Optional[str]] = mapped_column(String(64), nullable=True, index=True)
|
|
status: Mapped[str] = mapped_column(String(32), default="created")
|
|
error: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
imported_website_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("websites.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
imported_account_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
imported_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
|
imported_target_group_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
imported_target_group_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.now(timezone.utc))
|
|
updated_at: Mapped[datetime] = mapped_column(DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("upstream_id", "group_id", "key_name", name="uq_upstream_group_key"),
|
|
Index("ix_key_upstream_name", "upstream_id", "key_name"),
|
|
)
|