/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ package org.mozilla.android.sync.net.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.json.simple.parser.ParseException; import org.junit.Before; import org.junit.Test; import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper; import org.mozilla.android.sync.test.helpers.MockServer; import org.mozilla.android.sync.test.helpers.WaitHelper; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.MetaGlobal; import org.mozilla.gecko.sync.NonObjectJSONException; import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate; import org.mozilla.gecko.sync.net.BaseResource; import org.mozilla.gecko.sync.net.SyncStorageResponse; import org.simpleframework.http.Request; import org.simpleframework.http.Response; public class TestMetaGlobal { public static Object monitor = new Object(); private static final int TEST_PORT = HTTPServerTestHelper.getTestPort(); private static final String TEST_SERVER = "http://localhost:" + TEST_PORT; private static final String TEST_SYNC_ID = "foobar"; public static final String USER_PASS = "c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd:password"; public static final String META_URL = TEST_SERVER + "/1.1/c6o7dvmr2c4ud2fyv6woz2u4zi22bcyd/storage/meta/global"; private HTTPServerTestHelper data = new HTTPServerTestHelper(); public static final String TEST_META_GLOBAL_RESPONSE = "{\"id\":\"global\",\"payload\":\"{\\\"syncID\\\":\\\"zPSQTm7WBVWB\\\",\\\"storageVersion\\\":5,\\\"engines\\\":{\\\"clients\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"fDg0MS5bDtV7\\\"},\\\"bookmarks\\\":{\\\"version\\\":2,\\\"syncID\\\":\\\"NNaQr6_F-9dm\\\"},\\\"forms\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"GXF29AFprnvc\\\"},\\\"history\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"av75g4vm-_rp\\\"},\\\"passwords\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"LT_ACGpuKZ6a\\\"},\\\"prefs\\\":{\\\"version\\\":2,\\\"syncID\\\":\\\"-3nsksP9wSAs\\\"},\\\"tabs\\\":{\\\"version\\\":1,\\\"syncID\\\":\\\"W4H5lOMChkYA\\\"}}}\",\"username\":\"5817483\",\"modified\":1.32046073744E9}"; public static final String TEST_META_GLOBAL_NO_PAYLOAD_RESPONSE = "{\"id\":\"global\"," + "\"username\":\"5817483\",\"modified\":1.32046073744E9}"; public static final String TEST_META_GLOBAL_MALFORMED_PAYLOAD_RESPONSE = "{\"id\":\"global\"," + "\"payload\":\"{!!!}\"," + "\"username\":\"5817483\",\"modified\":1.32046073744E9}"; public static final String TEST_META_GLOBAL_EMPTY_PAYLOAD_RESPONSE = "{\"id\":\"global\"," + "\"payload\":\"{}\"," + "\"username\":\"5817483\",\"modified\":1.32046073744E9}"; @SuppressWarnings("static-method") @Before public void setUp() { BaseResource.rewriteLocalhost = false; } @SuppressWarnings("static-method") @Test public void testSyncID() { MetaGlobal g = new MetaGlobal(META_URL, USER_PASS); g.setSyncID("foobar"); assertEquals(g.getSyncID(), "foobar"); } public class MockMetaGlobalFetchDelegate implements MetaGlobalDelegate { boolean successCalled = false; MetaGlobal successGlobal = null; SyncStorageResponse successResponse = null; boolean failureCalled = false; SyncStorageResponse failureResponse = null; boolean errorCalled = false; Exception errorException = null; boolean missingCalled = false; MetaGlobal missingGlobal = null; SyncStorageResponse missingResponse = null; public void handleSuccess(MetaGlobal global, SyncStorageResponse response) { successCalled = true; successGlobal = global; successResponse = response; WaitHelper.getTestWaiter().performNotify(); } public void handleFailure(SyncStorageResponse response) { failureCalled = true; failureResponse = response; WaitHelper.getTestWaiter().performNotify(); } public void handleError(Exception e) { errorCalled = true; errorException = e; WaitHelper.getTestWaiter().performNotify(); } public void handleMissing(MetaGlobal global, SyncStorageResponse response) { missingCalled = true; missingGlobal = global; missingResponse = response; WaitHelper.getTestWaiter().performNotify(); } } public MockMetaGlobalFetchDelegate doFetch(final MetaGlobal global) { final MockMetaGlobalFetchDelegate delegate = new MockMetaGlobalFetchDelegate(); WaitHelper.getTestWaiter().performWait(WaitHelper.onThreadRunnable(new Runnable() { @Override public void run() { global.fetch(delegate); } })); return delegate; } @Test public void testFetchMissing() { MockServer missingMetaGlobalServer = new MockServer(404, "{}"); final MetaGlobal global = new MetaGlobal(META_URL, USER_PASS); global.setSyncID(TEST_SYNC_ID); assertEquals(TEST_SYNC_ID, global.getSyncID()); data.startHTTPServer(missingMetaGlobalServer); final MockMetaGlobalFetchDelegate delegate = doFetch(global); data.stopHTTPServer(); assertTrue(delegate.missingCalled); assertEquals(404, delegate.missingResponse.getStatusCode()); assertEquals(TEST_SYNC_ID, delegate.missingGlobal.getSyncID()); } @Test public void testFetchExisting() { MockServer existingMetaGlobalServer = new MockServer(200, TEST_META_GLOBAL_RESPONSE); MetaGlobal global = new MetaGlobal(META_URL, USER_PASS); assertNull(global.getSyncID()); assertNull(global.getEngines()); assertNull(global.getStorageVersion()); data.startHTTPServer(existingMetaGlobalServer); final MockMetaGlobalFetchDelegate delegate = doFetch(global); data.stopHTTPServer(); assertTrue(delegate.successCalled); assertEquals(200, delegate.successResponse.getStatusCode()); assertEquals("zPSQTm7WBVWB", global.getSyncID()); assertTrue(global.getEngines() instanceof ExtendedJSONObject); assertEquals(Long.valueOf(5), global.getStorageVersion()); } /** * A record that is valid JSON but invalid as a meta/global record will be * downloaded successfully, but will fail later. */ @Test public void testFetchNoPayload() { MockServer existingMetaGlobalServer = new MockServer(200, TEST_META_GLOBAL_NO_PAYLOAD_RESPONSE); MetaGlobal global = new MetaGlobal(META_URL, USER_PASS); data.startHTTPServer(existingMetaGlobalServer); final MockMetaGlobalFetchDelegate delegate = doFetch(global); data.stopHTTPServer(); assertTrue(delegate.successCalled); } @Test public void testFetchEmptyPayload() { MockServer existingMetaGlobalServer = new MockServer(200, TEST_META_GLOBAL_EMPTY_PAYLOAD_RESPONSE); MetaGlobal global = new MetaGlobal(META_URL, USER_PASS); data.startHTTPServer(existingMetaGlobalServer); final MockMetaGlobalFetchDelegate delegate = doFetch(global); data.stopHTTPServer(); assertTrue(delegate.successCalled); } /** * A record that is invalid JSON will fail to download at all. */ @Test public void testFetchMalformedPayload() { MockServer existingMetaGlobalServer = new MockServer(200, TEST_META_GLOBAL_MALFORMED_PAYLOAD_RESPONSE); MetaGlobal global = new MetaGlobal(META_URL, USER_PASS); data.startHTTPServer(existingMetaGlobalServer); final MockMetaGlobalFetchDelegate delegate = doFetch(global); data.stopHTTPServer(); assertTrue(delegate.errorCalled); assertNotNull(delegate.errorException); assertEquals(ParseException.class, delegate.errorException.getClass()); } @SuppressWarnings("static-method") @Test public void testSetFromRecord() throws IllegalStateException, NonObjectJSONException, IOException, ParseException { MetaGlobal mg = new MetaGlobal(null, null); mg.setFromRecord(CryptoRecord.fromJSONRecord(TEST_META_GLOBAL_RESPONSE)); assertEquals("zPSQTm7WBVWB", mg.getSyncID()); assertTrue(mg.getEngines() instanceof ExtendedJSONObject); assertEquals(Long.valueOf(5), mg.getStorageVersion()); } @SuppressWarnings("static-method") @Test public void testAsCryptoRecord() throws IllegalStateException, NonObjectJSONException, IOException, ParseException { MetaGlobal mg = new MetaGlobal(null, null); mg.setFromRecord(CryptoRecord.fromJSONRecord(TEST_META_GLOBAL_RESPONSE)); CryptoRecord rec = mg.asCryptoRecord(); assertEquals("global", rec.guid); mg.setFromRecord(rec); assertEquals("zPSQTm7WBVWB", mg.getSyncID()); assertTrue(mg.getEngines() instanceof ExtendedJSONObject); assertEquals(Long.valueOf(5), mg.getStorageVersion()); } @SuppressWarnings("static-method") @Test public void testGetEnabledEngineNames() throws IllegalStateException, NonObjectJSONException, IOException, ParseException { MetaGlobal mg = new MetaGlobal(null, null); mg.setFromRecord(CryptoRecord.fromJSONRecord(TEST_META_GLOBAL_RESPONSE)); assertEquals("zPSQTm7WBVWB", mg.getSyncID()); final Set<String> actual = mg.getEnabledEngineNames(); final Set<String> expected = new HashSet<String>(); for (String name : new String[] { "bookmarks", "clients", "forms", "history", "passwords", "prefs", "tabs" }) { expected.add(name); } assertEquals(expected, actual); } public MockMetaGlobalFetchDelegate doUpload(final MetaGlobal global) { final MockMetaGlobalFetchDelegate delegate = new MockMetaGlobalFetchDelegate(); WaitHelper.getTestWaiter().performWait(WaitHelper.onThreadRunnable(new Runnable() { @Override public void run() { global.upload(delegate); } })); return delegate; } @Test public void testUpload() { long TEST_STORAGE_VERSION = 111; String TEST_SYNC_ID = "testSyncID"; MetaGlobal mg = new MetaGlobal(META_URL, USER_PASS); mg.setSyncID(TEST_SYNC_ID); mg.setStorageVersion(Long.valueOf(TEST_STORAGE_VERSION)); final AtomicBoolean mgUploaded = new AtomicBoolean(false); final MetaGlobal uploadedMg = new MetaGlobal(null, null); MockServer server = new MockServer() { public void handle(Request request, Response response) { if (request.getMethod().equals("PUT")) { try { ExtendedJSONObject body = ExtendedJSONObject.parseJSONObject(request.getContent()); System.out.println(body.toJSONString()); assertTrue(body.containsKey("payload")); assertFalse(body.containsKey("default")); CryptoRecord rec = CryptoRecord.fromJSONRecord(body); uploadedMg.setFromRecord(rec); mgUploaded.set(true); } catch (Exception e) { throw new RuntimeException(e); } this.handle(request, response, 200, "success"); return; } this.handle(request, response, 404, "missing"); } }; data.startHTTPServer(server); final MockMetaGlobalFetchDelegate delegate = doUpload(mg); data.stopHTTPServer(); assertTrue(delegate.successCalled); assertTrue(mgUploaded.get()); assertEquals(TEST_SYNC_ID, uploadedMg.getSyncID()); assertEquals(TEST_STORAGE_VERSION, uploadedMg.getStorageVersion().longValue()); } }