1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-03-22 05:09:12 +00:00
community.general/tests/unit/plugins/modules/test_github_secrets.py
Thomas Sjögren 739b5ac6ff github_secrets: new module (#11514)
* add support for managing GitHub secrets

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* fix tab

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* update for sanity

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* more sanity fixes

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* update botmeta

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* formating

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* remove list function

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* remove docstring, format text strings and return codes

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* switch to deps

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* black and ruff doesnt get along

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* initial unit tests

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* update non-existing secret test

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* update description and details

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* handle when a secret cant be deleted

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* fail if not acceptable error codes

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* add test for non-acceptable status codes

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* remove local ruff config

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* allow empty strings

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* set required_

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* extend tests

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* cleanup

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* cover all, got a git urlopen error

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* cover all, got a git urlopen error

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* ensure value cant be None

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* check_mode

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* bump to 12.5.0

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* extend check_mode and related tests

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* split constants and return dict when checking secret

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* switch to HTTPStatus

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* replace DELETE and UPDATE with NO_CONTENT

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* update tests

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/github_secrets.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Signed-off-by: Thomas Sjögren <konstruktoid@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 46ffec6f0e)
2026-03-15 15:29:44 +00:00

360 lines
10 KiB
Python

# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
import json
from unittest.mock import MagicMock, patch
import pytest
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
AnsibleExitJson,
AnsibleFailJson,
exit_json,
fail_json,
set_module_args,
)
from ansible_collections.community.general.plugins.modules import github_secrets
PUBLIC_KEY_PAYLOAD = {
"key_id": "100",
"key": "j6gSA9mu5bO8ig+YBNU6oXRhGwd3Z4EFiS9rVmU9gwo=",
}
def make_fetch_url_response(body, status=200):
response = MagicMock()
response.read.return_value = json.dumps(body).encode("utf-8")
info = {"status": status, "msg": f"OK ({len(json.dumps(body))} bytes)"}
return (response, info)
@pytest.fixture(autouse=True)
def patch_module():
with patch.multiple(
"ansible.module_utils.basic.AnsibleModule",
exit_json=exit_json,
fail_json=fail_json,
):
yield
@pytest.fixture
def fetch_url_mock():
with patch.object(github_secrets, "fetch_url") as mock:
yield mock
def test_encrypt_secret():
result = github_secrets.encrypt_secret(PUBLIC_KEY_PAYLOAD["key"], "ansible")
assert isinstance(result, str)
assert result != "ansible"
assert len(result) > 70
def test_fail_without_parameters():
with pytest.raises(AnsibleFailJson):
with set_module_args({}):
github_secrets.main()
def test_fail_present_without_value():
with pytest.raises(AnsibleFailJson) as exc:
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": None,
"state": "present",
"token": "ghp_test_token",
}
):
github_secrets.main()
assert "value' parameter cannot be empty" in exc.value.args[0]["details"]
def test_fail_org_secret_present_without_visibility():
with pytest.raises(AnsibleFailJson) as exc:
with set_module_args(
{
"organization": "myorg",
"key": "ORG_SECRET",
"value": "org_value",
"state": "present",
"token": "ghp_test_token",
}
):
github_secrets.main()
assert "'visibility' must be provided" in exc.value.args[0]["details"]
def test_create_repo_secret(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=201),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "secret_value",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["status"] == 201
assert result["result"]["response"] == "Secret created"
def test_update_repo_secret(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=204),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "new_value",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["response"] == "Secret updated"
def test_create_repo_secret_check_mode(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=404),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "new_value",
"state": "present",
"token": "ghp_test_token",
"_ansible_check_mode": True,
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["status"] == 201
assert result["result"]["response"] == "Secret created"
def test_update_repo_secret_check_mode(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=200),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "new_value",
"state": "present",
"token": "ghp_test_token",
"_ansible_check_mode": True,
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["status"] == 204
assert result["result"]["response"] == "Secret updated"
def test_delete_repo_secret_check_mode(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=200),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"state": "absent",
"token": "ghp_test_token",
"_ansible_check_mode": True,
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["status"] == 204
assert result["result"]["response"] == "Secret deleted"
def test_update_empty_repo_secret(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=204),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["response"] == "Secret updated"
def test_update_missing_value_repo_secret(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=204),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleFailJson) as exc:
github_secrets.main()
assert "the following are missing: value" in exc.value.args[0]["msg"]
def test_delete_repo_secret(fetch_url_mock):
fetch_url_mock.return_value = make_fetch_url_response({}, status=204)
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"state": "absent",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is True
assert result["result"]["status"] == 204
assert result["result"]["response"] == "Secret deleted"
def test_delete_dne_repo_secret(fetch_url_mock):
fetch_url_mock.return_value = make_fetch_url_response({}, status=404)
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "DOES_NOT_EXIST",
"state": "absent",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleExitJson) as exc:
github_secrets.main()
result = exc.value.args[0]
assert result["changed"] is False
assert result["result"]["status"] == 404
assert result["result"]["response"] == "Secret not found"
def test_fail_get_public_key(fetch_url_mock):
fetch_url_mock.return_value = make_fetch_url_response({}, status=403)
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "secret_value",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleFailJson) as exc:
github_secrets.main()
assert "Failed to get public key" in exc.value.args[0]["msg"]
def test_fail_upsert_secret(fetch_url_mock):
fetch_url_mock.side_effect = [
make_fetch_url_response(PUBLIC_KEY_PAYLOAD),
make_fetch_url_response({}, status=422),
]
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"value": "secret_value",
"state": "present",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleFailJson) as exc:
github_secrets.main()
assert "Failed to upsert secret" in exc.value.args[0]["msg"]
def test_fail_delete_secret(fetch_url_mock):
fetch_url_mock.return_value = make_fetch_url_response({}, status=503)
with set_module_args(
{
"organization": "myorg",
"repository": "myrepo",
"key": "MY_SECRET",
"state": "absent",
"token": "ghp_test_token",
}
):
with pytest.raises(AnsibleFailJson) as exc:
github_secrets.main()
assert "Failed to delete secret" in exc.value.args[0]["msg"]