feat: queue master-agent chat replies

This commit is contained in:
kris
2026-03-31 19:59:08 +08:00
parent e741952295
commit 013d9566be
8 changed files with 930 additions and 17 deletions

View File

@@ -69,6 +69,31 @@ public class BossApiClientDispatchPlansTest {
assertEquals("{}", connection.requestBody());
}
@Test
public void getProjectAgentControlsUsesScopedEndpoint() throws Exception {
RecordingConnection connection = new RecordingConnection(new URL("https://boss.hyzq.net/api/v1/projects/master-agent/agent-controls"));
RecordingBossApiClient apiClient = new RecordingBossApiClient(connection);
BossApiClient.ApiResponse response = apiClient.getProjectAgentControls("master-agent");
assertEquals(200, response.statusCode);
assertEquals("/api/v1/projects/master-agent/agent-controls", apiClient.lastPath);
assertEquals("GET", connection.requestMethodValue);
}
@Test
public void updateProjectAgentControlsWritesModelAndReasoningOverrides() throws Exception {
RecordingConnection connection = new RecordingConnection(new URL("https://boss.hyzq.net/api/v1/projects/master-agent/agent-controls"));
RecordingBossApiClient apiClient = new RecordingBossApiClient(connection);
BossApiClient.ApiResponse response = apiClient.updateProjectAgentControls("master-agent", "gpt-5.4", "high");
assertEquals(200, response.statusCode);
assertEquals("/api/v1/projects/master-agent/agent-controls", apiClient.lastPath);
assertEquals("POST", connection.requestMethodValue);
assertEquals("{\"modelOverride\":\"gpt-5.4\",\"reasoningEffortOverride\":\"high\"}", connection.requestBody());
}
@Test
public void sendProjectMessageUsesExtendedReadTimeoutForMasterAgent() throws Exception {
RecordingConnection connection = new RecordingConnection(new URL("https://boss.hyzq.net/api/v1/projects/master-agent/messages"));

View File

@@ -0,0 +1,113 @@
package com.hyzq.boss;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import org.json.JSONArray;
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.shadows.ShadowDialog;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 34)
public class ProjectDetailActivityMasterAgentMenuTest {
@Test
public void masterAgentMoreMenuShowsWechatActions() {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "master-agent")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "主 Agent");
ProjectDetailActivityUiTest.TestProjectDetailActivity activity = Robolectric
.buildActivity(ProjectDetailActivityUiTest.TestProjectDetailActivity.class, intent)
.setup()
.get();
ReflectionHelpers.callInstanceMethod(activity, "showMasterAgentMoreMenu");
android.app.Dialog latestDialog = ShadowDialog.getLatestDialog();
assertTrue(latestDialog instanceof AlertDialog);
AlertDialog actionDialog = (AlertDialog) latestDialog;
ListView listView = actionDialog.getListView();
assertMenuItem(listView, 0, "模型");
assertMenuItem(listView, 1, "推理强度");
assertMenuItem(listView, 2, "会话信息");
assertMenuItem(listView, 3, "刷新");
}
@Test
public void masterAgentWaitingStateRendersThinkingPlaceholder() throws Exception {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "master-agent")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "主 Agent");
ProjectDetailActivityUiTest.TestProjectDetailActivity activity = Robolectric
.buildActivity(ProjectDetailActivityUiTest.TestProjectDetailActivity.class, intent)
.setup()
.get();
ReflectionHelpers.setField(activity, "conversationInfoReady", true);
ReflectionHelpers.setField(activity, "masterAgentReplyWaiting", true);
ReflectionHelpers.setField(activity, "masterAgentReplyBaselineMessageId", "msg-user-1");
JSONObject project = new JSONObject()
.put("id", "master-agent")
.put("name", "主 Agent")
.put("messages", new JSONArray()
.put(new JSONObject()
.put("id", "msg-user-1")
.put("sender", "user")
.put("senderLabel", "Boss 超级管理员")
.put("body", "帮我检查当前主控")
.put("kind", "text")));
JSONObject payload = new JSONObject().put("project", project);
ReflectionHelpers.callInstanceMethod(
activity,
"renderProject",
ReflectionHelpers.ClassParameter.from(JSONObject.class, payload),
ReflectionHelpers.ClassParameter.from(JSONArray.class, null),
ReflectionHelpers.ClassParameter.from(JSONObject.class, null)
);
View contentRoot = activity.findViewById(R.id.screen_content);
assertNotNull(contentRoot);
assertTrue(viewTreeContainsText(contentRoot, "主 Agent 思考中"));
}
private static void assertMenuItem(ListView listView, int index, String expectedText) {
View item = listView.getAdapter().getView(index, null, listView);
assertTrue(viewTreeContainsText(item, expectedText));
}
private static boolean viewTreeContainsText(View root, String expectedText) {
if (root instanceof TextView) {
CharSequence text = ((TextView) root).getText();
if (expectedText.contentEquals(text)) {
return true;
}
}
if (!(root instanceof ViewGroup)) {
return false;
}
ViewGroup group = (ViewGroup) root;
for (int index = 0; index < group.getChildCount(); index += 1) {
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
return true;
}
}
return false;
}
}

View File

@@ -232,6 +232,27 @@ public class ProjectDetailActivityUiTest {
assertFalse(viewTreeContainsText(messageView, "Boss 超级管理员 · 10:26"));
}
@Test
public void masterAgentHeaderUsesWechatMoreMenuLabel() {
Intent intent = new Intent()
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "master-agent")
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "主 Agent");
TestProjectDetailActivity activity = Robolectric
.buildActivity(TestProjectDetailActivity.class, intent)
.setup()
.get();
ReflectionHelpers.setField(activity, "conversationInfoReady", true);
ReflectionHelpers.setField(activity, "currentScreenTitle", "主 Agent");
ReflectionHelpers.setField(activity, "currentScreenSubtitle", "单聊会话");
ReflectionHelpers.callInstanceMethod(activity, "updateSelectionUi");
Button headerAction = activity.findViewById(R.id.screen_header_action);
assertEquals(View.VISIBLE, headerAction.getVisibility());
assertEquals("...", headerAction.getText().toString());
}
@Test
public void outgoingAttachmentMetaPrefersTimeOnly() throws Exception {
Intent intent = new Intent()