@@ -47,6 +47,24 @@ public void allHttpClientsSendEncodedValuesAndPreserveSession() throws Exception
4747 out .write (body );
4848 }
4949 });
50+ server .createContext ("/custom" , exchange -> {
51+ query .set (exchange .getRequestURI ().getRawQuery ());
52+ String result = exchange .getRequestMethod ();
53+ if ("POST" .equals (result )) {
54+ java .io .ByteArrayOutputStream body = new java .io .ByteArrayOutputStream ();
55+ byte [] buffer = new byte [128 ];
56+ int count ;
57+ while ((count = exchange .getRequestBody ().read (buffer )) != -1 ) {
58+ body .write (buffer , 0 , count );
59+ }
60+ result += ":" + new String (body .toByteArray (), StandardCharsets .UTF_8 );
61+ }
62+ byte [] body = ("{\" result\" :\" " + result + "\" }" ).getBytes (StandardCharsets .UTF_8 );
63+ exchange .sendResponseHeaders (200 , body .length );
64+ try (java .io .OutputStream out = exchange .getResponseBody ()) {
65+ out .write (body );
66+ }
67+ });
5068 server .start ();
5169 try (LogCapture logs = new LogCapture ()) {
5270 BaseWxMaServiceImpl <?, ?>[] services = {new WxMaServiceImpl (), new WxMaServiceHttpComponentsImpl (),
@@ -58,15 +76,22 @@ public void allHttpClientsSendEncodedValuesAndPreserveSession() throws Exception
5876 config .setApiHostUrl ("http://127.0.0.1:" + server .getAddress ().getPort ());
5977 service .setWxMaConfig (config );
6078 try {
79+ config .expireAccessToken ();
80+ assertEquals (service .getWithoutToken ("https://api.weixin.qq.com/custom" , "value=a%2Fb" ), "{\" result\" :\" GET\" }" );
81+ assertEquals (decode (query .get ()).get ("value" ), "a/b" );
82+ assertFalse (query .get ().contains ("access_token=" ));
83+ assertEquals (service .postWithoutToken ("https://api.weixin.qq.com/custom?value=1" , "payload" ),
84+ "{\" result\" :\" POST:payload\" }" );
85+ assertEquals (query .get (), "value=1" );
6186 response .set (RESULT );
6287 for (String code : new String []{"NORMAL_CODE" , "a b\n " , "a+&=#?%2F" , "中文\uD83D \uDE00 " , "" }) {
6388 assertEquals (service .jsCode2SessionInfo (code ).getSessionKey (), SESSION );
6489 Map <String , String > params = decode (query .get ());
65- assertEquals (params .size (), 5 );
90+ assertEquals (params .size (), 4 );
6691 assertEquals (params .get ("js_code" ), code );
6792 assertEquals (params .get ("secret" ), config .getSecret ());
6893 assertEquals (params .get ("appid" ), config .getAppid ());
69- assertEquals (params .get ("access_token" ), TOKEN );
94+ assertNull (params .get ("access_token" ));
7095 }
7196 response .set ("{\" errcode\" :40029,\" errmsg\" :\" " + SECRET + TOKEN + "\" }" );
7297 try {
@@ -98,10 +123,10 @@ public void errorsAndRetryLogsDoNotContainCredentials() throws Exception {
98123 TestService service = service ();
99124 service .getWxMaConfig ().setApiHostUrl ("http://proxy.invalid" );
100125 when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp )))
101- .thenThrow (error (40001 )). thenThrow ( error ( -1 )).thenReturn (RESULT );
126+ .thenThrow (error (-1 )).thenReturn (RESULT );
102127 assertEquals (service .jsCode2SessionInfo ("FAKE_CODE" ).getSessionKey (), SESSION );
103- assertEquals (service .tokenRequests , 1 );
104- verify (service .executor , times (3 )).execute (startsWith ("http://proxy.invalid/" ), anyString (), eq (WxType .MiniApp ));
128+ assertEquals (service .tokenRequests , 0 );
129+ verify (service .executor , times (2 )).execute (startsWith ("http://proxy.invalid/" ), anyString (), eq (WxType .MiniApp ));
105130
106131 for (Exception failure : new Exception []{error (40029 ), new IOException (SECRET + TOKEN ),
107132 new IllegalArgumentException (SECRET + TOKEN ), new IllegalStateException (SECRET + TOKEN )}) {
@@ -123,13 +148,13 @@ public void errorsAndRetryLogsDoNotContainCredentials() throws Exception {
123148 }
124149
125150 @ Test
126- public void tokenAndParsingFailuresAreProtectedAndNullRemainsNullFailure () throws Exception {
151+ public void loginDoesNotRequireTokenAndParsingFailuresRemainProtected () throws Exception {
127152 try (LogCapture logs = new LogCapture ()) {
128153 TestService service = service ();
129154 service .getWxMaConfig ().expireAccessToken ();
130155 when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp ))).thenReturn (RESULT );
131156 assertEquals (service .jsCode2SessionInfo ("FAKE_CODE" ).getSessionKey (), SESSION );
132- assertEquals (service .tokenRequests , 1 );
157+ assertEquals (service .tokenRequests , 0 );
133158 when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp )))
134159 .thenReturn ("{\" session_key\" :\" " + SESSION + "\" ,\" expires_in\" :" );
135160 try {
@@ -146,16 +171,82 @@ public void tokenAndParsingFailuresAreProtectedAndNullRemainsNullFailure() throw
146171 }
147172 service .getWxMaConfig ().expireAccessToken ();
148173 service .failToken = true ;
149- try {
150- service .jsCode2SessionInfo ("FAKE_CODE" );
151- fail ("Expected token failure" );
152- } catch (WxRuntimeException e ) {
153- assertSafe (e );
154- }
174+ when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp ))).thenReturn (RESULT );
175+ assertEquals (service .jsCode2SessionInfo ("FAKE_CODE" ).getSessionKey (), SESSION );
176+ assertEquals (service .tokenRequests , 0 );
155177 logs .assertSafe ();
156178 }
157179 }
158180
181+ @ Test
182+ public void tokenFreeExecutionRetriesBusyButDoesNotRefreshInvalidToken () throws Exception {
183+ TestService service = service ();
184+ service .getWxMaConfig ().setApiHostUrl ("http://proxy.invalid" );
185+ when (service .executor .execute (anyString (), eq ("payload" ), eq (WxType .MiniApp )))
186+ .thenThrow (error (-1 )).thenReturn ("ok" );
187+ assertEquals (service .executeWithoutToken (service .executor ,
188+ "https://api.weixin.qq.com/custom" , "payload" ), "ok" );
189+ verify (service .executor , times (2 )).execute ("http://proxy.invalid/custom" , "payload" , WxType .MiniApp );
190+ assertEquals (service .tokenRequests , 0 );
191+
192+ reset (service .executor );
193+ when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp ))).thenThrow (error (40001 ));
194+ try {
195+ service .executeWithoutToken (service .executor , "https://api.weixin.qq.com/custom" , "payload" );
196+ fail ("Expected invalid token error" );
197+ } catch (WxErrorException e ) {
198+ assertEquals (e .getError ().getErrorCode (), 40001 );
199+ }
200+ verify (service .executor ).execute (anyString (), anyString (), eq (WxType .MiniApp ));
201+ assertEquals (service .getWxMaConfig ().getAccessToken (), TOKEN );
202+ assertFalse (service .getWxMaConfig ().isAccessTokenExpired ());
203+ assertEquals (service .tokenRequests , 0 );
204+ }
205+
206+ @ Test
207+ public void tokenFreeExecutionStopsAtRetryLimit () throws Exception {
208+ TestService service = service ();
209+ service .setMaxRetryTimes (2 );
210+ when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp ))).thenThrow (error (-1 ));
211+ try {
212+ service .executeWithoutToken (service .executor , "https://api.weixin.qq.com/custom" , "payload" );
213+ fail ("Expected exhausted retries" );
214+ } catch (WxErrorException e ) {
215+ assertEquals (e .getError ().getErrorCode (), -1 );
216+ }
217+ verify (service .executor , times (3 )).execute (anyString (), anyString (), eq (WxType .MiniApp ));
218+ assertEquals (service .tokenRequests , 0 );
219+ }
220+
221+ @ Test
222+ public void customServiceDefaultExecutionPreservesTransportContract () throws Exception {
223+ cn .binarywang .wx .miniapp .api .WxMaService service =
224+ mock (cn .binarywang .wx .miniapp .api .WxMaService .class , CALLS_REAL_METHODS );
225+ RequestExecutor <String , String > executor = mock (RequestExecutor .class );
226+ when (executor .execute ("https://example.invalid/custom" , "payload" , WxType .MiniApp )).thenReturn ("ok" );
227+ assertEquals (service .executeWithoutToken (executor , "https://example.invalid/custom" , "payload" ), "ok" );
228+ IOException failure = new IOException ("transport failure" );
229+ when (executor .execute (anyString (), anyString (), eq (WxType .MiniApp ))).thenThrow (failure );
230+ try {
231+ service .executeWithoutToken (executor , "https://example.invalid/custom" , "payload" );
232+ fail ("Expected transport failure" );
233+ } catch (WxRuntimeException e ) {
234+ assertSame (e .getCause (), failure );
235+ }
236+ verify (service , never ()).getAccessToken (anyBoolean ());
237+ }
238+
239+ @ Test
240+ public void ordinaryExecutionStillAddsAndRefreshesToken () throws Exception {
241+ TestService service = service ();
242+ when (service .executor .execute (anyString (), anyString (), eq (WxType .MiniApp )))
243+ .thenThrow (error (40001 )).thenReturn ("ok" );
244+ assertEquals (service .get ("https://api.weixin.qq.com/custom" , "query=1" ), "ok" );
245+ verify (service .executor , times (2 )).execute (
246+ "https://api.weixin.qq.com/custom?access_token=" + TOKEN , "query=1" , WxType .MiniApp );
247+ assertEquals (service .tokenRequests , 1 );
248+ }
249+
159250 @ Test
160251 public void ordinaryGetKeepsSerializedQueryAndExceptionContract () throws Exception {
161252 TestService service = service ();
@@ -186,10 +277,10 @@ public void accountSwitchKeepsCredentialsWithTheirAccount() throws Exception {
186277 String uri = invocation .getArgument (0 );
187278 if ("SECOND_APP_ID" .equals (params .get ("appid" ))) {
188279 assertEquals (params .get ("secret" ), "SECOND_SECRET" );
189- assertTrue (uri .endsWith ("access_token=SECOND_TOKEN " ));
280+ assertFalse (uri .contains ("access_token=" ));
190281 } else {
191282 assertEquals (params .get ("secret" ), SECRET );
192- assertTrue (uri .endsWith ("access_token=" + TOKEN ));
283+ assertFalse (uri .contains ("access_token=" ));
193284 }
194285 return RESULT ;
195286 });
@@ -285,6 +376,11 @@ public String get(String url, String query) throws WxErrorException {
285376 return (String ) super .execute (executor , url , query );
286377 }
287378
379+ @ Override
380+ public String getWithoutToken (String url , String query ) throws WxErrorException {
381+ return (String ) super .executeWithoutToken (executor , url , query );
382+ }
383+
288384 @ Override
289385 protected String doGetAccessTokenRequest () throws IOException {
290386 tokenRequests ++;
0 commit comments