from dataclasses import replace from decimal import Decimal import pytest from synthgen import dictionary, inn from synthgen.config import Config from synthgen.group import build_group, member_inn, variant_spelling from synthgen.ecp import write_ecp from synthgen.rng import stable_hash @pytest.fixture(scope="module") def orgs(): return dictionary.load() def test_group_size_and_head(orgs): g = build_group(Config(group_size=5), orgs) assert len(g.members) == 5 assert [m.code for m in g.members] == ["C01", "C02", "C03", "C04", "C05"] assert g.members[0].is_head and g.members[0].org.form == "АО" and g.members[0].head_share is None for m in g.members: assert m.org.form in ("ООО", "АО") and m.org.kind != "банк" assert not m.is_head or m is g.members[0] assert g.name == "ГК SYN" and g.gk_id == "SYN-000001" for m in g.members: assert m.name == f'{m.org.form} "SYN {m.org.root}"' assert m.name_1c == f"SYN {m.org.root} {m.org.form}" assert len(m.kpp) == 9 and inn.is_valid(m.inn) def test_member_inn_range(): value = member_inn("SYN", 3) assert inn.is_valid(value) assert int(value[2:-1]) == 1000000 + 100 * stable_hash("SYN", 90000) + 3 def test_prefixes_do_not_collide(orgs): a = {m.inn for m in build_group(Config(prefix="SYN"), orgs).members} b = {m.inn for m in build_group(Config(prefix="DEMO2"), orgs).members} assert not a & b assert not (a | b) & {o.inn for o in orgs} def test_externals_exclude_group_and_banks(orgs): g = build_group(Config(group_size=8), orgs) chosen = {m.org.inn for m in g.members} assert all(o.kind != "банк" for o in g.externals) assert not chosen & {o.inn for o in g.externals} assert len(g.externals) == sum(o.kind != "банк" for o in orgs) - 8 assert len(g.banks) == 30 and all(o.kind == "банк" for o in g.banks) def test_no_inn_selection(orgs): g = build_group(Config(group_size=5, no_inn_companies=2), orgs) assert len(g.no_inn) == 2 and "C01" not in g.no_inn g = build_group(Config(group_size=5, no_inn_companies=4), orgs) assert len(g.no_inn) == 4 and "C01" not in g.no_inn g = build_group(Config(group_size=5, no_inn_companies=5), orgs) assert g.no_inn == frozenset(m.code for m in g.members) assert build_group(Config(no_inn_companies=0), orgs).no_inn == frozenset() @pytest.mark.parametrize("size,n", [(2, 2), (5, 2), (5, 5), (10, 3)]) def test_variant_name(orgs, size, n): g = build_group(Config(group_size=size, no_inn_companies=n), orgs) by_code = {m.code: m for m in g.members} assert set(g.variant_name) == set(g.no_inn) for code, other in g.variant_name.items(): assert other != code and other in by_code m = by_code[other] variant = variant_spelling(m) assert variant not in (m.name_1c, m.name) assert variant != by_code[code].name_1c def test_shares(orgs): for size in (2, 5, 12): g = build_group(Config(group_size=size), orgs) children = g.members[1:] for m in children: assert Decimal("0.51") <= m.head_share <= Decimal("1.00") with_min = [m for m in children if m.minority is not None] assert len(with_min) <= 2 if size > 2: assert 1 <= len(with_min) for m in with_min: assert m.head_share < 1 for m in children: if m.minority is None: assert m.head_share == Decimal("1.00") def test_prefix_cyrillic_and_digits(orgs, tmp_path): for prefix in ("ДЕМО", "2GK"): g = build_group(Config(prefix=prefix), orgs) assert g.gk_id == f"{prefix}-000001" write_ecp(g, tmp_path / f"ecp-{prefix}.xlsx") def test_group_deterministic(orgs): cfg = Config(group_size=7, no_inn_companies=2, seed=5) assert build_group(cfg, orgs) == build_group(cfg, orgs) assert build_group(cfg, orgs) != build_group(replace(cfg, seed=6), orgs)