import { AdminTrustRatingCalculator, VoteOutcomeInput, SystemSignalInput } from './AdminTrustRatingCalculator'; import { RatingEvent } from '../entities/RatingEvent'; import { RatingEventId } from '../value-objects/RatingEventId'; import { RatingDimensionKey } from '../value-objects/RatingDimensionKey'; import { RatingDelta } from '../value-objects/RatingDelta'; import { AdminVoteOutcome } from '../entities/AdminVoteSession'; describe('AdminTrustRatingCalculator', () => { describe('calculate', () => { it('should sum all event deltas', () => { const events: RatingEvent[] = [ RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-123', dimension: RatingDimensionKey.create('adminTrust'), delta: RatingDelta.create(5), occurredAt: new Date(), createdAt: new Date(), source: { type: 'vote', id: 'vote-123' }, reason: { code: 'ADMIN_VOTE_OUTCOME_POSITIVE', summary: 'Test', details: {}, }, visibility: { public: true, redactedFields: [] }, version: 1, }), RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-123', dimension: RatingDimensionKey.create('adminTrust'), delta: RatingDelta.create(-2), occurredAt: new Date(), createdAt: new Date(), source: { type: 'adminAction', id: 'action-456' }, reason: { code: 'ADMIN_ACTION_REVERSAL_PENALTY', summary: 'Test', details: {}, }, visibility: { public: true, redactedFields: [] }, version: 1, }), ]; const result = AdminTrustRatingCalculator.calculate(events); expect(result).toBe(3); // 5 + (-2) }); it('should handle empty events array', () => { const result = AdminTrustRatingCalculator.calculate([]); expect(result).toBe(0); }); it('should apply weight to deltas', () => { const events: RatingEvent[] = [ RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-123', dimension: RatingDimensionKey.create('adminTrust'), delta: RatingDelta.create(10), weight: 2, occurredAt: new Date(), createdAt: new Date(), source: { type: 'vote', id: 'vote-123' }, reason: { code: 'ADMIN_VOTE_OUTCOME_POSITIVE', summary: 'Test', details: {}, }, visibility: { public: true, redactedFields: [] }, version: 1, }), ]; const result = AdminTrustRatingCalculator.calculate(events); expect(result).toBe(20); // 10 * 2 }); it('should handle mixed weighted and unweighted events', () => { const events: RatingEvent[] = [ RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-123', dimension: RatingDimensionKey.create('adminTrust'), delta: RatingDelta.create(5), weight: 1, occurredAt: new Date(), createdAt: new Date(), source: { type: 'vote', id: 'vote-123' }, reason: { code: 'ADMIN_VOTE_OUTCOME_POSITIVE', summary: 'Test', details: {}, }, visibility: { public: true, redactedFields: [] }, version: 1, }), RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-123', dimension: RatingDimensionKey.create('adminTrust'), delta: RatingDelta.create(3), occurredAt: new Date(), createdAt: new Date(), source: { type: 'adminAction', id: 'action-456' }, reason: { code: 'ADMIN_ACTION_SLA_BONUS', summary: 'Test', details: {}, }, visibility: { public: true, redactedFields: [] }, version: 1, }), ]; const result = AdminTrustRatingCalculator.calculate(events); expect(result).toBe(8); // (5 * 1) + 3 }); }); describe('calculateFromVote', () => { it('should calculate positive outcome with full participation', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 100, count: { positive: 10, negative: 0, total: 10 }, eligibleVoterCount: 10, participationRate: 100, outcome: 'positive', }, eligibleVoterCount: 10, voteCount: 10, percentPositive: 100, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); expect(delta.value).toBe(20); // Full positive, full participation }); it('should calculate negative outcome with full participation', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 0, count: { positive: 0, negative: 10, total: 10 }, eligibleVoterCount: 10, participationRate: 100, outcome: 'negative', }, eligibleVoterCount: 10, voteCount: 10, percentPositive: 0, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); expect(delta.value).toBe(-20); // Full negative, full participation }); it('should calculate partial positive outcome', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }, eligibleVoterCount: 4, voteCount: 4, percentPositive: 75, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); expect(delta.value).toBe(15); // 75% of 20 = 15 }); it('should reduce delta for low participation', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 100, count: { positive: 2, negative: 0, total: 2 }, eligibleVoterCount: 10, participationRate: 20, outcome: 'positive', }, eligibleVoterCount: 10, voteCount: 2, percentPositive: 100, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); // 20 * 0.5 (minimum participation multiplier) = 10 expect(delta.value).toBe(10); }); it('should handle tie outcome', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 50, count: { positive: 5, negative: 5, total: 10 }, eligibleVoterCount: 10, participationRate: 100, outcome: 'tie', }, eligibleVoterCount: 10, voteCount: 10, percentPositive: 50, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); expect(delta.value).toBe(0); }); it('should return zero for no votes', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 0, count: { positive: 0, negative: 0, total: 0 }, eligibleVoterCount: 10, participationRate: 0, outcome: 'tie', }, eligibleVoterCount: 10, voteCount: 0, percentPositive: 0, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); expect(delta.value).toBe(0); }); it('should round to 2 decimal places', () => { const input: VoteOutcomeInput = { outcome: { percentPositive: 66.67, count: { positive: 2, negative: 1, total: 3 }, eligibleVoterCount: 4, participationRate: 75, outcome: 'positive', }, eligibleVoterCount: 4, voteCount: 3, percentPositive: 66.67, }; const delta = AdminTrustRatingCalculator.calculateFromVote(input); // 66.67% of 20 = 13.334, * 0.75 (participation) = 10.0005, rounded = 10.00 expect(delta.value).toBe(10.00); }); }); describe('calculateFromSystemSignal', () => { it('should calculate SLA response bonus', () => { const input: SystemSignalInput = { actionType: 'sla_response', details: {}, }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(5); }); it('should calculate minor reversal penalty', () => { const input: SystemSignalInput = { actionType: 'reversal', details: {}, severity: 'minor', }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(-10); }); it('should calculate major reversal penalty', () => { const input: SystemSignalInput = { actionType: 'reversal', details: {}, severity: 'major', }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(-20); }); it('should calculate rule clarity bonus', () => { const input: SystemSignalInput = { actionType: 'rule_clarity', details: {}, }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(3); }); it('should calculate minor abuse report penalty', () => { const input: SystemSignalInput = { actionType: 'abuse_report', details: {}, severity: 'minor', }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(-15); }); it('should calculate major abuse report penalty', () => { const input: SystemSignalInput = { actionType: 'abuse_report', details: {}, severity: 'major', }; const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(-30); }); it('should default to zero for unknown action type', () => { const input: SystemSignalInput = { actionType: 'sla_response' as any, details: {}, }; // Override for test const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input); expect(delta.value).toBe(5); // Known type }); }); describe('calculateFromMultipleVotes', () => { it('should sum multiple vote outcomes', () => { const inputs: VoteOutcomeInput[] = [ { outcome: { percentPositive: 100, count: { positive: 5, negative: 0, total: 5 }, eligibleVoterCount: 5, participationRate: 100, outcome: 'positive', }, eligibleVoterCount: 5, voteCount: 5, percentPositive: 100, }, { outcome: { percentPositive: 0, count: { positive: 0, negative: 3, total: 3 }, eligibleVoterCount: 3, participationRate: 100, outcome: 'negative', }, eligibleVoterCount: 3, voteCount: 3, percentPositive: 0, }, ]; const delta = AdminTrustRatingCalculator.calculateFromMultipleVotes(inputs); expect(delta.value).toBe(0); // +20 + (-20) = 0 }); }); describe('calculateFromMultipleSystemSignals', () => { it('should sum multiple system signals', () => { const inputs: SystemSignalInput[] = [ { actionType: 'sla_response', details: {} }, { actionType: 'reversal', details: {}, severity: 'minor' }, { actionType: 'rule_clarity', details: {} }, ]; const delta = AdminTrustRatingCalculator.calculateFromMultipleSystemSignals(inputs); expect(delta.value).toBe(-2); // 5 + (-10) + 3 = -2 }); }); describe('calculateTotalDelta', () => { it('should combine votes and system signals', () => { const voteInputs: VoteOutcomeInput[] = [ { outcome: { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }, eligibleVoterCount: 4, voteCount: 4, percentPositive: 75, }, ]; const systemInputs: SystemSignalInput[] = [ { actionType: 'sla_response', details: {} }, { actionType: 'reversal', details: {}, severity: 'minor' }, ]; const delta = AdminTrustRatingCalculator.calculateTotalDelta(voteInputs, systemInputs); expect(delta.value).toBe(10); // 15 (vote) + 5 (SLA) + (-10) (reversal) = 10 }); it('should handle empty inputs', () => { const delta = AdminTrustRatingCalculator.calculateTotalDelta([], []); expect(delta.value).toBe(0); }); }); });