start testing api call handling

This commit is contained in:
Phillip Thelen 2016-06-06 18:45:09 +02:00
parent 7c89c448a9
commit e78fea7375
7 changed files with 188 additions and 59 deletions

View file

@ -1,18 +1,66 @@
package com.habitrpg.android.habitica;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.amplitude.api.Amplitude;
import com.crashlytics.android.Crashlytics;
import com.habitrpg.android.habitica.database.CheckListItemExcludeStrategy;
import com.magicmicky.habitrpgwrapper.lib.api.ApiService;
import com.magicmicky.habitrpgwrapper.lib.api.Server;
import com.magicmicky.habitrpgwrapper.lib.models.ChatMessage;
import com.magicmicky.habitrpgwrapper.lib.models.ContentResult;
import com.magicmicky.habitrpgwrapper.lib.models.Customization;
import com.magicmicky.habitrpgwrapper.lib.models.FAQArticle;
import com.magicmicky.habitrpgwrapper.lib.models.Group;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationRequest;
import com.magicmicky.habitrpgwrapper.lib.models.PurchaseValidationResult;
import com.magicmicky.habitrpgwrapper.lib.models.Purchases;
import com.magicmicky.habitrpgwrapper.lib.models.Skill;
import com.magicmicky.habitrpgwrapper.lib.models.TutorialStep;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuth;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthResponse;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthSocial;
import com.magicmicky.habitrpgwrapper.lib.models.UserAuthSocialTokens;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.Egg;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.Food;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.HatchingPotion;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.Mount;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.Pet;
import com.magicmicky.habitrpgwrapper.lib.models.inventory.QuestContent;
import com.magicmicky.habitrpgwrapper.lib.models.responses.FeedResponse;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskList;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
import com.magicmicky.habitrpgwrapper.lib.utils.BooleanAsIntAdapter;
import com.magicmicky.habitrpgwrapper.lib.utils.ChatMessageDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.ChecklistItemSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.ContentDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.CustomizationDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.DateDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.EggListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.FAQArticleListDeserilializer;
import com.magicmicky.habitrpgwrapper.lib.utils.FeedResponseDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.FoodListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.GroupSerialization;
import com.magicmicky.habitrpgwrapper.lib.utils.HatchingPotionListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.ItemDataListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.MountListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PetListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PurchasedDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.QuestListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.SkillDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskTagDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TutorialStepListDeserializer;
import com.raizlabs.android.dbflow.structure.ModelAdapter;
import org.json.JSONException;
import org.json.JSONObject;
@ -22,10 +70,13 @@ import android.support.v7.app.AlertDialog;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -133,6 +184,62 @@ public class APIHelper implements Action1<Throwable> {
this.apiService = retrofitAdapter.create(ApiService.class);
}
public static GsonConverterFactory createGsonFactory() {
Type taskTagClassListType = new TypeToken<List<TaskTag>>() {}.getType();
Type skillListType = new TypeToken<List<Skill>>() {}.getType();
Type customizationListType = new TypeToken<List<Customization>>() {}.getType();
Type tutorialStepListType = new TypeToken<List<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<List<FAQArticle>>() {}.getType();
Type itemDataListType = new TypeToken<List<ItemData>>() {}.getType();
Type eggListType = new TypeToken<List<Egg>>() {}.getType();
Type foodListType = new TypeToken<List<Food>>() {}.getType();
Type hatchingPotionListType = new TypeToken<List<HatchingPotion>>() {}.getType();
Type questContentListType = new TypeToken<List<QuestContent>>() {}.getType();
Type petListType = new TypeToken<HashMap<String, Pet>>() {}.getType();
Type mountListType = new TypeToken<HashMap<String, Mount>>() {}.getType();
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
.setExclusionStrategies(new CheckListItemExcludeStrategy())
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredClass().equals(ModelAdapter.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(taskTagClassListType, new TaskTagDeserializer())
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(skillListType, new SkillDeserializer())
.registerTypeAdapter(ChecklistItem.class, new ChecklistItemSerializer())
.registerTypeAdapter(TaskList.class, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())
.registerTypeAdapter(tutorialStepListType, new TutorialStepListDeserializer())
.registerTypeAdapter(faqArticleListType, new FAQArticleListDeserilializer())
.registerTypeAdapter(Group.class, new GroupSerialization())
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(itemDataListType, new ItemDataListDeserializer())
.registerTypeAdapter(eggListType, new EggListDeserializer())
.registerTypeAdapter(foodListType, new FoodListDeserializer())
.registerTypeAdapter(hatchingPotionListType, new HatchingPotionListDeserializer())
.registerTypeAdapter(questContentListType, new QuestListDeserializer())
.registerTypeAdapter(petListType, new PetListDeserializer())
.registerTypeAdapter(mountListType, new MountListDeserializer())
.registerTypeAdapter(ChatMessage.class, new ChatMessageDeserializer())
.registerTypeAdapter(Task.class, new TaskSerializer())
.registerTypeAdapter(ContentResult.class, new ContentDeserializer())
.registerTypeAdapter(FeedResponse.class, new FeedResponseDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);
}
public Observable<UserAuthResponse> registerUser(String username, String email, String password, String confirmPassword) {
UserAuth auth = new UserAuth();
auth.setUsername(username);
@ -218,7 +325,7 @@ public class APIHelper implements Action1<Throwable> {
return habitRPGUser;
});
}
return userObservable.compose(configureApiCallObserver());
return userObservable;
}
private List<Task> sortTasks(Map<String, Task> taskMap, List<String> taskOrder){

View file

@ -81,59 +81,7 @@ public class ApiModule {
@Provides
public GsonConverterFactory providesGsonConverterFactory() {
Type taskTagClassListType = new TypeToken<List<TaskTag>>() {}.getType();
Type skillListType = new TypeToken<List<Skill>>() {}.getType();
Type customizationListType = new TypeToken<List<Customization>>() {}.getType();
Type tutorialStepListType = new TypeToken<List<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<List<FAQArticle>>() {}.getType();
Type itemDataListType = new TypeToken<List<ItemData>>() {}.getType();
Type eggListType = new TypeToken<List<Egg>>() {}.getType();
Type foodListType = new TypeToken<List<Food>>() {}.getType();
Type hatchingPotionListType = new TypeToken<List<HatchingPotion>>() {}.getType();
Type questContentListType = new TypeToken<List<QuestContent>>() {}.getType();
Type petListType = new TypeToken<HashMap<String, Pet>>() {}.getType();
Type mountListType = new TypeToken<HashMap<String, Mount>>() {}.getType();
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
.setExclusionStrategies(new CheckListItemExcludeStrategy())
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredClass().equals(ModelAdapter.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(taskTagClassListType, new TaskTagDeserializer())
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(skillListType, new SkillDeserializer())
.registerTypeAdapter(ChecklistItem.class, new ChecklistItemSerializer())
.registerTypeAdapter(TaskList.class, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())
.registerTypeAdapter(tutorialStepListType, new TutorialStepListDeserializer())
.registerTypeAdapter(faqArticleListType, new FAQArticleListDeserilializer())
.registerTypeAdapter(Group.class, new GroupSerialization())
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(itemDataListType, new ItemDataListDeserializer())
.registerTypeAdapter(eggListType, new EggListDeserializer())
.registerTypeAdapter(foodListType, new FoodListDeserializer())
.registerTypeAdapter(hatchingPotionListType, new HatchingPotionListDeserializer())
.registerTypeAdapter(questContentListType, new QuestListDeserializer())
.registerTypeAdapter(petListType, new PetListDeserializer())
.registerTypeAdapter(mountListType, new MountListDeserializer())
.registerTypeAdapter(ChatMessage.class, new ChatMessageDeserializer())
.registerTypeAdapter(Task.class, new TaskSerializer())
.registerTypeAdapter(ContentResult.class, new ContentDeserializer())
.registerTypeAdapter(FeedResponse.class, new FeedResponseDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);
return APIHelper.createGsonFactory();
}
@Provides

View file

@ -232,6 +232,7 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
if (this.lastSync == null || (new Date().getTime() - this.lastSync.getTime()) > 180000) {
if (this.apiHelper != null) {
this.apiHelper.retrieveUser(true)
.compose(apiHelper.configureApiCallObserver())
.subscribe(new HabitRPGUserCallback(this), throwable -> {});
this.checkMaintenance();
}
@ -672,6 +673,7 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
if (resultCode == SELECT_CLASS_RESULT) {
if (this.apiHelper != null) {
this.apiHelper.retrieveUser(true)
.compose(apiHelper.configureApiCallObserver())
.subscribe(new HabitRPGUserCallback(this), throwable -> {});
}
}

View file

@ -196,6 +196,7 @@ public class TasksFragment extends BaseMainFragment implements OnCheckedChangeLi
if (apiHelper != null) {
apiHelper.retrieveUser(true)
.compose(apiHelper.configureApiCallObserver())
.subscribe(
new HabitRPGUserCallback(activity),
throwable -> stopAnimatingRefreshItem()

View file

@ -30,8 +30,13 @@ public class PurchasedDeserializer implements JsonDeserializer<Purchases> {
List<Customization> customizations = new ArrayList<Customization>();
Purchases purchases = new Purchases();
List<Customization> existingCustomizations = new Select().from(Customization.class).queryList();
List<Customization> existingCustomizations;
try {
existingCustomizations = new Select().from(Customization.class).queryList();
} catch (RuntimeException e) {
//Tests don't have a database
existingCustomizations = new ArrayList<>();
}
for (Customization customization : existingCustomizations) {
if(object.has(customization.getType())) {
JsonObject nestedObject = object.get(customization.getType()).getAsJsonObject();

View file

@ -17,9 +17,15 @@ public class TaskTagDeserializer implements JsonDeserializer<List<TaskTag>> {
@Override
public List<TaskTag> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<TaskTag> taskTags = new ArrayList<>();
List<Tag> allTags = new Select()
.from(Tag.class)
.queryList();
List<Tag> allTags;
try {
allTags = new Select()
.from(Tag.class)
.queryList();
} catch (RuntimeException e) {
//Tests don't have a database
allTags = new ArrayList<>();
}
for (JsonElement tagElement : json.getAsJsonArray()) {
String tagId = tagElement.getAsString();

View file

@ -0,0 +1,60 @@
package com.habitrpg.android.habitica;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import android.os.Build;
import java.util.List;
import rx.observers.TestSubscriber;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
public class APIHelperTests {
private APIHelper apiHelper;
@Before
public void setUp() {
HostConfig hostConfig = new HostConfig(BuildConfig.BASE_URL,
BuildConfig.PORT,
BuildConfig.TEST_USER_KEY,
BuildConfig.TEST_USER_ID);
apiHelper = new APIHelper(APIHelper.createGsonFactory(), hostConfig);
}
@Test
public void shouldLoadUserFromServer() {
TestSubscriber<HabitRPGUser> testSubscriber = new TestSubscriber<>();
apiHelper.apiService.getUser().subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertCompleted();
testSubscriber.assertValueCount(1);
}
@Test
public void shouldLoadTasksFromServer() {
TestSubscriber<TaskList> testSubscriber = new TestSubscriber<>();
apiHelper.apiService.getTasks().subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertCompleted();
}
@Test
public void shouldLoadCompleteUserFromServer() {
TestSubscriber<HabitRPGUser> testSubscriber = new TestSubscriber<>();
apiHelper.retrieveUser(true).subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertCompleted();
testSubscriber.assertValueCount(1);
}
}