feat: add realtime sync and import project understanding
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class BossRealtimeClientTest {
|
||||
@Test
|
||||
public void parseEventBlockExtractsEventNameAndJsonPayload() {
|
||||
BossRealtimeEvent event = BossRealtimeClient.parseEventBlock(
|
||||
"event: project.messages.updated\n" +
|
||||
"data: {\"projectId\":\"project-1\",\"status\":\"completed\"}\n\n"
|
||||
);
|
||||
|
||||
assertEquals("project.messages.updated", event.eventName);
|
||||
assertEquals("project-1", event.payload.optString("projectId"));
|
||||
assertEquals("completed", event.payload.optString("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEventBlockReturnsNullForKeepaliveComment() {
|
||||
assertNull(BossRealtimeClient.parseEventBlock(": keepalive\n\n"));
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,9 @@ public class DeviceImportDraftActivityTest {
|
||||
"applyPayload",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildPendingDraft()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null)
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, null)
|
||||
);
|
||||
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
@@ -64,7 +66,9 @@ public class DeviceImportDraftActivityTest {
|
||||
"applyPayload",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildAppliedDraft()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildAppliedResolution()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null)
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, null)
|
||||
);
|
||||
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
@@ -92,13 +96,18 @@ public class DeviceImportDraftActivityTest {
|
||||
"applyPayload",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildPendingResolutionDraft()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, null),
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildQueuedReviewTask())
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildQueuedReviewTask()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, buildUnderstandingTasks()),
|
||||
ReflectionHelpers.ClassParameter.from(JSONArray.class, buildProjectUnderstandings())
|
||||
);
|
||||
|
||||
View content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent 审核中"));
|
||||
assertTrue(viewTreeContainsText(content, "审核任务"));
|
||||
assertTrue(viewTreeContainsText(content, "状态:queued"));
|
||||
assertTrue(viewTreeContainsText(content, "项目理解"));
|
||||
assertTrue(viewTreeContainsText(content, "北区试产线回归"));
|
||||
assertTrue(viewTreeContainsText(content, "树莓派二代接入与联调"));
|
||||
}
|
||||
|
||||
private static JSONObject buildPendingDraft() throws Exception {
|
||||
@@ -200,6 +209,27 @@ public class DeviceImportDraftActivityTest {
|
||||
.put("status", "queued");
|
||||
}
|
||||
|
||||
private static JSONArray buildUnderstandingTasks() throws Exception {
|
||||
return new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("taskId", "mastertask-understanding-1")
|
||||
.put("candidateId", "candidate-1")
|
||||
.put("threadDisplayName", "北区试产线回归")
|
||||
.put("status", "completed"));
|
||||
}
|
||||
|
||||
private static JSONArray buildProjectUnderstandings() throws Exception {
|
||||
return new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("candidateId", "candidate-1")
|
||||
.put("threadDisplayName", "北区试产线回归")
|
||||
.put("projectGoal", "完成树莓派二代接入与联调")
|
||||
.put("currentProgress", "正在核对接线和控制链路")
|
||||
.put("technicalArchitecture", "Next.js 控制台 + local-agent + Codex 线程")
|
||||
.put("currentBlockers", "串口稳定性待验证")
|
||||
.put("recommendedNextStep", "先确认串口日志"));
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class MainActivityRealtimeTest {
|
||||
@Test
|
||||
public void conversationRealtimeEventRefreshesVisibleConversationTab() throws Exception {
|
||||
TestMainActivity activity = Robolectric.buildActivity(TestMainActivity.class).setup().resume().get();
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"handleRealtimeEvent",
|
||||
ReflectionHelpers.ClassParameter.from(
|
||||
BossRealtimeEvent.class,
|
||||
new BossRealtimeEvent("conversation.updated", new JSONObject().put("projectId", "project-1"))
|
||||
)
|
||||
);
|
||||
|
||||
assertEquals(1, activity.refreshCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void devicesRealtimeEventDoesNotRefreshConversationTab() throws Exception {
|
||||
TestMainActivity activity = Robolectric.buildActivity(TestMainActivity.class).setup().resume().get();
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"handleRealtimeEvent",
|
||||
ReflectionHelpers.ClassParameter.from(
|
||||
BossRealtimeEvent.class,
|
||||
new BossRealtimeEvent("devices.updated", new JSONObject().put("deviceId", "mac-studio"))
|
||||
)
|
||||
);
|
||||
|
||||
assertEquals(0, activity.refreshCount);
|
||||
}
|
||||
|
||||
public static class TestMainActivity extends MainActivity {
|
||||
int refreshCount;
|
||||
|
||||
@Override
|
||||
void refreshCurrentTab() {
|
||||
refreshCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class ProjectDetailActivityRealtimeTest {
|
||||
@Test
|
||||
public void matchingProjectMessageEventTriggersReload() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线");
|
||||
TestRealtimeProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestRealtimeProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.resume()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"handleRealtimeEvent",
|
||||
ReflectionHelpers.ClassParameter.from(
|
||||
BossRealtimeEvent.class,
|
||||
new BossRealtimeEvent("project.messages.updated", new JSONObject().put("projectId", "project-1"))
|
||||
)
|
||||
);
|
||||
|
||||
assertEquals(1, activity.reloadCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unrelatedProjectMessageEventDoesNotTriggerReload() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "project-1")
|
||||
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "北区试产线");
|
||||
TestRealtimeProjectDetailActivity activity = Robolectric
|
||||
.buildActivity(TestRealtimeProjectDetailActivity.class, intent)
|
||||
.setup()
|
||||
.resume()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"handleRealtimeEvent",
|
||||
ReflectionHelpers.ClassParameter.from(
|
||||
BossRealtimeEvent.class,
|
||||
new BossRealtimeEvent("project.messages.updated", new JSONObject().put("projectId", "project-2"))
|
||||
)
|
||||
);
|
||||
|
||||
assertEquals(0, activity.reloadCount);
|
||||
}
|
||||
|
||||
public static class TestRealtimeProjectDetailActivity extends ProjectDetailActivity {
|
||||
int reloadCount;
|
||||
|
||||
@Override
|
||||
boolean shouldLoadOnCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
reloadCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user