Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package me.chanjar.weixin.channel.util;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.expectThrows;

import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import me.chanjar.weixin.channel.config.impl.WxChannelDefaultConfigImpl;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.util.crypto.SHA1;
import org.apache.commons.codec.binary.Base64;
import org.testng.annotations.Test;

/**
* {@link WxChCryptUtils} 单元测试
*/
public class WxChCryptUtilsTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 将 Channel 新增单测加入 TestNG suite

weixin-java-channel/pom.xml:131-133 同样固定使用 src/test/resources/testng.xml,而该 suite 的 <classes>weixin-java-channel/src/test/resources/testng.xml:5-9 中没有任何启用的测试类;按 Surefire suite XML 语义(https://maven.apache.org/surefire-archives/surefire-3.5.4/maven-surefire-plugin/examples/testng.html#using-suite-xml-files),启用常规 mvn -pl weixin-java-channel -am test 时既不会执行这个类,也不会执行同 PR 新增的 XmlUtilsTest,导致新增 Channel 单测没有回归保护。请将新增类加入 suite,或移除 suiteXmlFiles 让默认扫描生效。

Useful? React with 👍 / 👎.


private static final String APP_ID = "wx0000000000000002";
private static final String TOKEN = "test_channel_token";
/** 43 位 EncodingAESKey 占位值,非真实密钥 */
private static final String AES_KEY = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY";

private static final Pattern ENCRYPT_PATTERN = Pattern.compile("<Encrypt><!\\[CDATA\\[(.*?)]]></Encrypt>");
private static final Pattern SIGNATURE_PATTERN =
Pattern.compile("<MsgSignature><!\\[CDATA\\[(.*?)]]></MsgSignature>");
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("<TimeStamp>(.*?)</TimeStamp>");
private static final Pattern NONCE_PATTERN = Pattern.compile("<Nonce><!\\[CDATA\\[(.*?)]]></Nonce>");

private WxChCryptUtils cryptUtils() {
WxChannelDefaultConfigImpl config = new WxChannelDefaultConfigImpl();
config.setAppid(APP_ID);
config.setToken(TOKEN);
config.setAesKey(AES_KEY);
return new WxChCryptUtils(config);
}

private String group(Pattern pattern, String text) {
Matcher matcher = pattern.matcher(text);
assertTrue(matcher.find(), "未匹配到期望的节点:" + pattern.pattern());
return matcher.group(1);
}

@Test
public void testEncryptThenDecrypt() {
WxChCryptUtils cryptUtils = cryptUtils();
String plainText = "<xml><ToUserName><![CDATA[" + APP_ID + "]]></ToUserName>"
+ "<MsgType><![CDATA[event]]></MsgType></xml>";

String encryptedXml = cryptUtils.encrypt(plainText);
assertNotNull(encryptedXml);

String encrypt = group(ENCRYPT_PATTERN, encryptedXml);
String signature = group(SIGNATURE_PATTERN, encryptedXml);
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);

assertEquals(signature, SHA1.gen(TOKEN, timestamp, nonce, encrypt));
assertEquals(cryptUtils.decryptXml(signature, timestamp, nonce, encryptedXml), plainText);
}

@Test
public void testDecryptXmlWithWrongSignature() {
WxChCryptUtils cryptUtils = cryptUtils();
String encryptedXml = cryptUtils.encrypt("<xml><MsgType><![CDATA[event]]></MsgType></xml>");
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);

expectThrows(WxRuntimeException.class,
() -> cryptUtils.decryptXml("wrong_signature", timestamp, nonce, encryptedXml));
}

@Test
public void testDecryptWithSessionKey() throws Exception {
byte[] keyBytes = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = "fedcba9876543210".getBytes(StandardCharsets.UTF_8);
String plainText = "{\"openid\":\"o0000000000000000000\"}";

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(ivBytes));
String encryptedData = Base64.encodeBase64String(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));

String decrypted = WxChCryptUtils.decrypt(Base64.encodeBase64String(keyBytes), encryptedData,
Base64.encodeBase64String(ivBytes));
assertEquals(decrypted, plainText);
}

@Test
public void testDecryptWithInvalidSessionKey() {
expectThrows(RuntimeException.class,
() -> WxChCryptUtils.decrypt(Base64.encodeBase64String("short_key".getBytes(StandardCharsets.UTF_8)),
"invalid", Base64.encodeBase64String("fedcba9876543210".getBytes(StandardCharsets.UTF_8))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package me.chanjar.weixin.channel.util;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import me.chanjar.weixin.channel.bean.base.AttrInfo;
import org.testng.annotations.Test;

/**
* {@link XmlUtils} 单元测试
*/
public class XmlUtilsTest {

private static final String XML = "<AttrInfo><attr_key>这是Key</attr_key><attr_value>这是Value</attr_value></AttrInfo>";

@Test
public void testEncode() {
String xml = XmlUtils.encode(new AttrInfo("这是Key", "这是Value"));
assertNotNull(xml);
assertEquals(xml, XML);
}

@Test
public void testEncodeWithObjectMapper() {
String xml = XmlUtils.encode(new XmlMapper(), new AttrInfo("这是Key", "这是Value"));
assertNotNull(xml);
assertEquals(XmlUtils.decode(xml, AttrInfo.class).getKey(), "这是Key");
}

@Test
public void testDecode() {
AttrInfo info = XmlUtils.decode(XML, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "这是Key");
assertEquals(info.getValue(), "这是Value");
}

@Test
public void testDecodeUnknownProperty() {
String xml = "<AttrInfo><attr_key>k</attr_key><unknown>v</unknown></AttrInfo>";
AttrInfo info = XmlUtils.decode(xml, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "k");
assertNull(info.getValue());
}

@Test
public void testDecodeEmptyOrInvalidXml() {
assertNull(XmlUtils.decode((String) null, AttrInfo.class));
assertNull(XmlUtils.decode("", AttrInfo.class));
assertNull(XmlUtils.decode("not a xml", AttrInfo.class));
}

@Test
public void testDecodeWithTypeReference() {
AttrInfo info = XmlUtils.decode(XML, new TypeReference<AttrInfo>() {
});
assertNotNull(info);
assertEquals(info.getValue(), "这是Value");
}

@Test
public void testDecodeInputStream() {
InputStream is = new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8));
AttrInfo info = XmlUtils.decode(is, AttrInfo.class);
assertNotNull(info);
assertEquals(info.getKey(), "这是Key");
}
}
8 changes: 8 additions & 0 deletions weixin-java-channel/src/test/resources/testng.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@
<class name="me.chanjar.weixin.open.bean.result.WxFastMaBeenSetCategoryResultTest"/>-->
</classes>
</test>
<test name="Util_Test">
<classes>
<class name="me.chanjar.weixin.channel.util.JsonUtilsTest"/>
<class name="me.chanjar.weixin.channel.util.XmlUtilsTest"/>
<class name="me.chanjar.weixin.channel.util.WxChCryptUtilsTest"/>
<class name="me.chanjar.weixin.channel.util.ResponseUtilsTest"/>
</classes>
</test>
</suite>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.List;

Expand All @@ -16,11 +17,29 @@
@EqualsAndHashCode(callSuper = false)
public class WxFastMaCanSetCategoryResult extends WxOpenResult {
private static final long serialVersionUID = -2469386233538980102L;
@SerializedName("errcode")
private int errCode;
@SerializedName("categories_list")
private CategoriesListBean categoriesList;

/**
* 错误码,已弃用,未来将删除
*
* @see WxOpenResult#getErrcode() 应使用此方法
*/
@Deprecated
public int getErrCode() {
return NumberUtils.toInt(this.errcode);
}

/**
* 错误码,已弃用,未来将删除
*
* @see WxOpenResult#setErrcode(String) 应使用此方法
*/
@Deprecated
public void setErrCode(int errCode) {
this.errcode = String.valueOf(errCode);
}

@Data
public static class CategoriesListBean {
private List<CategoriesBean> categories;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package me.chanjar.weixin.open.bean.message;

import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.open.api.impl.WxOpenInMemoryConfigStorage;
import me.chanjar.weixin.open.util.WxOpenCryptUtil;
import org.testng.annotations.Test;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

/**
* {@link WxOpenXmlMessage} 单元测试
*/
public class WxOpenXmlMessageTest {

private static final String COMPONENT_APP_ID = "wx0000000000000001";
private static final String COMPONENT_TOKEN = "test_component_token";
/** 43 位 EncodingAESKey 占位值,非真实密钥 */
private static final String COMPONENT_AES_KEY = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY";

private static final Pattern ENCRYPT_PATTERN = Pattern.compile("<Encrypt><!\\[CDATA\\[(.*?)]]></Encrypt>");
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("<TimeStamp>(.*?)</TimeStamp>");
private static final Pattern NONCE_PATTERN = Pattern.compile("<Nonce><!\\[CDATA\\[(.*?)]]></Nonce>");

private static final String VERIFY_TICKET_XML = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1413192605</CreateTime>\n"
+ " <InfoType><![CDATA[component_verify_ticket]]></InfoType>\n"
+ " <ComponentVerifyTicket><![CDATA[ticket@@@abcdefg]]></ComponentVerifyTicket>\n"
+ "</xml>";

private WxOpenInMemoryConfigStorage config() {
WxOpenInMemoryConfigStorage config = new WxOpenInMemoryConfigStorage();
config.setComponentAppId(COMPONENT_APP_ID);
config.setComponentToken(COMPONENT_TOKEN);
config.setComponentAesKey(COMPONENT_AES_KEY);
return config;
}

private String group(Pattern pattern, String text) {
Matcher matcher = pattern.matcher(text);
assertTrue(matcher.find(), "未匹配到期望的节点:" + pattern.pattern());
return matcher.group(1);
}

@Test
public void testFromXmlComponentVerifyTicket() {
WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(VERIFY_TICKET_XML);
assertNotNull(message);
assertEquals(message.getAppId(), COMPONENT_APP_ID);
assertEquals(message.getCreateTime(), Long.valueOf(1413192605L));
assertEquals(message.getInfoType(), "component_verify_ticket");
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
}

@Test
public void testFromXmlAuthorized() {
String xml = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1413192760</CreateTime>\n"
+ " <InfoType><![CDATA[authorized]]></InfoType>\n"
+ " <AuthorizerAppid><![CDATA[wx0000000000000002]]></AuthorizerAppid>\n"
+ " <AuthorizationCode><![CDATA[auth_code_value]]></AuthorizationCode>\n"
+ " <AuthorizationCodeExpiredTime>600</AuthorizationCodeExpiredTime>\n"
+ " <PreAuthCode><![CDATA[pre_auth_code_value]]></PreAuthCode>\n"
+ "</xml>";

WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(xml);
assertNotNull(message);
assertEquals(message.getInfoType(), "authorized");
assertEquals(message.getAuthorizerAppid(), "wx0000000000000002");
assertEquals(message.getAuthorizationCode(), "auth_code_value");
assertEquals(message.getAuthorizationCodeExpiredTime(), Long.valueOf(600L));
assertEquals(message.getPreAuthCode(), "pre_auth_code_value");
}

@Test
public void testFromXmlFastRegisterWeApp() {
String xml = "<xml>\n"
+ " <AppId><![CDATA[" + COMPONENT_APP_ID + "]]></AppId>\n"
+ " <CreateTime>1535442403</CreateTime>\n"
+ " <InfoType><![CDATA[notify_third_fasteregister]]></InfoType>\n"
+ " <appid>wx0000000000000003</appid>\n"
+ " <status>0</status>\n"
+ " <auth_code>auth_code_value</auth_code>\n"
+ " <msg><![CDATA[OK]]></msg>\n"
+ " <info>\n"
+ " <name><![CDATA[тест]]></name>\n"
+ " </info>\n"
+ "</xml>";

WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(xml);
assertNotNull(message);
assertEquals(message.getSubAppId(), "wx0000000000000003");
assertEquals(message.getRegistAppId(), "wx0000000000000003");
assertEquals(message.getStatus(), 0);
assertEquals(message.getAuthCode(), "auth_code_value");
assertEquals(message.getMsg(), "OK");
assertNotNull(message.getInfo());
assertEquals(message.getInfo().getName(), "тест");
}

@Test
public void testFromXmlInputStream() {
InputStream is = new ByteArrayInputStream(VERIFY_TICKET_XML.getBytes(StandardCharsets.UTF_8));
WxOpenXmlMessage message = WxOpenXmlMessage.fromXml(is);
assertNotNull(message);
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
}

@Test
public void testFromEncryptedXml() {
WxOpenInMemoryConfigStorage config = config();
String encryptedXml = new WxOpenCryptUtil(config).encrypt(VERIFY_TICKET_XML);

String encrypt = group(ENCRYPT_PATTERN, encryptedXml);
String timestamp = group(TIMESTAMP_PATTERN, encryptedXml);
String nonce = group(NONCE_PATTERN, encryptedXml);
String signature = SHA1.gen(COMPONENT_TOKEN, timestamp, nonce, encrypt);

WxOpenXmlMessage message = WxOpenXmlMessage.fromEncryptedXml(encryptedXml, config, timestamp, nonce, signature);
assertNotNull(message);
assertEquals(message.getComponentVerifyTicket(), "ticket@@@abcdefg");
assertEquals(message.getContext(), VERIFY_TICKET_XML);

InputStream is = new ByteArrayInputStream(encryptedXml.getBytes(StandardCharsets.UTF_8));
WxOpenXmlMessage fromStream = WxOpenXmlMessage.fromEncryptedXml(is, config, timestamp, nonce, signature);
assertNotNull(fromStream);
assertEquals(fromStream.getInfoType(), "component_verify_ticket");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;


public class WxFastMaCanSetCategoryResultTest {
Expand Down Expand Up @@ -73,6 +75,10 @@ public void testFromJson() throws Exception {

assertNotNull(res);
assertNotNull(res.getCategoriesList());
assertEquals(res.getErrcode(), "0");
assertEquals(res.getErrmsg(), "ok");
assertEquals(res.getErrCode(), 0);
assertTrue(res.isSuccess());
System.out.println(res);
}

Expand Down
Loading