← Về thư mục
scripts/test_verify_submission_package.py
- load_schema · function · L30-L31 — def load_schema()
- run_dir · function · L34-L42 — def run_dir(package_dir, extra_args=())
- run_on · function · L45-L54 — def run_on(fixture_name, tmp_path, extra_args=())
- checks_by_id · function · L57-L58 — def checks_by_id(report)
- test_clean_package_family_c_passes · function · L63-L71 — def test_clean_package_family_c_passes(tmp_path): # Without a venue profile the Family B checks are NOT-CHECKED (§3.2), so # the honest exit code is 3 ("passed what was checkable", §8) — Family C # itself is fully green.
- test_clean_package_is_deterministic_joined_marker · function · L74-L82 — def test_clean_package_is_deterministic_joined_marker(tmp_path)
- test_no_venue_profile_family_b_not_checked · function · L87-L102 — def test_no_venue_profile_family_b_not_checked(tmp_path): # §3.2: without a venue profile every Family B check is # NOT-CHECKED(no venue profile) — never guessed from the journal name # (R-L3-2-D mirror). The checks stay visible (deterministic, # strict-eligible) so the slice-4 fail-closed path has something to see.
- test_clean_report_validates_against_schema · function · L105-L107 — def test_clean_report_validates_against_schema(tmp_path)
- test_policy_slug_is_null_in_standalone_runs · function · L110-L114 — def test_policy_slug_is_null_in_standalone_runs(tmp_path): # §5.2/§5.3: the script never reads terminal_policies; the slug is stamped # by the slice-4 orchestrator hook. A standalone run always emits null.
- test_report_written_into_package_dir · function · L117-L119 — def test_report_written_into_package_dir(tmp_path)
- test_full_profile_all_family_b_pass · function · L122-L137 — def test_full_profile_all_family_b_pass(tmp_path): # venue_clean satisfies every limit in profiles/full.yaml; with both # families green the exit code is a true 0.
- test_violated_profile_every_family_b_check_fails · function · L140-L155 — def test_violated_profile_every_family_b_check_fails(tmp_path): # Mutation discipline (§8): every Family B check has a fixture that fails # it. venue_violations breaks all five limits in profiles/tight.yaml.
- test_partial_profile_runs_what_it_can · function · L158-L174 — def test_partial_profile_runs_what_it_can(tmp_path): # §4: a partially-declared profile runs the checks it can and # NOT-CHECKEDs the rest, each with the undeclared field named.
- test_word_count_tolerance_two_percent · function · L177-L191 — def test_word_count_tolerance_two_percent(tmp_path): # §3.2: ±2% tolerance before fail (format-conversion noise). 101 words # against a 100 limit passes; 103 fails.
- test_invalid_venue_profile_is_usage_error · function · L194-L208 — def test_invalid_venue_profile_is_usage_error(tmp_path)
- test_missing_abstract_and_keywords_not_checked · function · L211-L224 — def test_missing_abstract_and_keywords_not_checked(tmp_path): # Declared limits whose actuals cannot be located are NOT-CHECKED with the # reason — never folded into pass (§1.4), never guessed.
- test_latex_manuscript_word_count_declares_detex · function · L227-L239 — def test_latex_manuscript_word_count_declares_detex(tmp_path): # §10 item 4 (adjudicated at slice 2): LaTeX counting = naive detex + # whitespace-split, the method is declared in the report, never promised # venue-exact.
- test_word_count_scope_all_counts_everything · function · L242-L259 — def test_word_count_scope_all_counts_everything(tmp_path): # codex P2:
all must actually count everything (keywords line included); # only body_only / body_plus_references exclude it. 10 body words + # "Keywords: a, b" (3 tokens) against limit 10: body_only passes, # all fails.
- test_profile_validation_matches_schema_strictness · function · L262-L278 — def test_profile_validation_matches_schema_strictness(tmp_path): # codex P2: the CLI gate must not be looser than # venue_profile.schema.json — unknown fields (additionalProperties false) # and booleans-as-integers are rejected.
- test_ambiguous_manuscript_not_checked · function · L281-L306 — def test_ambiguous_manuscript_not_checked(tmp_path): # codex P2: with several non-canonical candidates the verifier must not # silently pick the wordiest (it could be a response letter); it reports # NOT-CHECKED(ambiguous manuscript). A canonical name (paper. / # manuscript. / main.*) resolves the ambiguity.
- test_abstract_tolerance_and_b5_no_reference_list · function · L309-L328 — def test_abstract_tolerance_and_b5_no_reference_list(tmp_path): # codex P3: pin the B2 ±2% tolerance and the B5 # declared-limit-but-no-reference-list branch.
- test_profile_with_no_manuscript_not_checked · function · L331-L343 — def test_profile_with_no_manuscript_not_checked(tmp_path)
- test_family_a_not_applicable_without_trigger · function · L348-L361 — def test_family_a_not_applicable_without_trigger(tmp_path): # §3.1: the residue scan runs only when the package contains an anonymized # variant or the profile declares double-blind. Untriggered checks are # not_applicable — visibly distinct from not_checked (they did not need to # run), so a single-blind package is not condemned to exit 3 forever.
- test_declared_double_blind_without_variant_fails_A7 · function · L364-L380 — def test_declared_double_blind_without_variant_fails_A7(tmp_path): # §3.1: a declared-double-blind package with NO anonymized variant is # itself a fail — the most basic residue of all, the blind version is # missing. A1-A6 have nothing to scan: not_checked.
- test_anonymized_variant_triggers_family_a · function · L383-L401 — def test_anonymized_variant_triggers_family_a(tmp_path): # Presence of an anonymized variant triggers the scan even without a # profile (presence-or-declaration, §3.1). With an .md-only variant the # PDF/DOCX metadata checks have no object: not_applicable.
- make_docx · function · L420-L441 — def make_docx(path, creator="", last_modified_by="", ins_author=None, comment_author=None)
- _blind_package · function · L444-L450 — def _blind_package(tmp_path, **docx_kwargs)
- test_docx_metadata_author_residue_fails_A2 · function · L453-L461 — def test_docx_metadata_author_residue_fails_A2(tmp_path): # A2 (§3.1): docProps/core.xml creator / lastModifiedBy non-empty in the # blind variant is a deterministic fact about the file — invisible to any # rendered-view scan (§1.3).
- test_docx_clean_metadata_passes_A2_A3 · function · L464-L467 — def test_docx_clean_metadata_passes_A2_A3(tmp_path)
- test_docx_tracked_change_author_fails_A3 · function · L470-L473 — def test_docx_tracked_change_author_fails_A3(tmp_path)
- test_docx_comment_author_fails_A3 · function · L476-L479 — def test_docx_comment_author_fails_A3(tmp_path)
- test_corrupt_docx_not_checked · function · L482-L492 — def test_corrupt_docx_not_checked(tmp_path): # An unreadable artifact is incompleteness, never folded into pass (§1.4).
- test_pdf_metadata_author_fails_A1 · function · L495-L510 — def test_pdf_metadata_author_fails_A1(tmp_path)
- test_pdf_parser_unavailable_is_not_checked · function · L513-L527 — def test_pdf_parser_unavailable_is_not_checked(tmp_path, monkeypatch): # §1.4/#349: a missing parser must surface as NOT-CHECKED, never read as # covered. (The slice-4 strict mode turns this into # VERIFICATION-INCOMPLETE.)
- test_A4_strict_only_when_profile_forbids_acknowledgments · function · L530-L555 — def test_A4_strict_only_when_profile_forbids_acknowledgments(tmp_path): # §3.1 load-bearing: A4's SIGNAL is deterministic but the judgment is the # scholar's — strict-eligible ONLY when the venue profile explicitly # declares acknowledgments must be removed from the blind version.
- build · function · L534-L545 — def build(profile_body)
- test_A6_filename_leakage_from_original_metadata · function · L558-L574 — def test_A6_filename_leakage_from_original_metadata(tmp_path): # §3.1 A6: author-name tokens harvested from the NON-anonymized artifact's # metadata, matched against package filenames (heuristic — coincidental # tokens can false-positive). The metadata-source original itself is not # scanned (its identified name is expected).
- test_A6_without_original_metadata_not_checked · function · L577-L585 — def test_A6_without_original_metadata_not_checked(tmp_path)
- test_A5_zh_tw_self_citation_phrase · function · L588-L599 — def test_A5_zh_tw_self_citation_phrase(tmp_path): # §10 item 1: the phrasing list must not be anglophone-only.
- test_A5_zh_tw_third_person_author_self_reference · function · L602-L616 — def test_A5_zh_tw_third_person_author_self_reference(tmp_path): # zh-TW curation (#394 follow-up): third-person self-reference via # 本文作者 — the bare 作者先前 would false-positive on 該作者先前 # (a cited third party's author), so the phrase is anchored on the # 本文 prefix.
- test_A5_other_authors_prior_work_not_flagged · function · L619-L631 — def test_A5_other_authors_prior_work_not_flagged(tmp_path): # 該作者先前 refers to a cited third party, not the manuscript's own # authors — must stay clean (the reason the phrase list does not carry # a bare 作者先前 entry).
- test_A4_docx_only_variant_is_not_checked_not_applicable · function · L636-L647 — def test_A4_docx_only_variant_is_not_checked_not_applicable(tmp_path): # codex P1: a DOCX-only blind variant means the acknowledgments scan # SHOULD run but cannot (no text-form variant to read headings from) — # that is not_checked honesty, never not_applicable masquerade.
- test_A7_not_satisfied_by_blind_named_supplement · function · L650-L664 — def test_A7_not_satisfied_by_blind_named_supplement(tmp_path): # codex P1: a declared double-blind package whose only blind-named file is # an ancillary CSV has NO blind manuscript variant — A7 must still fail.
- test_untriggered_A4_is_never_strict_eligible · function · L667-L671 — def test_untriggered_A4_is_never_strict_eligible(tmp_path): # codex P1: A4's eligibility comes ONLY from the explicit profile # declaration — including on the untriggered/not_applicable path.
- test_A6_token_match_does_not_flag_substrings · function · L674-L685 — def test_A6_token_match_does_not_flag_substrings(tmp_path): # codex P2: token-to-token matching —
Smith must not flag # blacksmith_notes.md.
- test_oversized_docx_part_is_not_checked · function · L688-L702 — def test_oversized_docx_part_is_not_checked(tmp_path, monkeypatch): # codex P2: unbounded zip reads are a zip-bomb exposure; oversized parts # are reported as unreadable incompleteness, never scanned or passed.
- test_profile_rejects_nonboolean_acknowledgments_forbidden_field · function · L705-L713 — def test_profile_rejects_nonboolean_acknowledgments_forbidden_field(tmp_path)
- test_schema_accepts_not_applicable · function · L716-L719 — def test_schema_accepts_not_applicable(): # (Unknown statuses are rejected by test_schema_rejects_unknown_status.)
- test_venue_profile_fixtures_validate_against_schema · function · L722-L729 — def test_venue_profile_fixtures_validate_against_schema()
- test_orphan_intext_citation_fails_C1_exit_1 · function · L734-L744 — def test_orphan_intext_citation_fails_C1_exit_1(tmp_path)
- test_uncited_reference_entry_warns_C2_exit_0 · function · L747-L755 — def test_uncited_reference_entry_warns_C2_exit_0(tmp_path): # §3.3: uncited reference entry = warn (some venues allow further-reading # entries) — advisory, never a fail exit (3 = Family B not checked).
- test_markers_without_join_source_not_checked_exit_3 · function · L758-L772 — def test_markers_without_join_source_not_checked_exit_3(tmp_path): # §3.3 + §8 join test: markers present, passport supplies a corpus (a # reference list) but NO citation_verification_summary — never a guessed # comparison.
- test_join_map_resolves_the_no_join_case · function · L775-L788 — def test_join_map_resolves_the_no_join_case(tmp_path): # The explicit scholar-supplied join map is a valid join source (§3.3) and # joins the prose slug to the corpus citation_key.
- test_missing_package_dir_is_usage_error · function · L791-L792 — def test_missing_package_dir_is_usage_error(tmp_path)
- test_unparseable_passport_is_usage_error · function · L795-L802 — def test_unparseable_passport_is_usage_error(tmp_path)
- test_fallback_latex_cite_extraction_is_heuristic_best_effort · function · L807-L822 — def test_fallback_latex_cite_extraction_is_heuristic_best_effort(tmp_path): # §3.3: post-converted sources fall back to \cite{} extraction; the header # downgrades to best-effort and the whole path is heuristic-classed # (advisory-only) — even a true orphan fail is NOT strict-eligible.
- test_fallback_authoryear_extraction_matches_bib_metadata · function · L825-L839 — def test_fallback_authoryear_extraction_matches_bib_metadata(tmp_path)
- test_summary_join_consumes_real_prose_join · function · L842-L855 — def test_summary_join_consumes_real_prose_join(tmp_path): # The prose slug (smith-feedback-2024) differs from the citation_key # (smith2024): a pass proves the citation_verification_summary join was # consumed, not an identity guess (§3.3).
- test_no_machine_readable_reference_list_not_checked · function · L858-L870 — def test_no_machine_readable_reference_list_not_checked(tmp_path)
- test_fingerprint_follows_audit_snapshot_convention_excluding_report · function · L873-L890 — def test_fingerprint_follows_audit_snapshot_convention_excluding_report(tmp_path): # §10 open item 3 (adjudicated at slice 1):
<relative-path>:<sha256> # lines, byte-sorted, trailing newline, fingerprint = sha256 of the # manifest text; the report file itself is excluded. Pinned here by an # independent reimplementation.
- test_fingerprint_stable_across_reruns_with_report_present · function · L893-L902 — def test_fingerprint_stable_across_reruns_with_report_present(tmp_path): # Second run sees the first run's report inside the package dir; the # exclusion keeps the fingerprint stable (freshness guard usable, §5.2).
- test_partial_summary_join_never_falls_back_to_identity · function · L907-L925 — def test_partial_summary_join_never_falls_back_to_identity(tmp_path): # P1: a marker slug ABSENT from the join source must never be compared via # an identity guess — even (especially) when the slug coincidentally equals # a citation_key in the reference list (§3.3 "never a guessed comparison").
- test_C2_is_never_strict_eligible · function · L928-L935 — def test_C2_is_never_strict_eligible(tmp_path): # P2: C2's worst outcome is warn, which is advisory-only and never # policy-promotable (§5.3) — so the check itself is not strict-eligible, # even on the deterministic path.
- test_custom_report_out_inside_package_excluded_from_fingerprint · function · L938-L950 — def test_custom_report_out_inside_package_excluded_from_fingerprint(tmp_path): # P2: a --report-out path inside the package must be excluded from the # fingerprint like the default basename, or reruns self-reference.
- test_authoryear_fallback_tolerates_page_locators · function · L953-L969 — def test_authoryear_fallback_tolerates_page_locators(tmp_path): # P3:
Smith (2024, p. 12) / (Chen & Lee, 2023, pp. 45–67) are common # locator forms; missing them creates avoidable fallback false orphans.
- test_schema_rejects_warn_with_strict_eligible · function · L972-L979 — def test_schema_rejects_warn_with_strict_eligible(): # P2: warn is advisory-only and never policy-promotable — tightened # structurally like the heuristic exclusion.
- _minimal_report · function · L984-L1004 — def _minimal_report(**check_overrides)
- test_schema_rejects_heuristic_strict_eligible · function · L1007-L1014 — def test_schema_rejects_heuristic_strict_eligible(): # §3.1/§6: heuristic checks are advisory-only STRUCTURALLY — the schema # itself forbids the promotion, not just the emitter.
- test_schema_binds_check_id_prefix_to_family · function · L1017-L1025 — def test_schema_binds_check_id_prefix_to_family(): # The id prefix encodes the family (spec §3 tables); the contract binds # them so a later-slice emitter cannot ship mismatched pairs.
- test_schema_rejects_unknown_status · function · L1028-L1031 — def test_schema_rejects_unknown_status()
- _strict_run · function · L1042-L1047 — def _strict_run(fixture_name, tmp_path, capsys, extra_args=())
- test_strict_eligible_fail_blocks_with_token · function · L1050-L1055 — def test_strict_eligible_fail_blocks_with_token(tmp_path, capsys): # orphan_intext fails C1 on the joined marker path (deterministic class).
- test_strict_heuristic_fail_never_promotes · function · L1058-L1073 — def test_strict_heuristic_fail_never_promotes(tmp_path, capsys): # fallback_latex fails C1 on the FALLBACK path (heuristic class). Under # strict the heuristic fail must NEVER appear as a TERMINAL-BLOCK # (§3.1/§6, structural exclusion). The run exits 4, not 1: the # profileless Family B not_checked outranks the nonterminal fail — # itself evidence of the fail-closed precedence (a heuristic fail does # not short-circuit the incompleteness verdict the way a strict fail # would).
- test_evaluate_policy_unit_contract · function · L1076-L1127 — def test_evaluate_policy_unit_contract(): # Unit pin of the evaluator's whole decision table: token + exit per # policy×status×strict_eligible combination, including precedence (a # strict fail outranks incomplete) and the not_applicable # never-composes rule. The advisory/strict divergence lives inside the # evaluator (it takes the resolved policy), so non-strict policies are # part of the table.
- report_of · function · L1085-L1089 — def report_of(*checks)
- test_strict_eligible_not_checked_is_verification_incomplete · function · L1130-L1139 — def test_strict_eligible_not_checked_is_verification_incomplete( tmp_path, capsys): # venue_clean without a profile: Family B is strict-eligible not_checked # → fail-closed exit 4 + token (§5.2: a missing input must not silently # waive the class the scholar opted into blocking on).
- test_advisory_same_not_checked_stays_exit_3 · function · L1142-L1150 — def test_advisory_same_not_checked_stays_exit_3(tmp_path, capsys): # Explicit advisory: byte-identical slice-3 behavior except the stamp.
- test_no_policy_flag_stamps_null · function · L1153-L1157 — def test_no_policy_flag_stamps_null(tmp_path): # Standalone unevaluated run: argparse default is None (never "default # advisory" — gate-1 P1), the stamp stays null.
- test_strict_not_applicable_never_blocks · function · L1160-L1174 — def test_strict_not_applicable_never_blocks(tmp_path, capsys): # clean has no anonymized variant and no blind_review declaration: Family # A is not_applicable (untriggered). Under strict that must NOT read as # incomplete or block — the evaluator keys on status, and not_applicable # is neither fail nor not_checked. (Family B absent-profile not_checked # still yields exit 4 here, proving the discrimination is per-status.)
- test_strict_full_profile_clean_package_exits_0 · function · L1177-L1186 — def test_strict_full_profile_clean_package_exits_0(tmp_path, capsys): # The strict happy path: everything checked, everything green.
- _fresh_args · function · L1191-L1192 — def _fresh_args(policy="advisory")
- test_freshness_mutated_package_is_stale · function · L1195-L1207 — def test_freshness_mutated_package_is_stale(tmp_path, capsys)
- test_freshness_policy_mismatch_is_stale · function · L1210-L1218 — def test_freshness_policy_mismatch_is_stale(tmp_path, capsys): # Report stamped advisory, orchestrator now wants strict: stale, rerun.
- test_freshness_null_stamped_report_never_fresh · function · L1221-L1233 — def test_freshness_null_stamped_report_never_fresh(tmp_path, capsys): # A standalone (unevaluated, policy_slug=null) report never satisfies # pipeline freshness — gate-1 P1 (null must not impersonate advisory). # The reason token is pinned: null_policy_slug ("you handed the pipeline # a standalone report") is a different remediation from policy_mismatch # ("the policy changed since stamping") — without the dedicated branch # the null case would collapse into the mismatch reason.
- test_freshness_missing_report_is_stale · function · L1236-L1243 — def test_freshness_missing_report_is_stale(tmp_path, capsys)
- test_freshness_requires_policy · function · L1246-L1255 — def test_freshness_requires_policy(tmp_path, capsys): # --check-freshness without --policy is a usage error (exit 2): freshness # is always relative to an expected policy, never free-floating.
- test_freshness_does_not_rerun_checks · function · L1258-L1266 — def test_freshness_does_not_rerun_checks(tmp_path, capsys): # Freshness must not rewrite the report: mtime-stable bytes.
- test_provenance_summary_outside_fingerprint · function · L1269-L1288 — def test_provenance_summary_outside_fingerprint(tmp_path, capsys): # gate-1 P1 self-staleness: provenance_summary.md is the pipeline's own # advisory carrier (D4 appends to it AFTER the report is stamped) — it is # excluded from the fingerprint, so mutating it does NOT stale the report.
- test_advisory_run_is_byte_equivalent_for_package_files · function · L1291-L1309 — def test_advisory_run_is_byte_equivalent_for_package_files(tmp_path): # §11 byte-equivalence: an explicit-advisory run mutates NOTHING in the # package except adding the report file.
- test_freshness_fresh_strict_report_reemits_terminal_token · function · L1314-L1328 — def test_freshness_fresh_strict_report_reemits_terminal_token( tmp_path, capsys): # Gate-2 P1: freshness alone means "the report is trustworthy", not "the # package passed". A fresh strict report that recorded a blocking fail # must re-emit its terminal verdict on reuse — otherwise the verdict # silently evaporates on resume and the orchestrator (gating on tokens) # reads the reuse as a pass.
- test_freshness_fresh_advisory_report_reemits_underlying_code · function · L1331-L1342 — def test_freshness_fresh_advisory_report_reemits_underlying_code( tmp_path, capsys): # Same contract on the advisory side: a fresh advisory report with # not_checked rows re-emits exit 3, not a flat 0.
- test_freshness_changed_venue_profile_is_stale · function · L1345-L1360 — def test_freshness_changed_venue_profile_is_stale(tmp_path, capsys): # Gate-2 P1: Family B verdicts depend on the venue profile — a report # produced under one profile must never read as fresh under another # (a lenient-profile report would otherwise waive a stricter profile's # limits without any check ever running against them).
- test_freshness_same_venue_profile_stays_fresh · function · L1363-L1373 — def test_freshness_same_venue_profile_stays_fresh(tmp_path, capsys)
- test_freshness_dropped_input_is_stale · function · L1376-L1387 — def test_freshness_dropped_input_is_stale(tmp_path, capsys): # Running with a profile, then checking freshness WITHOUT one, is an # inputs change (declared → absent) and must be stale.
- test_freshness_thinned_roster_is_stale · function · L1390-L1407 — def test_freshness_thinned_roster_is_stale(tmp_path, capsys): # Final-round review P2: the report file is excluded from the package # fingerprint, so a hand-edited report with checks thinned out (here: # emptied) would otherwise read as fresh and re-evaluate to a clean # exit — the roster guard build_report enforces at write time must # hold on reuse too.