fix(e2e): require exact reviewer duty, reject viewer fallback
USER_B is assigned reviewer in UI-01. Accept only role=reviewer on the duty control, and reject assignable or enabled selects. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
evaluateReviewerOrViewerDuty,
|
||||
evaluateExpectedDuty,
|
||||
evaluateReviewerDuty,
|
||||
resolveDutyCell,
|
||||
} from './dutyCellAssertion';
|
||||
|
||||
@@ -23,13 +24,13 @@ describe('dutyCellAssertion', () => {
|
||||
() => resolveDutyCell({ source: 'row-text', text: FIELD_FAILURE_ROW }),
|
||||
/must_not_infer_duty_from_row_option_text/,
|
||||
);
|
||||
const verdict = evaluateReviewerOrViewerDuty({ source: 'row-text', text: FIELD_FAILURE_ROW });
|
||||
const verdict = evaluateReviewerDuty({ source: 'row-text', text: FIELD_FAILURE_ROW });
|
||||
assert.equal(verdict.ok, false);
|
||||
assert.match(verdict.reason, /must_not_infer_duty_from_row_option_text/);
|
||||
});
|
||||
|
||||
it('accepts a disabled reviewer select when the reviewer cannot assign leads', () => {
|
||||
const verdict = evaluateReviewerOrViewerDuty({
|
||||
it('accepts only a disabled non-assignable reviewer select', () => {
|
||||
const verdict = evaluateReviewerDuty({
|
||||
source: 'select',
|
||||
value: 'reviewer',
|
||||
disabled: true,
|
||||
@@ -40,7 +41,7 @@ describe('dutyCellAssertion', () => {
|
||||
});
|
||||
|
||||
it('rejects an enabled reviewer select when the reviewer cannot assign leads', () => {
|
||||
const verdict = evaluateReviewerOrViewerDuty({
|
||||
const verdict = evaluateReviewerDuty({
|
||||
source: 'select',
|
||||
value: 'reviewer',
|
||||
disabled: false,
|
||||
@@ -50,14 +51,32 @@ describe('dutyCellAssertion', () => {
|
||||
assert.match(verdict.reason, /disabled/);
|
||||
});
|
||||
|
||||
it('rejects author even if the caller only looked at option text before', () => {
|
||||
const verdict = evaluateReviewerOrViewerDuty({
|
||||
it('rejects canAssignLead=true even if the reviewer select is disabled', () => {
|
||||
const verdict = evaluateReviewerDuty({
|
||||
source: 'select',
|
||||
value: 'author',
|
||||
value: 'reviewer',
|
||||
disabled: true,
|
||||
canAssignLead: false,
|
||||
canAssignLead: true,
|
||||
});
|
||||
assert.equal(verdict.ok, false);
|
||||
assert.equal(verdict.role, 'author');
|
||||
assert.match(verdict.reason, /assign_lead/);
|
||||
});
|
||||
|
||||
it('rejects viewer, author, approver, and unknown values for USER_B', () => {
|
||||
assert.equal(
|
||||
evaluateReviewerDuty({ source: 'select', value: 'viewer', disabled: true, canAssignLead: false }).ok,
|
||||
false,
|
||||
);
|
||||
assert.equal(evaluateReviewerDuty({ source: 'badge', role: 'viewer' }).ok, false);
|
||||
assert.equal(
|
||||
evaluateReviewerDuty({ source: 'select', value: 'author', disabled: true, canAssignLead: false }).ok,
|
||||
false,
|
||||
);
|
||||
assert.equal(evaluateReviewerDuty({ source: 'badge', role: 'approver' }).ok, false);
|
||||
assert.equal(evaluateReviewerDuty({ source: 'badge', role: 'unknown' }).ok, false);
|
||||
assert.match(
|
||||
evaluateExpectedDuty({ source: 'badge', role: 'viewer' }, 'reviewer').reason,
|
||||
/expected_duty:reviewer;got:viewer/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,12 +36,7 @@ export function resolveDutyCell(input: DutyCellInput): { role: DocDutyRole; disa
|
||||
return { role: input.role };
|
||||
}
|
||||
|
||||
export function isAcceptableReviewerOrViewer(role: DocDutyRole): boolean {
|
||||
return role === 'reviewer' || role === 'viewer';
|
||||
}
|
||||
|
||||
/** Reviewer B intake: current control value, never the concatenated option labels. */
|
||||
export function evaluateReviewerOrViewerDuty(input: DutyCellInput): DutyCellVerdict {
|
||||
export function evaluateExpectedDuty(input: DutyCellInput, expected: DocDutyRole): DutyCellVerdict {
|
||||
if (input.source === 'row-text') {
|
||||
return { ok: false, reason: 'must_not_infer_duty_from_row_option_text' };
|
||||
}
|
||||
@@ -51,11 +46,28 @@ export function evaluateReviewerOrViewerDuty(input: DutyCellInput): DutyCellVerd
|
||||
} catch (err) {
|
||||
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
||||
}
|
||||
if (!isAcceptableReviewerOrViewer(role)) {
|
||||
return { ok: false, role, reason: `unexpected_duty:${role}` };
|
||||
}
|
||||
if (input.source === 'select' && !input.canAssignLead && !input.disabled) {
|
||||
return { ok: false, role, reason: 'reviewer_select_must_be_disabled_when_cannot_assign' };
|
||||
if (role !== expected) {
|
||||
return { ok: false, role, reason: `expected_duty:${expected};got:${role}` };
|
||||
}
|
||||
return { ok: true, role, reason: 'ok' };
|
||||
}
|
||||
|
||||
/**
|
||||
* USER_B was assigned reviewer in UI-01. Accept only role=reviewer.
|
||||
* A select must be non-assignable and disabled; viewer/author/approver fail closed.
|
||||
*/
|
||||
export function evaluateReviewerDuty(input: DutyCellInput): DutyCellVerdict {
|
||||
const verdict = evaluateExpectedDuty(input, 'reviewer');
|
||||
if (!verdict.ok) {
|
||||
return verdict;
|
||||
}
|
||||
if (input.source === 'select') {
|
||||
if (input.canAssignLead) {
|
||||
return { ok: false, role: 'reviewer', reason: 'reviewer_must_not_assign_lead' };
|
||||
}
|
||||
if (!input.disabled) {
|
||||
return { ok: false, role: 'reviewer', reason: 'reviewer_select_must_be_disabled' };
|
||||
}
|
||||
}
|
||||
return verdict;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user