fix: preserve requested transaction filter across pagination#40
Open
Flummy1 wants to merge 3 commits into
Open
fix: preserve requested transaction filter across pagination#40Flummy1 wants to merge 3 commits into
Flummy1 wants to merge 3 commits into
Conversation
`TransactionPreviewsBatch.next_batch()` re-derived the filter from `self.filter`,
which is scraped from the response HTML. FunPay omits the hidden `filter` input in
some responses, so `self.filter` became `None` and `self.filter or ''` silently
widened the filter to `TransactionFilter.ALL` — every page after the first returned
the entire transaction feed instead of the requested category.
`TransactionFilter.ALL` is `''` and therefore falsy, so no `or`-based fallback on
this enum can distinguish "no filter" from "unknown filter".
Thread the requested filter through the method context, the same way `GetSales`
threads `order_preview_type`, and let `next_batch()` fail loudly on an unknown
filter instead of defaulting to the widest one — mirroring
`OrderPreviewsBatch.next_batch()`.
- `GetTransactions` now passes `context={'transaction_filter': ...}`.
- `TransactionPreviewsBatch` prefers the requested filter over the scraped one.
- `next_batch()` raises `ValueError` when the filter is unknown.
- `GetTransactionsPage` marks its batch as `ALL` (the balance page is unfiltered).
- `GetTransactions()` no longer fails validation when called without arguments
(`from_transaction_id` defaulted to `None` against an `int` field).
Adds a regression suite covering the end-to-end path with a response that has no
hidden `filter` input.
Requested by maintainer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Requested by maintainer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Проблема
TransactionPreviewsBatch.next_batch()терял фильтр транзакций: первая страница приходилаотфильтрованной, а каждая следующая — со всей лентой транзакций. Молча, без исключения
и предупреждения.
Последствие для потребителя — тихая порча данных: любой код, который постранично читает
отфильтрованную ленту, начиная со второй страницы получает не то, что запросил. Аккаунты,
чья история помещается на одну страницу (~25 транзакций), не затронуты — это и есть
характерный признак.
Причина
next_batch()восстанавливал фильтр изself.filter, аself.filter— это не запрошенноезначение, а значение, вычитанное из HTML ответа:
FunPay в части ответов не отдаёт скрытый input
filter. Тогдаself.filterстановитсяNone, и выражениеself.filter or ''расширяет фильтр с «выводы» до «всё»:Усугубляет ситуацию то, что
TransactionFilter.ALL == '', то есть ложно в булевомконтексте. Поэтому любой fallback через
orна этом enum некорректен по построению: он неспособен отличить «фильтр не применялся» от «фильтр неизвестен».
При этом
GetTransactionsвообще не передавал запрошенный фильтр в собираемый объект —contextне заполнялся.Решение
Инвариант в кодовой базе уже сформулирован — просто не был применён к транзакциям.
GetSalesпробрасывает дискриминатор черезcontextметода, аOrderPreviewsBatch.next_batch()громко падает на неизвестном типе вместо подстановки значения по умолчанию.
Транзакции приведены к тому же контракту:
GetTransactionsпередаётcontext={'transaction_filter': method.filter}— запрошенноезначение, уже нормализованное pydantic в enum (в том числе если пользователь передал строку).
TransactionPreviewsBatch.model_post_initотдаёт приоритет фильтру из запроса; значениеиз HTML остаётся только как fallback (например, для объекта, собранного вручную).
next_batch()передаёт фильтр явно и бросаетValueError, если фильтр неизвестен,вместо тихого расширения до
ALL. Никаких проверок на «ложность» enum.GetTransactionsPageпомечает свой батч какTransactionFilter.ALL— страница балансавсегда отдаёт нефильтрованный список, поэтому пагинация оттуда продолжает работать.
Сопутствующий дефект (обнаружен при работе над основным):
GetTransactions()без аргументовпадал с
ValidationError, потому чтоfrom_transaction_idпо умолчанию былNoneпри полетипа
int. Значение по умолчанию исправлено на0.Изменения
funpaybotengine/methods/get_transactions.pycontext, исправлен дефолтfrom_transaction_idfunpaybotengine/types/finances.pynext_batch()funpaybotengine/methods/get_transactions_page.pyALLpyproject.toml[tool.pytest.ini_options], чтобыmake testработал;pythonpath = ["."]tests/test_transactions_pagination.pyОбратная совместимость
Меняется одно наблюдаемое поведение:
next_batch()теперь бросаетValueError('Unknown transaction filter.')вместо того, чтобы отдать неотфильтрованнуюстраницу. Это ровно тот случай, который раньше и приводил к порче данных, и он воспроизводится
только на батчах, собранных в обход
GetTransactions/GetTransactionsPage.batch.filterтеперь при обычном использовании всегда содержит запрошенный enum, а неNone.Каллерский workaround (ручная пагинация с явным фильтром) продолжает работать и больше не нужен.
Проверки
Регрессионный набор проверен на «мутацию»: против кода до фикса падают 7 из 10 тестов,
включая сквозной
test_filtered_pagination_survives_a_response_without_filter_input, которыйпрогоняет настоящий
TransactionPreviewsParserна ответе без скрытого inputfilter.pythonpath = ["."]в конфиге pytest добавлен не для удобства: без него голыйpytest(вне
uv run) импортируетfunpaybotengineиз установленного в окружении релиза, а не израбочего дерева, и тесты молча проверяют чужой код.
Зависимость
Ветка построена поверх #39 (
chore/fix-ruff-mypy-strict) — база PR должна бытьchore/fix-ruff-mypy-strict,иначе в диффе окажутся чужие изменения. После вливания #39 базу можно переключить на
dev.