Skip to content

Commit a293c4f

Browse files
committed
test(api): add coverage for int64_as_string strict-mode contract
- JsonFormatInt64AsStringTest: covers default behavior, int64/uint64 quoting, non-int64 fields unaffected, nested/map/boundary values (2^53 +/- 1, Long.MAX/MIN, -1), state cleanup (normal, after exception, explicit), thread isolation, thread-reuse anti-pollution, and Util.getInt64AsStringPost body parsing. - Servlet integration tests for GetReward / GetNowBlock / GetBlock / GetPaginatedProposalList. Tests bypass RateLimiterServlet.service so they explicitly clear the ThreadLocal in @Before/@after and simulate the URL-flag step where a GET test exercises that path. - ListExchangesServletTest: pins the strict-mode contract that this servlet does NOT support int64_as_string on POST. doPost does not parse the body, and service() does not read URL on POST, so neither source of the flag has any effect on POST. Only GET (via service()) honors the flag. - UtilMockTest: cover Util.decodeAddress (blank input, mainnet hex prefix) and Util.processAddressError; both were previously exercised only indirectly.
1 parent ed0ec16 commit a293c4f

7 files changed

Lines changed: 987 additions & 0 deletions

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package org.tron.core.services.http;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertNotNull;
5+
import static org.junit.Assert.assertTrue;
6+
import static org.junit.Assert.fail;
7+
8+
import com.alibaba.fastjson.JSONObject;
9+
import java.io.UnsupportedEncodingException;
10+
import javax.annotation.Resource;
11+
import org.junit.After;
12+
import org.junit.Before;
13+
import org.junit.Test;
14+
import org.springframework.mock.web.MockHttpServletRequest;
15+
import org.springframework.mock.web.MockHttpServletResponse;
16+
import org.tron.common.BaseTest;
17+
import org.tron.common.TestConstants;
18+
import org.tron.core.config.args.Args;
19+
20+
/**
21+
* Integration tests for GetBlockServlet. This servlet delegates both doGet and doPost to
22+
* a private handle() method whose parseParams() reads visible from either URL query (GET)
23+
* or JSON body (POST). The int64_as_string flag follows the same split (URL on GET, body
24+
* on POST) but is read via the centralized RateLimiterServlet.service / PostParams paths
25+
* rather than parseParams itself.
26+
*/
27+
public class GetBlockServletTest extends BaseTest {
28+
29+
@Resource
30+
private GetBlockServlet getBlockServlet;
31+
32+
static {
33+
Args.setParam(
34+
new String[]{
35+
"--output-directory", dbPath(),
36+
}, TestConstants.TEST_CONF
37+
);
38+
}
39+
40+
@Before
41+
public void clearBefore() {
42+
JsonFormat.clearInt64AsString();
43+
}
44+
45+
@After
46+
public void clearAfter() {
47+
JsonFormat.clearInt64AsString();
48+
}
49+
50+
private MockHttpServletRequest createGetRequest() {
51+
MockHttpServletRequest request = new MockHttpServletRequest();
52+
request.setMethod("GET");
53+
return request;
54+
}
55+
56+
private MockHttpServletRequest createPostJsonRequest(String jsonBody) {
57+
MockHttpServletRequest request = new MockHttpServletRequest();
58+
request.setMethod("POST");
59+
request.setContentType("application/json");
60+
request.setCharacterEncoding("UTF-8");
61+
request.setContent(jsonBody.getBytes());
62+
return request;
63+
}
64+
65+
@Test
66+
public void testGetDefaultKeepsInt64AsNumber() {
67+
// Default GET: parseParams reads visible/int64_as_string from URL query, both absent.
68+
// Response int64 fields (timestamp, number) must remain unquoted.
69+
MockHttpServletRequest request = createGetRequest();
70+
MockHttpServletResponse response = new MockHttpServletResponse();
71+
getBlockServlet.doGet(request, response);
72+
try {
73+
String body = response.getContentAsString();
74+
assertNotNull(JSONObject.parseObject(body));
75+
if (body.contains("\"timestamp\"")) {
76+
assertTrue("timestamp should be unquoted by default, got: " + body,
77+
body.matches("(?s).*\"timestamp\"\\s*:\\s*\\d+.*"));
78+
}
79+
} catch (UnsupportedEncodingException e) {
80+
fail(e.getMessage());
81+
}
82+
}
83+
84+
@Test
85+
public void testGetInt64AsStringFromQuery() {
86+
// GET URL query is read by RateLimiterServlet.service in production; tests bypass
87+
// service, so we simulate that step here.
88+
MockHttpServletRequest request = createGetRequest();
89+
request.addParameter("int64_as_string", "true");
90+
JsonFormat.setInt64AsString(Util.getInt64AsString(request));
91+
MockHttpServletResponse response = new MockHttpServletResponse();
92+
getBlockServlet.doGet(request, response);
93+
try {
94+
String body = response.getContentAsString();
95+
assertNotNull(JSONObject.parseObject(body));
96+
if (body.contains("\"timestamp\"")) {
97+
assertTrue("timestamp should be quoted when int64_as_string=true, got: " + body,
98+
body.matches("(?s).*\"timestamp\"\\s*:\\s*\"\\d+\".*"));
99+
}
100+
} catch (UnsupportedEncodingException e) {
101+
fail(e.getMessage());
102+
}
103+
}
104+
105+
@Test
106+
public void testPostInt64AsStringFromBody() {
107+
// POST branch delegates to PostParams.getPostParams which reads int64_as_string
108+
// from the JSON body (identical semantics to the visible flag).
109+
MockHttpServletRequest request = createPostJsonRequest(
110+
"{\"int64_as_string\": true}");
111+
MockHttpServletResponse response = new MockHttpServletResponse();
112+
getBlockServlet.doPost(request, response);
113+
try {
114+
String body = response.getContentAsString();
115+
assertNotNull(JSONObject.parseObject(body));
116+
if (body.contains("\"timestamp\"")) {
117+
assertTrue("timestamp should be quoted when body carries int64_as_string=true, got: "
118+
+ body, body.matches("(?s).*\"timestamp\"\\s*:\\s*\"\\d+\".*"));
119+
}
120+
} catch (UnsupportedEncodingException e) {
121+
fail(e.getMessage());
122+
}
123+
}
124+
125+
@Test
126+
public void testPostInt64AsStringFalseKeepsNumber() {
127+
// Regression: explicitly false behaves like the default.
128+
MockHttpServletRequest request = createPostJsonRequest(
129+
"{\"int64_as_string\": false}");
130+
MockHttpServletResponse response = new MockHttpServletResponse();
131+
getBlockServlet.doPost(request, response);
132+
try {
133+
String body = response.getContentAsString();
134+
assertNotNull(JSONObject.parseObject(body));
135+
if (body.contains("\"timestamp\"")) {
136+
assertTrue("timestamp should be unquoted when int64_as_string=false, got: " + body,
137+
body.matches("(?s).*\"timestamp\"\\s*:\\s*\\d+.*"));
138+
}
139+
} catch (UnsupportedEncodingException e) {
140+
fail(e.getMessage());
141+
}
142+
}
143+
144+
@Test
145+
public void testPostIgnoresUrlQuery() {
146+
// Mirror visible semantics: on POST, URL query parameters are NOT read;
147+
// only the JSON body controls the flag.
148+
MockHttpServletRequest request = createPostJsonRequest("{}");
149+
request.addParameter("int64_as_string", "true"); // must be ignored
150+
MockHttpServletResponse response = new MockHttpServletResponse();
151+
getBlockServlet.doPost(request, response);
152+
try {
153+
String body = response.getContentAsString();
154+
if (body.contains("\"timestamp\"")) {
155+
assertFalse("POST URL query must not leak into body-parsed flag, got: " + body,
156+
body.matches("(?s).*\"timestamp\"\\s*:\\s*\"\\d+\".*"));
157+
}
158+
} catch (UnsupportedEncodingException e) {
159+
fail(e.getMessage());
160+
}
161+
}
162+
}

framework/src/test/java/org/tron/core/services/http/GetNowBlockServletTest.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.alibaba.fastjson.JSONObject;
1111
import java.io.UnsupportedEncodingException;
1212
import javax.annotation.Resource;
13+
import org.junit.After;
14+
import org.junit.Before;
1315
import org.junit.Test;
1416
import org.springframework.mock.web.MockHttpServletRequest;
1517
import org.springframework.mock.web.MockHttpServletResponse;
@@ -30,6 +32,18 @@ public class GetNowBlockServletTest extends BaseTest {
3032
);
3133
}
3234

35+
@Before
36+
public void clearBefore() {
37+
// Tests bypass RateLimiterServlet.service which is where the centralized clear runs,
38+
// so each test starts with a known-clean ThreadLocal.
39+
JsonFormat.clearInt64AsString();
40+
}
41+
42+
@After
43+
public void clearAfter() {
44+
JsonFormat.clearInt64AsString();
45+
}
46+
3347
public MockHttpServletRequest createRequest(String contentType) {
3448
MockHttpServletRequest request = new MockHttpServletRequest();
3549
request.setMethod("POST");
@@ -118,4 +132,51 @@ public void testGetNowBlockEmptyParam() {
118132
fail(e.getMessage());
119133
}
120134
}
135+
136+
@Test
137+
public void testGetNowBlockInt64AsStringQueryTrue() {
138+
// Block.block_header.raw_data.timestamp and .number are int64; with int64_as_string=true
139+
// they should be serialized as quoted strings. In production RateLimiterServlet.service
140+
// sets the ThreadLocal from the URL query for GET requests; tests bypass that path.
141+
MockHttpServletRequest request = createRequest("application/x-www-form-urlencoded");
142+
request.setMethod("GET");
143+
request.addParameter("int64_as_string", "true");
144+
JsonFormat.setInt64AsString(Util.getInt64AsString(request));
145+
MockHttpServletResponse response = new MockHttpServletResponse();
146+
getNowBlockServlet.doGet(request, response);
147+
try {
148+
String contentAsString = response.getContentAsString();
149+
// At genesis the header's number field defaults to 0 and proto3 omits defaults,
150+
// so we assert on the witness_address/parentHash presence and on the absence of an
151+
// unquoted timestamp when the field is present.
152+
assertTrue("expected a block response: " + contentAsString,
153+
contentAsString.contains("block_header") || contentAsString.contains("blockID"));
154+
// If timestamp appears (>0), it must be quoted.
155+
if (contentAsString.contains("\"timestamp\"")) {
156+
assertTrue("timestamp should be quoted when int64_as_string=true, got: "
157+
+ contentAsString,
158+
contentAsString.matches("(?s).*\"timestamp\"\\s*:\\s*\"\\d+\".*"));
159+
}
160+
} catch (UnsupportedEncodingException e) {
161+
fail(e.getMessage());
162+
}
163+
}
164+
165+
@Test
166+
public void testGetNowBlockInt64AsStringDefaultUnquoted() {
167+
// Regression: without int64_as_string, response must be identical to prior behavior.
168+
MockHttpServletRequest request = createRequest("application/x-www-form-urlencoded");
169+
MockHttpServletResponse response = new MockHttpServletResponse();
170+
getNowBlockServlet.doPost(request, response);
171+
try {
172+
String contentAsString = response.getContentAsString();
173+
if (contentAsString.contains("\"timestamp\"")) {
174+
// Default: unquoted integer.
175+
assertTrue("timestamp should be unquoted by default, got: " + contentAsString,
176+
contentAsString.matches("(?s).*\"timestamp\"\\s*:\\s*\\d+.*"));
177+
}
178+
} catch (UnsupportedEncodingException e) {
179+
fail(e.getMessage());
180+
}
181+
}
121182
}

0 commit comments

Comments
 (0)