fix: preserve user chat messages after ai onboarding

This commit is contained in:
kris
2026-03-31 16:30:21 +08:00
parent 70494fc15b
commit 71d0979292
4 changed files with 223 additions and 4 deletions

View File

@@ -119,6 +119,27 @@ public class BossApiClientDispatchPlansTest {
);
}
@Test
public void rememberIdentityDoesNotOverwriteSessionIdentityFromAiAccountOnboardingResponse() throws Exception {
InMemorySharedPreferences prefs = new InMemorySharedPreferences();
prefs.edit()
.putString("account", "17600003315")
.putString("display_name", "Boss 超级管理员")
.apply();
BossApiClient apiClient = new BossApiClient(prefs, "https://boss.hyzq.net");
JSONObject onboardingResponse = new JSONObject()
.put("ok", true)
.put("accountId", "openai-api-primary")
.put("displayName", "OpenAI 平台账号")
.put("message", "OpenAI 平台账号已登录,并设为当前主控。");
apiClient.rememberIdentity(onboardingResponse);
assertEquals("17600003315", apiClient.getAccountLabel());
assertEquals("Boss 超级管理员", apiClient.getDisplayName());
}
@Test
public void onboardMasterNodeFallsBackToGenericAccountCreationWhenDedicatedRouteMissing() throws Exception {
RecordingConnection dedicated = new RecordingConnection(

View File

@@ -6,6 +6,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import android.content.Intent;
import android.content.SharedPreferences;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -24,6 +25,11 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowDialog;
import org.robolectric.util.ReflectionHelpers;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 34)
public class ProjectDetailActivityUiTest {
@@ -191,6 +197,41 @@ public class ProjectDetailActivityUiTest {
assertFalse(viewTreeContainsText(attachmentView, "已分析"));
}
@Test
public void userMessageRemainsOutgoingWhenAiAccountDisplayNameDiffers() throws Exception {
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();
InMemorySharedPreferences prefs = new InMemorySharedPreferences();
prefs.edit()
.putString("account", "17600003315")
.putString("display_name", "OpenAI 平台账号")
.apply();
ReflectionHelpers.setField(activity, "apiClient", new BossApiClient(prefs, "https://boss.hyzq.net"));
JSONObject message = new JSONObject()
.put("id", "msg-user-1")
.put("sender", "user")
.put("senderLabel", "Boss 超级管理员")
.put("body", "请只回复一句:聊天链路自检正常。")
.put("kind", "text")
.put("sentAt", "2026-03-31T10:26:00.000Z");
View messageView = ReflectionHelpers.callInstanceMethod(
activity,
"buildMessageView",
ReflectionHelpers.ClassParameter.from(JSONObject.class, message)
);
assertTrue(viewTreeContainsText(messageView, "10:26"));
assertFalse(viewTreeContainsText(messageView, "Boss 超级管理员 · 10:26"));
}
@Test
public void outgoingAttachmentMetaPrefersTimeOnly() throws Exception {
Intent intent = new Intent()
@@ -384,4 +425,137 @@ public class ProjectDetailActivityUiTest {
return false;
}
}
private static final class InMemorySharedPreferences implements SharedPreferences {
private final Map<String, Object> values = new HashMap<>();
@Override
public Map<String, ?> getAll() {
return Collections.unmodifiableMap(values);
}
@Override
public String getString(String key, String defValue) {
Object value = values.get(key);
return value instanceof String ? (String) value : defValue;
}
@Override
public Set<String> getStringSet(String key, Set<String> defValues) {
Object value = values.get(key);
return value instanceof Set ? (Set<String>) value : defValues;
}
@Override
public int getInt(String key, int defValue) {
Object value = values.get(key);
return value instanceof Integer ? (Integer) value : defValue;
}
@Override
public long getLong(String key, long defValue) {
Object value = values.get(key);
return value instanceof Long ? (Long) value : defValue;
}
@Override
public float getFloat(String key, float defValue) {
Object value = values.get(key);
return value instanceof Float ? (Float) value : defValue;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
Object value = values.get(key);
return value instanceof Boolean ? (Boolean) value : defValue;
}
@Override
public boolean contains(String key) {
return values.containsKey(key);
}
@Override
public Editor edit() {
return new Editor() {
private final Map<String, Object> staged = new HashMap<>();
private boolean clearRequested = false;
@Override
public Editor putString(String key, String value) {
staged.put(key, value);
return this;
}
@Override
public Editor putStringSet(String key, Set<String> value) {
staged.put(key, value);
return this;
}
@Override
public Editor putInt(String key, int value) {
staged.put(key, value);
return this;
}
@Override
public Editor putLong(String key, long value) {
staged.put(key, value);
return this;
}
@Override
public Editor putFloat(String key, float value) {
staged.put(key, value);
return this;
}
@Override
public Editor putBoolean(String key, boolean value) {
staged.put(key, value);
return this;
}
@Override
public Editor remove(String key) {
staged.put(key, null);
return this;
}
@Override
public Editor clear() {
clearRequested = true;
staged.clear();
return this;
}
@Override
public boolean commit() {
apply();
return true;
}
@Override
public void apply() {
if (clearRequested) {
values.clear();
}
for (Map.Entry<String, Object> entry : staged.entrySet()) {
if (entry.getValue() == null) {
values.remove(entry.getKey());
} else {
values.put(entry.getKey(), entry.getValue());
}
}
}
};
}
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {}
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {}
}
}