diff --git a/.github/workflows/deploy-test-stand.yml b/.github/workflows/deploy-test-stand.yml index 36c5cdc07..7de534faf 100644 --- a/.github/workflows/deploy-test-stand.yml +++ b/.github/workflows/deploy-test-stand.yml @@ -440,14 +440,17 @@ jobs: echo "::warning::seeding with an explicit --email from secrets.TEST_STAND_SEED_EMAIL — the dev-lead address is NOT being read from the realm this deploy applied. Unset that secret unless this stand's realm is provisioned outside deploy/gitops." fi - # 365, not more: the API refuses any period ≥ 400 days. + # Two years, matching the compose stand. Wider than any one request + # the API will answer — it refuses a period of 400 days or more — so + # a suite asking about "the seeded period" clamps to the queryable + # tail (tests/stand/api/analytics/query_window). kube_context="$(yq -r '.kubeContext' "$INVENTORY_FILE")" seed_rc=0 ./src/ingestion/tools/seed/seed-stand.sh \ -n "$STAND_NAMESPACE" \ --context "$kube_context" \ "${email_args[@]}" \ - --days 365 \ + --days 730 \ "${force_args[@]}" \ > "$RUNNER_TEMP/seed.log" 2>&1 || seed_rc=$? diff --git a/deploy/gitops/environments/test-stand/INFRA.md b/deploy/gitops/environments/test-stand/INFRA.md index 5306c8d16..df94e1590 100644 --- a/deploy/gitops/environments/test-stand/INFRA.md +++ b/deploy/gitops/environments/test-stand/INFRA.md @@ -141,7 +141,7 @@ not facts about any real deployment. |---|---|---| | **Fork-downgrade wedge** | Chart version resolved from `.insight-version` in a stale checkout → a downgrade; helm rewrites live Deployments into the older shape and wedges. A post-check comparing only against the *requested* version calls it a pass. | Resolver reads the OCI registry's latest tag first; the deploy workflow + `recreate-test-stand.sh` both refuse a version older than what's deployed. | | **`TEST_STAND_SEED_EMAIL` drift** | Dev-lead address declared in two places (realm + seeder flag) with no agreement check; only that one login fails. | `seed-stand.sh` reads the address out of the applied realm ConfigMap (keyed on the roster UUID). The flag survives only as an explicit, warned override. | -| **730-day seed window** | Seed window wider than the analytics API's max queryable period → every window-derived request 400s, looking like a broad data problem. | Seed window capped inside the API limit; one shared query-window helper caps both suites at the same value. | +| **730-day seed window** | Seed window wider than the analytics API's max queryable period → every window-derived request 400s, looking like a broad data problem. | Both stands seed 730 days on purpose, so the trap is live: no test may read `data_window` raw. One shared helper (`tests/stand/api/analytics/query_window`) clamps every request to the queryable tail, and a reconciliation must pass the same clamped period to both sides or it compares two different periods. | | **Pending-upgrade wedge** | An interrupted `helm upgrade` leaves the release `pending-upgrade`, which helm refuses to upgrade over; every later deploy fails generically. | `make deploy` checks release status first and refuses with an explicit fix; workflow step timeouts stay below helm's own `--timeout`. | | **Diagnostics-allowlist retreat** | The workflow published curated cluster diagnostics into the public log; an allowlist like that only ever grows. | A failed run prints only the dead stage + edge probe status codes. Full output is read operator-side. | | **`max_user_connections` crash-loop** | The shared MariaDB user defaulted to the operator's low per-user limit (§1). | `maxUserConnections: 100` on `User/insight`. | diff --git a/docker-compose.yml b/docker-compose.yml index 1b111c389..9751f0374 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -724,7 +724,11 @@ services: # ISO date pins the dataset reproducibly, the literal `today` (or unset) # tracks the calendar. Both are recorded in the emitted manifest. SEED_ANCHOR_DATE: "${SEED_ANCHOR_DATE:-}" - SEED_DAYS: "${SEED_DAYS:-60}" + # Two years, matching the deployed stand. Wider than any one request the + # analytics API will answer (it refuses a period of 400 days or more), so + # a test asking about "the seeded period" must clamp — see + # tests/stand/api/analytics/query_window. + SEED_DAYS: "${SEED_DAYS:-730}" # The issuer the authenticator resolved for this run. `test-stand up` # persists it after cmd_up so the manifest # records the real IdP instead of assuming the default. diff --git a/src/ingestion/tools/seed/insight_seed/generators/git.py b/src/ingestion/tools/seed/insight_seed/generators/git.py index 5b6cf4a4e..3dce709a5 100644 --- a/src/ingestion/tools/seed/insight_seed/generators/git.py +++ b/src/ingestion/tools/seed/insight_seed/generators/git.py @@ -121,11 +121,10 @@ def seed_class_git_commits( ] rows: list[tuple[object, ...]] = [] version = 1 - # Dealt to the dev lead's first commits in generation order (a merge - # commit is skipped — gold's evidence model filters those out, so a - # message on one would never reach the drilldown). Deterministic across - # re-seeds because the commit stream itself is. - hostile_messages = list(HOSTILE_COMMIT_MESSAGES) + # Row indices of the dev lead's non-merge commits, ascending by date. Gold's + # evidence model filters merge commits out, so a message on one would never + # reach the drilldown. + lead_commits: list[int] = [] for p in _eligible(roster): persona = persona_multiplier(p.uuid) weight = TEAM_PROFILES[p.team or ""].weights["github"] @@ -139,9 +138,8 @@ def seed_class_git_commits( # LOC per commit capped at ≤200 by construction. added = float(rng.randint(2, 180)) removed = float(rng.randint(0, 80)) - message = "" - if p.uuid == DEV_LEAD_UUID and not is_merge and hostile_messages: - message = hostile_messages.pop(0) + if p.uuid == DEV_LEAD_UUID and not is_merge: + lead_commits.append(len(rows)) rows.append( ( tenant_uuid, @@ -157,10 +155,24 @@ def seed_class_git_commits( added, removed, "insight_github", - message, + "", version, ) ) + + # The most recent of those commits, not the earliest: a suite asking about + # "the seeded period" asks about the tail the API will answer for, and a + # window wider than that cap would leave titles dealt to the oldest days + # unreachable. + message_at = cols.index("message") + for offset, hostile in enumerate(reversed(HOSTILE_COMMIT_MESSAGES), start=1): + if offset > len(lead_commits): + break + index = lead_commits[-offset] + row = list(rows[index]) + row[message_at] = hostile + rows[index] = tuple(row) + return bulk_insert(client, "silver", "class_git_commits", cols, rows) diff --git a/tests/stand/api/analytics/test_drilldown.py b/tests/stand/api/analytics/test_drilldown.py index 8d1763c55..f37037dd1 100644 --- a/tests/stand/api/analytics/test_drilldown.py +++ b/tests/stand/api/analytics/test_drilldown.py @@ -66,6 +66,7 @@ MetricDrilldownColumnType, MetricDrilldownResponse, ) +from . import query_window from .drilldown_matrix import EXPORT_SHAPES, MATRIX, Expectation, Tier DRILLDOWN = analytics_path("/v1/metric-drilldown") @@ -154,7 +155,7 @@ def _request_for( filters: Sequence[JsonValue] = (), display_dimensions: Sequence[str] = (), ) -> dict[str, JsonValue]: - start, _, end = manifest.data_window.partition("..") + start, end = query_window(manifest) request: dict[str, JsonValue] = { "metric_key": metric_key, "entity": { @@ -286,7 +287,7 @@ def _period_value( person with no observations in the period has no value at all, and that is what an empty evidence page has to agree with. """ - start, _, end = manifest.data_window.partition("..") + start, end = query_window(manifest) person_id = manifest.fixture("dev_lead").uuid response = api.post( METRIC_RESULTS, diff --git a/tests/stand/api/analytics/test_drilldown_rebuild.py b/tests/stand/api/analytics/test_drilldown_rebuild.py index 7ec01f638..61ad55964 100644 --- a/tests/stand/api/analytics/test_drilldown_rebuild.py +++ b/tests/stand/api/analytics/test_drilldown_rebuild.py @@ -48,6 +48,7 @@ from ..schemas import MetricResultsResponse, PeriodView, ProblemDocument from ..schemas.analytics import MetricDrilldownResponse +from . import query_window pytestmark = pytest.mark.reliability @@ -195,7 +196,7 @@ def _remove_seed_containers(project: str) -> None: def _request( manifest: Manifest, *, limit: int | None = None, cursor: str | None = None ) -> dict[str, JsonValue]: - start, _, end = manifest.data_window.partition("..") + start, end = query_window(manifest) request: dict[str, JsonValue] = { "metric_key": GIT_COMMITS, "entity": {"type": "person", "id": manifest.fixture("dev_lead").uuid}, @@ -229,7 +230,7 @@ def _walk(api: ApiClient, manifest: Manifest) -> list[Mapping[str, Any]]: def _period_value(api: ApiClient, manifest: Manifest) -> float | None: """The scalar the dashboard shows for the same selection — the walk's oracle.""" - start, _, end = manifest.data_window.partition("..") + start, end = query_window(manifest) person_id = manifest.fixture("dev_lead").uuid response = api.post( METRIC_RESULTS,