Skip to content

Commit e61cbb2

Browse files
BarbatosBarbatos
authored andcommitted
test(plugins): improve keystore test coverage and assertions
- Add tests for update: JSON output, single-line password file, no TTY, password file not found, SM2 keystore, multiple same-address keystores, large password file, BOM password file - Add tests for import: 0x/0X prefix key, corrupted file warning, POSIX file permissions - Add tests for new: large password file, BOM password file, POSIX file permissions - Add tests for list: empty/nonexistent directory JSON output, corrupted file warning, output content verification - Strengthen existing tests with error message assertions to prevent false passes from wrong failure reasons
1 parent 88df0ff commit e61cbb2

4 files changed

Lines changed: 565 additions & 9 deletions

File tree

plugins/src/test/java/org/tron/plugins/KeystoreImportTest.java

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,16 @@ public void testImportDuplicateAddressBlocked() throws Exception {
193193
"--password-file", pwFile.getAbsolutePath()));
194194

195195
// Second import of same key is blocked
196+
java.io.StringWriter err = new java.io.StringWriter();
196197
CommandLine cmd2 = new CommandLine(new Toolkit());
198+
cmd2.setErr(new java.io.PrintWriter(err));
197199
assertEquals("Duplicate import should be blocked", 1,
198200
cmd2.execute("keystore", "import",
199201
"--keystore-dir", dir.getAbsolutePath(),
200202
"--key-file", keyFile.getAbsolutePath(),
201203
"--password-file", pwFile.getAbsolutePath()));
204+
assertTrue("Error should mention already exists",
205+
err.toString().contains("already exists"));
202206

203207
File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
204208
assertNotNull(files);
@@ -251,4 +255,122 @@ public void testImportKeyFileNotFound() throws Exception {
251255

252256
assertEquals("Should fail when key file not found", 1, exitCode);
253257
}
258+
259+
@Test
260+
public void testImportWith0xPrefix() throws Exception {
261+
File dir = tempFolder.newFolder("keystore-0x");
262+
SignInterface keyPair = SignUtils.getGeneratedRandomSign(
263+
SecureRandom.getInstance("NativePRNG"), true);
264+
String privateKeyHex = ByteArray.toHexString(keyPair.getPrivateKey());
265+
String expectedAddress = Credentials.create(keyPair).getAddress();
266+
267+
File keyFile = tempFolder.newFile("0x.key");
268+
Files.write(keyFile.toPath(),
269+
("0x" + privateKeyHex).getBytes(StandardCharsets.UTF_8));
270+
File pwFile = tempFolder.newFile("pw-0x.txt");
271+
Files.write(pwFile.toPath(), "test123456".getBytes(StandardCharsets.UTF_8));
272+
273+
CommandLine cmd = new CommandLine(new Toolkit());
274+
int exitCode = cmd.execute("keystore", "import",
275+
"--keystore-dir", dir.getAbsolutePath(),
276+
"--key-file", keyFile.getAbsolutePath(),
277+
"--password-file", pwFile.getAbsolutePath());
278+
279+
assertEquals("Import with 0x prefix should succeed", 0, exitCode);
280+
File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
281+
assertNotNull(files);
282+
assertEquals(1, files.length);
283+
Credentials creds = WalletUtils.loadCredentials("test123456", files[0], true);
284+
assertEquals("Address must match", expectedAddress, creds.getAddress());
285+
}
286+
287+
@Test
288+
public void testImportWith0XUppercasePrefix() throws Exception {
289+
File dir = tempFolder.newFolder("keystore-0X");
290+
SignInterface keyPair = SignUtils.getGeneratedRandomSign(
291+
SecureRandom.getInstance("NativePRNG"), true);
292+
String privateKeyHex = ByteArray.toHexString(keyPair.getPrivateKey());
293+
294+
File keyFile = tempFolder.newFile("0X.key");
295+
Files.write(keyFile.toPath(),
296+
("0X" + privateKeyHex).getBytes(StandardCharsets.UTF_8));
297+
File pwFile = tempFolder.newFile("pw-0X.txt");
298+
Files.write(pwFile.toPath(), "test123456".getBytes(StandardCharsets.UTF_8));
299+
300+
CommandLine cmd = new CommandLine(new Toolkit());
301+
int exitCode = cmd.execute("keystore", "import",
302+
"--keystore-dir", dir.getAbsolutePath(),
303+
"--key-file", keyFile.getAbsolutePath(),
304+
"--password-file", pwFile.getAbsolutePath());
305+
306+
assertEquals("Import with 0X prefix should succeed", 0, exitCode);
307+
}
308+
309+
@Test
310+
public void testImportWarnsOnCorruptedFile() throws Exception {
311+
File dir = tempFolder.newFolder("keystore-corrupt");
312+
SignInterface keyPair = SignUtils.getGeneratedRandomSign(
313+
SecureRandom.getInstance("NativePRNG"), true);
314+
String privateKeyHex = ByteArray.toHexString(keyPair.getPrivateKey());
315+
316+
// Create a corrupted JSON in the keystore dir
317+
Files.write(new File(dir, "corrupted.json").toPath(),
318+
"not valid json{{{".getBytes(StandardCharsets.UTF_8));
319+
320+
File keyFile = tempFolder.newFile("warn.key");
321+
Files.write(keyFile.toPath(), privateKeyHex.getBytes(StandardCharsets.UTF_8));
322+
File pwFile = tempFolder.newFile("pw-warn.txt");
323+
Files.write(pwFile.toPath(), "test123456".getBytes(StandardCharsets.UTF_8));
324+
325+
java.io.StringWriter out = new java.io.StringWriter();
326+
java.io.StringWriter err = new java.io.StringWriter();
327+
CommandLine cmd = new CommandLine(new Toolkit());
328+
cmd.setOut(new java.io.PrintWriter(out));
329+
cmd.setErr(new java.io.PrintWriter(err));
330+
int exitCode = cmd.execute("keystore", "import",
331+
"--keystore-dir", dir.getAbsolutePath(),
332+
"--key-file", keyFile.getAbsolutePath(),
333+
"--password-file", pwFile.getAbsolutePath());
334+
335+
assertEquals(0, exitCode);
336+
String errOutput = err.toString();
337+
assertTrue("Should warn about corrupted file",
338+
errOutput.contains("Warning: skipping unreadable file: corrupted.json"));
339+
}
340+
341+
@Test
342+
public void testImportKeystoreFilePermissions() throws Exception {
343+
String os = System.getProperty("os.name").toLowerCase();
344+
org.junit.Assume.assumeTrue("POSIX permissions test, skip on Windows",
345+
!os.contains("win"));
346+
347+
File dir = tempFolder.newFolder("keystore-perms");
348+
SignInterface keyPair = SignUtils.getGeneratedRandomSign(
349+
SecureRandom.getInstance("NativePRNG"), true);
350+
String privateKeyHex = ByteArray.toHexString(keyPair.getPrivateKey());
351+
352+
File keyFile = tempFolder.newFile("perm.key");
353+
Files.write(keyFile.toPath(), privateKeyHex.getBytes(StandardCharsets.UTF_8));
354+
File pwFile = tempFolder.newFile("pw-perm.txt");
355+
Files.write(pwFile.toPath(), "test123456".getBytes(StandardCharsets.UTF_8));
356+
357+
CommandLine cmd = new CommandLine(new Toolkit());
358+
int exitCode = cmd.execute("keystore", "import",
359+
"--keystore-dir", dir.getAbsolutePath(),
360+
"--key-file", keyFile.getAbsolutePath(),
361+
"--password-file", pwFile.getAbsolutePath());
362+
363+
assertEquals(0, exitCode);
364+
File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
365+
assertNotNull(files);
366+
assertEquals(1, files.length);
367+
368+
java.util.Set<java.nio.file.attribute.PosixFilePermission> perms =
369+
Files.getPosixFilePermissions(files[0].toPath());
370+
assertEquals("Keystore file should have owner-only permissions (rw-------)",
371+
java.util.EnumSet.of(
372+
java.nio.file.attribute.PosixFilePermission.OWNER_READ,
373+
java.nio.file.attribute.PosixFilePermission.OWNER_WRITE),
374+
perms);
375+
}
254376
}

plugins/src/test/java/org/tron/plugins/KeystoreListTest.java

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ public void testListMultipleKeystores() throws Exception {
3939
CommandLine cmd = new CommandLine(new Toolkit());
4040
cmd.setOut(new PrintWriter(out));
4141
cmd.setErr(new PrintWriter(err));
42-
4342
int exitCode = cmd.execute("keystore", "list",
4443
"--keystore-dir", dir.getAbsolutePath());
4544

@@ -49,28 +48,75 @@ public void testListMultipleKeystores() throws Exception {
4948
// Should have 3 lines of output (one per keystore)
5049
String[] lines = output.split("\\n");
5150
assertEquals("Should list 3 keystores", 3, lines.length);
51+
// Each line should contain a T-address and a .json filename
52+
for (String line : lines) {
53+
assertTrue("Each line should contain an address starting with T",
54+
line.trim().startsWith("T"));
55+
assertTrue("Each line should reference a .json file",
56+
line.contains(".json"));
57+
}
5258
}
5359

5460
@Test
5561
public void testListEmptyDirectory() throws Exception {
5662
File dir = tempFolder.newFolder("empty");
5763

64+
StringWriter out = new StringWriter();
5865
CommandLine cmd = new CommandLine(new Toolkit());
66+
cmd.setOut(new PrintWriter(out));
5967
int exitCode = cmd.execute("keystore", "list",
6068
"--keystore-dir", dir.getAbsolutePath());
6169

6270
assertEquals(0, exitCode);
71+
assertTrue("Should print no-keystores message",
72+
out.toString().contains("No keystores found"));
6373
}
6474

6575
@Test
6676
public void testListNonExistentDirectory() throws Exception {
6777
File dir = new File(tempFolder.getRoot(), "nonexistent");
6878

79+
StringWriter out = new StringWriter();
6980
CommandLine cmd = new CommandLine(new Toolkit());
81+
cmd.setOut(new PrintWriter(out));
7082
int exitCode = cmd.execute("keystore", "list",
7183
"--keystore-dir", dir.getAbsolutePath());
7284

7385
assertEquals(0, exitCode);
86+
assertTrue("Should print no-keystores message",
87+
out.toString().contains("No keystores found"));
88+
}
89+
90+
@Test
91+
public void testListEmptyDirectoryJsonOutput() throws Exception {
92+
File dir = tempFolder.newFolder("empty-json");
93+
94+
StringWriter out = new StringWriter();
95+
CommandLine cmd = new CommandLine(new Toolkit());
96+
cmd.setOut(new PrintWriter(out));
97+
int exitCode = cmd.execute("keystore", "list",
98+
"--keystore-dir", dir.getAbsolutePath(), "--json");
99+
100+
assertEquals(0, exitCode);
101+
String output = out.toString().trim();
102+
assertTrue("Empty dir JSON should have empty keystores array",
103+
output.contains("{\"keystores\":[]}"));
104+
}
105+
106+
@Test
107+
public void testListNonExistentDirectoryJsonOutput() throws Exception {
108+
File dir = new File(tempFolder.getRoot(), "nonexistent-json");
109+
110+
StringWriter out = new StringWriter();
111+
CommandLine cmd = new CommandLine(new Toolkit());
112+
cmd.setOut(new PrintWriter(out));
113+
int exitCode = cmd.execute("keystore", "list",
114+
"--keystore-dir", dir.getAbsolutePath(), "--json");
115+
116+
assertEquals(0, exitCode);
117+
String output = out.toString().trim();
118+
assertTrue("Non-existent dir JSON should have empty keystores array",
119+
output.contains("{\"keystores\":[]}"));
74120
}
75121

76122
@Test
@@ -86,7 +132,6 @@ public void testListJsonOutput() throws Exception {
86132
CommandLine cmd = new CommandLine(new Toolkit());
87133
cmd.setOut(new PrintWriter(out));
88134
cmd.setErr(new PrintWriter(err));
89-
90135
int exitCode = cmd.execute("keystore", "list",
91136
"--keystore-dir", dir.getAbsolutePath(), "--json");
92137

@@ -119,7 +164,6 @@ public void testListSkipsNonKeystoreFiles() throws Exception {
119164
CommandLine cmd = new CommandLine(new Toolkit());
120165
cmd.setOut(new PrintWriter(out));
121166
cmd.setErr(new PrintWriter(err));
122-
123167
int exitCode = cmd.execute("keystore", "list",
124168
"--keystore-dir", dir.getAbsolutePath());
125169

@@ -130,4 +174,36 @@ public void testListSkipsNonKeystoreFiles() throws Exception {
130174
// Should list only the valid keystore, not the readme.json or notes.txt
131175
assertEquals("Should list only 1 valid keystore", 1, lines.length);
132176
}
177+
178+
@Test
179+
public void testListWarnsOnCorruptedJsonFiles() throws Exception {
180+
File dir = tempFolder.newFolder("keystore-corrupt");
181+
String password = "test123456";
182+
183+
// Create one valid keystore
184+
SignInterface key = SignUtils.getGeneratedRandomSign(
185+
SecureRandom.getInstance("NativePRNG"), true);
186+
WalletUtils.generateWalletFile(password, key, dir, false);
187+
188+
// Create a corrupted JSON file
189+
Files.write(new File(dir, "corrupted.json").toPath(),
190+
"not valid json{{{".getBytes(StandardCharsets.UTF_8));
191+
192+
StringWriter out = new StringWriter();
193+
StringWriter err = new StringWriter();
194+
CommandLine cmd = new CommandLine(new Toolkit());
195+
cmd.setOut(new PrintWriter(out));
196+
cmd.setErr(new PrintWriter(err));
197+
int exitCode = cmd.execute("keystore", "list",
198+
"--keystore-dir", dir.getAbsolutePath());
199+
200+
assertEquals(0, exitCode);
201+
String errOutput = err.toString();
202+
assertTrue("Should warn about corrupted file",
203+
errOutput.contains("Warning: skipping unreadable file: corrupted.json"));
204+
205+
// Valid keystore should still be listed
206+
String output = out.toString().trim();
207+
assertTrue("Should still list the valid keystore", output.length() > 0);
208+
}
133209
}

plugins/src/test/java/org/tron/plugins/KeystoreNewTest.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ public void testNewKeystoreJsonOutput() throws Exception {
6060
CommandLine cmd = new CommandLine(new Toolkit());
6161
cmd.setOut(new PrintWriter(out));
6262
cmd.setErr(new PrintWriter(err));
63-
6463
int exitCode = cmd.execute("keystore", "new",
6564
"--keystore-dir", dir.getAbsolutePath(),
6665
"--password-file", pwFile.getAbsolutePath(),
@@ -80,12 +79,16 @@ public void testNewKeystoreInvalidPassword() throws Exception {
8079
File pwFile = tempFolder.newFile("short.txt");
8180
Files.write(pwFile.toPath(), "abc".getBytes(StandardCharsets.UTF_8));
8281

82+
StringWriter err = new StringWriter();
8383
CommandLine cmd = new CommandLine(new Toolkit());
84+
cmd.setErr(new PrintWriter(err));
8485
int exitCode = cmd.execute("keystore", "new",
8586
"--keystore-dir", dir.getAbsolutePath(),
8687
"--password-file", pwFile.getAbsolutePath());
8788

8889
assertEquals("Should fail with short password", 1, exitCode);
90+
assertTrue("Error should mention password length",
91+
err.toString().contains("at least 6 characters"));
8992
}
9093

9194
@Test
@@ -125,12 +128,16 @@ public void testNewKeystoreEmptyPassword() throws Exception {
125128
File pwFile = tempFolder.newFile("empty.txt");
126129
Files.write(pwFile.toPath(), "".getBytes(StandardCharsets.UTF_8));
127130

131+
StringWriter err = new StringWriter();
128132
CommandLine cmd = new CommandLine(new Toolkit());
133+
cmd.setErr(new PrintWriter(err));
129134
int exitCode = cmd.execute("keystore", "new",
130135
"--keystore-dir", dir.getAbsolutePath(),
131136
"--password-file", pwFile.getAbsolutePath());
132137

133138
assertEquals("Should fail with empty password", 1, exitCode);
139+
assertTrue("Error should mention password length",
140+
err.toString().contains("at least 6 characters"));
134141
}
135142

136143
@Test
@@ -203,4 +210,71 @@ public void testNewKeystoreDirIsFile() throws Exception {
203210

204211
assertEquals("Should fail when dir is a file", 1, exitCode);
205212
}
213+
214+
@Test
215+
public void testNewKeystorePasswordFileTooLarge() throws Exception {
216+
File dir = tempFolder.newFolder("keystore-bigpw");
217+
File pwFile = tempFolder.newFile("bigpw.txt");
218+
byte[] bigContent = new byte[1025];
219+
java.util.Arrays.fill(bigContent, (byte) 'a');
220+
Files.write(pwFile.toPath(), bigContent);
221+
222+
StringWriter err = new StringWriter();
223+
CommandLine cmd = new CommandLine(new Toolkit());
224+
cmd.setErr(new PrintWriter(err));
225+
int exitCode = cmd.execute("keystore", "new",
226+
"--keystore-dir", dir.getAbsolutePath(),
227+
"--password-file", pwFile.getAbsolutePath());
228+
229+
assertEquals("Should fail with large password file", 1, exitCode);
230+
assertTrue("Error should mention file too large",
231+
err.toString().contains("too large"));
232+
}
233+
234+
@Test
235+
public void testNewKeystorePasswordFileWithBom() throws Exception {
236+
File dir = tempFolder.newFolder("keystore-bom");
237+
File pwFile = tempFolder.newFile("bom.txt");
238+
Files.write(pwFile.toPath(),
239+
("\uFEFF" + "test123456").getBytes(StandardCharsets.UTF_8));
240+
241+
CommandLine cmd = new CommandLine(new Toolkit());
242+
int exitCode = cmd.execute("keystore", "new",
243+
"--keystore-dir", dir.getAbsolutePath(),
244+
"--password-file", pwFile.getAbsolutePath());
245+
246+
assertEquals("Should succeed with BOM password file", 0, exitCode);
247+
File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
248+
assertNotNull(files);
249+
assertEquals(1, files.length);
250+
}
251+
252+
@Test
253+
public void testNewKeystoreFilePermissions() throws Exception {
254+
String os = System.getProperty("os.name").toLowerCase();
255+
org.junit.Assume.assumeTrue("POSIX permissions test, skip on Windows",
256+
!os.contains("win"));
257+
258+
File dir = tempFolder.newFolder("keystore-perms");
259+
File pwFile = tempFolder.newFile("pw-perms.txt");
260+
Files.write(pwFile.toPath(), "test123456".getBytes(StandardCharsets.UTF_8));
261+
262+
CommandLine cmd = new CommandLine(new Toolkit());
263+
int exitCode = cmd.execute("keystore", "new",
264+
"--keystore-dir", dir.getAbsolutePath(),
265+
"--password-file", pwFile.getAbsolutePath());
266+
267+
assertEquals(0, exitCode);
268+
File[] files = dir.listFiles((d, name) -> name.endsWith(".json"));
269+
assertNotNull(files);
270+
assertEquals(1, files.length);
271+
272+
java.util.Set<java.nio.file.attribute.PosixFilePermission> perms =
273+
Files.getPosixFilePermissions(files[0].toPath());
274+
assertEquals("Keystore file should have owner-only permissions (rw-------)",
275+
java.util.EnumSet.of(
276+
java.nio.file.attribute.PosixFilePermission.OWNER_READ,
277+
java.nio.file.attribute.PosixFilePermission.OWNER_WRITE),
278+
perms);
279+
}
206280
}

0 commit comments

Comments
 (0)