refactor(team): extract error policies
This commit is contained in:
parent
e723a62ed0
commit
7f828c2e63
3 changed files with 101 additions and 27 deletions
|
|
@ -45,6 +45,11 @@ import {
|
|||
normalizeTeamGetDataOptions,
|
||||
} from '../team/teamDataRequestKeys';
|
||||
import { selectTeamDataForName } from '../team/teamDataSelectors';
|
||||
import {
|
||||
mapReviewError,
|
||||
mapSendMessageError,
|
||||
shouldInvalidateCachedTeamDataForError,
|
||||
} from '../team/teamErrorPolicies';
|
||||
import {
|
||||
captureTeamLocalStateEpoch,
|
||||
clearAllTeamLocalStateEpochs,
|
||||
|
|
@ -1202,24 +1207,6 @@ function preserveKnownTaskChangePresence(
|
|||
return changed ? mergedTasks : nextTasks;
|
||||
}
|
||||
|
||||
function mapSendMessageError(error: unknown): string {
|
||||
const message =
|
||||
error instanceof IpcError ? error.message : error instanceof Error ? error.message : '';
|
||||
if (message.includes('Failed to verify inbox write')) {
|
||||
return 'Message was written but not verified (race). Please try again.';
|
||||
}
|
||||
return message || 'Failed to send message';
|
||||
}
|
||||
|
||||
function mapReviewError(error: unknown): string {
|
||||
const message =
|
||||
error instanceof IpcError ? error.message : error instanceof Error ? error.message : '';
|
||||
if (message.includes('Task status update verification failed')) {
|
||||
return 'Failed to update task status (possible agent conflict).';
|
||||
}
|
||||
return message || 'Failed to perform review action';
|
||||
}
|
||||
|
||||
export interface GlobalTaskDetailState {
|
||||
teamName: string;
|
||||
taskId: string;
|
||||
|
|
@ -1943,15 +1930,6 @@ function isVisibleInActiveTeamSurface(
|
|||
});
|
||||
}
|
||||
|
||||
function shouldInvalidateCachedTeamDataForError(teamName: string, message: string): boolean {
|
||||
return (
|
||||
message === 'TEAM_DRAFT' ||
|
||||
message.includes('TEAM_DRAFT') ||
|
||||
message === `Team not found: ${teamName}` ||
|
||||
message === 'Team config not found'
|
||||
);
|
||||
}
|
||||
|
||||
export interface TeamSlice {
|
||||
teams: TeamSummary[];
|
||||
/** O(1) lookup to avoid array scans in render-hot paths */
|
||||
|
|
|
|||
33
src/renderer/store/team/teamErrorPolicies.ts
Normal file
33
src/renderer/store/team/teamErrorPolicies.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { IpcError } from '@renderer/utils/unwrapIpc';
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
return error instanceof IpcError ? error.message : error instanceof Error ? error.message : '';
|
||||
}
|
||||
|
||||
export function mapSendMessageError(error: unknown): string {
|
||||
const message = getErrorMessage(error);
|
||||
if (message.includes('Failed to verify inbox write')) {
|
||||
return 'Message was written but not verified (race). Please try again.';
|
||||
}
|
||||
return message || 'Failed to send message';
|
||||
}
|
||||
|
||||
export function mapReviewError(error: unknown): string {
|
||||
const message = getErrorMessage(error);
|
||||
if (message.includes('Task status update verification failed')) {
|
||||
return 'Failed to update task status (possible agent conflict).';
|
||||
}
|
||||
return message || 'Failed to perform review action';
|
||||
}
|
||||
|
||||
export function shouldInvalidateCachedTeamDataForError(
|
||||
teamName: string,
|
||||
message: string
|
||||
): boolean {
|
||||
return (
|
||||
message === 'TEAM_DRAFT' ||
|
||||
message.includes('TEAM_DRAFT') ||
|
||||
message === `Team not found: ${teamName}` ||
|
||||
message === 'Team config not found'
|
||||
);
|
||||
}
|
||||
63
test/renderer/store/teamErrorPolicies.test.ts
Normal file
63
test/renderer/store/teamErrorPolicies.test.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
mapReviewError,
|
||||
mapSendMessageError,
|
||||
shouldInvalidateCachedTeamDataForError,
|
||||
} from '../../../src/renderer/store/team/teamErrorPolicies';
|
||||
import { IpcError } from '../../../src/renderer/utils/unwrapIpc';
|
||||
|
||||
describe('teamErrorPolicies', () => {
|
||||
it('maps send-message verification races to the user-facing retry copy', () => {
|
||||
expect(mapSendMessageError(new Error('Failed to verify inbox write for message-1'))).toBe(
|
||||
'Message was written but not verified (race). Please try again.'
|
||||
);
|
||||
expect(
|
||||
mapSendMessageError(
|
||||
new IpcError('team:sendMessage', 'Failed to verify inbox write after timeout')
|
||||
)
|
||||
).toBe('Message was written but not verified (race). Please try again.');
|
||||
});
|
||||
|
||||
it('maps send-message errors to original messages or fallback copy', () => {
|
||||
expect(mapSendMessageError(new Error('Transport failed'))).toBe('Transport failed');
|
||||
expect(mapSendMessageError('plain failure')).toBe('Failed to send message');
|
||||
expect(mapSendMessageError(null)).toBe('Failed to send message');
|
||||
});
|
||||
|
||||
it('maps review verification conflicts to the user-facing conflict copy', () => {
|
||||
expect(mapReviewError(new Error('Task status update verification failed for task-1'))).toBe(
|
||||
'Failed to update task status (possible agent conflict).'
|
||||
);
|
||||
expect(
|
||||
mapReviewError(
|
||||
new IpcError('team:updateKanban', 'Task status update verification failed after retry')
|
||||
)
|
||||
).toBe('Failed to update task status (possible agent conflict).');
|
||||
});
|
||||
|
||||
it('maps review errors to original messages or fallback copy', () => {
|
||||
expect(mapReviewError(new Error('Review failed'))).toBe('Review failed');
|
||||
expect(mapReviewError({ message: 'ignored non-error shape' })).toBe(
|
||||
'Failed to perform review action'
|
||||
);
|
||||
expect(mapReviewError(undefined)).toBe('Failed to perform review action');
|
||||
});
|
||||
|
||||
it('invalidates cached team data for draft and missing-team errors', () => {
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'TEAM_DRAFT')).toBe(true);
|
||||
expect(
|
||||
shouldInvalidateCachedTeamDataForError('my-team', 'Cannot read team: TEAM_DRAFT')
|
||||
).toBe(true);
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'Team not found: my-team')).toBe(true);
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'Team config not found')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not invalidate cached team data for unrelated or other-team errors', () => {
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'Network timeout')).toBe(false);
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'Team not found: other-team')).toBe(
|
||||
false
|
||||
);
|
||||
expect(shouldInvalidateCachedTeamDataForError('my-team', 'Team config missing')).toBe(false);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue