All files / server/services/tutor/evaluation reveal-check.ts

100% Statements 21/21
100% Branches 14/14
100% Functions 7/7
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55          5x 5x     270x                             118x 118x 112x       104x       27x 27x 10x 10x 10x 8x   2x   19x         21x 21x 22x 27x      
/**
 * Declared literal leaks (`expected.mustNotReveal`, SSOT R-REV-1..3).
 * A miss means only "no declared literal leak", never "no solution revealed".
 */
 
const MIN_LITERAL_LENGTH = 3;
const WORD_CHARACTER = /[\p{L}\p{N}_]/u;
 
function normalize(text: string): string {
  return text
    .normalize("NFKC")
    .toLowerCase()
    .replaceAll(/[`*]/g, "")
    .replaceAll(/\s+/g, " ")
    .trim()
    .replaceAll(/ ?([^\p{L}\p{N}_ ]) ?/gu, "$1");
}
 
/**
 * A literal is a specific expression or phrase (R-REV-1): after normalization at least three characters with a
 * letter, and either more than one word or an operator/punctuation character. A single plain word such as `drei`,
 * `low`, or `counter`, and a bare value such as `3`, are not specific enough.
 */
export function isValidRevealLiteral(literal: string): boolean {
  const normalized = normalize(literal);
  if (normalized.length < MIN_LITERAL_LENGTH || !/\p{L}/u.test(normalized)) return false;
  return normalized.includes(" ") || /[^\p{L}\p{N}_]/u.test(normalized);
}
 
export function normalizeRevealLiteral(literal: string): string {
  return normalize(literal);
}
 
function containsLiteral(text: string, literal: string): boolean {
  let from = text.indexOf(literal);
  while (from !== -1) {
    const before = text[from - 1];
    const after = text[from + literal.length];
    if ((before === undefined || !WORD_CHARACTER.test(before)) && (after === undefined || !WORD_CHARACTER.test(after))) {
      return true;
    }
    from = text.indexOf(literal, from + 1);
  }
  return false;
}
 
/** Returns the first declared literal that occurs in one of the texts. Texts are never joined. */
export function findRevealedLiteral(texts: readonly string[], literals: readonly string[]): string | undefined {
  const normalizedTexts = texts.map(normalize);
  return literals.find((literal) => {
    const normalizedLiteral = normalize(literal);
    return normalizedLiteral.length > 0 && normalizedTexts.some((text) => containsLiteral(text, normalizedLiteral));
  });
}