From a197179a1b78850e1273943611487b331b546af4 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 9 May 2014 01:09:51 +0200 Subject: Add ValidatingJsonDeserializerTest (JUnit) --- test/data/ValidatingJsonDeserializerTest.java | 438 ++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 test/data/ValidatingJsonDeserializerTest.java (limited to 'test/data/ValidatingJsonDeserializerTest.java') diff --git a/test/data/ValidatingJsonDeserializerTest.java b/test/data/ValidatingJsonDeserializerTest.java new file mode 100644 index 0000000..ef19cd3 --- /dev/null +++ b/test/data/ValidatingJsonDeserializerTest.java @@ -0,0 +1,438 @@ +package data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Tests whether the ValidatingJsonDeserializer class properly rejects + * incomplete or malformed Tweet or User objects. + * + * @author Peter Wu + */ +public class ValidatingJsonDeserializerTest { + + private final static String exampleTweet = "{" + + " \"retweeted\": false," + + " \"in_reply_to_screen_name\": null," + + " \"possibly_sensitive\": false," + + " \"truncated\": false," + + " \"lang\": \"en\"," + + " \"in_reply_to_status_id_str\": null," + + " \"id\": 461441338630619136," + + " \"in_reply_to_user_id_str\": null," + + " \"in_reply_to_status_id\": null," + + " \"created_at\": \"Wed Apr 30 09:46:02 +0000 2014\"," + + " \"favorite_count\": 0," + + " \"place\": null," + + " \"coordinates\": null," + + " \"metadata\": {" + + " \"result_type\": \"recent\"," + + " \"iso_language_code\": \"en\"" + + " }," + + " \"text\": \"RT @verge: Samsung's Galaxy K zoom is a high-end smartphone and an incredible selfie machine http://t.co/I5WmnHV9HM http://t.co/V8Pm2bxzcH\"," + + " \"contributors\": null," + + " \"retweeted_status\": {" + + " \"contributors\": null," + + " \"text\": \"Samsung's Galaxy K zoom is a high-end smartphone and an incredible selfie machine http://t.co/I5WmnHV9HM http://t.co/V8Pm2bxzcH\"," + + " \"geo\": null," + + " \"retweeted\": false," + + " \"in_reply_to_screen_name\": null," + + " \"possibly_sensitive\": false," + + " \"truncated\": false," + + " \"lang\": \"en\"," + + " \"entities\": {" + + " \"symbols\": []," + + " \"urls\": [{" + + " \"expanded_url\": \"http://vrge.co/1ivZTla\"," + + " \"indices\": [82, 104]," + + " \"display_url\": \"vrge.co/1ivZTla\"," + + " \"url\": \"http://t.co/I5WmnHV9HM\"" + + " }]," + + " \"hashtags\": []," + + " \"media\": [{" + + " \"sizes\": {" + + " \"small\": {" + + " \"w\": 340," + + " \"resize\": \"fit\"," + + " \"h\": 225" + + " }," + + " \"thumb\": {" + + " \"w\": 150," + + " \"resize\": \"crop\"," + + " \"h\": 150" + + " }," + + " \"large\": {" + + " \"w\": 1020," + + " \"resize\": \"fit\"," + + " \"h\": 675" + + " }," + + " \"medium\": {" + + " \"w\": 599," + + " \"resize\": \"fit\"," + + " \"h\": 397" + + " }" + + " }," + + " \"id\": 461125128559263744," + + " \"media_url_https\": \"https://pbs.twimg.com/media/BmY-3HzIAAAR1d6.jpg\"," + + " \"media_url\": \"http://pbs.twimg.com/media/BmY-3HzIAAAR1d6.jpg\"," + + " \"expanded_url\": \"http://twitter.com/verge/status/461125128710262784/photo/1\"," + + " \"indices\": [105, 127]," + + " \"id_str\": \"461125128559263744\"," + + " \"type\": \"photo\"," + + " \"display_url\": \"pic.twitter.com/V8Pm2bxzcH\"," + + " \"url\": \"http://t.co/V8Pm2bxzcH\"" + + " }]," + + " \"user_mentions\": []" + + " }," + + " \"in_reply_to_status_id_str\": null," + + " \"id\": 461125128710262784," + + " \"source\": \"Twitter for Mac<\\/a>\"," + + " \"in_reply_to_user_id_str\": null," + + " \"favorited\": false," + + " \"in_reply_to_status_id\": null," + + " \"retweet_count\": 113," + + " \"created_at\": \"Tue Apr 29 12:49:32 +0000 2014\"," + + " \"in_reply_to_user_id\": null," + + " \"favorite_count\": 79," + + " \"id_str\": \"461125128710262784\"," + + " \"place\": null," + + " \"user\": {" + + " \"location\": \"The Universe\"," + + " \"default_profile\": false," + + " \"profile_background_tile\": false," + + " \"statuses_count\": 45365," + + " \"lang\": \"en\"," + + " \"profile_link_color\": \"FA4D2A\"," + + " \"profile_banner_url\": \"https://pbs.twimg.com/profile_banners/275686563/1348153919\"," + + " \"id\": 275686563," + + " \"following\": null," + + " \"protected\": false," + + " \"favourites_count\": 679," + + " \"profile_text_color\": \"333333\"," + + " \"description\": \"The culture of now. We tell stories at the intersection of science, technology, art, and the humanities.\"," + + " \"verified\": true," + + " \"contributors_enabled\": false," + + " \"profile_sidebar_border_color\": \"FFFFFF\"," + + " \"name\": \"The Verge\"," + + " \"profile_background_color\": \"FFFFFF\"," + + " \"created_at\": \"Fri Apr 01 19:54:22 +0000 2011\"," + + " \"is_translation_enabled\": true," + + " \"default_profile_image\": false," + + " \"followers_count\": 442637," + + " \"profile_image_url_https\": \"https://pbs.twimg.com/profile_images/1450127603/TheVerge_Badge_Color1_normal.png\"," + + " \"geo_enabled\": false," + + " \"profile_background_image_url\": \"http://pbs.twimg.com/profile_background_images/378800000021313532/66906cdb8cdbfe2de5ac7ada880ca029.png\"," + + " \"profile_background_image_url_https\": \"https://pbs.twimg.com/profile_background_images/378800000021313532/66906cdb8cdbfe2de5ac7ada880ca029.png\"," + + " \"follow_request_sent\": null," + + " \"entities\": {" + + " \"description\": {" + + " \"urls\": []" + + " }," + + " \"url\": {" + + " \"urls\": [{" + + " \"expanded_url\": \"http://www.theverge.com\"," + + " \"indices\": [0, 22]," + + " \"display_url\": \"theverge.com\"," + + " \"url\": \"http://t.co/W2SFxIGhA4\"" + + " }]" + + " }" + + " }," + + " \"url\": \"http://t.co/W2SFxIGhA4\"," + + " \"utc_offset\": -14400," + + " \"time_zone\": \"Eastern Time (US & Canada)\"," + + " \"notifications\": null," + + " \"profile_use_background_image\": true," + + " \"friends_count\": 112," + + " \"profile_sidebar_fill_color\": \"EFEFEF\"," + + " \"screen_name\": \"verge\"," + + " \"id_str\": \"275686563\"," + + " \"profile_image_url\": \"http://pbs.twimg.com/profile_images/1450127603/TheVerge_Badge_Color1_normal.png\"," + + " \"listed_count\": 17450," + + " \"is_translator\": false" + + " }," + + " \"coordinates\": null," + + " \"metadata\": {" + + " \"result_type\": \"recent\"," + + " \"iso_language_code\": \"en\"" + + " }" + + " }," + + " \"geo\": null," + + " \"entities\": {" + + " \"symbols\": []," + + " \"urls\": [{" + + " \"expanded_url\": \"http://vrge.co/1ivZTla\"," + + " \"indices\": [93, 115]," + + " \"display_url\": \"vrge.co/1ivZTla\"," + + " \"url\": \"http://t.co/I5WmnHV9HM\"" + + " }]," + + " \"hashtags\": []," + + " \"media\": [{" + + " \"sizes\": {" + + " \"small\": {" + + " \"w\": 340," + + " \"resize\": \"fit\"," + + " \"h\": 225" + + " }," + + " \"thumb\": {" + + " \"w\": 150," + + " \"resize\": \"crop\"," + + " \"h\": 150" + + " }," + + " \"large\": {" + + " \"w\": 1020," + + " \"resize\": \"fit\"," + + " \"h\": 675" + + " }," + + " \"medium\": {" + + " \"w\": 599," + + " \"resize\": \"fit\"," + + " \"h\": 397" + + " }" + + " }," + + " \"id\": 461125128559263744," + + " \"media_url_https\": \"https://pbs.twimg.com/media/BmY-3HzIAAAR1d6.jpg\"," + + " \"media_url\": \"http://pbs.twimg.com/media/BmY-3HzIAAAR1d6.jpg\"," + + " \"expanded_url\": \"http://twitter.com/verge/status/461125128710262784/photo/1\"," + + " \"indices\": [116, 138]," + + " \"id_str\": \"461125128559263744\"," + + " \"type\": \"photo\"," + + " \"display_url\": \"pic.twitter.com/V8Pm2bxzcH\"," + + " \"url\": \"http://t.co/V8Pm2bxzcH\"" + + " }]," + + " \"user_mentions\": [{" + + " \"id\": 275686563," + + " \"name\": \"The Verge\"," + + " \"indices\": [3, 9]," + + " \"screen_name\": \"verge\"," + + " \"id_str\": \"275686563\"" + + " }]" + + " }," + + " \"source\": \"Twitter for Android Tablets<\\/a>\"," + + " \"favorited\": false," + + " \"in_reply_to_user_id\": null," + + " \"retweet_count\": 113," + + " \"id_str\": \"461441338630619136\"," + + " \"user\": {" + + " \"location\": \"GURGAON\"," + + " \"default_profile\": false," + + " \"profile_background_tile\": true," + + " \"statuses_count\": 2208," + + " \"lang\": \"en\"," + + " \"profile_link_color\": \"2FC2EF\"," + + " \"profile_banner_url\": \"https://pbs.twimg.com/profile_banners/106707648/1398635609\"," + + " \"id\": 106707648," + + " \"following\": null," + + " \"protected\": false," + + " \"favourites_count\": 547," + + " \"profile_text_color\": \"666666\"," + + " \"description\": \"A WANNABE WRITER.....\"," + + " \"verified\": false," + + " \"contributors_enabled\": false," + + " \"profile_sidebar_border_color\": \"FFFFFF\"," + + " \"name\": \"MANGO PEOPLE\"," + + " \"profile_background_color\": \"1A1B1F\"," + + " \"created_at\": \"Wed Jan 20 12:59:53 +0000 2010\"," + + " \"is_translation_enabled\": false," + + " \"default_profile_image\": false," + + " \"followers_count\": 573," + + " \"profile_image_url_https\": \"https://pbs.twimg.com/profile_images/461180974823272448/RpSM6o2f_normal.jpeg\"," + + " \"geo_enabled\": true," + + " \"profile_background_image_url\": \"http://pbs.twimg.com/profile_background_images/378800000157705856/1unyAtzk.jpeg\"," + + " \"profile_background_image_url_https\": \"https://pbs.twimg.com/profile_background_images/378800000157705856/1unyAtzk.jpeg\"," + + " \"follow_request_sent\": null," + + " \"entities\": {" + + " \"description\": {" + + " \"urls\": []" + + " }," + + " \"url\": {" + + " \"urls\": [{" + + " \"expanded_url\": \"http://FACEBOOK.COM/BENXROCKS\"," + + " \"indices\": [0, 22]," + + " \"display_url\": \"FACEBOOK.COM/BENXROCKS\"," + + " \"url\": \"http://t.co/Uwbbg0oN3T\"" + + " }]" + + " }" + + " }," + + " \"url\": \"http://t.co/Uwbbg0oN3T\"," + + " \"utc_offset\": 19800," + + " \"time_zone\": \"New Delhi\"," + + " \"notifications\": null," + + " \"profile_use_background_image\": true," + + " \"friends_count\": 1719," + + " \"profile_sidebar_fill_color\": \"252429\"," + + " \"screen_name\": \"BH4NUKALRA\"," + + " \"id_str\": \"106707648\"," + + " \"profile_image_url\": \"http://pbs.twimg.com/profile_images/461180974823272448/RpSM6o2f_normal.jpeg\"," + + " \"listed_count\": 2," + + " \"is_translator\": false" + + " }" + + "}"; + + private Object deserializeJson(String json, Class cls) { + Gson gson = new GsonBuilder() + .registerTypeAdapter(cls, new ValidatingJsonDeserializer<>()) + .create(); + return gson.fromJson(json, cls); + } + + @Test + public void testUserEmpty() { + try { + deserializeJson("{}", User.class); + fail("Empty object literal should be rejected."); + } catch (JsonParseException ex) { + // assume id to be the first field + assertEquals("Missing field: id", ex.getMessage()); + } + } + + @Test + public void testTweetEmpty() { + try { + deserializeJson("{}", Tweet.class); + fail("Empty object literal should be rejected."); + } catch (JsonParseException ex) { + // assume id to be the first field + assertEquals("Missing field: id", ex.getMessage()); + } + } + + /** + * Test of deserialize method, of class ValidatingJsonDeserializer. + */ + @Test + public void testDeserializeTweet() { + Tweet tweet = (Tweet) deserializeJson(exampleTweet, Tweet.class); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + //System.out.println(gson.toJson(tweet)); + assertEquals(461441338630619136L, tweet.id); + assertNotNull(tweet.retweeted_status); + assertEquals("Wed Apr 30 09:46:02 +0000 2014", tweet.created_at); + + assertNotNull(tweet.user); + assertEquals("GURGAON", tweet.user.location); + assertNotNull(tweet.user.entities); + assertNotNull(tweet.user.entities.url); + assertEquals(1, tweet.user.entities.url.urls.length); + assertEquals("http://FACEBOOK.COM/BENXROCKS", + tweet.user.entities.url.urls[0].expanded_url); + + assertEquals("en", tweet.retweeted_status.user.lang); + assertEquals(1, tweet.entities.user_mentions.length); + assertEquals(275686563, tweet.entities.user_mentions[0].id); + assertEquals(0, tweet.entities.hashtags.length); + assertEquals(1, tweet.entities.urls.length); + assertEquals("http://vrge.co/1ivZTla", tweet.entities.urls[0].expanded_url); + } + + private JsonObject buildMinimalUser() { + JsonObject user = new JsonObject(); + user.addProperty("id", 4); + user.addProperty("name", "X"); + user.addProperty("time_zone", "X"); + user.addProperty("statuses_count", 4); + user.addProperty("followers_count", 4); + user.addProperty("friends_count", 4); + user.addProperty("location", "X"); + user.addProperty("screen_name", "X"); + user.addProperty("created_at", "X"); + JsonObject entities = new JsonObject(); + JsonObject urlEntity = new JsonObject(); + urlEntity.add("urls", new JsonArray()); + entities.add("url", urlEntity); + user.add("entities", entities); + user.addProperty("lang", "X"); + return user; + } + + private JsonObject buildMinimalTweet(JsonObject user) { + JsonObject tweet = new JsonObject(); + tweet.addProperty("id", 4); + tweet.addProperty("lang", "X"); + tweet.addProperty("in_reply_to_user_id", 4); + tweet.addProperty("created_at", "X"); + tweet.addProperty("favorite_count", 4); + tweet.addProperty("place", "X"); + tweet.addProperty("coordinates", "X"); + tweet.addProperty("text", "X"); + tweet.add("retweeted_status", JsonNull.INSTANCE); // Tweet object + JsonObject entities = new JsonObject(); + entities.add("hashtags", new JsonArray()); + entities.add("urls", new JsonArray()); + entities.add("user_mentions", new JsonArray()); + tweet.add("entities", entities); + tweet.addProperty("retweet_count", 4); + tweet.add("user", user); + return tweet; + } + + private void removeProperty(JsonObject obj, String... names) { + String prop; + for (int i = 0; i < names.length - 1; i++) { + prop = names[i]; + assert obj.has(prop) : "Missing object property: " + prop; + obj = obj.getAsJsonObject(prop); + } + prop = names[names.length - 1]; + assert obj.has(prop) : "Cannot remove missing property: " + prop; + obj.remove(prop); + } + + private void checkImpairedTweet(String... names) { + JsonObject tweet = buildMinimalTweet(buildMinimalUser()); + removeProperty(tweet, names); + String prop = names[names.length - 1]; + checkTweetFail(tweet, "Missing field: " + prop); + } + + private void checkTweetFail(JsonObject tweet, String exp) { + Gson gson = new Gson(); + System.out.println("Checking for failure: " + exp); + String json = gson.toJson(tweet); + try { + deserializeJson(json, Tweet.class); + System.out.println("JSON that should have failed: " + json); + fail("Expected: " + exp); + } catch (JsonParseException ex) { + System.out.println("Failure message: " + ex.getMessage()); + assertEquals(exp, ex.getMessage()); + } + } + + private Tweet checkTweetPass(JsonObject tweet) { + Gson gson = new Gson(); + String json = gson.toJson(tweet); + return (Tweet) deserializeJson(json, Tweet.class); + } + + @Test + public void testMinimal() { + checkTweetPass(buildMinimalTweet(buildMinimalUser())); + checkTweetFail(buildMinimalTweet(null), "Missing field: user"); + } + + @Test + public void testTweet() { + checkImpairedTweet("text"); + } + + @Test + public void testTweetEntities() { + checkImpairedTweet("entities"); + checkImpairedTweet("entities", "hashtags"); + checkImpairedTweet("entities", "urls"); + } + + @Test + public void testUserEntities() { + checkImpairedTweet("user", "entities"); + checkImpairedTweet("user", "entities", "url"); + checkImpairedTweet("user", "entities", "url", "urls"); + } +} -- cgit v1.2.1