mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-05 17:35:33 -03:00
fix(ui): explain a 0 Buzz threshold in the price alerts panel
The default threshold is 0 ("only tell me when a version becomes free") and the
settings copy says so, but the panel did not: a real instance with 52 priced paid
versions and an untouched threshold showed "Nothing is under your price threshold
right now" with only a small "Alert threshold: 0 Buzz" in the corner, which reads
as a broken feature.
- the payload now carries pricedCount, so the empty state can say how many paid
versions already have a known price
- when the threshold is 0 and prices are known, the empty state says so and
points at Settings - Library instead of implying there is nothing to show
- the read-time threshold comparison means setting one takes effect immediately;
measured on a copy of that instance: 0 Buzz -> 0 alerts, 100 -> 44,
500 -> 48, 5000 -> 51
This commit is contained in:
@@ -671,3 +671,15 @@ a price-only pass cannot silently extend the metadata TTL. Two related semantics
|
|||||||
Verified by copying a real instance's update DB into a sandbox and running a non-forced check:
|
Verified by copying a real instance's update DB into a sandbox and running a non-forced check:
|
||||||
`bulk metadata fetches: 0` (version lists came entirely from cache) while priced versions went
|
`bulk metadata fetches: 0` (version lists came entirely from cache) while priced versions went
|
||||||
**1 → 20** and the panel listed 19 alerts.
|
**1 → 20** and the panel listed 19 alerts.
|
||||||
|
|
||||||
|
### 11.12 The empty state has to explain a 0 Buzz threshold
|
||||||
|
|
||||||
|
The default threshold is 0 ("only tell me when a version becomes free"), and the settings copy says
|
||||||
|
so. The panel did not: a real instance with **52 priced paid versions** and an untouched threshold
|
||||||
|
showed "Nothing is under your price threshold right now" plus a small "Alert threshold: 0 Buzz" in
|
||||||
|
the corner, which reads as "the feature is broken".
|
||||||
|
|
||||||
|
The empty state is now threshold-aware, using a new `pricedCount` in the payload: when the threshold
|
||||||
|
is 0 and prices are known, it says how many paid versions have a price and where to set a threshold.
|
||||||
|
The read-time threshold comparison means changing it takes effect immediately — no re-check needed
|
||||||
|
(measured on a copy of that instance's DB: 0 Buzz -> 0 alerts, 100 -> 44, 500 -> 48, 5000 -> 51).
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "dropped {when}",
|
"droppedAgo": "dropped {when}",
|
||||||
"openCivitai": "Open on CivitAI",
|
"openCivitai": "Open on CivitAI",
|
||||||
"openLocal": "Open",
|
"openLocal": "Open",
|
||||||
"unavailable": "{count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "{count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "{count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
+2
-1
@@ -2411,7 +2411,8 @@
|
|||||||
"droppedAgo": "[TODO: Translate] dropped {when}",
|
"droppedAgo": "[TODO: Translate] dropped {when}",
|
||||||
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
"openCivitai": "[TODO: Translate] Open on CivitAI",
|
||||||
"openLocal": "[TODO: Translate] Open",
|
"openLocal": "[TODO: Translate] Open",
|
||||||
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)"
|
"unavailable": "[TODO: Translate] {count} paid version(s) have no readable price (mature models can only be read in a browser)",
|
||||||
|
"emptyZeroThreshold": "[TODO: Translate] {count} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"support": {
|
"support": {
|
||||||
|
|||||||
@@ -4385,6 +4385,7 @@ class PriceAlertsHandler:
|
|||||||
)
|
)
|
||||||
newest_checked_at = update_service.newest_price_checked_at()
|
newest_checked_at = update_service.newest_price_checked_at()
|
||||||
unavailable_count = update_service.count_unavailable_prices()
|
unavailable_count = update_service.count_unavailable_prices()
|
||||||
|
priced_count = update_service.count_priced_versions()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Failed to load price alerts: %s", exc, exc_info=True)
|
logger.error("Failed to load price alerts: %s", exc, exc_info=True)
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
@@ -4410,6 +4411,9 @@ class PriceAlertsHandler:
|
|||||||
# Gated versions whose price could not be read (mature models
|
# Gated versions whose price could not be read (mature models
|
||||||
# are served only by the challenged civitai.red host).
|
# are served only by the challenged civitai.red host).
|
||||||
"unavailableCount": unavailable_count,
|
"unavailableCount": unavailable_count,
|
||||||
|
# Lets the empty state explain a 0 Buzz threshold instead of
|
||||||
|
# claiming there is simply nothing to show.
|
||||||
|
"pricedCount": priced_count,
|
||||||
"alerts": alerts,
|
"alerts": alerts,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1362,6 +1362,36 @@ class ModelUpdateService:
|
|||||||
return None
|
return None
|
||||||
return float(row["newest"])
|
return float(row["newest"])
|
||||||
|
|
||||||
|
def count_priced_versions(self, model_type: Optional[str] = None) -> int:
|
||||||
|
"""Versions with a known price, for the panel's empty state.
|
||||||
|
|
||||||
|
Without it, a user whose threshold is 0 sees "nothing is under your price
|
||||||
|
threshold" while dozens of paid versions already have a price.
|
||||||
|
"""
|
||||||
|
|
||||||
|
params: List[Any] = []
|
||||||
|
type_filter = ""
|
||||||
|
if model_type:
|
||||||
|
type_filter = "AND s.model_type = ?"
|
||||||
|
params.append(model_type)
|
||||||
|
|
||||||
|
with self._connect() as conn:
|
||||||
|
row = conn.execute(
|
||||||
|
f"""
|
||||||
|
SELECT COUNT(*) AS priced
|
||||||
|
FROM model_update_versions v
|
||||||
|
JOIN model_update_status s ON s.model_id = v.model_id
|
||||||
|
WHERE v.should_ignore = 0
|
||||||
|
AND s.should_ignore_model = 0
|
||||||
|
{type_filter}
|
||||||
|
AND v.price_buzz IS NOT NULL
|
||||||
|
""",
|
||||||
|
tuple(params),
|
||||||
|
).fetchone()
|
||||||
|
if row is None:
|
||||||
|
return 0
|
||||||
|
return int(row["priced"])
|
||||||
|
|
||||||
def count_unavailable_prices(self, model_type: Optional[str] = None) -> int:
|
def count_unavailable_prices(self, model_type: Optional[str] = None) -> int:
|
||||||
"""Gated versions whose price we tried to read and could not.
|
"""Gated versions whose price we tried to read and could not.
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ async function fetchPriceAlerts(limit = 200) {
|
|||||||
enabled: payload.enabled === true,
|
enabled: payload.enabled === true,
|
||||||
thresholdBuzz: payload.thresholdBuzz ?? 0,
|
thresholdBuzz: payload.thresholdBuzz ?? 0,
|
||||||
newestCheckedAt: payload.newestCheckedAt ?? null,
|
newestCheckedAt: payload.newestCheckedAt ?? null,
|
||||||
unavailableCount: payload.unavailableCount ?? 0
|
unavailableCount: payload.unavailableCount ?? 0,
|
||||||
|
pricedCount: payload.pricedCount ?? 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +115,7 @@ export class UpdateService {
|
|||||||
this.priceAlertsThreshold = 0;
|
this.priceAlertsThreshold = 0;
|
||||||
this.priceAlertsNewestCheckedAt = null;
|
this.priceAlertsNewestCheckedAt = null;
|
||||||
this.priceAlertsUnavailableCount = 0;
|
this.priceAlertsUnavailableCount = 0;
|
||||||
|
this.priceAlertsPricedCount = 0;
|
||||||
this.priceAlertsLoading = false;
|
this.priceAlertsLoading = false;
|
||||||
this.priceAlertSegment = 'below_threshold';
|
this.priceAlertSegment = 'below_threshold';
|
||||||
this.unreadPriceAlertCount = 0;
|
this.unreadPriceAlertCount = 0;
|
||||||
@@ -407,6 +409,7 @@ export class UpdateService {
|
|||||||
this.priceAlertsThreshold = payload.thresholdBuzz;
|
this.priceAlertsThreshold = payload.thresholdBuzz;
|
||||||
this.priceAlertsNewestCheckedAt = payload.newestCheckedAt;
|
this.priceAlertsNewestCheckedAt = payload.newestCheckedAt;
|
||||||
this.priceAlertsUnavailableCount = payload.unavailableCount || 0;
|
this.priceAlertsUnavailableCount = payload.unavailableCount || 0;
|
||||||
|
this.priceAlertsPricedCount = payload.pricedCount || 0;
|
||||||
this.refreshUnreadPriceAlertCount();
|
this.refreshUnreadPriceAlertCount();
|
||||||
this.renderPriceAlerts();
|
this.renderPriceAlerts();
|
||||||
return this.priceAlerts;
|
return this.priceAlerts;
|
||||||
@@ -512,17 +515,7 @@ export class UpdateService {
|
|||||||
|
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
if (!alerts.length) {
|
if (!alerts.length) {
|
||||||
empty.textContent = this.priceAlertSegment === 'became_free'
|
empty.textContent = this.buildPriceAlertsEmptyText();
|
||||||
? translate(
|
|
||||||
'update.priceAlerts.emptyFree',
|
|
||||||
{},
|
|
||||||
'No version has become free yet.'
|
|
||||||
)
|
|
||||||
: translate(
|
|
||||||
'update.priceAlerts.empty',
|
|
||||||
{},
|
|
||||||
'Nothing is under your price threshold right now.'
|
|
||||||
);
|
|
||||||
empty.classList.remove('hidden');
|
empty.classList.remove('hidden');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -533,6 +526,33 @@ export class UpdateService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The empty state has to explain a 0 Buzz threshold: "nothing is under your
|
||||||
|
* threshold" is technically true but useless when dozens of paid versions
|
||||||
|
* already have a known price.
|
||||||
|
*/
|
||||||
|
buildPriceAlertsEmptyText() {
|
||||||
|
if (this.priceAlertSegment === 'became_free') {
|
||||||
|
return translate(
|
||||||
|
'update.priceAlerts.emptyFree',
|
||||||
|
{},
|
||||||
|
'No version has become free yet.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!this.priceAlertsThreshold && this.priceAlertsPricedCount > 0) {
|
||||||
|
return translate(
|
||||||
|
'update.priceAlerts.emptyZeroThreshold',
|
||||||
|
{ count: this.priceAlertsPricedCount },
|
||||||
|
`${this.priceAlertsPricedCount} paid version(s) have a known price, but your threshold is 0 Buzz so only free versions are listed. Set a threshold in Settings - Library to catch price drops.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return translate(
|
||||||
|
'update.priceAlerts.empty',
|
||||||
|
{},
|
||||||
|
'Nothing is under your price threshold right now.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
buildPriceAlertItem(alert) {
|
buildPriceAlertItem(alert) {
|
||||||
const item = document.createElement('li');
|
const item = document.createElement('li');
|
||||||
item.className = 'price-alert-item';
|
item.className = 'price-alert-item';
|
||||||
|
|||||||
@@ -218,6 +218,36 @@ describe('UpdateService price alerts panel', () => {
|
|||||||
expect(note.textContent).toContain('3 paid version(s) have no readable price');
|
expect(note.textContent).toContain('3 paid version(s) have no readable price');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('explains a 0 Buzz threshold instead of claiming there is nothing', async () => {
|
||||||
|
// The exact confusion a real user hit: 52 priced versions, threshold 0,
|
||||||
|
// and an empty panel saying "nothing is under your threshold".
|
||||||
|
global.fetch = vi.fn().mockResolvedValue(
|
||||||
|
createFetchResponse(
|
||||||
|
alertPayload([], { thresholdBuzz: 0, pricedCount: 52 })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.loadPriceAlerts({ force: true });
|
||||||
|
|
||||||
|
const empty = document.getElementById('priceAlertsEmpty');
|
||||||
|
expect(empty.classList.contains('hidden')).toBe(false);
|
||||||
|
expect(empty.textContent).toContain('52 paid version(s) have a known price');
|
||||||
|
expect(empty.textContent).toContain('threshold is 0 Buzz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the plain empty message when a real threshold is set', async () => {
|
||||||
|
global.fetch = vi.fn().mockResolvedValue(
|
||||||
|
createFetchResponse(
|
||||||
|
alertPayload([], { thresholdBuzz: 500, pricedCount: 52 })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.loadPriceAlerts({ force: true });
|
||||||
|
|
||||||
|
const empty = document.getElementById('priceAlertsEmpty');
|
||||||
|
expect(empty.textContent).toBe('Nothing is under your price threshold right now.');
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps the last known list when the request fails', async () => {
|
it('keeps the last known list when the request fails', async () => {
|
||||||
global.fetch = vi
|
global.fetch = vi
|
||||||
.fn()
|
.fn()
|
||||||
|
|||||||
@@ -2950,10 +2950,11 @@ def _price_alerts_adapter(update_service, scanners=None):
|
|||||||
|
|
||||||
|
|
||||||
class _FakeUpdateService:
|
class _FakeUpdateService:
|
||||||
def __init__(self, alerts, *, unavailable_count=0):
|
def __init__(self, alerts, *, unavailable_count=0, priced_count=0):
|
||||||
self.alerts = alerts
|
self.alerts = alerts
|
||||||
self.calls = []
|
self.calls = []
|
||||||
self.unavailable_count = unavailable_count
|
self.unavailable_count = unavailable_count
|
||||||
|
self.priced_count = priced_count
|
||||||
|
|
||||||
async def get_price_alerts(self, model_type=None, *, threshold_buzz=None, limit=200):
|
async def get_price_alerts(self, model_type=None, *, threshold_buzz=None, limit=200):
|
||||||
self.calls.append((model_type, threshold_buzz, limit))
|
self.calls.append((model_type, threshold_buzz, limit))
|
||||||
@@ -2965,6 +2966,9 @@ class _FakeUpdateService:
|
|||||||
def count_unavailable_prices(self, model_type=None):
|
def count_unavailable_prices(self, model_type=None):
|
||||||
return self.unavailable_count
|
return self.unavailable_count
|
||||||
|
|
||||||
|
def count_priced_versions(self, model_type=None):
|
||||||
|
return self.priced_count
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_price_alerts_handler_returns_the_global_list():
|
async def test_price_alerts_handler_returns_the_global_list():
|
||||||
@@ -3002,6 +3006,7 @@ async def test_price_alerts_handler_returns_the_global_list():
|
|||||||
assert payload["thresholdBuzz"] == 300
|
assert payload["thresholdBuzz"] == 300
|
||||||
assert payload["newestCheckedAt"] == 1791039694.5
|
assert payload["newestCheckedAt"] == 1791039694.5
|
||||||
assert payload["unavailableCount"] == 0
|
assert payload["unavailableCount"] == 0
|
||||||
|
assert payload["pricedCount"] == 0
|
||||||
assert payload["alerts"][0]["civitaiUrl"] == (
|
assert payload["alerts"][0]["civitaiUrl"] == (
|
||||||
"https://civitai.com/models/2981320?modelVersionId=3379626"
|
"https://civitai.com/models/2981320?modelVersionId=3379626"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2023,6 +2023,20 @@ async def test_failed_price_attempt_is_not_retried_within_the_ttl(tmp_path):
|
|||||||
assert service.count_unavailable_prices("lora") == 1
|
assert service.count_unavailable_prices("lora") == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_priced_count_reports_known_prices(tmp_path):
|
||||||
|
service = _price_service(tmp_path, price_tracking_enabled=True)
|
||||||
|
scanner = DummyScanner(LOCAL_RAW_DATA)
|
||||||
|
provider = PriceProvider(GATED_RESPONSE, prices=PRICE_PAYLOAD)
|
||||||
|
|
||||||
|
assert service.count_priced_versions("lora") == 0
|
||||||
|
|
||||||
|
await service.refresh_for_model_type("lora", scanner, provider)
|
||||||
|
|
||||||
|
assert service.count_priced_versions("lora") == 1
|
||||||
|
assert service.count_priced_versions("checkpoint") == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_forced_refresh_reprices_within_the_ttl(tmp_path):
|
async def test_forced_refresh_reprices_within_the_ttl(tmp_path):
|
||||||
service = _long_ttl_service(tmp_path, price_tracking_enabled=True)
|
service = _long_ttl_service(tmp_path, price_tracking_enabled=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user