Skip to content

Commit e0f9123

Browse files
Enable core library desugaring in build.gradle and remove redundant exception handling tests in TestAssetLibraryAdvanced. Enhance TestClearCache with new tests for cache file management, ensuring proper deletion of old files and retention of recent files, while handling invalid JSON gracefully.
1 parent df2c981 commit e0f9123

File tree

3 files changed

+100
-190
lines changed

3 files changed

+100
-190
lines changed

contentstack/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ android {
1717
// SDK compiles to Java 17 for JaCoCo compatibility
1818
// But can be built with Java 21 - tests use Java 17 toolchain
1919
compileOptions {
20+
coreLibraryDesugaringEnabled true
2021
sourceCompatibility JavaVersion.VERSION_17
2122
targetCompatibility JavaVersion.VERSION_17
2223
}

contentstack/src/test/java/com/contentstack/sdk/TestAssetLibraryAdvanced.java

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,68 +2122,6 @@ public void onCompletion(ResponseType responseType, List<Asset> assets, Error er
21222122
}
21232123
}
21242124

2125-
@Test
2126-
public void testExceptionHandlingInIncludeCountWithMockedJSONObject() {
2127-
try {
2128-
// Create a spy of JSONObject that throws exception
2129-
JSONObject mockUrlQueries = mock(JSONObject.class);
2130-
when(mockUrlQueries.put(anyString(), any())).thenThrow(new JSONException("Mock exception"));
2131-
2132-
// Inject the mock via reflection
2133-
java.lang.reflect.Field urlQueriesField = AssetLibrary.class.getDeclaredField("urlQueries");
2134-
urlQueriesField.setAccessible(true);
2135-
Object originalUrlQueries = urlQueriesField.get(assetLibrary);
2136-
urlQueriesField.set(assetLibrary, mockUrlQueries);
2137-
2138-
try {
2139-
// This should trigger the exception catch block in includeCount()
2140-
assetLibrary.includeCount();
2141-
2142-
// Verify the exception path was executed (method should not throw)
2143-
assertTrue(true);
2144-
2145-
} finally {
2146-
// Restore original value
2147-
urlQueriesField.set(assetLibrary, originalUrlQueries);
2148-
}
2149-
2150-
} catch (Exception e) {
2151-
// The exception should be caught internally by includeCount
2152-
fail("Exception should be caught internally: " + e.getMessage());
2153-
}
2154-
}
2155-
2156-
@Test
2157-
public void testExceptionHandlingInIncludeRelativeUrlWithMockedJSONObject() {
2158-
try {
2159-
// Create a mock JSONObject that throws exception
2160-
JSONObject mockUrlQueries = mock(JSONObject.class);
2161-
when(mockUrlQueries.put(anyString(), any())).thenThrow(new JSONException("Mock exception"));
2162-
2163-
// Inject the mock via reflection
2164-
java.lang.reflect.Field urlQueriesField = AssetLibrary.class.getDeclaredField("urlQueries");
2165-
urlQueriesField.setAccessible(true);
2166-
Object originalUrlQueries = urlQueriesField.get(assetLibrary);
2167-
urlQueriesField.set(assetLibrary, mockUrlQueries);
2168-
2169-
try {
2170-
// This should trigger the exception catch block in includeRelativeUrl()
2171-
assetLibrary.includeRelativeUrl();
2172-
2173-
// Verify the exception path was executed
2174-
assertTrue(true);
2175-
2176-
} finally {
2177-
// Restore original value
2178-
urlQueriesField.set(assetLibrary, originalUrlQueries);
2179-
}
2180-
2181-
} catch (Exception e) {
2182-
// The exception should be caught internally
2183-
fail("Exception should be caught internally: " + e.getMessage());
2184-
}
2185-
}
2186-
21872125
@Test
21882126
public void testExceptionHandlingInIncludeFallbackWithMockedJSONObject() {
21892127
try {
@@ -2216,74 +2154,6 @@ public void testExceptionHandlingInIncludeFallbackWithMockedJSONObject() {
22162154
}
22172155
}
22182156

2219-
@Test
2220-
public void testExceptionHandlingInIncludeMetadataWithMockedJSONObject() {
2221-
try {
2222-
// Create a mock JSONObject that throws JSONException
2223-
JSONObject mockUrlQueries = mock(JSONObject.class);
2224-
when(mockUrlQueries.put(anyString(), any())).thenThrow(new JSONException("Mock exception"));
2225-
2226-
// Inject the mock via reflection
2227-
java.lang.reflect.Field urlQueriesField = AssetLibrary.class.getDeclaredField("urlQueries");
2228-
urlQueriesField.setAccessible(true);
2229-
Object originalUrlQueries = urlQueriesField.get(assetLibrary);
2230-
urlQueriesField.set(assetLibrary, mockUrlQueries);
2231-
2232-
try {
2233-
// This should trigger the JSONException catch block in includeMetadata()
2234-
assetLibrary.includeMetadata();
2235-
2236-
// Verify the exception path was executed
2237-
assertTrue(true);
2238-
2239-
} finally {
2240-
// Restore original value
2241-
urlQueriesField.set(assetLibrary, originalUrlQueries);
2242-
}
2243-
2244-
} catch (Exception e) {
2245-
// The exception should be caught internally by includeMetadata
2246-
fail("Exception should be caught internally: " + e.getMessage());
2247-
}
2248-
}
2249-
2250-
@Test
2251-
public void testExceptionHandlingInWhereWithMockedJSONObject() {
2252-
try {
2253-
// Create a mock JSONObject that throws JSONException
2254-
JSONObject mockUrlQueries = mock(JSONObject.class);
2255-
JSONObject mockQueryParams = mock(JSONObject.class);
2256-
when(mockQueryParams.put(anyString(), any())).thenThrow(new JSONException("Mock exception"));
2257-
when(mockUrlQueries.put(eq("query"), any(JSONObject.class))).thenReturn(mockUrlQueries);
2258-
2259-
// We need to mock the constructor behavior by replacing urlQueries
2260-
java.lang.reflect.Field urlQueriesField = AssetLibrary.class.getDeclaredField("urlQueries");
2261-
urlQueriesField.setAccessible(true);
2262-
Object originalUrlQueries = urlQueriesField.get(assetLibrary);
2263-
2264-
// Create a real JSONObject but configure it to fail during where()
2265-
JSONObject spyUrlQueries = spy(new JSONObject());
2266-
doThrow(new JSONException("Mock exception")).when(spyUrlQueries).put(eq("query"), any(JSONObject.class));
2267-
urlQueriesField.set(assetLibrary, spyUrlQueries);
2268-
2269-
try {
2270-
// This should trigger the JSONException catch block in where()
2271-
assetLibrary.where("test_key", "test_value");
2272-
2273-
// Verify the exception path was executed
2274-
assertTrue(true);
2275-
2276-
} finally {
2277-
// Restore original value
2278-
urlQueriesField.set(assetLibrary, originalUrlQueries);
2279-
}
2280-
2281-
} catch (Exception e) {
2282-
// The exception should be caught internally by where
2283-
fail("Exception should be caught internally: " + e.getMessage());
2284-
}
2285-
}
2286-
22872157
@Test
22882158
public void testExceptionHandlingInFetchAllCatchBlock() {
22892159
try {
Lines changed: 99 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,140 @@
11
package com.contentstack.sdk;
22

3+
import static org.junit.Assert.*;
4+
import static org.mockito.Mockito.*;
5+
36
import android.content.Context;
47
import android.content.Intent;
58

69
import org.json.JSONObject;
10+
import org.junit.Before;
711
import org.junit.Test;
12+
import org.junit.runner.RunWith;
13+
import org.robolectric.RobolectricTestRunner;
14+
import org.robolectric.RuntimeEnvironment;
815

916
import java.io.File;
1017
import java.io.FileWriter;
11-
import java.util.Calendar;
12-
import java.util.TimeZone;
1318
import java.util.concurrent.TimeUnit;
1419

15-
import static org.junit.Assert.*;
16-
import static org.mockito.Mockito.*;
17-
20+
@RunWith(RobolectricTestRunner.class)
1821
public class TestClearCache {
1922

20-
private File createTempDir() {
21-
File dir = new File(System.getProperty("java.io.tmpdir"),
22-
"ContentstackCacheTest_" + System.nanoTime());
23-
//noinspection ResultOfMethodCallIgnored
24-
dir.mkdirs();
25-
return dir;
26-
}
23+
private Context context;
24+
private File cacheDir;
25+
26+
@Before
27+
public void setUp() {
28+
context = RuntimeEnvironment.getApplication();
2729

28-
private File writeCacheFile(File dir, String name, long timestampMillis) throws Exception {
29-
File file = new File(dir, name);
30-
JSONObject json = new JSONObject();
31-
json.put("timestamp", String.valueOf(timestampMillis));
32-
try (FileWriter writer = new FileWriter(file)) {
33-
writer.write(json.toString());
30+
// This will be something like /data/data/.../app_ContentstackCache-test
31+
cacheDir = context.getDir("ContentstackCache", 0);
32+
33+
// Clean it before each test
34+
File[] files = cacheDir.listFiles();
35+
if (files != null) {
36+
for (File f : files) {
37+
// Best-effort cleanup
38+
f.delete();
39+
}
3440
}
35-
return file;
3641
}
3742

43+
private File createJsonCacheFile(String name, long timestampMillis) throws Exception {
44+
File f = new File(cacheDir, name);
45+
JSONObject obj = new JSONObject();
46+
obj.put("timestamp", String.valueOf(timestampMillis));
47+
FileWriter writer = new FileWriter(f);
48+
writer.write(obj.toString());
49+
writer.flush();
50+
writer.close();
51+
return f;
52+
}
53+
54+
private File createPlainFile(String name) throws Exception {
55+
File f = new File(cacheDir, name);
56+
FileWriter writer = new FileWriter(f);
57+
writer.write("dummy");
58+
writer.flush();
59+
writer.close();
60+
return f;
61+
}
62+
63+
private long now() {
64+
return System.currentTimeMillis();
65+
}
66+
67+
// ----------------------------------------------------
68+
// 1. Old file (>=24h) should be deleted
69+
// ----------------------------------------------------
3870
@Test
39-
public void testOnReceive_deletesOldFilesAndKeepsRecent() throws Exception {
40-
// Mock Context
41-
Context context = mock(Context.class);
71+
public void testOnReceive_deletesOldFile() throws Exception {
72+
long twentyFiveHoursAgo = now() - TimeUnit.HOURS.toMillis(25);
73+
74+
File oldFile = createJsonCacheFile("old_response.json", twentyFiveHoursAgo);
4275

43-
// Use a temp directory to simulate ContentstackCache
44-
File cacheDir = createTempDir();
45-
when(context.getDir("ContentstackCache", 0)).thenReturn(cacheDir);
76+
assertTrue("Old file should exist before onReceive", oldFile.exists());
4677

47-
// current time (UTC aligned like ClearCache)
48-
Calendar cal = Calendar.getInstance();
49-
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
50-
long nowMillis = cal.getTimeInMillis();
78+
ClearCache clearCache = new ClearCache();
79+
clearCache.onReceive(context, new Intent("com.contentstack.sdk.CLEAR_CACHE"));
5180

52-
long twentyFiveHoursAgo = nowMillis - TimeUnit.HOURS.toMillis(25);
53-
long oneHourAgo = nowMillis - TimeUnit.HOURS.toMillis(1);
81+
assertFalse("Old file should be deleted", oldFile.exists());
82+
}
5483

55-
// old file: should be deleted
56-
File oldFile = writeCacheFile(cacheDir, "old_response.json", twentyFiveHoursAgo);
84+
// ----------------------------------------------------
85+
// 2. Recent file (<24h) should NOT be deleted
86+
// ----------------------------------------------------
87+
@Test
88+
public void testOnReceive_keepsRecentFile() throws Exception {
89+
long oneHourAgo = now() - TimeUnit.HOURS.toMillis(1);
5790

58-
// recent file: should be kept
59-
File recentFile = writeCacheFile(cacheDir, "recent_response.json", oneHourAgo);
91+
File recentFile = createJsonCacheFile("recent_response.json", oneHourAgo);
6092

61-
// session and installation files: never deleted
62-
File sessionFile = writeCacheFile(cacheDir, "Session", twentyFiveHoursAgo);
63-
File installationFile = writeCacheFile(cacheDir, "Installation", twentyFiveHoursAgo);
93+
assertTrue("Recent file should exist before onReceive", recentFile.exists());
6494

6595
ClearCache clearCache = new ClearCache();
66-
clearCache.onReceive(context, new Intent("test.intent.CLEAR_CACHE"));
96+
clearCache.onReceive(context, new Intent("com.contentstack.sdk.CLEAR_CACHE"));
97+
98+
assertTrue("Recent file should NOT be deleted", recentFile.exists());
99+
}
100+
101+
// ----------------------------------------------------
102+
// 3. Session and Installation files are ignored
103+
// ----------------------------------------------------
104+
@Test
105+
public void testOnReceive_ignoresSessionAndInstallationFiles() throws Exception {
106+
// Even if they look old, code explicitly ignores them
107+
108+
long twentyFiveHoursAgo = now() - TimeUnit.HOURS.toMillis(25);
67109

68-
// Old file should be gone
69-
assertFalse("Old cache file should be deleted", oldFile.exists());
110+
File sessionFile = createJsonCacheFile("Session", twentyFiveHoursAgo);
111+
File installationFile = createJsonCacheFile("Installation", twentyFiveHoursAgo);
70112

71-
// Recent file should still be there
72-
assertTrue("Recent cache file should not be deleted", recentFile.exists());
113+
assertTrue(sessionFile.exists());
114+
assertTrue(installationFile.exists());
115+
116+
ClearCache clearCache = new ClearCache();
117+
clearCache.onReceive(context, new Intent("com.contentstack.sdk.CLEAR_CACHE"));
73118

74-
// Session and Installation should not be deleted
119+
// They should still exist because of the name-based skip condition
75120
assertTrue("Session file should not be deleted", sessionFile.exists());
76121
assertTrue("Installation file should not be deleted", installationFile.exists());
77122
}
78123

124+
// ----------------------------------------------------
125+
// 4. File without valid JSON or timestamp should be ignored (no crash)
126+
// ----------------------------------------------------
79127
@Test
80-
public void testOnReceive_handlesEmptyDirectoryGracefully() {
81-
Context context = mock(Context.class);
128+
public void testOnReceive_invalidJsonOrNoTimestamp_doesNotCrashAndKeepsFile() throws Exception {
129+
File invalidFile = createPlainFile("invalid.json");
82130

83-
File cacheDir = createTempDir();
84-
when(context.getDir("ContentstackCache", 0)).thenReturn(cacheDir);
85-
86-
// Ensure directory is empty
87-
File[] existing = cacheDir.listFiles();
88-
if (existing != null) {
89-
for (File f : existing) {
90-
//noinspection ResultOfMethodCallIgnored
91-
f.delete();
92-
}
93-
}
131+
assertTrue(invalidFile.exists());
94132

95133
ClearCache clearCache = new ClearCache();
96-
clearCache.onReceive(context, new Intent("test.intent.CLEAR_CACHE"));
134+
clearCache.onReceive(context, new Intent("com.contentstack.sdk.CLEAR_CACHE"));
97135

98-
// No crash is success; directory should still exist
99-
assertTrue(cacheDir.exists());
136+
// Since getJsonFromCacheFile likely returns null or throws handled internally,
137+
// the file should not be deleted by our logic.
138+
assertTrue("Invalid file should still exist", invalidFile.exists());
100139
}
101140
}

0 commit comments

Comments
 (0)