Skip to content

Commit c8507ca

Browse files
tannaya7claude
andcommitted
test(tck): add unit tests for appendFile handler; drop redundant comment
Cover parse_json_params validation, setter wiring/order, common-params application, and receipt/precheck error propagation for the appendFile TCK handler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012pHMCjruc9zs24St46vPDx Signed-off-by: tannaya7 <234580934+tannaya7@users.noreply.github.com>
1 parent 1dd07f9 commit c8507ca

2 files changed

Lines changed: 151 additions & 1 deletion

File tree

tck/handlers/file.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ def _build_append_file_transaction(params: AppendFileParams) -> FileAppendTransa
6969
# INVALID_FILE_ID rather than raising a client-side ValueError (TCK FileId #5).
7070
transaction.set_file_id(FileId.from_string(params.fileId) if params.fileId is not None else FileId())
7171

72-
# chunk count is derived from contents/chunk_size, so set those before max_chunks
7372
if params.contents is not None:
7473
transaction.set_contents(params.contents)
7574

tests/tck/file_test.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""Test cases for the appendFile TCK handler and its parameter parsing."""
2+
3+
from __future__ import annotations
4+
5+
from unittest.mock import MagicMock, patch
6+
7+
import pytest
8+
9+
from hiero_sdk_python.exceptions import PrecheckError, ReceiptStatusError
10+
from hiero_sdk_python.response_code import ResponseCode
11+
from tck.handlers import registry
12+
from tck.handlers.file import append_file
13+
from tck.param.file import AppendFileParams
14+
from tck.response.base import StatusOnlyResponse
15+
16+
17+
pytestmark = pytest.mark.unit
18+
19+
20+
def _mock_transaction():
21+
"""A MagicMock standing in for FileAppendTransaction's fluent setter chain."""
22+
tx = MagicMock()
23+
tx.set_grpc_deadline.return_value = tx
24+
tx.set_file_id.return_value = tx
25+
tx.set_contents.return_value = tx
26+
tx.set_chunk_size.return_value = tx
27+
tx.set_max_chunks.return_value = tx
28+
29+
receipt = MagicMock(status=ResponseCode.SUCCESS)
30+
tx.execute.return_value.get_receipt.return_value = receipt
31+
return tx
32+
33+
34+
def test_append_file_is_registered():
35+
handler = registry.get_handler("appendFile")
36+
assert handler is not None and callable(handler)
37+
38+
39+
def test_parse_json_params():
40+
"""Only contents is required; maxChunks/chunkSize go through to_int."""
41+
params = AppendFileParams.parse_json_params(
42+
{
43+
"sessionId": "session-1",
44+
"fileId": "0.0.100",
45+
"contents": "hello world",
46+
"maxChunks": "5",
47+
"chunkSize": "1024",
48+
}
49+
)
50+
51+
assert params.fileId == "0.0.100"
52+
assert params.contents == "hello world"
53+
assert params.maxChunks == 5
54+
assert params.chunkSize == 1024
55+
56+
minimal = AppendFileParams.parse_json_params({"sessionId": "session-1", "contents": "hello"})
57+
assert minimal.fileId is None
58+
assert minimal.maxChunks is None
59+
assert minimal.chunkSize is None
60+
61+
62+
def test_parse_json_params_requires_contents():
63+
"""contents is spec-required; missing or non-string values must fail fast, not build an empty append."""
64+
with pytest.raises(ValueError):
65+
AppendFileParams.parse_json_params({"sessionId": "session-1"})
66+
67+
with pytest.raises(ValueError):
68+
AppendFileParams.parse_json_params({"sessionId": "session-1", "contents": 123})
69+
70+
71+
def test_append_file_wires_setters_and_returns_status():
72+
"""contents/chunkSize must land before maxChunks, since chunk count derives from them."""
73+
params = AppendFileParams(
74+
sessionId="session-1", fileId="0.0.100", contents="hello world", maxChunks=5, chunkSize=1024
75+
)
76+
tx = _mock_transaction()
77+
78+
with (
79+
patch("tck.handlers.file.get_client", return_value=MagicMock()),
80+
patch("tck.handlers.file.FileAppendTransaction", return_value=tx),
81+
):
82+
result = append_file(params)
83+
84+
assert isinstance(result, StatusOnlyResponse)
85+
assert result.status == "SUCCESS"
86+
87+
tx.set_file_id.assert_called_once()
88+
tx.set_contents.assert_called_once_with("hello world")
89+
tx.set_chunk_size.assert_called_once_with(1024)
90+
tx.set_max_chunks.assert_called_once_with(5)
91+
92+
setter_order = [call[0] for call in tx.method_calls if call[0].startswith("set_")]
93+
assert setter_order.index("set_contents") < setter_order.index("set_max_chunks")
94+
assert setter_order.index("set_chunk_size") < setter_order.index("set_max_chunks")
95+
96+
97+
def test_append_file_applies_common_transaction_params():
98+
"""commonTransactionParams.signers has to reach freeze/sign via apply_common_params."""
99+
common_params = MagicMock()
100+
params = AppendFileParams(sessionId="session-1", contents="hello", commonTransactionParams=common_params)
101+
tx = _mock_transaction()
102+
client = MagicMock()
103+
104+
with (
105+
patch("tck.handlers.file.get_client", return_value=client),
106+
patch("tck.handlers.file.FileAppendTransaction", return_value=tx),
107+
):
108+
append_file(params)
109+
110+
common_params.apply_common_params.assert_called_once_with(tx, client)
111+
112+
113+
def test_append_file_propagates_receipt_failure():
114+
"""A bad receipt status (e.g. non-existent file -> INVALID_FILE_ID) should raise, not be swallowed."""
115+
params = AppendFileParams(sessionId="session-1", fileId="0.0.999999", contents="hello")
116+
tx = _mock_transaction()
117+
tx.execute.return_value.get_receipt.side_effect = ReceiptStatusError(
118+
status=ResponseCode.INVALID_FILE_ID,
119+
transaction_id=None,
120+
transaction_receipt=MagicMock(),
121+
message="INVALID_FILE_ID",
122+
)
123+
124+
with (
125+
patch("tck.handlers.file.get_client", return_value=MagicMock()),
126+
patch("tck.handlers.file.FileAppendTransaction", return_value=tx),
127+
pytest.raises(ReceiptStatusError),
128+
):
129+
append_file(params)
130+
131+
132+
def test_append_file_propagates_later_chunk_failure():
133+
"""A later chunk failing at submission must surface, not get masked by the first chunk's SUCCESS."""
134+
params = AppendFileParams(sessionId="session-1", fileId="0.0.100", contents="x" * 10_000)
135+
tx = _mock_transaction()
136+
tx.execute.side_effect = PrecheckError(status=1, transaction_id="0.0.1@1.1", message="later chunk failed")
137+
138+
with (
139+
patch("tck.handlers.file.get_client", return_value=MagicMock()),
140+
patch("tck.handlers.file.FileAppendTransaction", return_value=tx),
141+
pytest.raises(PrecheckError),
142+
):
143+
append_file(params)
144+
145+
146+
def test_append_file_invalid_file_id_raises():
147+
"""A malformed fileId should fail via FileId.from_string before hitting the network."""
148+
params = AppendFileParams(sessionId="session-1", fileId="not-a-file-id", contents="hello")
149+
150+
with patch("tck.handlers.file.get_client", return_value=MagicMock()), pytest.raises(ValueError):
151+
append_file(params)

0 commit comments

Comments
 (0)