Mholloway has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/334371 )
Change subject: Get user ID after logging in or creating an account ...................................................................... Get user ID after logging in or creating an account The MW API doesn't return user id on login or account creation success. We have to get it separately. Currently, we don't do this, and the user id in the app is always 0. It seems the numeric user ID is needed for EventLogging, so we'd better get it. For the login case, this adds another paramter to the request that's already made after login to retrieve group memberships, so that the user ID is also returned. For account creation, a simple new UserIdClient is added to make the follow-up request. TODO: Update the user ID with the new UserIdClient when the app language state changes, since a user's numeric ID varies by wiki. This will come in the next patch in this series. Bug: T149915 Change-Id: I3dc7871805edf51d2de6598dd5a5e67140f416b5 --- M app/src/main/java/org/wikipedia/createaccount/CreateAccountActivity.java M app/src/main/java/org/wikipedia/createaccount/CreateAccountClient.java M app/src/main/java/org/wikipedia/createaccount/CreateAccountSuccessResult.java D app/src/main/java/org/wikipedia/login/GroupMembershipClient.java M app/src/main/java/org/wikipedia/login/LoginClient.java M app/src/main/java/org/wikipedia/login/User.java A app/src/main/java/org/wikipedia/login/UserExtendedInfoClient.java A app/src/main/java/org/wikipedia/login/UserIdClient.java M app/src/main/java/org/wikipedia/useroption/dataclient/DefaultUserOptionDataClient.java M app/src/main/java/org/wikipedia/useroption/dataclient/UserInfo.java A app/src/test/java/org/wikipedia/login/UserIdClientTest.java A app/src/test/res/raw/user_info.json 12 files changed, 373 insertions(+), 178 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia refs/changes/71/334371/1 diff --git a/app/src/main/java/org/wikipedia/createaccount/CreateAccountActivity.java b/app/src/main/java/org/wikipedia/createaccount/CreateAccountActivity.java index f101268..a63c8d7 100644 --- a/app/src/main/java/org/wikipedia/createaccount/CreateAccountActivity.java +++ b/app/src/main/java/org/wikipedia/createaccount/CreateAccountActivity.java @@ -27,6 +27,8 @@ import org.wikipedia.captcha.CaptchaResult; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.mwapi.MwQueryResponse; +import org.wikipedia.login.UserIdClient; +import org.wikipedia.useroption.dataclient.UserInfo; import org.wikipedia.util.FeedbackUtil; import org.wikipedia.util.log.L; import org.wikipedia.views.NonEmptyValidator; @@ -262,21 +264,24 @@ captchaHandler.isActive() ? captchaHandler.captchaWord() : "null", new CreateAccountClient.Callback() { @Override - public void success(@NonNull Call<CreateAccountResponse> call, @NonNull CreateAccountSuccessResult result) { + public void success(@NonNull Call<CreateAccountResponse> call, + @NonNull final CreateAccountSuccessResult result) { if (!progressDialog.isShowing()) { // no longer attached to activity! return; } - createAccountResult = result; - progressDialog.dismiss(); - captchaHandler.cancelCaptcha(); - funnel.logSuccess(); - hideSoftKeyboard(CreateAccountActivity.this); - Intent resultIntent = new Intent(); - resultIntent.putExtra("username", result.getUsername()); - resultIntent.putExtra("password", passwordEdit.getText().toString()); - setResult(RESULT_ACCOUNT_CREATED, resultIntent); - finish(); + getUserId(new UserIdCallback() { + @Override + public void success(@NonNull Call<MwQueryResponse<UserInfo>> call, int userId) { + finishWithUserResult(result, userId); + } + + @Override + public void failure(@NonNull Call<MwQueryResponse<UserInfo>> call, @NonNull Throwable caught) { + finishWithUserResult(result, 0); + L.e("Failed to obtain new user ID", caught); + } + }); } @Override @@ -311,4 +316,41 @@ } super.onStop(); } + + // Get the new user ID, which the createaccount API doesn't provide to us on success :( + // TODO: See about getting this into the API success response so we dont' have to make another call. + private void getUserId(@NonNull final UserIdCallback cb) { + UserIdClient infoClient = new UserIdClient(); + infoClient.request(wiki, new UserIdClient.Callback() { + @Override + public void success(@NonNull Call<MwQueryResponse<UserInfo>> call, int userId) { + cb.success(call, userId); + } + + @Override + public void failure(@NonNull Call<MwQueryResponse<UserInfo>> call, @NonNull Throwable caught) { + cb.failure(call, caught); + } + }); + } + + private void finishWithUserResult(@NonNull CreateAccountSuccessResult result, int userId) { + Intent resultIntent = new Intent(); + resultIntent.putExtra("username", result.getUsername()); + resultIntent.putExtra("password", passwordEdit.getText().toString()); + resultIntent.putExtra("userId", userId); + setResult(RESULT_ACCOUNT_CREATED, resultIntent); + + createAccountResult = result; + progressDialog.dismiss(); + captchaHandler.cancelCaptcha(); + funnel.logSuccess(); + hideSoftKeyboard(CreateAccountActivity.this); + finish(); + } + + private interface UserIdCallback { + void success(@NonNull Call<MwQueryResponse<UserInfo>> call, int userId); + void failure(@NonNull Call<MwQueryResponse<UserInfo>> call, @NonNull Throwable caught); + } } diff --git a/app/src/main/java/org/wikipedia/createaccount/CreateAccountClient.java b/app/src/main/java/org/wikipedia/createaccount/CreateAccountClient.java index 45ede87..e673fce 100644 --- a/app/src/main/java/org/wikipedia/createaccount/CreateAccountClient.java +++ b/app/src/main/java/org/wikipedia/createaccount/CreateAccountClient.java @@ -49,8 +49,7 @@ if (response.isSuccessful()) { if (response.body().hasResult()) { CreateAccountResponse result = response.body(); - String status = result.status(); - if ("PASS".equals(status)) { + if ("PASS".equals(result.status())) { cb.success(call, new CreateAccountSuccessResult(result.user())); } else { cb.failure(call, new CreateAccountException(result.message())); diff --git a/app/src/main/java/org/wikipedia/createaccount/CreateAccountSuccessResult.java b/app/src/main/java/org/wikipedia/createaccount/CreateAccountSuccessResult.java index 61827a6..835b75b 100644 --- a/app/src/main/java/org/wikipedia/createaccount/CreateAccountSuccessResult.java +++ b/app/src/main/java/org/wikipedia/createaccount/CreateAccountSuccessResult.java @@ -2,16 +2,17 @@ import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; -public class CreateAccountSuccessResult extends CreateAccountResult implements Parcelable { - private final String username; +class CreateAccountSuccessResult extends CreateAccountResult implements Parcelable { + private String username; - public CreateAccountSuccessResult(String username) { + CreateAccountSuccessResult(@NonNull String username) { super("PASS", "Account created"); this.username = username; } - public String getUsername() { + String getUsername() { return username; } @@ -21,7 +22,7 @@ parcel.writeString(username); } - protected CreateAccountSuccessResult(Parcel in) { + private CreateAccountSuccessResult(Parcel in) { super(in); username = in.readString(); } diff --git a/app/src/main/java/org/wikipedia/login/GroupMembershipClient.java b/app/src/main/java/org/wikipedia/login/GroupMembershipClient.java deleted file mode 100644 index dcad9e4..0000000 --- a/app/src/main/java/org/wikipedia/login/GroupMembershipClient.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.wikipedia.login; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.google.gson.annotations.SerializedName; - -import org.wikipedia.dataclient.WikiSite; -import org.wikipedia.dataclient.mwapi.MwQueryResponse; -import org.wikipedia.dataclient.retrofit.MwCachedService; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; -import retrofit2.http.POST; -import retrofit2.http.Query; - -/** - * Retrofit DataClient to retrieve implicit group membership information for a specific user. - */ -class GroupMembershipClient { - @NonNull private final MwCachedService<Service> cachedService = new MwCachedService<>(Service.class); - - @Nullable private Call<MwQueryResponse<UserMemberships>> groupCall; - - interface GroupMembershipCallback { - void success(@NonNull Set<String> result); - void error(@NonNull Throwable caught); - } - - public void request(@NonNull final WikiSite wiki, @NonNull final String userName, - @NonNull final GroupMembershipCallback cb) { - cancel(); - - groupCall = cachedService.service(wiki).listUsers(userName); - groupCall.enqueue(new Callback<MwQueryResponse<UserMemberships>>() { - @Override - public void onResponse(Call<MwQueryResponse<UserMemberships>> call, - Response<MwQueryResponse<UserMemberships>> response) { - if (response.isSuccessful()) { - final MwQueryResponse<UserMemberships> body = response.body(); - final UserMemberships query = body.query(); - if (query != null) { - cb.success(query.getGroupsFor(userName)); - } else if (body.getError() != null) { - cb.error(new LoginClient.LoginFailedException( - "Failed to retrieve group membership data. " - + body.getError().toString())); - } else { - cb.error(new LoginClient.LoginFailedException( - "Unexpected error trying to retrieve group membership data. " - + body.toString())); - } - } else { - cb.error(new LoginClient.LoginFailedException(response.message())); - } - } - - @Override - public void onFailure(Call<MwQueryResponse<UserMemberships>> call, Throwable caught) { - cb.error(caught); - } - }); - } - - public void cancel() { - cancelTokenRequest(); - } - - private void cancelTokenRequest() { - if (groupCall == null) { - return; - } - groupCall.cancel(); - groupCall = null; - } - - private interface Service { - - /** Request the implicit groups a user belongs to. */ - @NonNull - @POST("w/api.php?format=json&formatversion=2&action=query" - + "&list=users&usprop=implicitgroups") - Call<MwQueryResponse<UserMemberships>> listUsers( - @Query("ususers") @NonNull String userName); - } - - private static final class UserMemberships { - @SuppressWarnings("MismatchedReadAndWriteOfArray") @SerializedName("users") @NonNull - private List<ListUsersResponse> users = Collections.emptyList(); - - @NonNull Set<String> getGroupsFor(@NonNull String userName) { - if (!users.isEmpty()) { - for (ListUsersResponse user : users) { - final Set<String> groups = user.getGroupsFor(userName); - if (groups != null) { - return groups; - } - } - } - return Collections.emptySet(); - } - - private static final class ListUsersResponse { - @SerializedName("name") @Nullable private String name; - - @SerializedName("implicitgroups") @Nullable private String[] implicitGroups; - - @Nullable Set<String> getGroupsFor(@NonNull String userName) { - if (userName.equals(name) && implicitGroups != null) { - Set<String> groups = new HashSet<>(); - groups.addAll(Arrays.asList(implicitGroups)); - return Collections.unmodifiableSet(groups); - } else { - return null; - } - } - } - } -} diff --git a/app/src/main/java/org/wikipedia/login/LoginClient.java b/app/src/main/java/org/wikipedia/login/LoginClient.java index 6909692..46d09f9 100644 --- a/app/src/main/java/org/wikipedia/login/LoginClient.java +++ b/app/src/main/java/org/wikipedia/login/LoginClient.java @@ -95,7 +95,7 @@ // The server could do some transformations on user names, e.g. on some // wikis is uppercases the first letter. String actualUserName = loginResult.getUser().getUsername(); - getGroupMemberships(wiki, actualUserName, loginResult, cb); + getExtendedInfo(wiki, actualUserName, loginResult, cb); } else if ("UI".equals(loginResult.getStatus())) { //TODO: Don't just assume this is a 2FA UI result cb.twoFactorPrompt(new LoginFailedException(loginResult.getMessage()), loginToken); @@ -118,20 +118,21 @@ }); } - private void getGroupMemberships(@NonNull WikiSite wiki, @NonNull String userName, - @NonNull final LoginResult loginResult, - @NonNull final LoginCallback cb) { - GroupMembershipClient groupClient = new GroupMembershipClient(); - groupClient.request(wiki, userName, new GroupMembershipClient.GroupMembershipCallback() { + private void getExtendedInfo(@NonNull WikiSite wiki, @NonNull String userName, + @NonNull final LoginResult loginResult, @NonNull final LoginCallback cb) { + UserExtendedInfoClient infoClient = new UserExtendedInfoClient(); + infoClient.request(wiki, userName, new UserExtendedInfoClient.GroupMembershipCallback() { @Override - public void success(@NonNull Set<String> groups) { + public void success(@NonNull Call<MwQueryResponse<UserExtendedInfoClient.QueryResult>> call, + int id, @NonNull Set<String> groups) { final User user = loginResult.getUser(); - User.setUser(new User(user, groups)); + User.setUser(new User(user, id, groups)); cb.success(loginResult); } @Override - public void error(@NonNull Throwable caught) { + public void error(@NonNull Call<MwQueryResponse<UserExtendedInfoClient.QueryResult>> call, + @NonNull Throwable caught) { L.e("Login suceeded but getting group information failed. " + caught); cb.error(caught); } @@ -222,7 +223,7 @@ User user = null; String userMessage = null; if ("PASS".equals(status)) { - user = new User(userName, password, 0); + user = new User(userName, password); } else if ("FAIL".equals(status)) { userMessage = message; } else if ("UI".equals(status)) { diff --git a/app/src/main/java/org/wikipedia/login/User.java b/app/src/main/java/org/wikipedia/login/User.java index b379c60..bbfa036 100644 --- a/app/src/main/java/org/wikipedia/login/User.java +++ b/app/src/main/java/org/wikipedia/login/User.java @@ -51,12 +51,12 @@ private final int userID; @NonNull private final Set<String> groups; - public User(@NonNull String username, @NonNull String password, int userID) { - this(username, password, userID, null); + public User(@NonNull String username, @NonNull String password) { + this(username, password, 0, null); } - public User(@NonNull User other, @Nullable Set<String> groups) { - this(other.username, other.password, other.userID, groups); + public User(@NonNull User other, int id, @Nullable Set<String> groups) { + this(other.username, other.password, id, groups); } public User(@NonNull String username, @NonNull String password, int userID, diff --git a/app/src/main/java/org/wikipedia/login/UserExtendedInfoClient.java b/app/src/main/java/org/wikipedia/login/UserExtendedInfoClient.java new file mode 100644 index 0000000..848c976 --- /dev/null +++ b/app/src/main/java/org/wikipedia/login/UserExtendedInfoClient.java @@ -0,0 +1,129 @@ +package org.wikipedia.login; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.gson.annotations.SerializedName; + +import org.wikipedia.dataclient.WikiSite; +import org.wikipedia.dataclient.mwapi.MwQueryResponse; +import org.wikipedia.dataclient.retrofit.MwCachedService; +import org.wikipedia.useroption.dataclient.UserInfo; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import retrofit2.http.POST; +import retrofit2.http.Query; + +/** + * Retrofit DataClient to retrieve implicit user info and group membership information for a specific user. + */ +class UserExtendedInfoClient { + @NonNull private final MwCachedService<Service> cachedService = new MwCachedService<>(Service.class); + + @Nullable private Call<MwQueryResponse<QueryResult>> groupCall; + + interface GroupMembershipCallback { + void success(@NonNull Call<MwQueryResponse<QueryResult>> call, int id, @NonNull Set<String> groups); + void error(@NonNull Call<MwQueryResponse<QueryResult>> call, @NonNull Throwable caught); + } + + public void request(@NonNull final WikiSite wiki, @NonNull final String userName, + @NonNull final GroupMembershipCallback cb) { + cancel(); + + groupCall = cachedService.service(wiki).request(userName); + groupCall.enqueue(new Callback<MwQueryResponse<QueryResult>>() { + @Override + public void onResponse(Call<MwQueryResponse<QueryResult>> call, + Response<MwQueryResponse<QueryResult>> response) { + if (response.isSuccessful()) { + final MwQueryResponse<QueryResult> body = response.body(); + final QueryResult query = body.query(); + if (response.body().success()) { + cb.success(call, query.id(), query.getGroupsFor(userName)); + } else if (response.body().hasError()) { + cb.error(call, new LoginClient.LoginFailedException( + "Failed to retrieve group membership data. " + body.getError().toString())); + } else { + cb.error(call, new LoginClient.LoginFailedException( + "Unexpected error trying to retrieve group membership data. " + body.toString())); + } + } else { + cb.error(call, new LoginClient.LoginFailedException(response.message())); + } + } + + @Override + public void onFailure(Call<MwQueryResponse<QueryResult>> call, Throwable caught) { + cb.error(call, caught); + } + }); + } + + public void cancel() { + cancelTokenRequest(); + } + + private void cancelTokenRequest() { + if (groupCall == null) { + return; + } + groupCall.cancel(); + groupCall = null; + } + + private interface Service { + + /** Request the implicit groups a user belongs to. */ + @NonNull + @POST("w/api.php?action=query&format=json&formatversion=2&meta=userinfo&list=users&usprop=implicitgroups") + Call<MwQueryResponse<QueryResult>> request(@Query("ususers") @NonNull String userName); + } + + static final class QueryResult { + @SuppressWarnings("MismatchedReadAndWriteOfArray") @SerializedName("users") @NonNull + private List<ListUsersResponse> users = Collections.emptyList(); + + @SuppressWarnings("unused") @SerializedName("userinfo") private UserInfo userInfo; + + int id() { + return userInfo.id(); + } + + @NonNull Set<String> getGroupsFor(@NonNull String userName) { + if (!users.isEmpty()) { + for (ListUsersResponse user : users) { + final Set<String> groups = user.getGroupsFor(userName); + if (groups != null) { + return groups; + } + } + } + return Collections.emptySet(); + } + + private static final class ListUsersResponse { + @SerializedName("name") @Nullable private String name; + + @SerializedName("implicitgroups") @Nullable private String[] implicitGroups; + + @Nullable Set<String> getGroupsFor(@NonNull String userName) { + if (userName.equals(name) && implicitGroups != null) { + Set<String> groups = new HashSet<>(); + groups.addAll(Arrays.asList(implicitGroups)); + return Collections.unmodifiableSet(groups); + } else { + return null; + } + } + } + } +} diff --git a/app/src/main/java/org/wikipedia/login/UserIdClient.java b/app/src/main/java/org/wikipedia/login/UserIdClient.java new file mode 100644 index 0000000..7c3b3d0 --- /dev/null +++ b/app/src/main/java/org/wikipedia/login/UserIdClient.java @@ -0,0 +1,61 @@ +package org.wikipedia.login; + +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; + +import org.wikipedia.dataclient.WikiSite; +import org.wikipedia.dataclient.mwapi.MwApiException; +import org.wikipedia.dataclient.mwapi.MwQueryResponse; +import org.wikipedia.dataclient.retrofit.MwCachedService; +import org.wikipedia.dataclient.retrofit.RetrofitException; +import org.wikipedia.useroption.dataclient.UserInfo; + +import java.io.IOException; + +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.http.GET; + +public class UserIdClient { + @NonNull private final MwCachedService<Service> cachedService = new MwCachedService<>(Service.class); + + public interface Callback { + void success(@NonNull Call<MwQueryResponse<UserInfo>> call, int userId); + void failure(@NonNull Call<MwQueryResponse<UserInfo>> call, @NonNull Throwable caught); + } + + public Call<MwQueryResponse<UserInfo>> request(@NonNull WikiSite wiki, @NonNull Callback cb) { + return request(cachedService.service(wiki), cb); + } + + public Call<MwQueryResponse<UserInfo>> request(@NonNull Service service, @NonNull final Callback cb) { + Call<MwQueryResponse<UserInfo>> call = service.request(); + call.enqueue(new retrofit2.Callback<MwQueryResponse<UserInfo>>() { + @Override + public void onResponse(Call<MwQueryResponse<UserInfo>> call, Response<MwQueryResponse<UserInfo>> response) { + if (response.isSuccessful()) { + if (response.body().success()) { + cb.success(call, response.body().query().id()); + } else if (response.body().hasError()) { + cb.failure(call, new MwApiException(response.body().getError())); + } else { + cb.failure(call, new IOException("An unknown error occurred.")); + } + } else { + cb.failure(call, RetrofitException.httpError(response, cachedService.retrofit())); + } + } + + @Override + public void onFailure(Call<MwQueryResponse<UserInfo>> call, Throwable caught) { + cb.failure(call, caught); + } + }); + return call; + } + + @VisibleForTesting interface Service { + @GET("w/api.php?action=query&format=json&formatversion=2&meta=userinfo") + @NonNull Call<MwQueryResponse<UserInfo>> request(); + } +} diff --git a/app/src/main/java/org/wikipedia/useroption/dataclient/DefaultUserOptionDataClient.java b/app/src/main/java/org/wikipedia/useroption/dataclient/DefaultUserOptionDataClient.java index 94145d1..cd43db0 100644 --- a/app/src/main/java/org/wikipedia/useroption/dataclient/DefaultUserOptionDataClient.java +++ b/app/src/main/java/org/wikipedia/useroption/dataclient/DefaultUserOptionDataClient.java @@ -118,7 +118,7 @@ } private static class PostResponse extends MwPostResponse { - private String options; + @SuppressWarnings("unused") private String options; public String result() { return options; @@ -136,10 +136,9 @@ } private static class QueryUserInfo { - @SerializedName("userinfo") - private UserInfo userInfo; + @SuppressWarnings("unused") @SerializedName("userinfo") private UserInfo userInfo; - public UserInfo userInfo() { + UserInfo userInfo() { return userInfo; } } diff --git a/app/src/main/java/org/wikipedia/useroption/dataclient/UserInfo.java b/app/src/main/java/org/wikipedia/useroption/dataclient/UserInfo.java index 9e01b25..7ea9748 100644 --- a/app/src/main/java/org/wikipedia/useroption/dataclient/UserInfo.java +++ b/app/src/main/java/org/wikipedia/useroption/dataclient/UserInfo.java @@ -1,6 +1,7 @@ package org.wikipedia.useroption.dataclient; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.google.gson.annotations.SerializedName; @@ -11,36 +12,31 @@ import java.util.Map; public class UserInfo { - @SerializedName("name") - private String username; - private int id; + @SuppressWarnings("unused") @SerializedName("userinfo") private UserInfo userInfo; + @SuppressWarnings("unused") @SerializedName("name") private String username; + @SuppressWarnings("unused") private int id; // Object type is any JSON type. - @NonNull private Map<String, ?> options; + @SuppressWarnings("unused") @Nullable private Map<String, ?> options; public int id() { return id; } - @NonNull - public String username() { - return username; - } - - @NonNull - public Collection<UserOption> userjsOptions() { + @NonNull public Collection<UserOption> userjsOptions() { Collection<UserOption> ret = new ArrayList<>(); - for (Map.Entry<String, ?> entry : options.entrySet()) { - if (entry.getKey().startsWith("userjs-")) { - ret.add(new UserOption(entry.getKey(), (String) entry.getValue())); + if (options != null) { + for (Map.Entry<String, ?> entry : options.entrySet()) { + if (entry.getKey().startsWith("userjs-")) { + ret.add(new UserOption(entry.getKey(), (String) entry.getValue())); + } } } return ret; } // Auto-generated - @Override - public String toString() { + @Override public String toString() { return "UserInfo{" + "username='" + username + '\'' + ", id=" + id diff --git a/app/src/test/java/org/wikipedia/login/UserIdClientTest.java b/app/src/test/java/org/wikipedia/login/UserIdClientTest.java new file mode 100644 index 0000000..b304981 --- /dev/null +++ b/app/src/test/java/org/wikipedia/login/UserIdClientTest.java @@ -0,0 +1,84 @@ +package org.wikipedia.login; + +import android.support.annotation.NonNull; + +import com.google.gson.stream.MalformedJsonException; + +import org.junit.Test; +import org.wikipedia.dataclient.mwapi.MwApiException; +import org.wikipedia.dataclient.mwapi.MwQueryResponse; +import org.wikipedia.dataclient.retrofit.RetrofitException; +import org.wikipedia.test.MockWebServerTest; +import org.wikipedia.useroption.dataclient.UserInfo; + +import retrofit2.Call; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class UserIdClientTest extends MockWebServerTest { + private UserIdClient subject = new UserIdClient(); + + @Test public void testRequestSuccess() throws Throwable { + enqueueFromFile("user_info.json"); + + UserIdClient.Callback cb = mock(UserIdClient.Callback.class); + Call<MwQueryResponse<UserInfo>> call = request(cb); + + server().takeRequest(); + assertCallbackSuccess(call, cb); + } + + @Test public void testRequestResponseApiError() throws Throwable { + enqueueFromFile("api_error.json"); + + UserIdClient.Callback cb = mock(UserIdClient.Callback.class); + Call<MwQueryResponse<UserInfo>> call = request(cb); + + server().takeRequest(); + assertCallbackFailure(call, cb, MwApiException.class); + } + + @Test public void testRequestResponse404() throws Throwable { + enqueue404(); + + UserIdClient.Callback cb = mock(UserIdClient.Callback.class); + Call<MwQueryResponse<UserInfo>> call = request(cb); + + server().takeRequest(); + assertCallbackFailure(call, cb, RetrofitException.class); + } + + @Test public void testRequestResponseMalformed() throws Throwable { + server().enqueue("┏━┓ ︵ /(^.^/)"); + + UserIdClient.Callback cb = mock(UserIdClient.Callback.class); + Call<MwQueryResponse<UserInfo>> call = request(cb); + + server().takeRequest(); + assertCallbackFailure(call, cb, MalformedJsonException.class); + } + + private void assertCallbackSuccess(@NonNull Call<MwQueryResponse<UserInfo>> call, + @NonNull UserIdClient.Callback cb) { + verify(cb).success(eq(call), any(Integer.class)); + //noinspection unchecked + verify(cb, never()).failure(any(Call.class), any(Throwable.class)); + } + + private void assertCallbackFailure(@NonNull Call<MwQueryResponse<UserInfo>> call, + @NonNull UserIdClient.Callback cb, + @NonNull Class<? extends Throwable> throwable) { + //noinspection unchecked + verify(cb, never()).success(any(Call.class), any(Integer.class)); + verify(cb).failure(eq(call), isA(throwable)); + } + + private Call<MwQueryResponse<UserInfo>> request(@NonNull UserIdClient.Callback cb) { + return subject.request(service(UserIdClient.Service.class), cb); + } +} diff --git a/app/src/test/res/raw/user_info.json b/app/src/test/res/raw/user_info.json new file mode 100644 index 0000000..afe22cc --- /dev/null +++ b/app/src/test/res/raw/user_info.json @@ -0,0 +1,9 @@ +{ + "batchcomplete": true, + "query": { + "userinfo": { + "id": 24531888, + "name": "MHolloway (WMF)" + } + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/334371 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I3dc7871805edf51d2de6598dd5a5e67140f416b5 Gerrit-PatchSet: 1 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Mholloway <mhollo...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits