Files
devx/tests/unit/test_nightly_gate.py
T
Emil SimeonovandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> b5138ec65b
CI / validate (pull_request) Failing after 1m4s
CI / auto-merge (pull_request) Skipped
fix: read Gitea repo-variable data field; fail closed on unknown nightly status
get_repo_variable read the value field, but Gitea returns the payload in
data — every read returned None, so the nightly gate always treated real
status as bootstrap-allow (run 5800 recorded passed while integration
tests failed). Unknown non-empty statuses also allowed deploys; they now
block (fail closed).

Also backfills correct bg/de/pl/ru/zh translations across the catalog and
removes stray nested keys polluting every entry.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-17 11:33:57 +02:00

118 lines
5.4 KiB
Python

"""Unit tests for devx.ci.nightly_gate."""
from unittest.mock import MagicMock, patch
from click.testing import CliRunner
from devx.ci.nightly_gate import cli, get_nightly_status, set_nightly_status
class TestGetNightlyStatus:
@patch("devx.ci.nightly_gate.GiteaClient")
def test_returns_value_when_set(self, mock_client_cls: MagicMock) -> None:
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = "passed:12345"
result = get_nightly_status(mock_client)
assert result == "passed:12345"
@patch("devx.ci.nightly_gate.GiteaClient")
def test_returns_empty_when_not_set(self, mock_client_cls: MagicMock) -> None:
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = None
result = get_nightly_status(mock_client)
assert result == ""
class TestSetNightlyStatus:
@patch("devx.ci.nightly_gate.GiteaClient")
def test_sets_passed(self, mock_client_cls: MagicMock) -> None:
mock_client = mock_client_cls.return_value
set_nightly_status(mock_client, "passed:12345")
mock_client.set_repo_variable.assert_called_once_with("NIGHTLY_STATUS", "passed:12345")
@patch("devx.ci.nightly_gate.GiteaClient")
def test_sets_failed(self, mock_client_cls: MagicMock) -> None:
mock_client = mock_client_cls.return_value
set_nightly_status(mock_client, "failed:99999")
mock_client.set_repo_variable.assert_called_once_with("NIGHTLY_STATUS", "failed:99999")
class TestCli:
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_check_bootstrap_allows_deploy(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = None
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "check"])
assert result.exit_code == 0
assert "bootstrap" in result.output.lower()
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_check_passed_allows_deploy(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = "passed:12345"
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "check"])
assert result.exit_code == 0
assert "passed" in result.output.lower()
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_check_failed_blocks_deploy(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = "failed:99999"
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "check"])
assert result.exit_code != 0
assert "blocked" in result.output.lower()
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_check_unknown_status_blocks_deploy(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
mock_client.get_repo_variable.return_value = "garbage-value"
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "check"])
assert result.exit_code != 0
assert "fail closed" in result.output.lower()
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_set_passed(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "set-passed", "--run-id", "12345"])
assert result.exit_code == 0
mock_client.set_repo_variable.assert_called_once_with("NIGHTLY_STATUS", "passed:12345")
@patch("devx.ci.nightly_gate.GiteaClient")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_set_failed(self, mock_token: MagicMock, mock_client_cls: MagicMock) -> None:
mock_token.return_value = "fake-token"
mock_client = mock_client_cls.return_value
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "set-failed", "--run-id", "99999"])
assert result.exit_code == 0
mock_client.set_repo_variable.assert_called_once_with("NIGHTLY_STATUS", "failed:99999")
@patch("devx.ci.nightly_gate.get_ci_token")
def test_fails_without_token(self, mock_token: MagicMock) -> None:
import click as click_mod
mock_token.side_effect = click_mod.ClickException("No token")
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "oblachno/infra", "--action", "check"])
assert result.exit_code != 0
def test_fails_with_invalid_repo(self) -> None:
runner = CliRunner()
result = runner.invoke(cli, ["--repo", "invalid", "--action", "check"])
assert result.exit_code != 0