fix: recover master-agent takeover session on android
This commit is contained in:
@@ -40,7 +40,7 @@ public class MasterAgentTakeoverActivity extends BossScreenActivity {
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.getProjectAgentControls(projectId);
|
||||
BossApiClient.ApiResponse response = loadTakeoverControls();
|
||||
if (!response.ok()) {
|
||||
throw new IllegalStateException(response.message());
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public class MasterAgentTakeoverActivity extends BossScreenActivity {
|
||||
setRefreshing(true);
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
BossApiClient.ApiResponse response = apiClient.updateProjectTakeoverSettings(
|
||||
BossApiClient.ApiResponse response = saveTakeoverControls(
|
||||
projectId,
|
||||
null,
|
||||
enabled
|
||||
@@ -120,6 +120,44 @@ public class MasterAgentTakeoverActivity extends BossScreenActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private BossApiClient.ApiResponse loadTakeoverControls() throws Exception {
|
||||
BossApiClient.ApiResponse response = apiClient.getProjectAgentControls(projectId);
|
||||
if (response.ok() || !isUnauthorized(response)) {
|
||||
return response;
|
||||
}
|
||||
BossApiClient.ApiResponse loginResponse = apiClient.autoLogin();
|
||||
if (!loginResponse.ok()) {
|
||||
return response;
|
||||
}
|
||||
return apiClient.getProjectAgentControls(projectId);
|
||||
}
|
||||
|
||||
private BossApiClient.ApiResponse saveTakeoverControls(
|
||||
String projectId,
|
||||
@Nullable Boolean takeoverEnabled,
|
||||
@Nullable Boolean globalTakeoverEnabled
|
||||
) throws Exception {
|
||||
BossApiClient.ApiResponse response = apiClient.updateProjectTakeoverSettings(
|
||||
projectId,
|
||||
takeoverEnabled,
|
||||
globalTakeoverEnabled
|
||||
);
|
||||
if (response.ok() || !isUnauthorized(response)) {
|
||||
return response;
|
||||
}
|
||||
BossApiClient.ApiResponse loginResponse = apiClient.autoLogin();
|
||||
if (!loginResponse.ok()) {
|
||||
return response;
|
||||
}
|
||||
return apiClient.updateProjectTakeoverSettings(projectId, takeoverEnabled, globalTakeoverEnabled);
|
||||
}
|
||||
|
||||
private boolean isUnauthorized(BossApiClient.ApiResponse response) {
|
||||
return response != null
|
||||
&& response.statusCode == 401
|
||||
&& "UNAUTHORIZED".equals(response.message());
|
||||
}
|
||||
|
||||
private void updateSaveAvailability() {
|
||||
if (headerActionButton != null) {
|
||||
headerActionButton.setEnabled(contentLoaded);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@@ -15,6 +18,11 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class MasterAgentTakeoverActivityTest {
|
||||
@@ -44,6 +52,61 @@ public class MasterAgentTakeoverActivityTest {
|
||||
assertTrue(viewTreeContainsText(content, "线程会话默认跟随全局协同推进"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadAutoRecoversUnauthorizedAndRendersTakeoverSettings() {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
TestMasterAgentTakeoverActivity activity = Robolectric
|
||||
.buildActivity(TestMasterAgentTakeoverActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
RecordingBossApiClient apiClient = new RecordingBossApiClient(
|
||||
activity.getSharedPreferences("master-agent-takeover-test", Context.MODE_PRIVATE),
|
||||
"https://boss.hyzq.net"
|
||||
);
|
||||
ReflectionHelpers.setField(activity, "apiClient", apiClient);
|
||||
ReflectionHelpers.setField(activity, "reloadEnabled", true);
|
||||
ReflectionHelpers.setField(activity, "executor", new DirectExecutorService());
|
||||
|
||||
activity.reload();
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "全局主 Agent 协同接管"));
|
||||
assertEquals(2, apiClient.getProjectAgentControlsCalls);
|
||||
assertEquals(1, apiClient.autoLoginCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveAutoRecoversUnauthorizedAndPersistsTakeoverSettings() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||
.putExtra(MasterAgentTakeoverActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||
TestMasterAgentTakeoverActivity activity = Robolectric
|
||||
.buildActivity(TestMasterAgentTakeoverActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
RecordingBossApiClient apiClient = new RecordingBossApiClient(
|
||||
activity.getSharedPreferences("master-agent-takeover-save-test", Context.MODE_PRIVATE),
|
||||
"https://boss.hyzq.net"
|
||||
);
|
||||
apiClient.failFirstLoad = false;
|
||||
apiClient.failFirstSave = true;
|
||||
ReflectionHelpers.setField(activity, "apiClient", apiClient);
|
||||
ReflectionHelpers.setField(activity, "reloadEnabled", true);
|
||||
ReflectionHelpers.setField(activity, "executor", new DirectExecutorService());
|
||||
|
||||
activity.reload();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "saveTakeoverSettings");
|
||||
|
||||
assertEquals(1, apiClient.updateTakeoverCalls);
|
||||
assertEquals(1, apiClient.retryUpdateTakeoverCalls);
|
||||
assertEquals(1, apiClient.autoLoginCalls);
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
@@ -62,4 +125,122 @@ public class MasterAgentTakeoverActivityTest {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final class TestMasterAgentTakeoverActivity extends MasterAgentTakeoverActivity {
|
||||
private boolean reloadEnabled;
|
||||
|
||||
@Override
|
||||
protected void reload() {
|
||||
if (!reloadEnabled) {
|
||||
return;
|
||||
}
|
||||
super.reload();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingBossApiClient extends BossApiClient {
|
||||
private int getProjectAgentControlsCalls;
|
||||
private int autoLoginCalls;
|
||||
private int updateTakeoverCalls;
|
||||
private int retryUpdateTakeoverCalls;
|
||||
private boolean failFirstLoad = true;
|
||||
private boolean failFirstSave;
|
||||
|
||||
RecordingBossApiClient(android.content.SharedPreferences prefs, String baseUrl) {
|
||||
super(prefs, baseUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getProjectAgentControls(String projectId) throws java.io.IOException, org.json.JSONException {
|
||||
getProjectAgentControlsCalls += 1;
|
||||
if (failFirstLoad && getProjectAgentControlsCalls == 1) {
|
||||
return ApiResponse.error(
|
||||
401,
|
||||
new JSONObject()
|
||||
.put("ok", false)
|
||||
.put("message", "UNAUTHORIZED")
|
||||
);
|
||||
}
|
||||
return new ApiResponse(
|
||||
200,
|
||||
new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("controls", new JSONObject().put("globalTakeoverEnabled", true))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse autoLogin() throws java.io.IOException, org.json.JSONException {
|
||||
autoLoginCalls += 1;
|
||||
return new ApiResponse(
|
||||
200,
|
||||
new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("session", new JSONObject().put("account", "17600003315"))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse updateProjectTakeoverSettings(
|
||||
String projectId,
|
||||
Boolean takeoverEnabled,
|
||||
Boolean globalTakeoverEnabled
|
||||
) throws java.io.IOException, org.json.JSONException {
|
||||
if (failFirstSave && updateTakeoverCalls == 0) {
|
||||
updateTakeoverCalls += 1;
|
||||
return ApiResponse.error(
|
||||
401,
|
||||
new JSONObject()
|
||||
.put("ok", false)
|
||||
.put("message", "UNAUTHORIZED")
|
||||
);
|
||||
}
|
||||
if (failFirstSave) {
|
||||
retryUpdateTakeoverCalls += 1;
|
||||
} else {
|
||||
updateTakeoverCalls += 1;
|
||||
}
|
||||
return new ApiResponse(
|
||||
200,
|
||||
new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("controls", new JSONObject().put("globalTakeoverEnabled", true))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DirectExecutorService extends AbstractExecutorService {
|
||||
private boolean shutdown;
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown = true;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
command.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user