diff --git a/result_server/routes/admin.py b/result_server/routes/admin.py index fe0d8a8..b9ff4fe 100644 --- a/result_server/routes/admin.py +++ b/result_server/routes/admin.py @@ -133,6 +133,13 @@ def _parse_bool_form(name): return request.form.get(name) == "on" +def _profile_scope_csv(profile, key): + if not profile: + return "" + values = profile.get(key) or [] + return ",".join(str(value).strip() for value in values if str(value).strip()) + + def _build_execution_pipeline_plan(store): """Resolve the submitted target and build a GitLab pipeline plan.""" target_ref = request.form.get("target_ref", "").strip() or "develop" @@ -153,12 +160,15 @@ def _build_execution_pipeline_plan(store): exp=exp, ) profile = resolve_result.profile + effective_code = code or _profile_scope_csv(profile, "code") + effective_system = system or _profile_scope_csv(profile, "system") + effective_exp = exp or _profile_scope_csv(profile, "exp") gitlab_target, target_errors = configured_gitlab_target(gitlab_target_id) plan = build_pipeline_plan( gitlab_repo=gitlab_target.repo if gitlab_target else "", target_ref=target_ref, - code=code, - system=system, + code=effective_code, + system=effective_system, app=app, benchpark=benchpark, park_only=park_only, @@ -166,7 +176,7 @@ def _build_execution_pipeline_plan(store): scheduler_extra_args=resolve_result.scheduler_extra_args, target_id=gitlab_target.id if gitlab_target else gitlab_target_id, ) - if exp: + if effective_exp: plan.warnings.append( "Profile Exp is used for Portal profile matching and is not sent to GitLab CI." ) @@ -175,9 +185,9 @@ def _build_execution_pipeline_plan(store): "profile_id": profile_id, "gitlab_target": gitlab_target, "gitlab_target_id": gitlab_target.id if gitlab_target else gitlab_target_id, - "code": code, - "system": system, - "exp": exp, + "code": effective_code, + "system": effective_system, + "exp": effective_exp, "profile": profile, "plan": plan, "errors": target_errors + resolve_result.errors + plan.errors, diff --git a/result_server/tests/test_execution_profiles.py b/result_server/tests/test_execution_profiles.py index f885324..dc60463 100644 --- a/result_server/tests/test_execution_profiles.py +++ b/result_server/tests/test_execution_profiles.py @@ -423,6 +423,53 @@ def test_admin_execution_profiles_dry_run_submit_renders_payload(tmp_path, monke _cleanup(temp_dirs) +def test_admin_execution_profiles_dry_run_uses_profile_scope_values( + tmp_path, + monkeypatch, +): + monkeypatch.setenv("RESULT_SERVER_GITLAB_REPO", "gitlab.example.org/group/benchkit.git") + db_path = tmp_path / "cx_portal.sqlite3" + ExecutionProfileStore(str(db_path)).upsert_profile(_profile(), actor="admin") + app, temp_dirs = _admin_app(db_path) + try: + with app.test_client() as client: + _login_admin(client) + resp = client.post( + "/admin/execution-profiles/dry-run-submit", + data={ + "target_ref": "develop", + "profile_id": "rikyu-qws-nightly", + }, + ) + + html = resp.data.decode() + assert resp.status_code == 200 + assert "dry_run_ready" in html + assert "Profile Exp is used for Portal profile matching" in html + + with sqlite3.connect(db_path) as conn: + row = conn.execute( + """ + SELECT status, profile_id, code, system, exp, payload_json + FROM execution_requests + """ + ).fetchone() + assert row[:5] == ( + "dry_run_ready", + "rikyu-qws-nightly", + "qws", + "RIKYU", + "case0", + ) + payload_record = json.loads(row[5]) + variables = payload_record["payload"]["variables"] + assert variables["code"] == "qws" + assert variables["system"] == "RIKYU" + assert "exp" not in variables + finally: + _cleanup(temp_dirs) + + def test_admin_execution_profiles_dry_run_blocks_without_matching_profile( tmp_path, monkeypatch, @@ -643,6 +690,67 @@ def fake_submit(plan, *, token): _cleanup(temp_dirs) +def test_admin_execution_profiles_submit_uses_profile_scope_values( + tmp_path, + monkeypatch, +): + monkeypatch.setenv("RESULT_SERVER_GITLAB_REPO", "gitlab.example.org/group/benchkit.git") + monkeypatch.setenv("RESULT_SERVER_GITLAB_TRIGGER_TOKEN", "secret-token") + db_path = tmp_path / "cx_portal.sqlite3" + ExecutionProfileStore(str(db_path)).upsert_profile( + _profile(system=["Fugaku", "MiyabiG"]), + actor="admin", + ) + app, temp_dirs = _admin_app(db_path) + + def fake_submit(plan, *, token): + assert token == "secret-token" + assert plan.payload["variables"]["code"] == "qws" + assert plan.payload["variables"]["system"] == "Fugaku,MiyabiG" + assert "exp" not in plan.payload["variables"] + return GitLabPipelineSubmitResult( + status_code=201, + response={"id": 789, "web_url": "https://gitlab.example.org/p/789"}, + errors=[], + ) + + monkeypatch.setattr("routes.admin.submit_pipeline_plan", fake_submit) + try: + with app.test_client() as client: + _login_admin(client) + resp = client.post( + "/admin/execution-profiles/submit", + data={ + "target_ref": "develop", + "profile_id": "rikyu-qws-nightly", + "confirm_submit": "on", + }, + ) + + html = resp.data.decode() + assert resp.status_code == 200 + assert "submitted" in html + + with sqlite3.connect(db_path) as conn: + row = conn.execute( + """ + SELECT status, profile_id, code, system, exp, payload_json + FROM execution_requests + """ + ).fetchone() + assert row[:5] == ( + "submitted", + "rikyu-qws-nightly", + "qws", + "Fugaku,MiyabiG", + "case0", + ) + payload_record = json.loads(row[5]) + assert payload_record["submit"]["response"]["id"] == 789 + finally: + _cleanup(temp_dirs) + + def test_admin_execution_profiles_submit_requires_confirmation(tmp_path, monkeypatch): monkeypatch.setenv("RESULT_SERVER_GITLAB_REPO", "gitlab.example.org/group/benchkit.git") monkeypatch.setenv("RESULT_SERVER_GITLAB_TRIGGER_TOKEN", "secret-token")