import test from "node:test"; import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; async function readSource(path: string) { return readFile(new URL(path, import.meta.url), "utf8"); } test("ProjectDetailActivity reads group dispatch and participant state from project detail payload", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java"); assert.match( source, /detailResponse\.json\.optJSONArray\("dispatchPlans"\)/, "expected project chat detail refreshes to read dispatchPlans directly from project detail payload", ); assert.match( source, /detailResponse\.json\.optJSONObject\("participantsPayload"\)/, "expected project chat detail refreshes to read participantsPayload directly from project detail payload", ); assert.doesNotMatch( source, /apiClient\.getDispatchPlans\(projectId\)/, "expected project chat detail refreshes to stop issuing a separate dispatch plans request", ); assert.doesNotMatch( source, /apiClient\.getConversationParticipants\(projectId\)/, "expected project chat detail refreshes to stop issuing a separate participants request", ); }); test("GroupInfoActivity derives participants state from project detail payload", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/GroupInfoActivity.java"); assert.match( source, /JSONObject participantsPayload = extractParticipantsPayload\(detailResponse\.json\);/, "expected group info page to derive participants from the detail payload before rendering", ); assert.doesNotMatch( source, /apiClient\.getConversationParticipants\(projectId\)/, "expected group info page to stop issuing a separate participants request", ); assert.match( source, /groupRepairJustApplied = true;/, "expected group info page to persist a successful repair flag across the immediate reload", ); assert.match( source, /if \(groupRepairJustApplied\) \{/, "expected group info page to render a durable success acknowledgement after repair reload", ); assert.match( source, /群成员已更新/, "expected group info page to show explicit success copy after a repair completes", ); }); test("ConversationInfoActivity derives participants state from project detail payload", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java"); assert.match( source, /JSONObject participantsPayload = extractParticipantsPayload\(detailResponse\.json\);/, "expected conversation info page to derive participants from the detail payload before rendering", ); assert.doesNotMatch( source, /apiClient\.getConversationParticipants\(projectId\)/, "expected conversation info page to stop issuing a separate participants request", ); }); test("GroupCreateActivity reuses project detail payload when launched from a source conversation", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/GroupCreateActivity.java"); assert.match( source, /BossApiClient\.ApiResponse detailResponse = apiClient\.getProjectDetail\(sourceProjectId\);/, "expected group creation from a source conversation to load source detail once", ); assert.match( source, /JSONObject participantsPayload = extractParticipantsPayload\(detailResponse\.json,\s*sourceProjectId\);/, "expected group creation page to derive source participants from project detail payload", ); assert.doesNotMatch( source, /apiClient\.getConversationParticipants\(sourceProjectId\)/, "expected group creation page to stop issuing a separate participants request for the source conversation", ); }); test("BossApiClient no longer keeps unused group detail compatibility wrappers", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/BossApiClient.java"); assert.doesNotMatch( source, /public ApiResponse getDispatchPlans\(String projectId\)/, "expected BossApiClient to drop the unused dispatch plans wrapper once all screens read from project detail", ); assert.doesNotMatch( source, /public ApiResponse getConversationParticipants\(String projectId\)/, "expected BossApiClient to drop the unused participants wrapper once all screens read from project detail", ); }); test("ProjectDetailActivity uses a distinct repair meta copy when only one valid thread remains", async () => { const source = await readSource("../android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java"); assert.match( source, /validParticipantCount > 0[\s\S]*当前仅有 "\s*\+\s*validParticipantCount\s*\+\s*" 个真实线程成员/, "expected group repair card copy to distinguish one-valid-thread state from zero-valid-thread state", ); });