Skip to content

Commit 8886d6a

Browse files
Allow2 Devruvnet
andcommitted
Add tests
JUnit 5 tests: VoiceCodeTest (generate/verify, HMAC-SHA256, cross-secret), CheckResultTest (fromApiResponse, inference, day type keys), PermissionCheckerTest (caching, 401/403 handling, normalizeActivities), MemoryCacheTest (TTL). MockHttpClient for test isolation. Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent 0a1f616 commit 8886d6a

File tree

5 files changed

+860
-0
lines changed

5 files changed

+860
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package com.allow2.service;
2+
3+
import com.allow2.service.models.Activity;
4+
import com.allow2.service.models.CheckResult;
5+
import com.allow2.service.models.DayType;
6+
import org.json.JSONArray;
7+
import org.json.JSONObject;
8+
import org.junit.jupiter.api.Test;
9+
10+
import java.util.Arrays;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
14+
/**
15+
* Tests for CheckResult model parsing and query methods.
16+
*/
17+
class CheckResultTest {
18+
19+
@Test
20+
void constructorSetsAllProperties() {
21+
Activity internet = new Activity(1, "Internet", true, 3600, false, true);
22+
DayType today = new DayType(1, "School Day");
23+
DayType tomorrow = new DayType(2, "Weekend");
24+
JSONObject raw = new JSONObject().put("test", "data");
25+
26+
CheckResult result = new CheckResult(true, Arrays.asList(internet), today, tomorrow, raw);
27+
28+
assertTrue(result.isAllowed());
29+
assertEquals(1, result.getActivities().size());
30+
assertEquals("School Day", result.getTodayDayType().getName());
31+
assertEquals("Weekend", result.getTomorrowDayType().getName());
32+
assertEquals("data", result.getRaw().getString("test"));
33+
}
34+
35+
@Test
36+
void getActivityReturnsMatchingActivity() {
37+
Activity internet = new Activity(1, "Internet", true, 3600, false, true);
38+
Activity gaming = new Activity(3, "Gaming", false, 0, true, false);
39+
40+
CheckResult result = new CheckResult(false, Arrays.asList(internet, gaming), null, null, null);
41+
42+
Activity found = result.getActivity(3);
43+
assertNotNull(found);
44+
assertEquals("Gaming", found.getName());
45+
assertTrue(found.isBanned());
46+
}
47+
48+
@Test
49+
void getActivityReturnsNullForMissing() {
50+
CheckResult result = new CheckResult(true, Arrays.asList(), null, null, null);
51+
assertNull(result.getActivity(999));
52+
}
53+
54+
@Test
55+
void isActivityAllowedReturnsTrueForAllowedActivity() {
56+
Activity internet = new Activity(1, "Internet", true, 3600, false, true);
57+
CheckResult result = new CheckResult(true, Arrays.asList(internet), null, null, null);
58+
assertTrue(result.isActivityAllowed(1));
59+
}
60+
61+
@Test
62+
void isActivityAllowedReturnsFalseForBlockedActivity() {
63+
Activity gaming = new Activity(3, "Gaming", false, 0, true, false);
64+
CheckResult result = new CheckResult(false, Arrays.asList(gaming), null, null, null);
65+
assertFalse(result.isActivityAllowed(3));
66+
}
67+
68+
@Test
69+
void isActivityAllowedReturnsFalseForMissingActivity() {
70+
CheckResult result = new CheckResult(true, Arrays.asList(), null, null, null);
71+
assertFalse(result.isActivityAllowed(999));
72+
}
73+
74+
@Test
75+
void getRemainingSecondsReturnsValueForExistingActivity() {
76+
Activity internet = new Activity(1, "Internet", true, 1800, false, true);
77+
CheckResult result = new CheckResult(true, Arrays.asList(internet), null, null, null);
78+
assertEquals(1800, result.getRemainingSeconds(1));
79+
}
80+
81+
@Test
82+
void getRemainingSecondsReturnsZeroForMissingActivity() {
83+
CheckResult result = new CheckResult(true, Arrays.asList(), null, null, null);
84+
assertEquals(0, result.getRemainingSeconds(999));
85+
}
86+
87+
@Test
88+
void fromApiResponseParsesAllowedResponse() {
89+
JSONObject response = new JSONObject();
90+
response.put("allowed", true);
91+
92+
JSONArray activities = new JSONArray();
93+
activities.put(new JSONObject()
94+
.put("id", 1).put("activity", "Internet").put("allowed", true)
95+
.put("remaining", 3600).put("banned", false).put("timeblock", true));
96+
activities.put(new JSONObject()
97+
.put("id", 3).put("activity", "Gaming").put("allowed", true)
98+
.put("remaining", 1800).put("banned", false).put("timeblock", true));
99+
response.put("activities", activities);
100+
response.put("dayType", new JSONObject().put("id", 1).put("name", "School Day"));
101+
response.put("tomorrowDayType", new JSONObject().put("id", 2).put("name", "Weekend"));
102+
103+
CheckResult result = CheckResult.fromApiResponse(response);
104+
105+
assertTrue(result.isAllowed());
106+
assertEquals(2, result.getActivities().size());
107+
assertEquals("Internet", result.getActivities().get(0).getName());
108+
assertEquals(3600, result.getActivities().get(0).getRemaining());
109+
assertNotNull(result.getTodayDayType());
110+
assertEquals("School Day", result.getTodayDayType().getName());
111+
assertNotNull(result.getTomorrowDayType());
112+
assertEquals("Weekend", result.getTomorrowDayType().getName());
113+
}
114+
115+
@Test
116+
void fromApiResponseParsesBlockedResponse() {
117+
JSONObject response = new JSONObject();
118+
response.put("allowed", false);
119+
120+
JSONArray activities = new JSONArray();
121+
activities.put(new JSONObject()
122+
.put("id", 3).put("activity", "Gaming").put("allowed", false)
123+
.put("remaining", 0).put("banned", true).put("timeblock", false));
124+
response.put("activities", activities);
125+
response.put("dayType", new JSONObject().put("id", 1).put("name", "School Day"));
126+
127+
CheckResult result = CheckResult.fromApiResponse(response);
128+
129+
assertFalse(result.isAllowed());
130+
assertEquals(1, result.getActivities().size());
131+
assertTrue(result.getActivities().get(0).isBanned());
132+
assertFalse(result.getActivities().get(0).isAllowed());
133+
}
134+
135+
@Test
136+
void fromApiResponseUsesAlternativeDayTypeKeys() {
137+
JSONObject response = new JSONObject();
138+
response.put("activities", new JSONArray());
139+
response.put("today", new JSONObject().put("id", 5).put("name", "Holiday"));
140+
response.put("tomorrow", new JSONObject().put("id", 1).put("name", "School Day"));
141+
142+
CheckResult result = CheckResult.fromApiResponse(response);
143+
144+
assertNotNull(result.getTodayDayType());
145+
assertEquals("Holiday", result.getTodayDayType().getName());
146+
assertNotNull(result.getTomorrowDayType());
147+
assertEquals("School Day", result.getTomorrowDayType().getName());
148+
}
149+
150+
@Test
151+
void fromApiResponseHandlesMissingDayTypes() {
152+
JSONObject response = new JSONObject();
153+
response.put("activities", new JSONArray());
154+
155+
CheckResult result = CheckResult.fromApiResponse(response);
156+
157+
assertNull(result.getTodayDayType());
158+
assertNull(result.getTomorrowDayType());
159+
}
160+
161+
@Test
162+
void fromApiResponseInfersAllowedFromActivitiesWhenNotExplicit() {
163+
JSONObject response = new JSONObject();
164+
JSONArray activities = new JSONArray();
165+
activities.put(new JSONObject().put("id", 1).put("activity", "Internet").put("allowed", true).put("remaining", 3600));
166+
activities.put(new JSONObject().put("id", 3).put("activity", "Gaming").put("allowed", false).put("remaining", 0));
167+
response.put("activities", activities);
168+
169+
CheckResult result = CheckResult.fromApiResponse(response);
170+
171+
// One activity is not allowed, so global should be false
172+
assertFalse(result.isAllowed());
173+
}
174+
175+
@Test
176+
void fromApiResponseExplicitAllowedOverridesInference() {
177+
JSONObject response = new JSONObject();
178+
response.put("allowed", true);
179+
JSONArray activities = new JSONArray();
180+
activities.put(new JSONObject().put("id", 1).put("activity", "Internet").put("allowed", true).put("remaining", 3600));
181+
activities.put(new JSONObject().put("id", 3).put("activity", "Gaming").put("allowed", false).put("remaining", 0));
182+
response.put("activities", activities);
183+
184+
CheckResult result = CheckResult.fromApiResponse(response);
185+
186+
assertTrue(result.isAllowed());
187+
}
188+
189+
@Test
190+
void fromApiResponsePreservesRawData() {
191+
JSONObject response = new JSONObject();
192+
response.put("allowed", true);
193+
response.put("activities", new JSONArray());
194+
response.put("extra_field", "extra_value");
195+
196+
CheckResult result = CheckResult.fromApiResponse(response);
197+
198+
assertEquals("extra_value", result.getRaw().getString("extra_field"));
199+
}
200+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.allow2.service;
2+
3+
import com.allow2.service.cache.MemoryCache;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
/**
10+
* Tests for MemoryCache TTL behavior.
11+
*/
12+
class MemoryCacheTest {
13+
14+
private MemoryCache cache;
15+
16+
@BeforeEach
17+
void setUp() {
18+
cache = new MemoryCache();
19+
}
20+
21+
@Test
22+
void getReturnsNullForMissingKey() {
23+
assertNull(cache.get("nonexistent"));
24+
}
25+
26+
@Test
27+
void setAndGetReturnsValue() {
28+
cache.set("key1", "value1", 60);
29+
assertEquals("value1", cache.get("key1"));
30+
}
31+
32+
@Test
33+
void deleteRemovesEntry() {
34+
cache.set("key1", "value1", 60);
35+
cache.delete("key1");
36+
assertNull(cache.get("key1"));
37+
}
38+
39+
@Test
40+
void expiredEntryReturnsNull() {
41+
// Set with 0 TTL (expires immediately)
42+
cache.set("key1", "value1", 0);
43+
assertNull(cache.get("key1"));
44+
}
45+
46+
@Test
47+
void setOverwritesPreviousValue() {
48+
cache.set("key1", "value1", 60);
49+
cache.set("key1", "value2", 60);
50+
assertEquals("value2", cache.get("key1"));
51+
}
52+
53+
@Test
54+
void deleteNonexistentKeyDoesNotThrow() {
55+
assertDoesNotThrow(() -> cache.delete("nonexistent"));
56+
}
57+
58+
@Test
59+
void differentKeysAreSeparate() {
60+
cache.set("key1", "value1", 60);
61+
cache.set("key2", "value2", 60);
62+
assertEquals("value1", cache.get("key1"));
63+
assertEquals("value2", cache.get("key2"));
64+
}
65+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.allow2.service;
2+
3+
import org.json.JSONObject;
4+
5+
import java.util.*;
6+
import java.util.concurrent.ConcurrentLinkedQueue;
7+
8+
/**
9+
* Mock HTTP client for testing. Enqueue responses and inspect requests.
10+
*/
11+
public final class MockHttpClient implements HttpClientInterface {
12+
13+
private final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
14+
private final List<Map<String, Object>> requests = new ArrayList<>();
15+
16+
/**
17+
* Add a canned response to the queue.
18+
*
19+
* @param statusCode HTTP status code.
20+
* @param body Response body as a map (will be JSON-serialized).
21+
*/
22+
public void addResponse(int statusCode, Map<String, Object> body) {
23+
responseQueue.add(new HttpResponse(statusCode, new JSONObject(body).toString()));
24+
}
25+
26+
/**
27+
* Get the total number of requests made.
28+
*/
29+
public int getRequestCount() {
30+
return requests.size();
31+
}
32+
33+
/**
34+
* Get the last request made.
35+
*/
36+
public Map<String, Object> getLastRequest() {
37+
if (requests.isEmpty()) {
38+
return null;
39+
}
40+
return requests.get(requests.size() - 1);
41+
}
42+
43+
/**
44+
* Get all recorded requests.
45+
*/
46+
public List<Map<String, Object>> getAllRequests() {
47+
return Collections.unmodifiableList(requests);
48+
}
49+
50+
@Override
51+
public HttpResponse post(String url, Map<String, Object> data, Map<String, String> headers) {
52+
Map<String, Object> record = new LinkedHashMap<>();
53+
record.put("method", "POST");
54+
record.put("url", url);
55+
record.put("data", data);
56+
record.put("headers", headers);
57+
requests.add(record);
58+
59+
HttpResponse response = responseQueue.poll();
60+
if (response == null) {
61+
return new HttpResponse(500, "{\"error\":\"No mock response queued\"}");
62+
}
63+
return response;
64+
}
65+
66+
@Override
67+
public HttpResponse get(String url, Map<String, String> headers) {
68+
Map<String, Object> record = new LinkedHashMap<>();
69+
record.put("method", "GET");
70+
record.put("url", url);
71+
record.put("data", Collections.emptyMap());
72+
record.put("headers", headers);
73+
requests.add(record);
74+
75+
HttpResponse response = responseQueue.poll();
76+
if (response == null) {
77+
return new HttpResponse(500, "{\"error\":\"No mock response queued\"}");
78+
}
79+
return response;
80+
}
81+
}

0 commit comments

Comments
 (0)