Migrate all repos into monorepo context folders

Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
@@ -0,0 +1,361 @@
# Feature: team-profile-matching, Task 5.5: Unit tests for build_team_profile / serialize_team_profile
"""Example-based unit tests for the team-profile builder and serializer.
These tests complement the property-based tests in
``tests/test_team_profile_serialization_pbt.py`` by pinning down concrete
example outputs for ``build_team_profile`` and ``serialize_team_profile``
defined in ``teamlandkarte_mcp.matching.profiles``.
Covered scenarios (see task 5.5 in the team-profile-matching spec):
* Empty strings on every text field plus empty competence/reference lists
still produce an output that contains all seven fixed German headings,
with empty values rendered after the heading and lists rendered as
``Kompetenzen: (keine)`` / ``Referenzen: (keine)``.
* Mixed top / non-top competences are rendered with the ``(Top)`` suffix
only on the top entries.
* References with a non-empty ``partner_name`` use the
``Partner: <name> Projekte: <projects>`` form (en-dash U+2013).
* References with an empty ``partner_name`` use the bare
``Projekte: <projects>`` form (no ``Partner:`` token, no placeholder).
* ``build_team_profile`` performs a 1:1 mapping from ``Team`` to
``TeamProfile`` without re-sorting, including the ouid, team_id and the
competence / reference lists.
* Empty competence / reference lists pass through ``build_team_profile``
unchanged.
**Validates: Requirements 5.1, 5.3, 5.4, 5.5, 5.6**
"""
from __future__ import annotations
from teamlandkarte_mcp.matching.profiles import (
TeamCompetenceEntry,
TeamProfile,
TeamReferenceEntry,
build_team_profile,
serialize_team_profile,
)
from teamlandkarte_mcp.models import Team, TeamCompetence, TeamReference
# ---------------------------------------------------------------------------
# build_team_profile
# ---------------------------------------------------------------------------
def test_build_team_profile_maps_all_fields_one_to_one() -> None:
"""``build_team_profile`` copies every ``Team`` field onto the profile.
Validates requirement 5.1: every team field (team_id, ouid, team_name,
focus_name, about_us, offerings, interests, competences, references)
appears on the resulting :class:`TeamProfile` with the exact same
value, in the exact same list order. ``team_id`` is converted to
``str`` for the profile ``id`` field.
"""
team = Team(
team_id="T-42",
ouid="ou-9",
team_name="Alpha Team",
focus_name="Backend",
about_us="Wir bauen APIs.",
offerings="Beratung",
interests="Cloud",
competences=[
TeamCompetence(name="Python", top_competency=True),
TeamCompetence(name="FastAPI", top_competency=False),
],
references=[
TeamReference(partner_name="Acme", projects="Project X"),
TeamReference(partner_name="", projects="Project Y"),
],
)
profile = build_team_profile(team)
assert profile.id == "T-42"
assert profile.ouid == "ou-9"
assert profile.team_name == "Alpha Team"
assert profile.focus_name == "Backend"
assert profile.about_us == "Wir bauen APIs."
assert profile.offerings == "Beratung"
assert profile.interests == "Cloud"
# Competences are preserved 1:1 and in the original order.
assert profile.competences == [
TeamCompetenceEntry(name="Python", top_competency=True),
TeamCompetenceEntry(name="FastAPI", top_competency=False),
]
# References are preserved 1:1 and in the original order.
assert profile.references == [
TeamReferenceEntry(partner_name="Acme", projects="Project X"),
TeamReferenceEntry(partner_name="", projects="Project Y"),
]
def test_build_team_profile_passes_empty_lists_through() -> None:
"""Empty ``competences`` and ``references`` lists pass through unchanged.
Validates requirement 5.1: an empty list on the input team yields an
empty list on the resulting profile, without dropping the profile or
inserting placeholder entries.
"""
team = Team(
team_id="T-empty",
ouid="ou-empty",
team_name="",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[],
references=[],
)
profile = build_team_profile(team)
assert profile.competences == []
assert profile.references == []
# The string fields stay empty without any placeholder.
assert profile.team_name == ""
assert profile.focus_name == ""
assert profile.about_us == ""
assert profile.offerings == ""
assert profile.interests == ""
def test_build_team_profile_does_not_reorder_competences() -> None:
"""``build_team_profile`` preserves the input order of competences.
The DB layer is the single source of truth for the deterministic
competence order ``(top_competency desc, name asc)``; the builder
must not re-sort. Validates requirement 5.1 / 13.4.
"""
team = Team(
team_id="T-1",
ouid="ou-1",
team_name="Team",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[
# Intentionally not in (top desc, name asc) order so a
# re-sort would be visible.
TeamCompetence(name="Zeta", top_competency=False),
TeamCompetence(name="Alpha", top_competency=True),
TeamCompetence(name="Beta", top_competency=False),
],
references=[],
)
profile = build_team_profile(team)
assert [c.name for c in profile.competences] == ["Zeta", "Alpha", "Beta"]
assert [c.top_competency for c in profile.competences] == [False, True, False]
# ---------------------------------------------------------------------------
# serialize_team_profile
# ---------------------------------------------------------------------------
def test_serialize_team_profile_empty_profile_emits_all_headings() -> None:
"""An empty profile still emits the seven fixed German headings.
Validates requirements 5.4 (fixed heading order) and the ``(keine)``
fallback for empty competence and reference lists.
"""
profile = TeamProfile(
id="",
ouid="",
team_name="",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[],
references=[],
)
output = serialize_team_profile(profile)
expected = "\n".join(
[
"Teamname: ",
"Schwerpunkt: ",
"Über uns: ",
"Leistungen: ",
"Interessen: ",
"Kompetenzen: (keine)",
"Referenzen: (keine)",
]
)
assert output == expected
def test_serialize_team_profile_mixed_top_and_non_top_competences() -> None:
"""Top competences carry the ``(Top)`` suffix; non-top entries do not.
Validates requirement 5.5: the marker is rendered as ``" (Top)"``
directly after the competence name, exactly for entries with
``top_competency=True``.
"""
profile = TeamProfile(
id="T-1",
ouid="ou-1",
team_name="Team Alpha",
focus_name="Backend",
about_us="",
offerings="",
interests="",
competences=[
TeamCompetenceEntry(name="Python", top_competency=True),
TeamCompetenceEntry(name="FastAPI", top_competency=False),
TeamCompetenceEntry(name="SQL", top_competency=True),
],
references=[],
)
output = serialize_team_profile(profile)
assert "Kompetenzen:\n- Python (Top)\n- FastAPI\n- SQL (Top)" in output
# Non-top entries must not accidentally pick up the marker.
assert "FastAPI (Top)" not in output
def test_serialize_team_profile_reference_with_partner_name() -> None:
"""References with a partner render as ``Partner: <name> Projekte: <projects>``.
Validates requirement 5.6: both partner name and projects text are
visible, joined with the en-dash U+2013 (``\u2013``).
"""
profile = TeamProfile(
id="T-1",
ouid="ou-1",
team_name="Team",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[],
references=[
TeamReferenceEntry(partner_name="Acme", projects="Project X"),
],
)
output = serialize_team_profile(profile)
assert "Referenzen:\n- Partner: Acme \u2013 Projekte: Project X" in output
def test_serialize_team_profile_reference_without_partner_name() -> None:
"""References without a partner render as bare ``Projekte: <projects>``.
Validates requirement 5.3: an empty ``partner_name`` suppresses the
``Partner: `` token entirely, and no placeholder is inserted.
"""
profile = TeamProfile(
id="T-1",
ouid="ou-1",
team_name="Team",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[],
references=[
TeamReferenceEntry(partner_name="", projects="Project Y"),
],
)
output = serialize_team_profile(profile)
# Locate the references block to make the absence-assertion robust
# against unrelated text in other fields.
refs_start = output.index("Referenzen:")
refs_block = output[refs_start:]
assert "- Projekte: Project Y" in refs_block
assert "Partner: " not in refs_block
# No en-dash filler when there is no partner.
assert "\u2013" not in refs_block
def test_serialize_team_profile_mixed_references_with_and_without_partner() -> None:
"""Mixed references render each entry with the appropriate shape.
Validates requirements 5.3 and 5.6 jointly: in a single profile, a
reference with a partner uses the ``Partner: ... Projekte: ...``
form, and a reference without a partner uses the bare
``Projekte: ...`` form. Order is preserved 1:1.
"""
profile = TeamProfile(
id="T-1",
ouid="ou-1",
team_name="Team",
focus_name="",
about_us="",
offerings="",
interests="",
competences=[],
references=[
TeamReferenceEntry(partner_name="Acme", projects="Project X"),
TeamReferenceEntry(partner_name="", projects="Project Y"),
],
)
output = serialize_team_profile(profile)
expected_block = (
"Referenzen:\n"
"- Partner: Acme \u2013 Projekte: Project X\n"
"- Projekte: Project Y"
)
assert expected_block in output
def test_serialize_team_profile_full_example_round_trip() -> None:
"""End-to-end example: build then serialize a populated team.
Validates requirements 5.1, 5.3, 5.4, 5.5, 5.6 together: the full
serialized output for a representative team matches the expected
fixed-format string exactly, including heading order, ``(Top)``
markers, and reference shapes.
"""
team = Team(
team_id="T-42",
ouid="ou-9",
team_name="Alpha Team",
focus_name="Backend",
about_us="Wir bauen APIs.",
offerings="Beratung",
interests="Cloud",
competences=[
TeamCompetence(name="Python", top_competency=True),
TeamCompetence(name="FastAPI", top_competency=False),
],
references=[
TeamReference(partner_name="Acme", projects="Project X"),
TeamReference(partner_name="", projects="Project Y"),
],
)
output = serialize_team_profile(build_team_profile(team))
expected = "\n".join(
[
"Teamname: Alpha Team",
"Schwerpunkt: Backend",
"Über uns: Wir bauen APIs.",
"Leistungen: Beratung",
"Interessen: Cloud",
"Kompetenzen:",
"- Python (Top)",
"- FastAPI",
"Referenzen:",
"- Partner: Acme \u2013 Projekte: Project X",
"- Projekte: Project Y",
]
)
assert output == expected