File size: 2,918 Bytes
97b64b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
56
"""The verification test-case taxonomy: ``case_type`` -> ground-truth verdict.

Each :class:`CaseSpec` encodes one row of the signature-verification test
plan: what should actually be rendered into the target signature box
(``signature_mode``), whether a companion box on the same page should also
be filled with its own correctly-authorized signature (``fill_companion`` --
only meaningful for the multi-signer-context cases), and the ground-truth
``verdict``/``reject_code`` a downstream verifier is expected to produce.

Driven by the existing ``quality.case_type`` config field (see
``configs/examples/hard_degraded.yaml`` for its prior, simpler use as a
plain metadata label) -- setting it to one of the codes below switches
:meth:`~src.generator.GeneratorContext.run_sample` into scenario-driven
generation. Any other value (or ``None``) leaves generation entirely
unaffected.
"""

from __future__ import annotations

from dataclasses import dataclass

SignatureMode = str  # one of the _SIGNATURE_MODES below, kept as a plain str for simplicity

NO_SIGNATURE = "NO_SIGNATURE"
IDENTITY_MISMATCH = "IDENTITY_MISMATCH"
ROLE_MISMATCH = "ROLE_MISMATCH"
REQUIRED_SIGNER_ABSENT = "REQUIRED_SIGNER_ABSENT"


@dataclass(frozen=True)
class CaseSpec:
    """One row of the verification test-case taxonomy."""

    code: str
    description: str
    verdict: str  # "PASS" | "REJECT"
    reject_code: "str | None"
    signature_mode: SignatureMode
    fill_companion: bool = False


CASE_TABLE: dict[str, CaseSpec] = {
    "1a": CaseSpec("1a", "Correct signer signs normally", "PASS", None, "genuine_correct"),
    "1b": CaseSpec("1b", "Correct signer, natural pen variation applied", "PASS", None, "genuine_variation"),
    "1c": CaseSpec("1c", "Multi-signer form, both sign correctly", "PASS", None, "genuine_correct", fill_companion=True),
    "1d": CaseSpec("1d", "Correct signer, scan degraded to test robustness", "PASS", None, "genuine_correct"),
    "2a": CaseSpec("2a", "Box left completely blank", "REJECT", NO_SIGNATURE, "blank"),
    "2b": CaseSpec("2b", "Printed/typed name only, no handwriting", "REJECT", NO_SIGNATURE, "printed_text"),
    "3a": CaseSpec("3a", "Wrong real person signs the box", "REJECT", IDENTITY_MISMATCH, "wrong_signer_genuine"),
    "3b": CaseSpec("3b", "Skilled-forgery sample used", "REJECT", IDENTITY_MISMATCH, "forged_skilled"),
    "4a": CaseSpec("4a", "Authorized signer, but wrong role's box", "REJECT", ROLE_MISMATCH, "role_swap", fill_companion=True),
    "4b": CaseSpec("4b", "Signer from a different form entirely", "REJECT", ROLE_MISMATCH, "foreign_template_signer", fill_companion=True),
    "5a": CaseSpec("5a", "Required signer's box left empty", "REJECT", REQUIRED_SIGNER_ABSENT, "blank", fill_companion=True),
    "5b": CaseSpec("5b", "Someone else signs in the required signer's place", "REJECT", REQUIRED_SIGNER_ABSENT, "wrong_signer_genuine", fill_companion=True),
}