From df62098cd1757be5b12810d730cd754d99b8c563 Mon Sep 17 00:00:00 2001 From: fusion44 Date: Fri, 3 Jul 2026 22:22:05 +0200 Subject: [PATCH] refactor(lightning): migrate SendCoinsInput validator to Pydantic v2 Replace the deprecated Pydantic v1 @validator('amount', pre=True, always=True) with a v2 @model_validator(mode='after'). The model validator always runs and can see both amount and send_all, preserving the cross-field rule (and the always=True semantics that reject the empty/default case). Removes the last Pydantic v1 deprecation warning. Co-Authored-By: Claude Fable 5 --- app/lightning/models.py | 24 ++++++--------------- tests/test_send_coins_input.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 tests/test_send_coins_input.py diff --git a/app/lightning/models.py b/app/lightning/models.py index 62738b0..c3697b3 100644 --- a/app/lightning/models.py +++ b/app/lightning/models.py @@ -6,7 +6,7 @@ from typing import List, Optional, Union from deepdiff import DeepDiff from fastapi.param_functions import Query from loguru import logger -from pydantic import BaseModel, validator +from pydantic import BaseModel, model_validator from pydantic.types import conint import app.lightning.docs as docs @@ -1320,12 +1320,10 @@ class SendCoinsInput(BaseModel): ), ) - @validator("amount", pre=True, always=True) - def check_amount_or_send_all(cls, amount, values): - if amount is None: - amount = 0 - - send_all = values.get("send_all") if "send_all" in values else False + @model_validator(mode="after") + def check_amount_or_send_all(self): + amount = self.amount if self.amount is not None else 0 + send_all = self.send_all if amount < 0: raise ValueError("Amount must not be negative") @@ -1339,10 +1337,6 @@ class SendCoinsInput(BaseModel): ) ) - if amount > 0 and not send_all: - # amount is set and send_all is false - return amount - if amount > 0 and send_all: # amount is set and send_all is true raise ValueError( @@ -1352,12 +1346,8 @@ class SendCoinsInput(BaseModel): ) ) - if amount == 0 and send_all: - # amount is not set and send_all is true - return amount - - # normally this should never be reached - raise ValueError("Unknown input.") + # valid: (amount > 0 and not send_all) or (amount == 0 and send_all) + return self class SendCoinsResponse(BaseModel): diff --git a/tests/test_send_coins_input.py b/tests/test_send_coins_input.py new file mode 100644 index 0000000..2442ea7 --- /dev/null +++ b/tests/test_send_coins_input.py @@ -0,0 +1,39 @@ +""" +Characterization tests for SendCoinsInput.amount / send_all validation. + +These lock the behaviour of the amount/send_all cross-field rule while the +Pydantic v1 `@validator` is migrated to v2. They must pass before and after +the migration. +""" + +import pytest +from pydantic import ValidationError + +from app.lightning.models import SendCoinsInput + + +def test_neither_amount_nor_send_all_is_rejected(): + with pytest.raises(ValidationError): + SendCoinsInput(address="bc1qexample") + + +def test_zero_amount_without_send_all_is_rejected(): + with pytest.raises(ValidationError): + SendCoinsInput(address="bc1qexample", amount=0) + + +def test_amount_and_send_all_together_is_rejected(): + with pytest.raises(ValidationError): + SendCoinsInput(address="bc1qexample", amount=1000, send_all=True) + + +def test_positive_amount_is_accepted(): + m = SendCoinsInput(address="bc1qexample", amount=1000) + assert m.amount == 1000 + assert m.send_all is False + + +def test_send_all_without_amount_is_accepted(): + m = SendCoinsInput(address="bc1qexample", send_all=True) + assert m.send_all is True + assert m.amount == 0