Files
Developer 6df4f306dd
Build & Deploy (Nix) / build-and-deploy (backend) (push) Successful in 2m30s
Build & Deploy (Nix) / build-and-deploy (discord-gateway) (push) Successful in 3m7s
Build & Deploy (Nix) / build-and-deploy (proxy) (push) Successful in 3m20s
refactor: remove unused text analysis module and integrate Qdrant enhancements
- Deleted the text analysis prompt constants and helpers as they are no longer needed.
- Added batch search functionality for Qdrant to optimize vector searches.
- Implemented methods for deleting expired Qdrant points and invalidating cache based on content hash.
- Updated text batch processor to use new timeout configurations and modified content building for moderation prompts.
- Enhanced text cache store to support new Qdrant integration and improved cache invalidation logic.
- Introduced a new user reputation model with a more nuanced trust scoring system, including penalties and rewards for user behavior.
- Added unit tests for the new trust model to ensure correctness of penalty and trust gain calculations.
- Updated configuration schema to reflect new timeout settings and removed deprecated OpenAI moderation keys.
2026-07-31 23:09:00 +07:00

94 lines
3.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ═══════════════════════════════════════════════════════════════════════════
// Trust model v2 — pure math tests (no DB required)
// ═══════════════════════════════════════════════════════════════════════════
import { describe, expect, it } from "vitest";
import {
computeCleanTrustGain,
computeInfractionPenalty,
INFRACTION_FLOORS,
INFRACTION_PENALTIES,
TRUST_DEFAULTS,
} from "../src/modules/ai-moderation/userReputationStore.js";
describe("computeCleanTrustGain — trust CAN rise", () => {
it("grants +1 every CLEAN_MESSAGES_PER_POINT clean messages", () => {
const before = computeCleanTrustGain(14);
expect(before.newStreak).toBe(15);
expect(before.trustGain).toBe(1);
const after = computeCleanTrustGain(15);
expect(after.newStreak).toBe(16);
expect(after.trustGain).toBe(0);
});
it("keeps compounding past the threshold (no wasted progress)", () => {
expect(computeCleanTrustGain(29).trustGain).toBe(1);
expect(computeCleanTrustGain(44).trustGain).toBe(1);
// 45 clean messages from a fresh start → 3 points of recovery
let gain = 0;
let streak = 0;
for (let i = 0; i < 45; i++) {
const r = computeCleanTrustGain(streak);
streak = r.newStreak;
gain += r.trustGain;
}
expect(gain).toBe(3);
});
});
describe("computeInfractionPenalty — fair and escalating", () => {
const NOW = Date.now();
it("applies base penalty for a repeat offender outside the window", () => {
const r = computeInfractionPenalty({
totalInfractions: 3,
lastInfractionAt: NOW - TRUST_DEFAULTS.REPEAT_OFFENSE_WINDOW_MS - 1000,
severity: "medium",
now: NOW,
});
expect(r.penalty).toBe(INFRACTION_PENALTIES.medium); // 6
expect(r.appliedRules.firstOffense).toBe(false);
expect(r.appliedRules.repeatEscalation).toBe(false);
});
it("halves the penalty for a first offense (leniency)", () => {
const r = computeInfractionPenalty({
totalInfractions: 0,
lastInfractionAt: null,
severity: "high",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.high / 2)); // 6
expect(r.appliedRules.firstOffense).toBe(true);
});
it("escalates ×1.5 for a repeat offense within 7 days", () => {
const r = computeInfractionPenalty({
totalInfractions: 2,
lastInfractionAt: NOW - 60 * 60 * 1000, // 1h ago
severity: "medium",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.medium * 1.5)); // 9
expect(r.appliedRules.repeatEscalation).toBe(true);
});
it("critical first offense still hurts but is halved", () => {
const r = computeInfractionPenalty({
totalInfractions: 0,
lastInfractionAt: null,
severity: "critical",
now: NOW,
});
expect(r.penalty).toBe(Math.ceil(INFRACTION_PENALTIES.critical / 2)); // 13
});
it("severity floors prevent minor offenses from zeroing a user", () => {
expect(INFRACTION_FLOORS.low).toBeGreaterThan(0);
expect(INFRACTION_FLOORS.medium).toBeGreaterThan(0);
// high/critical can still reach zero — severe behavior has consequences
expect(INFRACTION_FLOORS.high).toBe(0);
expect(INFRACTION_FLOORS.critical).toBe(0);
});
});