Merge branch 'master' of https://github.com/certbot/certbot into docs-rewrite/renewal

This commit is contained in:
zoracon 2023-03-07 13:53:53 -08:00
commit 26d951fc7d
No known key found for this signature in database
GPG key ID: EF9F92E86D8CF643
202 changed files with 5854 additions and 5632 deletions

View file

@ -1,8 +1,18 @@
trigger: none
# We run the test suite on commits to master so codecov gets coverage data
# about the master branch and can use it to track coverage changes.
trigger:
- master
pr:
- master
- '*.x'
variables:
# We set this here to avoid coverage data being uploaded from things like our
# nightly pipeline. This is done because codecov (helpfully) keeps track of
# the number of coverage uploads for a commit and displays a warning when
# comparing two commits with an unequal number of uploads. Only uploading
# coverage here should keep the number of uploads it sees consistent.
uploadCoverage: true
jobs:
- template: templates/jobs/standard-tests-jobs.yml

View file

@ -15,11 +15,5 @@ stages:
- template: templates/stages/changelog-stage.yml
- template: templates/stages/deploy-stage.yml
parameters:
${{ if startsWith(variables['Build.SourceBranchName'], 'v2') }}:
snapReleaseChannel: beta
${{ elseif startsWith(variables['Build.SourceBranchName'], 'v1') }}:
snapReleaseChannel: candidate
${{ else }}:
# This should never happen
snapReleaseChannel: somethingInvalid
snapReleaseChannel: beta
- template: templates/stages/notify-failure-stage.yml

View file

@ -12,14 +12,13 @@ parameters:
values:
- edge
- beta
- candidate
jobs:
# This job relies on credentials used to publish the Certbot snaps. This
# credential file was created by running:
#
# snapcraft logout
# snapcraft export-login --channels=candidate,beta,edge snapcraft.cfg
# snapcraft export-login --channels=beta,edge snapcraft.cfg
# (provide the shared snapcraft credentials when prompted)
#
# Then the file was added as a secure file in Azure pipelines
@ -30,7 +29,7 @@ jobs:
# https://docs.microsoft.com/en-us/azure/devops/pipelines/library/secure-files?view=azure-devops#q-how-do-i-authorize-a-secure-file-for-use-in-a-specific-pipeline.
#
# This file has a maximum lifetime of one year and the current file will
# expire on 2023-09-06. The file will need to be updated before then to
# expire on 2024-02-10. The file will need to be updated before then to
# prevent automated deploys from breaking.
#
# Revoking these credentials can be done by changing the password of the

View file

@ -7,10 +7,10 @@ jobs:
macos-py37-cover:
IMAGE_NAME: macOS-12
PYTHON_VERSION: 3.7
TOXENV: py37-cover
TOXENV: cover
macos-cover:
IMAGE_NAME: macOS-12
TOXENV: py3-cover
TOXENV: cover
windows-py37:
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.7
@ -18,7 +18,7 @@ jobs:
windows-py39-cover:
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.9
TOXENV: py39-cover-win
TOXENV: cover-win
windows-integration-certbot:
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.9
@ -37,7 +37,7 @@ jobs:
TOXENV: py37
linux-cover:
IMAGE_NAME: ubuntu-22.04
TOXENV: py3-cover
TOXENV: cover
linux-lint:
IMAGE_NAME: ubuntu-22.04
TOXENV: lint-posix

View file

@ -11,7 +11,23 @@ stages:
- template: ../jobs/snap-deploy-job.yml
parameters:
snapReleaseChannel: ${{ parameters.snapReleaseChannel }}
- job: publish_docker
# The credentials used in the following jobs are for the shared
# certbotbot account on Docker Hub. The credentials are stored
# in a service account which was created by following the
# instructions at
# https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-docreg.
# The name given to this service account must match the value
# given to containerRegistry below. The authentication used when
# creating this service account was a personal access token
# rather than a password to bypass 2FA. When Brad set this up,
# Azure Pipelines failed to verify the credentials with an error
# like "access is forbidden with a JWT issued from a personal
# access token", but after saving them without verification, the
# access token worked when the pipeline actually ran. "Grant
# access to all pipelines" should also be checked on the service
# account. The access token can be deleted on Docker Hub if
# these credentials need to be revoked.
- job: publish_docker_by_arch
pool:
vmImage: ubuntu-22.04
strategy:
@ -33,22 +49,19 @@ stages:
- task: Docker@2
inputs:
command: login
# The credentials used here are for the shared certbotbot account
# on Docker Hub. The credentials are stored in a service account
# which was created by following the instructions at
# https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-docreg.
# The name given to this service account must match the value
# given to containerRegistry below. The authentication used when
# creating this service account was a personal access token
# rather than a password to bypass 2FA. When Brad set this up,
# Azure Pipelines failed to verify the credentials with an error
# like "access is forbidden with a JWT issued from a personal
# access token", but after saving them without verification, the
# access token worked when the pipeline actually ran. "Grant
# access to all pipelines" should also be checked on the service
# account. The access token can be deleted on Docker Hub if
# these credentials need to be revoked.
containerRegistry: docker-hub
displayName: Login to Docker Hub
- bash: set -e && tools/docker/deploy.sh $(dockerTag) $DOCKER_ARCH
displayName: Deploy the Docker images
- bash: set -e && tools/docker/deploy_by_arch.sh $(dockerTag) $DOCKER_ARCH
displayName: Deploy the Docker images by architecture
- job: publish_docker_multiarch
dependsOn: publish_docker_by_arch
pool:
vmImage: ubuntu-22.04
steps:
- task: Docker@2
inputs:
command: login
containerRegistry: docker-hub
displayName: Login to Docker Hub
- bash: set -e && tools/docker/deploy_multiarch.sh $(dockerTag)
displayName: Deploy the Docker multiarch manifests

View file

@ -21,6 +21,7 @@ steps:
nginx-light \
openssl
sudo systemctl stop nginx
sudo sysctl net.ipv4.ip_unprivileged_port_start=0
condition: startswith(variables['IMAGE_NAME'], 'ubuntu')
displayName: Install Linux dependencies
- task: UsePythonVersion@0
@ -55,3 +56,28 @@ steps:
AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
AWS_EC2_PEM_FILE: $(testFarmPem.secureFilePath)
displayName: Run tox
# For now, let's omit `set -e` and avoid the script exiting with a nonzero
# status code to prevent problems here from causing build failures. If
# this turns out to work well, we can change this.
- bash: |
python3 tools/pip_install.py -I coverage
case "$AGENT_OS" in
Darwin)
CODECOV_URL="https://uploader.codecov.io/latest/macos/codecov"
;;
Linux)
CODECOV_URL="https://uploader.codecov.io/latest/linux/codecov"
;;
Windows_NT)
CODECOV_URL="https://uploader.codecov.io/latest/windows/codecov.exe"
;;
*)
echo "Unexpected OS"
exit 0
esac
curl --retry 3 -o codecov "$CODECOV_URL"
chmod +x codecov
coverage xml
./codecov || echo "Uploading coverage data failed"
condition: and(eq(variables['uploadCoverage'], true), or(startsWith(variables['TOXENV'], 'cover'), startsWith(variables['TOXENV'], 'integration')))
displayName: Upload coverage data

7
.github/codecov.yml vendored Normal file
View file

@ -0,0 +1,7 @@
# This disables all reporting from codecov. Let's just set it up to collect
# data for now and then we can play with the settings here.
comment: false
coverage:
status:
project: off
patch: off

35
.github/stale.yml vendored
View file

@ -1,35 +0,0 @@
# Configuration for https://github.com/marketplace/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 365
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
# When changing this value, be sure to also update markComment below.
daysUntilClose: 30
# Ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking as stale
staleLabel: needs-update
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
We've made a lot of changes to Certbot since this issue was opened. If you
still have this issue with an up-to-date version of Certbot, can you please
add a comment letting us know? This helps us to better see what issues are
still affecting our users. If there is no activity in the next 30 days, this
issue will be automatically closed.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been closed due to lack of activity, but if you think it
should be reopened, please open a new issue with a link to this one and we'll
take a look.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Don't mark pull requests as stale.
only: issues

25
.github/workflows/notify_weekly.yaml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Weekly Github Update
on:
schedule:
# Every week on Thursday @ 13:00
- cron: "0 13 * * 4"
jobs:
send-mattermost-message:
runs-on: ubuntu-latest
steps:
- name: Create Mattermost Message
run: |
DATE=$(date --date="7 days ago" +"%Y-%m-%d")
MERGED_URL="https://github.com/pulls?q=merged%3A%3E${DATE}+org%3Acertbot"
UPDATED_URL="https://github.com/pulls?q=updated%3A%3E${DATE}+org%3Acertbot"
echo "{\"text\":\"## Updates Across Certbot Repos\n\n
- Certbot team members SHOULD look at: [link]($MERGED_URL)\n\n
- Certbot team members MAY also want to look at: [link]($UPDATED_URL)\n\n
- Want to Discuss something today? Place it [here](https://docs.google.com/document/d/17YMUbtC1yg6MfiTMwT8zVm9LmO-cuGVBom0qFn8XJBM/edit?usp=sharing) and we can meet today on Zoom.\n\n
- The key words SHOULD and MAY in this message are to be interpreted as described in [RFC 8147](https://www.rfc-editor.org/rfc/rfc8174). \"
}" > mattermost.json
- uses: mattermost/action-mattermost-notify@master
env:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK_URL }}

45
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: Update Stale Issues
on:
schedule:
# Run at midnight every night
- cron: '24 1 * * *'
permissions:
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
with:
# Idle number of days before marking issues stale
days-before-issue-stale: 365
# Never mark PRs as stale
days-before-pr-stale: -1
# Idle number of days before closing stale issues
days-before-issue-close: 30
# Never close PRs
days-before-pr-close: -1
# Ignore issues with an assignee
exempt-all-issue-assignees: true
# Label to use when marking as stale
stale-issue-label: needs-update
stale-issue-message: >
We've made a lot of changes to Certbot since this issue was opened. If you
still have this issue with an up-to-date version of Certbot, can you please
add a comment letting us know? This helps us to better see what issues are
still affecting our users. If there is no activity in the next 30 days, this
issue will be automatically closed.
close-issue-message: >
This issue has been closed due to lack of activity, but if you think it
should be reopened, please open a new issue with a link to this one and we'll
take a look.
# Limit the number of actions per hour, from 1-30. Default is 30
operations-per-run: 30

View file

@ -4,3 +4,4 @@ force_sort_within_sections=True
force_single_line=True
order_by_type=False
line_length=400
src_paths=acme/acme,acme/tests,certbot*/certbot*,certbot*/tests

View file

@ -68,6 +68,7 @@ Authors
* [Daniel Convissor](https://github.com/convissor)
* [Daniel "Drex" Drexler](https://github.com/aeturnum)
* [Daniel Huang](https://github.com/dhuang)
* [Daniel McMahon] (https://github.com/igloodan)
* [Dave Guarino](https://github.com/daguar)
* [David cz](https://github.com/dave-cz)
* [David Dworken](https://github.com/ddworken)

5
SECURITY.md Normal file
View file

@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Security vulnerabilities can be reported using GitHub's [private vulnerability reporting tool](https://github.com/certbot/certbot/security/advisories/new).

View file

@ -5,8 +5,8 @@ import functools
import hashlib
import logging
import socket
from typing import cast
from typing import Any
from typing import cast
from typing import Dict
from typing import Mapping
from typing import Optional

View file

@ -1,8 +1,7 @@
"""ACME JSON fields."""
import datetime
from typing import Any
import logging
from typing import Any
import josepy as jose
import pyrfc3339

View file

@ -1,6 +1,6 @@
"""ACME protocol messages."""
import datetime
from collections.abc import Hashable
import datetime
import json
from typing import Any
from typing import Dict
@ -21,7 +21,6 @@ from acme import fields
from acme import jws
from acme import util
ERROR_PREFIX = "urn:ietf:params:acme:error:"
ERROR_CODES = {
@ -123,6 +122,9 @@ class Error(jose.JSONObjectWithFields, errors.Error):
https://datatracker.ietf.org/doc/html/rfc7807
Note: Although Error inherits from JSONObjectWithFields, which is immutable,
we add mutability for Error to comply with the Python exception API.
:ivar str typ:
:ivar str title:
:ivar str detail:
@ -185,6 +187,10 @@ class Error(jose.JSONObjectWithFields, errors.Error):
return code
return None
# Hack to allow mutability on Errors (see GH #9539)
def __setattr__(self, name: str, value: Any) -> None:
return object.__setattr__(self, name, value)
def __str__(self) -> str:
result = b' :: '.join(
part.encode('ascii', 'backslashreplace') for part in

View file

@ -3,6 +3,6 @@ usage: jws [-h] [--compact] {sign,verify} ...
positional arguments:
{sign,verify}
optional arguments:
options:
-h, --help show this help message and exit
--compact

View file

@ -3,7 +3,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'cryptography>=2.5.0',

View file

@ -1,15 +1,16 @@
"""Tests for acme.challenges."""
import urllib.parse as urllib_parse
import sys
import unittest
from unittest import mock
import urllib.parse as urllib_parse
import josepy as jose
import OpenSSL
import requests
from josepy.jwk import JWKEC
import OpenSSL
import pytest
import requests
from acme import errors
import test_util
CERT = test_util.load_comparable_cert('cert.pem')
@ -22,7 +23,7 @@ class ChallengeTest(unittest.TestCase):
from acme.challenges import Challenge
from acme.challenges import UnrecognizedChallenge
chall = UnrecognizedChallenge({"type": "foo"})
self.assertEqual(chall, Challenge.from_json(chall.jobj))
assert chall == Challenge.from_json(chall.jobj)
class UnrecognizedChallengeTest(unittest.TestCase):
@ -33,12 +34,11 @@ class UnrecognizedChallengeTest(unittest.TestCase):
self.chall = UnrecognizedChallenge(self.jobj)
def test_to_partial_json(self):
self.assertEqual(self.jobj, self.chall.to_partial_json())
assert self.jobj == self.chall.to_partial_json()
def test_from_json(self):
from acme.challenges import UnrecognizedChallenge
self.assertEqual(
self.chall, UnrecognizedChallenge.from_json(self.jobj))
assert self.chall == UnrecognizedChallenge.from_json(self.jobj)
class KeyAuthorizationChallengeResponseTest(unittest.TestCase):
@ -54,26 +54,26 @@ class KeyAuthorizationChallengeResponseTest(unittest.TestCase):
from acme.challenges import KeyAuthorizationChallengeResponse
response = KeyAuthorizationChallengeResponse(
key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY')
self.assertTrue(response.verify(self.chall, KEY.public_key()))
assert response.verify(self.chall, KEY.public_key())
def test_verify_wrong_token(self):
from acme.challenges import KeyAuthorizationChallengeResponse
response = KeyAuthorizationChallengeResponse(
key_authorization='bar.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY')
self.assertFalse(response.verify(self.chall, KEY.public_key()))
assert not response.verify(self.chall, KEY.public_key())
def test_verify_wrong_thumbprint(self):
from acme.challenges import KeyAuthorizationChallengeResponse
response = KeyAuthorizationChallengeResponse(
key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxv')
self.assertFalse(response.verify(self.chall, KEY.public_key()))
assert not response.verify(self.chall, KEY.public_key())
def test_verify_wrong_form(self):
from acme.challenges import KeyAuthorizationChallengeResponse
response = KeyAuthorizationChallengeResponse(
key_authorization='.foo.oKGqedy-b-acd5eoybm2f-'
'NVFxvyOoET5CNy3xnv8WY')
self.assertFalse(response.verify(self.chall, KEY.public_key()))
assert not response.verify(self.chall, KEY.public_key())
class DNS01ResponseTest(unittest.TestCase):
@ -92,11 +92,11 @@ class DNS01ResponseTest(unittest.TestCase):
self.response = self.chall.response(KEY)
def test_to_partial_json(self):
self.assertEqual({}, self.msg.to_partial_json())
assert {} == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import DNS01Response
self.assertEqual(self.msg, DNS01Response.from_json(self.jmsg))
assert self.msg == DNS01Response.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import DNS01Response
@ -106,12 +106,12 @@ class DNS01ResponseTest(unittest.TestCase):
key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem'))
public_key = key2.public_key()
verified = self.response.simple_verify(self.chall, "local", public_key)
self.assertFalse(verified)
assert not verified
def test_simple_verify_success(self):
public_key = KEY.public_key()
verified = self.response.simple_verify(self.chall, "local", public_key)
self.assertTrue(verified)
assert verified
class DNS01Test(unittest.TestCase):
@ -126,20 +126,19 @@ class DNS01Test(unittest.TestCase):
}
def test_validation_domain_name(self):
self.assertEqual('_acme-challenge.www.example.com',
self.msg.validation_domain_name('www.example.com'))
assert '_acme-challenge.www.example.com' == \
self.msg.validation_domain_name('www.example.com')
def test_validation(self):
self.assertEqual(
"rAa7iIg4K2y63fvUhCfy8dP1Xl7wEhmQq0oChTcE3Zk",
self.msg.validation(KEY))
assert "rAa7iIg4K2y63fvUhCfy8dP1Xl7wEhmQq0oChTcE3Zk" == \
self.msg.validation(KEY)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
assert self.jmsg == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import DNS01
self.assertEqual(self.msg, DNS01.from_json(self.jmsg))
assert self.msg == DNS01.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import DNS01
@ -162,12 +161,11 @@ class HTTP01ResponseTest(unittest.TestCase):
self.response = self.chall.response(KEY)
def test_to_partial_json(self):
self.assertEqual({}, self.msg.to_partial_json())
assert {} == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import HTTP01Response
self.assertEqual(
self.msg, HTTP01Response.from_json(self.jmsg))
assert self.msg == HTTP01Response.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import HTTP01Response
@ -181,16 +179,16 @@ class HTTP01ResponseTest(unittest.TestCase):
def test_simple_verify_good_validation(self, mock_get):
validation = self.chall.validation(KEY)
mock_get.return_value = mock.MagicMock(text=validation)
self.assertTrue(self.response.simple_verify(
self.chall, "local", KEY.public_key()))
assert self.response.simple_verify(
self.chall, "local", KEY.public_key())
mock_get.assert_called_once_with(self.chall.uri("local"), verify=False,
timeout=mock.ANY)
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_bad_validation(self, mock_get):
mock_get.return_value = mock.MagicMock(text="!")
self.assertFalse(self.response.simple_verify(
self.chall, "local", KEY.public_key()))
assert not self.response.simple_verify(
self.chall, "local", KEY.public_key())
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_whitespace_validation(self, mock_get):
@ -198,24 +196,24 @@ class HTTP01ResponseTest(unittest.TestCase):
mock_get.return_value = mock.MagicMock(
text=(self.chall.validation(KEY) +
HTTP01Response.WHITESPACE_CUTSET))
self.assertTrue(self.response.simple_verify(
self.chall, "local", KEY.public_key()))
assert self.response.simple_verify(
self.chall, "local", KEY.public_key())
mock_get.assert_called_once_with(self.chall.uri("local"), verify=False,
timeout=mock.ANY)
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_connection_error(self, mock_get):
mock_get.side_effect = requests.exceptions.RequestException
self.assertFalse(self.response.simple_verify(
self.chall, "local", KEY.public_key()))
assert not self.response.simple_verify(
self.chall, "local", KEY.public_key())
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_port(self, mock_get):
self.response.simple_verify(
self.chall, domain="local",
account_public_key=KEY.public_key(), port=8080)
self.assertEqual("local:8080", urllib_parse.urlparse(
mock_get.mock_calls[0][1][0]).netloc)
assert "local:8080" == urllib_parse.urlparse(
mock_get.mock_calls[0][1][0]).netloc
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_timeout(self, mock_get):
@ -241,30 +239,28 @@ class HTTP01Test(unittest.TestCase):
}
def test_path(self):
self.assertEqual(self.msg.path, '/.well-known/acme-challenge/'
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA')
assert self.msg.path == '/.well-known/acme-challenge/' \
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA'
def test_uri(self):
self.assertEqual(
'http://example.com/.well-known/acme-challenge/'
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA',
self.msg.uri('example.com'))
assert 'http://example.com/.well-known/acme-challenge/' \
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA' == \
self.msg.uri('example.com')
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
assert self.jmsg == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import HTTP01
self.assertEqual(self.msg, HTTP01.from_json(self.jmsg))
assert self.msg == HTTP01.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import HTTP01
hash(HTTP01.from_json(self.jmsg))
def test_good_token(self):
self.assertTrue(self.msg.good_token)
self.assertFalse(
self.msg.update(token=b'..').good_token)
assert self.msg.good_token
assert not self.msg.update(token=b'..').good_token
class TLSALPN01ResponseTest(unittest.TestCase):
@ -284,11 +280,11 @@ class TLSALPN01ResponseTest(unittest.TestCase):
}
def test_to_partial_json(self):
self.assertEqual({}, self.response.to_partial_json())
assert {} == self.response.to_partial_json()
def test_from_json(self):
from acme.challenges import TLSALPN01Response
self.assertEqual(self.response, TLSALPN01Response.from_json(self.jmsg))
assert self.response == TLSALPN01Response.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import TLSALPN01Response
@ -297,23 +293,23 @@ class TLSALPN01ResponseTest(unittest.TestCase):
def test_gen_verify_cert(self):
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
cert, key2 = self.response.gen_cert(self.domain, key1)
self.assertEqual(key1, key2)
self.assertTrue(self.response.verify_cert(self.domain, cert))
assert key1 == key2
assert self.response.verify_cert(self.domain, cert)
def test_gen_verify_cert_gen_key(self):
cert, key = self.response.gen_cert(self.domain)
self.assertIsInstance(key, OpenSSL.crypto.PKey)
self.assertTrue(self.response.verify_cert(self.domain, cert))
assert isinstance(key, OpenSSL.crypto.PKey)
assert self.response.verify_cert(self.domain, cert)
def test_verify_bad_cert(self):
self.assertFalse(self.response.verify_cert(self.domain,
test_util.load_cert('cert.pem')))
assert not self.response.verify_cert(self.domain,
test_util.load_cert('cert.pem'))
def test_verify_bad_domain(self):
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
cert, key2 = self.response.gen_cert(self.domain, key1)
self.assertEqual(key1, key2)
self.assertFalse(self.response.verify_cert(self.domain2, cert))
assert key1 == key2
assert not self.response.verify_cert(self.domain2, cert)
def test_simple_verify_bad_key_authorization(self):
key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem'))
@ -322,10 +318,9 @@ class TLSALPN01ResponseTest(unittest.TestCase):
@mock.patch('acme.challenges.TLSALPN01Response.verify_cert', autospec=True)
def test_simple_verify(self, mock_verify_cert):
mock_verify_cert.return_value = mock.sentinel.verification
self.assertEqual(
mock.sentinel.verification, self.response.simple_verify(
assert mock.sentinel.verification == self.response.simple_verify(
self.chall, self.domain, KEY.public_key(),
cert=mock.sentinel.cert))
cert=mock.sentinel.cert)
mock_verify_cert.assert_called_once_with(
self.response, self.domain, mock.sentinel.cert)
@ -347,8 +342,8 @@ class TLSALPN01ResponseTest(unittest.TestCase):
@mock.patch('acme.challenges.TLSALPN01Response.probe_cert')
def test_simple_verify_false_on_probe_error(self, mock_probe_cert):
mock_probe_cert.side_effect = errors.Error
self.assertFalse(self.response.simple_verify(
self.chall, self.domain, KEY.public_key()))
assert not self.response.simple_verify(
self.chall, self.domain, KEY.public_key())
class TLSALPN01Test(unittest.TestCase):
@ -363,11 +358,11 @@ class TLSALPN01Test(unittest.TestCase):
}
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
assert self.jmsg == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import TLSALPN01
self.assertEqual(self.msg, TLSALPN01.from_json(self.jmsg))
assert self.msg == TLSALPN01.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import TLSALPN01
@ -376,14 +371,14 @@ class TLSALPN01Test(unittest.TestCase):
def test_from_json_invalid_token_length(self):
from acme.challenges import TLSALPN01
self.jmsg['token'] = jose.encode_b64jose(b'abcd')
self.assertRaises(
jose.DeserializationError, TLSALPN01.from_json, self.jmsg)
with pytest.raises(jose.DeserializationError):
TLSALPN01.from_json(self.jmsg)
@mock.patch('acme.challenges.TLSALPN01Response.gen_cert')
def test_validation(self, mock_gen_cert):
mock_gen_cert.return_value = ('cert', 'key')
self.assertEqual(('cert', 'key'), self.msg.validation(
KEY, cert_key=mock.sentinel.cert_key, domain=mock.sentinel.domain))
assert ('cert', 'key') == self.msg.validation(
KEY, cert_key=mock.sentinel.cert_key, domain=mock.sentinel.domain)
mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key,
domain=mock.sentinel.domain)
@ -400,11 +395,11 @@ class DNSTest(unittest.TestCase):
}
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
assert self.jmsg == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import DNS
self.assertEqual(self.msg, DNS.from_json(self.jmsg))
assert self.msg == DNS.from_json(self.jmsg)
def test_from_json_hashable(self):
from acme.challenges import DNS
@ -414,13 +409,13 @@ class DNSTest(unittest.TestCase):
ec_key_secp384r1 = JWKEC(key=test_util.load_ecdsa_private_key('ec_secp384r1_key.pem'))
for key, alg in [(KEY, jose.RS256), (ec_key_secp384r1, jose.ES384)]:
with self.subTest(key=key, alg=alg):
self.assertTrue(self.msg.check_validation(
self.msg.gen_validation(key, alg=alg), key.public_key()))
assert self.msg.check_validation(
self.msg.gen_validation(key, alg=alg), key.public_key())
def test_gen_check_validation_wrong_key(self):
key2 = jose.JWKRSA.load(test_util.load_vector('rsa1024_key.pem'))
self.assertFalse(self.msg.check_validation(
self.msg.gen_validation(KEY), key2.public_key()))
assert not self.msg.check_validation(
self.msg.gen_validation(KEY), key2.public_key())
def test_check_validation_wrong_payload(self):
validations = tuple(
@ -428,33 +423,32 @@ class DNSTest(unittest.TestCase):
for payload in (b'', b'{}')
)
for validation in validations:
self.assertFalse(self.msg.check_validation(
validation, KEY.public_key()))
assert not self.msg.check_validation(
validation, KEY.public_key())
def test_check_validation_wrong_fields(self):
bad_validation = jose.JWS.sign(
payload=self.msg.update(
token=b'x' * 20).json_dumps().encode('utf-8'),
alg=jose.RS256, key=KEY)
self.assertFalse(self.msg.check_validation(bad_validation, KEY.public_key()))
assert not self.msg.check_validation(bad_validation, KEY.public_key())
def test_gen_response(self):
with mock.patch('acme.challenges.DNS.gen_validation') as mock_gen:
mock_gen.return_value = mock.sentinel.validation
response = self.msg.gen_response(KEY)
from acme.challenges import DNSResponse
self.assertIsInstance(response, DNSResponse)
self.assertEqual(response.validation, mock.sentinel.validation)
assert isinstance(response, DNSResponse)
assert response.validation == mock.sentinel.validation
def test_validation_domain_name(self):
self.assertEqual('_acme-challenge.le.wtf', self.msg.validation_domain_name('le.wtf'))
assert '_acme-challenge.le.wtf' == self.msg.validation_domain_name('le.wtf')
def test_validation_domain_name_ecdsa(self):
ec_key_secp384r1 = JWKEC(key=test_util.load_ecdsa_private_key('ec_secp384r1_key.pem'))
self.assertIs(self.msg.check_validation(
assert self.msg.check_validation(
self.msg.gen_validation(ec_key_secp384r1, alg=jose.ES384),
ec_key_secp384r1.public_key()), True
)
ec_key_secp384r1.public_key()) is True
class DNSResponseTest(unittest.TestCase):
@ -479,18 +473,18 @@ class DNSResponseTest(unittest.TestCase):
}
def test_to_partial_json(self):
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
assert self.jmsg_to == self.msg.to_partial_json()
def test_from_json(self):
from acme.challenges import DNSResponse
self.assertEqual(self.msg, DNSResponse.from_json(self.jmsg_from))
assert self.msg == DNSResponse.from_json(self.jmsg_from)
def test_from_json_hashable(self):
from acme.challenges import DNSResponse
hash(DNSResponse.from_json(self.jmsg_from))
def test_check_validation(self):
self.assertTrue(self.msg.check_validation(self.chall, KEY.public_key()))
assert self.msg.check_validation(self.chall, KEY.public_key())
class JWSPayloadRFC8555Compliant(unittest.TestCase):
@ -502,8 +496,8 @@ class JWSPayloadRFC8555Compliant(unittest.TestCase):
jobj = challenge_body.json_dumps(indent=2).encode()
# RFC8555 states that challenge responses must have an empty payload.
self.assertEqual(jobj, b'{}')
assert jobj == b'{}'
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,11 +4,13 @@ import copy
import datetime
import http.client as http_client
import json
import unittest
import sys
from typing import Dict
import unittest
from unittest import mock
import josepy as jose
import pytest
import requests
from acme import challenges
@ -101,7 +103,7 @@ class ClientV2Test(unittest.TestCase):
self.response.json.return_value = self.regr.body.to_json()
self.response.headers['Location'] = self.regr.uri
self.assertEqual(self.regr, self.client.new_account(self.new_reg))
assert self.regr == self.client.new_account(self.new_reg)
def test_new_account_tos_link(self):
self.response.status_code = http_client.CREATED
@ -111,14 +113,15 @@ class ClientV2Test(unittest.TestCase):
'terms-of-service': {'url': 'https://www.letsencrypt-demo.org/tos'},
})
self.assertEqual(self.client.new_account(self.new_reg).terms_of_service,
'https://www.letsencrypt-demo.org/tos')
assert self.client.new_account(self.new_reg).terms_of_service == \
'https://www.letsencrypt-demo.org/tos'
def test_new_account_conflict(self):
self.response.status_code = http_client.OK
self.response.headers['Location'] = self.regr.uri
self.assertRaises(errors.ConflictError, self.client.new_account, self.new_reg)
with pytest.raises(errors.ConflictError):
self.client.new_account(self.new_reg)
def test_deactivate_account(self):
deactivated_regr = self.regr.update(
@ -126,16 +129,16 @@ class ClientV2Test(unittest.TestCase):
self.response.json.return_value = deactivated_regr.body.to_json()
self.response.status_code = http_client.OK
self.response.headers['Location'] = self.regr.uri
self.assertEqual(self.client.deactivate_registration(self.regr), deactivated_regr)
assert self.client.deactivate_registration(self.regr) == deactivated_regr
def test_deactivate_authorization(self):
deactivated_authz = self.authzr.update(
body=self.authzr.body.update(status=messages.STATUS_DEACTIVATED))
self.response.json.return_value = deactivated_authz.body.to_json()
authzr = self.client.deactivate_authorization(self.authzr)
self.assertEqual(deactivated_authz.body, authzr.body)
self.assertEqual(self.client.net.post.call_count, 1)
self.assertIn(self.authzr.uri, self.net.post.call_args_list[0][0])
assert deactivated_authz.body == authzr.body
assert self.client.net.post.call_count == 1
assert self.authzr.uri in self.net.post.call_args_list[0][0]
def test_new_order(self):
order_response = copy.deepcopy(self.response)
@ -153,7 +156,7 @@ class ClientV2Test(unittest.TestCase):
with mock.patch('acme.client.ClientV2._post_as_get') as mock_post_as_get:
mock_post_as_get.side_effect = (authz_response, authz_response2)
self.assertEqual(self.client.new_order(CSR_MIXED_PEM), self.orderr)
assert self.client.new_order(CSR_MIXED_PEM) == self.orderr
def test_answer_challege(self):
self.response.links['up'] = {'url': self.challr.authzr_uri}
@ -161,13 +164,12 @@ class ClientV2Test(unittest.TestCase):
chall_response = challenges.DNSResponse(validation=None)
self.client.answer_challenge(self.challr.body, chall_response)
self.assertRaises(errors.UnexpectedUpdate, self.client.answer_challenge,
self.challr.body.update(uri='foo'), chall_response)
with pytest.raises(errors.UnexpectedUpdate):
self.client.answer_challenge(self.challr.body.update(uri='foo'), chall_response)
def test_answer_challenge_missing_next(self):
self.assertRaises(
errors.ClientError, self.client.answer_challenge,
self.challr.body, challenges.DNSResponse(validation=None))
with pytest.raises(errors.ClientError):
self.client.answer_challenge(self.challr.body, challenges.DNSResponse(validation=None))
@mock.patch('acme.client.datetime')
def test_poll_and_finalize(self, mock_datetime):
@ -178,7 +180,7 @@ class ClientV2Test(unittest.TestCase):
self.client.poll_authorizations = mock.Mock(return_value=self.orderr)
self.client.finalize_order = mock.Mock(return_value=self.orderr)
self.assertEqual(self.client.poll_and_finalize(self.orderr), self.orderr)
assert self.client.poll_and_finalize(self.orderr) == self.orderr
self.client.poll_authorizations.assert_called_once_with(self.orderr, expected_deadline)
self.client.finalize_order.assert_called_once_with(self.orderr, expected_deadline)
@ -191,8 +193,8 @@ class ClientV2Test(unittest.TestCase):
self.response.json.side_effect = [
self.authz.to_json(), self.authz2.to_json(), self.authz2.to_json()]
self.assertRaises(
errors.TimeoutError, self.client.poll_authorizations, self.orderr, now_side_effect[1])
with pytest.raises(errors.TimeoutError):
self.client.poll_authorizations(self.orderr, now_side_effect[1])
def test_poll_authorizations_failure(self):
deadline = datetime.datetime(9999, 9, 9)
@ -201,8 +203,8 @@ class ClientV2Test(unittest.TestCase):
authz = self.authz.update(status=messages.STATUS_INVALID, challenges=(challb,))
self.response.json.return_value = authz.to_json()
self.assertRaises(
errors.ValidationError, self.client.poll_authorizations, self.orderr, deadline)
with pytest.raises(errors.ValidationError):
self.client.poll_authorizations(self.orderr, deadline)
def test_poll_authorizations_success(self):
deadline = datetime.datetime(9999, 9, 9)
@ -213,12 +215,13 @@ class ClientV2Test(unittest.TestCase):
self.response.json.side_effect = (
self.authz.to_json(), self.authz2.to_json(), updated_authz2.to_json())
self.assertEqual(self.client.poll_authorizations(self.orderr, deadline), updated_orderr)
assert self.client.poll_authorizations(self.orderr, deadline) == updated_orderr
def test_poll_unexpected_update(self):
updated_authz = self.authz.update(identifier=self.identifier.update(value='foo'))
self.response.json.return_value = updated_authz.to_json()
self.assertRaises(errors.UnexpectedUpdate, self.client.poll, self.authzr)
with pytest.raises(errors.UnexpectedUpdate):
self.client.poll(self.authzr)
def test_finalize_order_success(self):
updated_order = self.order.update(
@ -230,7 +233,7 @@ class ClientV2Test(unittest.TestCase):
self.response.text = CERT_SAN_PEM
deadline = datetime.datetime(9999, 9, 9)
self.assertEqual(self.client.finalize_order(self.orderr, deadline), updated_orderr)
assert self.client.finalize_order(self.orderr, deadline) == updated_orderr
def test_finalize_order_error(self):
updated_order = self.order.update(
@ -239,19 +242,20 @@ class ClientV2Test(unittest.TestCase):
self.response.json.return_value = updated_order.to_json()
deadline = datetime.datetime(9999, 9, 9)
self.assertRaises(errors.IssuanceError, self.client.finalize_order, self.orderr, deadline)
with pytest.raises(errors.IssuanceError):
self.client.finalize_order(self.orderr, deadline)
def test_finalize_order_invalid_status(self):
# https://github.com/certbot/certbot/issues/9296
order = self.order.update(error=None, status=messages.STATUS_INVALID)
self.response.json.return_value = order.to_json()
with self.assertRaises(errors.Error) as error:
with pytest.raises(errors.Error, match="The certificate order failed"):
self.client.finalize_order(self.orderr, datetime.datetime(9999, 9, 9))
self.assertIn("The certificate order failed", str(error.exception))
def test_finalize_order_timeout(self):
deadline = datetime.datetime.now() - datetime.timedelta(seconds=60)
self.assertRaises(errors.TimeoutError, self.client.finalize_order, self.orderr, deadline)
with pytest.raises(errors.TimeoutError):
self.client.finalize_order(self.orderr, deadline)
def test_finalize_order_alt_chains(self):
updated_order = self.order.update(
@ -274,11 +278,11 @@ class ClientV2Test(unittest.TestCase):
mock.ANY, new_nonce_url=mock.ANY)
self.net.post.assert_any_call('https://example.com/acme/cert/2',
mock.ANY, new_nonce_url=mock.ANY)
self.assertEqual(resp, updated_orderr)
assert resp == updated_orderr
del self.response.headers['Link']
resp = self.client.finalize_order(self.orderr, deadline, fetch_alternative_chains=True)
self.assertEqual(resp, updated_orderr.update(alternative_fullchains_pem=[]))
assert resp == updated_orderr.update(alternative_fullchains_pem=[])
def test_revoke(self):
self.client.revoke(messages_test.CERT, self.rsn)
@ -287,20 +291,18 @@ class ClientV2Test(unittest.TestCase):
def test_revoke_bad_status_raises_error(self):
self.response.status_code = http_client.METHOD_NOT_ALLOWED
self.assertRaises(
errors.ClientError,
self.client.revoke,
messages_test.CERT,
with pytest.raises(errors.ClientError):
self.client.revoke(messages_test.CERT,
self.rsn)
def test_update_registration(self):
# "Instance of 'Field' has no to_json/update member" bug:
self.response.headers['Location'] = self.regr.uri
self.response.json.return_value = self.regr.body.to_json()
self.assertEqual(self.regr, self.client.update_registration(self.regr))
self.assertIsNotNone(self.client.net.account)
self.assertEqual(self.client.net.post.call_count, 2)
self.assertIn(DIRECTORY_V2.newAccount, self.net.post.call_args_list[0][0])
assert self.regr == self.client.update_registration(self.regr)
assert self.client.net.account is not None
assert self.client.net.post.call_count == 2
assert DIRECTORY_V2.newAccount in self.net.post.call_args_list[0][0]
self.response.json.return_value = self.regr.body.update(
contact=()).to_json()
@ -310,22 +312,22 @@ class ClientV2Test(unittest.TestCase):
'meta': messages.Directory.Meta(external_account_required=True)
})
self.assertTrue(self.client.external_account_required())
assert self.client.external_account_required()
def test_external_account_required_false(self):
self.client.directory = messages.Directory({
'meta': messages.Directory.Meta(external_account_required=False)
})
self.assertFalse(self.client.external_account_required())
assert not self.client.external_account_required()
def test_external_account_required_default(self):
self.assertFalse(self.client.external_account_required())
assert not self.client.external_account_required()
def test_query_registration_client(self):
self.response.json.return_value = self.regr.body.to_json()
self.response.headers['Location'] = 'https://www.letsencrypt-demo.org/acme/reg/1'
self.assertEqual(self.regr, self.client.query_registration(self.regr))
assert self.regr == self.client.query_registration(self.regr)
def test_post_as_get(self):
with mock.patch('acme.client.ClientV2._authzr_from_response') as mock_client:
@ -340,9 +342,8 @@ class ClientV2Test(unittest.TestCase):
def test_retry_after_date(self):
self.response.headers['Retry-After'] = 'Fri, 31 Dec 1999 23:59:59 GMT'
self.assertEqual(
datetime.datetime(1999, 12, 31, 23, 59, 59),
self.client.retry_after(response=self.response, default=10))
assert datetime.datetime(1999, 12, 31, 23, 59, 59) == \
self.client.retry_after(response=self.response, default=10)
@mock.patch('acme.client.datetime')
def test_retry_after_invalid(self, dt_mock):
@ -350,9 +351,8 @@ class ClientV2Test(unittest.TestCase):
dt_mock.timedelta = datetime.timedelta
self.response.headers['Retry-After'] = 'foooo'
self.assertEqual(
datetime.datetime(2015, 3, 27, 0, 0, 10),
self.client.retry_after(response=self.response, default=10))
assert datetime.datetime(2015, 3, 27, 0, 0, 10) == \
self.client.retry_after(response=self.response, default=10)
@mock.patch('acme.client.datetime')
def test_retry_after_overflow(self, dt_mock):
@ -361,9 +361,8 @@ class ClientV2Test(unittest.TestCase):
dt_mock.datetime.side_effect = datetime.datetime
self.response.headers['Retry-After'] = "Tue, 116 Feb 2016 11:50:00 MST"
self.assertEqual(
datetime.datetime(2015, 3, 27, 0, 0, 10),
self.client.retry_after(response=self.response, default=10))
assert datetime.datetime(2015, 3, 27, 0, 0, 10) == \
self.client.retry_after(response=self.response, default=10)
@mock.patch('acme.client.datetime')
def test_retry_after_seconds(self, dt_mock):
@ -371,24 +370,21 @@ class ClientV2Test(unittest.TestCase):
dt_mock.timedelta = datetime.timedelta
self.response.headers['Retry-After'] = '50'
self.assertEqual(
datetime.datetime(2015, 3, 27, 0, 0, 50),
self.client.retry_after(response=self.response, default=10))
assert datetime.datetime(2015, 3, 27, 0, 0, 50) == \
self.client.retry_after(response=self.response, default=10)
@mock.patch('acme.client.datetime')
def test_retry_after_missing(self, dt_mock):
dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27)
dt_mock.timedelta = datetime.timedelta
self.assertEqual(
datetime.datetime(2015, 3, 27, 0, 0, 10),
self.client.retry_after(response=self.response, default=10))
assert datetime.datetime(2015, 3, 27, 0, 0, 10) == \
self.client.retry_after(response=self.response, default=10)
def test_get_directory(self):
self.response.json.return_value = DIRECTORY_V2.to_json()
self.assertEqual(
DIRECTORY_V2.to_partial_json(),
ClientV2.get_directory('https://example.com/dir', self.net).to_partial_json())
assert DIRECTORY_V2.to_partial_json() == \
ClientV2.get_directory('https://example.com/dir', self.net).to_partial_json()
class MockJSONDeSerializable(jose.JSONDeSerializable):
@ -420,15 +416,15 @@ class ClientNetworkTest(unittest.TestCase):
self.response.links = {}
def test_init(self):
self.assertIs(self.net.verify_ssl, self.verify_ssl)
assert self.net.verify_ssl is self.verify_ssl
def test_wrap_in_jws(self):
# pylint: disable=protected-access
jws_dump = self.net._wrap_in_jws(
MockJSONDeSerializable('foo'), nonce=b'Tg', url="url")
jws = acme_jws.JWS.json_loads(jws_dump)
self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
self.assertEqual(jws.signature.combined.nonce, b'Tg')
assert json.loads(jws.payload.decode()) == {'foo': 'foo'}
assert jws.signature.combined.nonce == b'Tg'
def test_wrap_in_jws_v2(self):
self.net.account = {'uri': 'acct-uri'}
@ -436,10 +432,10 @@ class ClientNetworkTest(unittest.TestCase):
jws_dump = self.net._wrap_in_jws(
MockJSONDeSerializable('foo'), nonce=b'Tg', url="url")
jws = acme_jws.JWS.json_loads(jws_dump)
self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
self.assertEqual(jws.signature.combined.nonce, b'Tg')
self.assertEqual(jws.signature.combined.kid, u'acct-uri')
self.assertEqual(jws.signature.combined.url, u'url')
assert json.loads(jws.payload.decode()) == {'foo': 'foo'}
assert jws.signature.combined.nonce == b'Tg'
assert jws.signature.combined.kid == u'acct-uri'
assert jws.signature.combined.url == u'url'
def test_check_response_not_ok_jobj_no_error(self):
self.response.ok = False
@ -447,31 +443,31 @@ class ClientNetworkTest(unittest.TestCase):
with mock.patch('acme.client.messages.Error.from_json') as from_json:
from_json.side_effect = jose.DeserializationError
# pylint: disable=protected-access
self.assertRaises(
errors.ClientError, self.net._check_response, self.response)
with pytest.raises(errors.ClientError):
self.net._check_response(self.response)
def test_check_response_not_ok_jobj_error(self):
self.response.ok = False
self.response.json.return_value = messages.Error.with_code(
'serverInternal', detail='foo', title='some title').to_json()
# pylint: disable=protected-access
self.assertRaises(
messages.Error, self.net._check_response, self.response)
with pytest.raises(messages.Error):
self.net._check_response(self.response)
def test_check_response_not_ok_no_jobj(self):
self.response.ok = False
self.response.json.side_effect = ValueError
# pylint: disable=protected-access
self.assertRaises(
errors.ClientError, self.net._check_response, self.response)
with pytest.raises(errors.ClientError):
self.net._check_response(self.response)
def test_check_response_ok_no_jobj_ct_required(self):
self.response.json.side_effect = ValueError
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access
self.assertRaises(
errors.ClientError, self.net._check_response, self.response,
with pytest.raises(errors.ClientError):
self.net._check_response(self.response,
content_type=self.net.JSON_CONTENT_TYPE)
def test_check_response_ok_no_jobj_no_ct(self):
@ -479,16 +475,15 @@ class ClientNetworkTest(unittest.TestCase):
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access
self.assertEqual(
self.response, self.net._check_response(self.response))
assert self.response == self.net._check_response(self.response)
@mock.patch('acme.client.logger')
def test_check_response_ok_ct_with_charset(self, mock_logger):
self.response.json.return_value = {}
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
# pylint: disable=protected-access
self.assertEqual(self.response, self.net._check_response(
self.response, content_type='application/json'))
assert self.response == self.net._check_response(
self.response, content_type='application/json')
try:
mock_logger.debug.assert_called_with(
'Ignoring wrong Content-Type (%r) for JSON decodable response',
@ -504,8 +499,8 @@ class ClientNetworkTest(unittest.TestCase):
self.response.json.return_value = {}
self.response.headers['Content-Type'] = 'text/plain'
# pylint: disable=protected-access
self.assertEqual(self.response, self.net._check_response(
self.response, content_type='application/json'))
assert self.response == self.net._check_response(
self.response, content_type='application/json')
mock_logger.debug.assert_called_with(
'Ignoring wrong Content-Type (%r) for JSON decodable response',
'text/plain'
@ -515,22 +510,22 @@ class ClientNetworkTest(unittest.TestCase):
self.response.ok = False
self.response.status_code = 409
# pylint: disable=protected-access
self.assertRaises(errors.ConflictError, self.net._check_response, self.response)
with pytest.raises(errors.ConflictError):
self.net._check_response(self.response)
def test_check_response_jobj(self):
self.response.json.return_value = {}
for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
self.response.headers['Content-Type'] = response_ct
# pylint: disable=protected-access
self.assertEqual(
self.response, self.net._check_response(self.response))
assert self.response == self.net._check_response(self.response)
def test_send_request(self):
self.net.session = mock.MagicMock()
self.net.session.request.return_value = self.response
# pylint: disable=protected-access
self.assertEqual(self.response, self.net._send_request(
'HEAD', 'http://example.com/', 'foo', bar='baz'))
assert self.response == self.net._send_request(
'HEAD', 'http://example.com/', 'foo', bar='baz')
self.net.session.request.assert_called_once_with(
'HEAD', 'http://example.com/', 'foo',
headers=mock.ANY, verify=mock.ANY, timeout=mock.ANY, bar='baz')
@ -552,8 +547,8 @@ class ClientNetworkTest(unittest.TestCase):
self.net.session = mock.MagicMock()
self.net.session.request.return_value = self.response
# pylint: disable=protected-access
self.assertEqual(self.response, self.net._send_request(
'POST', 'http://example.com/', 'foo', data='qux', bar='baz'))
assert self.response == self.net._send_request(
'POST', 'http://example.com/', 'foo', data='qux', bar='baz')
self.net.session.request.assert_called_once_with(
'POST', 'http://example.com/', 'foo',
headers=mock.ANY, verify=mock.ANY, timeout=mock.ANY, data='qux', bar='baz')
@ -565,9 +560,8 @@ class ClientNetworkTest(unittest.TestCase):
self.net.session.request.return_value = self.response
self.net.verify_ssl = verify
# pylint: disable=protected-access
self.assertEqual(
self.response,
self.net._send_request('GET', 'http://example.com/'))
assert self.response == \
self.net._send_request('GET', 'http://example.com/')
self.net.session.request.assert_called_once_with(
'GET', 'http://example.com/', verify=verify,
timeout=mock.ANY, headers=mock.ANY)
@ -615,8 +609,8 @@ class ClientNetworkTest(unittest.TestCase):
mock_requests.exceptions = requests.exceptions
mock_requests.request.side_effect = requests.exceptions.RequestException
# pylint: disable=protected-access
self.assertRaises(requests.exceptions.RequestException,
self.net._send_request, 'GET', 'uri')
with pytest.raises(requests.exceptions.RequestException):
self.net._send_request('GET', 'uri')
def test_urllib_error(self):
# Using a connection error to test a properly formatted error message
@ -626,12 +620,12 @@ class ClientNetworkTest(unittest.TestCase):
# Value Error Generated Exceptions
except ValueError as y:
self.assertEqual("Requesting localhost/nonexistent: "
"Connection refused", str(y))
assert "Requesting localhost/nonexistent: " \
"Connection refused" == str(y)
# Requests Library Exceptions
except requests.exceptions.ConnectionError as z: #pragma: no cover
self.assertTrue("'Connection aborted.'" in str(z) or "[WinError 10061]" in str(z))
assert "'Connection aborted.'" in str(z) or "[WinError 10061]" in str(z)
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
@ -658,7 +652,7 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
def send_request(*args, **kwargs):
# pylint: disable=unused-argument,missing-docstring
self.assertNotIn("new_nonce_url", kwargs)
assert "new_nonce_url" not in kwargs
method = args[0]
uri = args[1]
if method == 'HEAD' and uri != "new_nonce_uri":
@ -682,58 +676,60 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
def check_response(self, response, content_type):
# pylint: disable=missing-docstring
self.assertEqual(self.response, response)
self.assertEqual(self.content_type, content_type)
self.assertTrue(self.response.ok)
assert self.response == response
assert self.content_type == content_type
assert self.response.ok
self.response.checked = True
return self.response
def test_head(self):
self.assertEqual(self.acmev1_nonce_response, self.net.head(
'http://example.com/', 'foo', bar='baz'))
assert self.acmev1_nonce_response == self.net.head(
'http://example.com/', 'foo', bar='baz')
self.send_request.assert_called_once_with(
'HEAD', 'http://example.com/', 'foo', bar='baz')
def test_head_v2(self):
self.assertEqual(self.response, self.net.head(
'new_nonce_uri', 'foo', bar='baz'))
assert self.response == self.net.head(
'new_nonce_uri', 'foo', bar='baz')
self.send_request.assert_called_once_with(
'HEAD', 'new_nonce_uri', 'foo', bar='baz')
def test_get(self):
self.assertEqual(self.response, self.net.get(
'http://example.com/', content_type=self.content_type, bar='baz'))
self.assertTrue(self.response.checked)
assert self.response == self.net.get(
'http://example.com/', content_type=self.content_type, bar='baz')
assert self.response.checked
self.send_request.assert_called_once_with(
'GET', 'http://example.com/', bar='baz')
def test_post_no_content_type(self):
self.content_type = self.net.JOSE_CONTENT_TYPE
self.assertEqual(self.response, self.net.post('uri', self.obj))
self.assertTrue(self.response.checked)
assert self.response == self.net.post('uri', self.obj)
assert self.response.checked
def test_post(self):
# pylint: disable=protected-access
self.assertEqual(self.response, self.net.post(
'uri', self.obj, content_type=self.content_type))
self.assertTrue(self.response.checked)
assert self.response == self.net.post(
'uri', self.obj, content_type=self.content_type)
assert self.response.checked
self.net._wrap_in_jws.assert_called_once_with(
self.obj, jose.b64decode(self.all_nonces.pop()), "uri")
self.available_nonces = []
self.assertRaises(errors.MissingNonce, self.net.post,
'uri', self.obj, content_type=self.content_type)
with pytest.raises(errors.MissingNonce):
self.net.post('uri', self.obj, content_type=self.content_type)
self.net._wrap_in_jws.assert_called_with(
self.obj, jose.b64decode(self.all_nonces.pop()), "uri")
def test_post_wrong_initial_nonce(self): # HEAD
self.available_nonces = [b'f', jose.b64encode(b'good')]
self.assertRaises(errors.BadNonce, self.net.post, 'uri',
with pytest.raises(errors.BadNonce):
self.net.post('uri',
self.obj, content_type=self.content_type)
def test_post_wrong_post_response_nonce(self):
self.available_nonces = [jose.b64encode(b'good'), b'f']
self.assertRaises(errors.BadNonce, self.net.post, 'uri',
with pytest.raises(errors.BadNonce):
self.net.post('uri',
self.obj, content_type=self.content_type)
def test_post_failed_retry(self):
@ -742,7 +738,8 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
# pylint: disable=protected-access
self.net._check_response = check_response
self.assertRaises(messages.Error, self.net.post, 'uri',
with pytest.raises(messages.Error):
self.net.post('uri',
self.obj, content_type=self.content_type)
def test_post_not_retried(self):
@ -752,7 +749,8 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
# pylint: disable=protected-access
self.net._check_response = check_response
self.assertRaises(messages.Error, self.net.post, 'uri',
with pytest.raises(messages.Error):
self.net.post('uri',
self.obj, content_type=self.content_type)
def test_post_successful_retry(self):
@ -761,16 +759,16 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.response]
# pylint: disable=protected-access
self.assertEqual(self.response, self.net.post(
'uri', self.obj, content_type=self.content_type))
assert self.response == self.net.post(
'uri', self.obj, content_type=self.content_type)
def test_head_get_post_error_passthrough(self):
self.send_request.side_effect = requests.exceptions.RequestException
for method in self.net.head, self.net.get:
self.assertRaises(
requests.exceptions.RequestException, method, 'GET', 'uri')
self.assertRaises(requests.exceptions.RequestException,
self.net.post, 'uri', obj=self.obj)
with pytest.raises(requests.exceptions.RequestException):
method('GET', 'uri')
with pytest.raises(requests.exceptions.RequestException):
self.net.post('uri', obj=self.obj)
def test_post_bad_nonce_head(self):
# pylint: disable=protected-access
@ -781,10 +779,11 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.content_type = None
check_response = mock.MagicMock()
self.net._check_response = check_response
self.assertRaises(errors.ClientError, self.net.post, 'uri',
with pytest.raises(errors.ClientError):
self.net.post('uri',
self.obj, content_type=self.content_type,
new_nonce_url='new_nonce_uri')
self.assertEqual(check_response.call_count, 1)
assert check_response.call_count == 1
def test_new_nonce_uri_removed(self):
self.content_type = None
@ -792,4 +791,4 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,15 +1,17 @@
"""Tests for acme.crypto_util."""
import itertools
import ipaddress
import itertools
import socket
import socketserver
import sys
import threading
import time
import unittest
from typing import List
import unittest
import josepy as jose
import OpenSSL
import pytest
from acme import errors
import test_util
@ -54,18 +56,20 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
def test_probe_ok(self):
self._start_server()
self.assertEqual(self.cert, self._probe(b'foo'))
assert self.cert == self._probe(b'foo')
def test_probe_not_recognized_name(self):
self._start_server()
self.assertRaises(errors.Error, self._probe, b'bar')
with pytest.raises(errors.Error):
self._probe(b'bar')
def test_probe_connection_error(self):
self.server.server_close()
original_timeout = socket.getdefaulttimeout()
try:
socket.setdefaulttimeout(1)
self.assertRaises(errors.Error, self._probe, b'bar')
with pytest.raises(errors.Error):
self._probe(b'bar')
finally:
socket.setdefaulttimeout(original_timeout)
@ -75,10 +79,10 @@ class SSLSocketTest(unittest.TestCase):
def test_ssl_socket_invalid_arguments(self):
from acme.crypto_util import SSLSocket
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
_ = SSLSocket(None, {'sni': ('key', 'cert')},
cert_selection=lambda _: None)
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
_ = SSLSocket(None)
@ -95,15 +99,15 @@ class PyOpenSSLCertOrReqAllNamesTest(unittest.TestCase):
return self._call(test_util.load_cert, name)
def test_cert_one_san_no_common(self):
self.assertEqual(self._call_cert('cert-nocn.der'),
['no-common-name.badssl.com'])
assert self._call_cert('cert-nocn.der') == \
['no-common-name.badssl.com']
def test_cert_no_sans_yes_common(self):
self.assertEqual(self._call_cert('cert.pem'), ['example.com'])
assert self._call_cert('cert.pem') == ['example.com']
def test_cert_two_sans_yes_common(self):
self.assertEqual(self._call_cert('cert-san.pem'),
['example.com', 'www.example.com'])
assert self._call_cert('cert-san.pem') == \
['example.com', 'www.example.com']
class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
@ -131,47 +135,47 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
return self._call(test_util.load_csr, name)
def test_cert_no_sans(self):
self.assertEqual(self._call_cert('cert.pem'), [])
assert self._call_cert('cert.pem') == []
def test_cert_two_sans(self):
self.assertEqual(self._call_cert('cert-san.pem'),
['example.com', 'www.example.com'])
assert self._call_cert('cert-san.pem') == \
['example.com', 'www.example.com']
def test_cert_hundred_sans(self):
self.assertEqual(self._call_cert('cert-100sans.pem'),
['example{0}.com'.format(i) for i in range(1, 101)])
assert self._call_cert('cert-100sans.pem') == \
['example{0}.com'.format(i) for i in range(1, 101)]
def test_cert_idn_sans(self):
self.assertEqual(self._call_cert('cert-idnsans.pem'),
self._get_idn_names())
assert self._call_cert('cert-idnsans.pem') == \
self._get_idn_names()
def test_csr_no_sans(self):
self.assertEqual(self._call_csr('csr-nosans.pem'), [])
assert self._call_csr('csr-nosans.pem') == []
def test_csr_one_san(self):
self.assertEqual(self._call_csr('csr.pem'), ['example.com'])
assert self._call_csr('csr.pem') == ['example.com']
def test_csr_two_sans(self):
self.assertEqual(self._call_csr('csr-san.pem'),
['example.com', 'www.example.com'])
assert self._call_csr('csr-san.pem') == \
['example.com', 'www.example.com']
def test_csr_six_sans(self):
self.assertEqual(self._call_csr('csr-6sans.pem'),
assert self._call_csr('csr-6sans.pem') == \
['example.com', 'example.org', 'example.net',
'example.info', 'subdomain.example.com',
'other.subdomain.example.com'])
'other.subdomain.example.com']
def test_csr_hundred_sans(self):
self.assertEqual(self._call_csr('csr-100sans.pem'),
['example{0}.com'.format(i) for i in range(1, 101)])
assert self._call_csr('csr-100sans.pem') == \
['example{0}.com'.format(i) for i in range(1, 101)]
def test_csr_idn_sans(self):
self.assertEqual(self._call_csr('csr-idnsans.pem'),
self._get_idn_names())
assert self._call_csr('csr-idnsans.pem') == \
self._get_idn_names()
def test_critical_san(self):
self.assertEqual(self._call_cert('critical-san.pem'),
['chicago-cubs.venafi.example', 'cubs.venafi.example'])
assert self._call_cert('critical-san.pem') == \
['chicago-cubs.venafi.example', 'cubs.venafi.example']
class PyOpenSSLCertOrReqSANIPTest(unittest.TestCase):
@ -190,30 +194,30 @@ class PyOpenSSLCertOrReqSANIPTest(unittest.TestCase):
return self._call(test_util.load_csr, name)
def test_cert_no_sans(self):
self.assertEqual(self._call_cert('cert.pem'), [])
assert self._call_cert('cert.pem') == []
def test_csr_no_sans(self):
self.assertEqual(self._call_csr('csr-nosans.pem'), [])
assert self._call_csr('csr-nosans.pem') == []
def test_cert_domain_sans(self):
self.assertEqual(self._call_cert('cert-san.pem'), [])
assert self._call_cert('cert-san.pem') == []
def test_csr_domain_sans(self):
self.assertEqual(self._call_csr('csr-san.pem'), [])
assert self._call_csr('csr-san.pem') == []
def test_cert_ip_two_sans(self):
self.assertEqual(self._call_cert('cert-ipsans.pem'), ['192.0.2.145', '203.0.113.1'])
assert self._call_cert('cert-ipsans.pem') == ['192.0.2.145', '203.0.113.1']
def test_csr_ip_two_sans(self):
self.assertEqual(self._call_csr('csr-ipsans.pem'), ['192.0.2.145', '203.0.113.1'])
assert self._call_csr('csr-ipsans.pem') == ['192.0.2.145', '203.0.113.1']
def test_csr_ipv6_sans(self):
self.assertEqual(self._call_csr('csr-ipv6sans.pem'),
['0:0:0:0:0:0:0:1', 'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5'])
assert self._call_csr('csr-ipv6sans.pem') == \
['0:0:0:0:0:0:0:1', 'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5']
def test_cert_ipv6_sans(self):
self.assertEqual(self._call_cert('cert-ipv6sans.pem'),
['0:0:0:0:0:0:0:1', 'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5'])
assert self._call_cert('cert-ipv6sans.pem') == \
['0:0:0:0:0:0:0:1', 'A3BE:32F3:206E:C75D:956:CEE:9858:5EC5']
class GenSsCertTest(unittest.TestCase):
@ -232,12 +236,12 @@ class GenSsCertTest(unittest.TestCase):
cert = gen_ss_cert(self.key, ['dummy'], force_san=True,
ips=[ipaddress.ip_address("10.10.10.10")])
self.serial_num.append(cert.get_serial_number())
self.assertGreaterEqual(len(set(self.serial_num)), self.cert_count)
assert len(set(self.serial_num)) >= self.cert_count
def test_no_name(self):
from acme.crypto_util import gen_ss_cert
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
gen_ss_cert(self.key, ips=[ipaddress.ip_address("1.1.1.1")])
gen_ss_cert(self.key)
@ -255,41 +259,39 @@ class MakeCSRTest(unittest.TestCase):
def test_make_csr(self):
csr_pem = self._call_with_key(["a.example", "b.example"])
self.assertIn(b'--BEGIN CERTIFICATE REQUEST--', csr_pem)
self.assertIn(b'--END CERTIFICATE REQUEST--', csr_pem)
assert b'--BEGIN CERTIFICATE REQUEST--' in csr_pem
assert b'--END CERTIFICATE REQUEST--' in csr_pem
csr = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_PEM, csr_pem)
# In pyopenssl 0.13 (used with TOXENV=py27-oldest), csr objects don't
# have a get_extensions() method, so we skip this test if the method
# isn't available.
if hasattr(csr, 'get_extensions'):
self.assertEqual(len(csr.get_extensions()), 1)
self.assertEqual(csr.get_extensions()[0].get_data(),
assert len(csr.get_extensions()) == 1
assert csr.get_extensions()[0].get_data() == \
OpenSSL.crypto.X509Extension(
b'subjectAltName',
critical=False,
value=b'DNS:a.example, DNS:b.example',
).get_data(),
)
).get_data()
def test_make_csr_ip(self):
csr_pem = self._call_with_key(["a.example"], False, [ipaddress.ip_address('127.0.0.1'), ipaddress.ip_address('::1')])
self.assertIn(b'--BEGIN CERTIFICATE REQUEST--' , csr_pem)
self.assertIn(b'--END CERTIFICATE REQUEST--' , csr_pem)
assert b'--BEGIN CERTIFICATE REQUEST--' in csr_pem
assert b'--END CERTIFICATE REQUEST--' in csr_pem
csr = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_PEM, csr_pem)
# In pyopenssl 0.13 (used with TOXENV=py27-oldest), csr objects don't
# have a get_extensions() method, so we skip this test if the method
# isn't available.
if hasattr(csr, 'get_extensions'):
self.assertEqual(len(csr.get_extensions()), 1)
self.assertEqual(csr.get_extensions()[0].get_data(),
assert len(csr.get_extensions()) == 1
assert csr.get_extensions()[0].get_data() == \
OpenSSL.crypto.X509Extension(
b'subjectAltName',
critical=False,
value=b'DNS:a.example, IP:127.0.0.1, IP:::1',
).get_data(),
)
).get_data()
# for IP san it's actually need to be octet-string,
# but somewhere downstream thankfully handle it for us
@ -302,25 +304,26 @@ class MakeCSRTest(unittest.TestCase):
# have a get_extensions() method, so we skip this test if the method
# isn't available.
if hasattr(csr, 'get_extensions'):
self.assertEqual(len(csr.get_extensions()), 2)
assert len(csr.get_extensions()) == 2
# NOTE: Ideally we would filter by the TLS Feature OID, but
# OpenSSL.crypto.X509Extension doesn't give us the extension's raw OID,
# and the shortname field is just "UNDEF"
must_staple_exts = [e for e in csr.get_extensions()
if e.get_data() == b"0\x03\x02\x01\x05"]
self.assertEqual(len(must_staple_exts), 1,
"Expected exactly one Must Staple extension")
assert len(must_staple_exts) == 1, \
"Expected exactly one Must Staple extension"
def test_make_csr_without_hostname(self):
self.assertRaises(ValueError, self._call_with_key)
with pytest.raises(ValueError):
self._call_with_key()
def test_make_csr_correct_version(self):
csr_pem = self._call_with_key(["a.example"])
csr = OpenSSL.crypto.load_certificate_request(
OpenSSL.crypto.FILETYPE_PEM, csr_pem)
self.assertEqual(csr.get_version(), 0,
"Expected CSR version to be v1 (encoded as 0), per RFC 2986, section 4")
assert csr.get_version() == 0, \
"Expected CSR version to be v1 (encoded as 0), per RFC 2986, section 4"
class DumpPyopensslChainTest(unittest.TestCase):
@ -338,7 +341,7 @@ class DumpPyopensslChainTest(unittest.TestCase):
length = sum(
len(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
for cert in loaded)
self.assertEqual(len(self._call(loaded)), length)
assert len(self._call(loaded)) == length
def test_dump_pyopenssl_chain_wrapped(self):
names = ['cert.pem', 'cert-san.pem', 'cert-idnsans.pem']
@ -347,8 +350,8 @@ class DumpPyopensslChainTest(unittest.TestCase):
wrapped = [wrap_func(cert) for cert in loaded]
dump_func = OpenSSL.crypto.dump_certificate
length = sum(len(dump_func(OpenSSL.crypto.FILETYPE_PEM, cert)) for cert in loaded)
self.assertEqual(len(self._call(wrapped)), length)
assert len(self._call(wrapped)) == length
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Tests for acme.errors."""
import sys
import unittest
from unittest import mock
import pytest
class BadNonceTest(unittest.TestCase):
"""Tests for acme.errors.BadNonce."""
@ -11,7 +14,7 @@ class BadNonceTest(unittest.TestCase):
self.error = BadNonce(nonce="xxx", error="error")
def test_str(self):
self.assertEqual("Invalid nonce ('xxx'): error", str(self.error))
assert "Invalid nonce ('xxx'): error" == str(self.error)
class MissingNonceTest(unittest.TestCase):
@ -24,8 +27,8 @@ class MissingNonceTest(unittest.TestCase):
self.error = MissingNonce(self.response)
def test_str(self):
self.assertIn("FOO", str(self.error))
self.assertIn("{}", str(self.error))
assert "FOO" in str(self.error)
assert "{}" in str(self.error)
class PollErrorTest(unittest.TestCase):
@ -40,13 +43,13 @@ class PollErrorTest(unittest.TestCase):
mock.sentinel.AR: mock.sentinel.AR2})
def test_timeout(self):
self.assertTrue(self.timeout.timeout)
self.assertFalse(self.invalid.timeout)
assert self.timeout.timeout
assert not self.invalid.timeout
def test_repr(self):
self.assertEqual('PollError(exhausted=%s, updated={sentinel.AR: '
'sentinel.AR2})' % repr(set()), repr(self.invalid))
assert 'PollError(exhausted=%s, updated={sentinel.AR: ' \
'sentinel.AR2})' % repr(set()) == repr(self.invalid)
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,9 +1,11 @@
"""Tests for acme.fields."""
import datetime
import sys
import unittest
import warnings
import josepy as jose
import pytest
import pytz
@ -15,16 +17,17 @@ class FixedTest(unittest.TestCase):
self.field = fixed('name', 'x')
def test_decode(self):
self.assertEqual('x', self.field.decode('x'))
assert 'x' == self.field.decode('x')
def test_decode_bad(self):
self.assertRaises(jose.DeserializationError, self.field.decode, 'y')
with pytest.raises(jose.DeserializationError):
self.field.decode('y')
def test_encode(self):
self.assertEqual('x', self.field.encode('x'))
assert 'x' == self.field.encode('x')
def test_encode_override(self):
self.assertEqual('y', self.field.encode('y'))
assert 'y' == self.field.encode('y')
class RFC3339FieldTest(unittest.TestCase):
@ -36,24 +39,22 @@ class RFC3339FieldTest(unittest.TestCase):
def test_default_encoder(self):
from acme.fields import RFC3339Field
self.assertEqual(
self.encoded, RFC3339Field.default_encoder(self.decoded))
assert self.encoded == RFC3339Field.default_encoder(self.decoded)
def test_default_encoder_naive_fails(self):
from acme.fields import RFC3339Field
self.assertRaises(
ValueError, RFC3339Field.default_encoder, datetime.datetime.now())
with pytest.raises(ValueError):
RFC3339Field.default_encoder(datetime.datetime.now())
def test_default_decoder(self):
from acme.fields import RFC3339Field
self.assertEqual(
self.decoded, RFC3339Field.default_decoder(self.encoded))
assert self.decoded == RFC3339Field.default_decoder(self.encoded)
def test_default_decoder_raises_deserialization_error(self):
from acme.fields import RFC3339Field
self.assertRaises(
jose.DeserializationError, RFC3339Field.default_decoder, '')
with pytest.raises(jose.DeserializationError):
RFC3339Field.default_decoder('')
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,53 +1,54 @@
"""Tests for acme.jose shim."""
import importlib
import sys
import unittest
import pytest
class JoseTest(unittest.TestCase):
"""Tests for acme.jose shim."""
def _test_it(self, submodule, attribute):
if submodule:
acme_jose_path = 'acme.jose.' + submodule
josepy_path = 'josepy.' + submodule
else:
acme_jose_path = 'acme.jose'
josepy_path = 'josepy'
acme_jose_mod = importlib.import_module(acme_jose_path)
josepy_mod = importlib.import_module(josepy_path)
def _test_it(submodule, attribute):
if submodule:
acme_jose_path = 'acme.jose.' + submodule
josepy_path = 'josepy.' + submodule
else:
acme_jose_path = 'acme.jose'
josepy_path = 'josepy'
acme_jose_mod = importlib.import_module(acme_jose_path)
josepy_mod = importlib.import_module(josepy_path)
self.assertIs(acme_jose_mod, josepy_mod)
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))
assert acme_jose_mod is josepy_mod
assert getattr(acme_jose_mod, attribute) is getattr(josepy_mod, attribute)
# We use the imports below with eval, but pylint doesn't
# understand that.
import acme # pylint: disable=unused-import
import josepy # pylint: disable=unused-import
acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used
josepy_mod = eval(josepy_path) # pylint: disable=eval-used
self.assertIs(acme_jose_mod, josepy_mod)
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))
# We use the imports below with eval, but pylint doesn't
# understand that.
import josepy # pylint: disable=unused-import
def test_top_level(self):
self._test_it('', 'RS512')
import acme # pylint: disable=unused-import
acme_jose_mod = eval(acme_jose_path) # pylint: disable=eval-used
josepy_mod = eval(josepy_path) # pylint: disable=eval-used
assert acme_jose_mod is josepy_mod
assert getattr(acme_jose_mod, attribute) is getattr(josepy_mod, attribute)
def test_submodules(self):
# This test ensures that the modules in josepy that were
# available at the time it was moved into its own package are
# available under acme.jose. Backwards compatibility with new
# modules or testing code is not maintained.
mods_and_attrs = [('b64', 'b64decode',),
('errors', 'Error',),
('interfaces', 'JSONDeSerializable',),
('json_util', 'Field',),
('jwa', 'HS256',),
('jwk', 'JWK',),
('jws', 'JWS',),
('util', 'ImmutableMap',),]
def test_top_level():
_test_it('', 'RS512')
for mod, attr in mods_and_attrs:
self._test_it(mod, attr)
def test_submodules():
# This test ensures that the modules in josepy that were
# available at the time it was moved into its own package are
# available under acme.jose. Backwards compatibility with new
# modules or testing code is not maintained.
mods_and_attrs = [('b64', 'b64decode',),
('errors', 'Error',),
('interfaces', 'JSONDeSerializable',),
('json_util', 'Field',),
('jwa', 'HS256',),
('jwk', 'JWK',),
('jws', 'JWS',),
('util', 'ImmutableMap',),]
for mod, attr in mods_and_attrs:
_test_it(mod, attr)
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,9 @@
"""Tests for acme.jws."""
import sys
import unittest
import josepy as jose
import pytest
import test_util
@ -25,9 +27,9 @@ class HeaderTest(unittest.TestCase):
from acme.jws import Header
nonce_field = Header._fields['nonce']
self.assertRaises(
jose.DeserializationError, nonce_field.decode, self.wrong_nonce)
self.assertEqual(b'foo', nonce_field.decode(self.good_nonce))
with pytest.raises(jose.DeserializationError):
nonce_field.decode(self.wrong_nonce)
assert b'foo' == nonce_field.decode(self.good_nonce)
class JWSTest(unittest.TestCase):
@ -45,22 +47,22 @@ class JWSTest(unittest.TestCase):
jws = JWS.sign(payload=b'foo', key=self.privkey,
alg=jose.RS256, nonce=self.nonce,
url=self.url, kid=self.kid)
self.assertEqual(jws.signature.combined.nonce, self.nonce)
self.assertEqual(jws.signature.combined.url, self.url)
self.assertEqual(jws.signature.combined.kid, self.kid)
self.assertIsNone(jws.signature.combined.jwk)
assert jws.signature.combined.nonce == self.nonce
assert jws.signature.combined.url == self.url
assert jws.signature.combined.kid == self.kid
assert jws.signature.combined.jwk is None
# TODO: check that nonce is in protected header
self.assertEqual(jws, JWS.from_json(jws.to_json()))
assert jws == JWS.from_json(jws.to_json())
def test_jwk_serialize(self):
from acme.jws import JWS
jws = JWS.sign(payload=b'foo', key=self.privkey,
alg=jose.RS256, nonce=self.nonce,
url=self.url)
self.assertIsNone(jws.signature.combined.kid)
self.assertEqual(jws.signature.combined.jwk, self.pubkey)
assert jws.signature.combined.kid is None
assert jws.signature.combined.jwk == self.pubkey
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,10 +1,13 @@
"""Tests for acme.messages."""
import contextlib
import sys
from typing import Dict
import unittest
from unittest import mock
import warnings
import josepy as jose
import pytest
from acme import challenges
import test_util
@ -18,7 +21,10 @@ class ErrorTest(unittest.TestCase):
"""Tests for acme.messages.Error."""
def setUp(self):
from acme.messages import Error, ERROR_PREFIX, Identifier, IDENTIFIER_FQDN
from acme.messages import Error
from acme.messages import ERROR_PREFIX
from acme.messages import Identifier
from acme.messages import IDENTIFIER_FQDN
self.error = Error.with_code('malformed', detail='foo', title='title')
self.jobj = {
'detail': 'foo',
@ -33,11 +39,11 @@ class ErrorTest(unittest.TestCase):
def test_default_typ(self):
from acme.messages import Error
self.assertEqual(Error().typ, 'about:blank')
assert Error().typ == 'about:blank'
def test_from_json_empty(self):
from acme.messages import Error
self.assertEqual(Error(), Error.from_json('{}'))
assert Error() == Error.from_json('{}')
def test_from_json_hashable(self):
from acme.messages import Error
@ -48,48 +54,63 @@ class ErrorTest(unittest.TestCase):
parsed_error = Error.from_json(self.error_with_subproblems.to_json())
self.assertEqual(1, len(parsed_error.subproblems))
self.assertEqual(self.subproblem, parsed_error.subproblems[0])
assert 1 == len(parsed_error.subproblems)
assert self.subproblem == parsed_error.subproblems[0]
def test_description(self):
self.assertEqual('The request message was malformed', self.error.description)
self.assertIsNone(self.error_custom.description)
assert 'The request message was malformed' == self.error.description
assert self.error_custom.description is None
def test_code(self):
from acme.messages import Error
self.assertEqual('malformed', self.error.code)
self.assertIsNone(self.error_custom.code)
self.assertIsNone(Error().code)
assert 'malformed' == self.error.code
assert self.error_custom.code is None
assert Error().code is None
def test_is_acme_error(self):
from acme.messages import is_acme_error, Error
self.assertTrue(is_acme_error(self.error))
self.assertFalse(is_acme_error(self.error_custom))
self.assertFalse(is_acme_error(Error()))
self.assertFalse(is_acme_error(self.empty_error))
self.assertFalse(is_acme_error("must pet all the {dogs|rabbits}"))
from acme.messages import Error
from acme.messages import is_acme_error
assert is_acme_error(self.error)
assert not is_acme_error(self.error_custom)
assert not is_acme_error(Error())
assert not is_acme_error(self.empty_error)
assert not is_acme_error("must pet all the {dogs|rabbits}")
def test_unicode_error(self):
from acme.messages import Error, is_acme_error
from acme.messages import Error
from acme.messages import is_acme_error
arabic_error = Error.with_code(
'malformed', detail=u'\u0639\u062f\u0627\u0644\u0629', title='title')
self.assertTrue(is_acme_error(arabic_error))
assert is_acme_error(arabic_error)
def test_with_code(self):
from acme.messages import Error, is_acme_error
self.assertTrue(is_acme_error(Error.with_code('badCSR')))
self.assertRaises(ValueError, Error.with_code, 'not an ACME error code')
from acme.messages import Error
from acme.messages import is_acme_error
assert is_acme_error(Error.with_code('badCSR'))
with pytest.raises(ValueError):
Error.with_code('not an ACME error code')
def test_str(self):
self.assertEqual(
str(self.error),
u"{0.typ} :: {0.description} :: {0.detail} :: {0.title}"
.format(self.error))
self.assertEqual(
str(self.error_with_subproblems),
assert str(self.error) == \
u"{0.typ} :: {0.description} :: {0.detail} :: {0.title}" \
.format(self.error)
assert str(self.error_with_subproblems) == \
(u"{0.typ} :: {0.description} :: {0.detail} :: {0.title}\n"+
u"Problem for {1.identifier.value}: {1.typ} :: {1.description} :: {1.detail} :: {1.title}").format(
self.error_with_subproblems, self.subproblem))
self.error_with_subproblems, self.subproblem)
# this test is based on a minimal reproduction of a contextmanager/immutable
# exception related error: https://github.com/python/cpython/issues/99856
def test_setting_traceback(self):
assert self.error_custom.__traceback__ is None
try:
1/0
except ZeroDivisionError as e:
self.error_custom.__traceback__ = e.__traceback__
assert self.error_custom.__traceback__ is not None
class ConstantTest(unittest.TestCase):
"""Tests for acme.messages._Constant."""
@ -105,28 +126,28 @@ class ConstantTest(unittest.TestCase):
self.const_b = MockConstant('b')
def test_to_partial_json(self):
self.assertEqual('a', self.const_a.to_partial_json())
self.assertEqual('b', self.const_b.to_partial_json())
assert 'a' == self.const_a.to_partial_json()
assert 'b' == self.const_b.to_partial_json()
def test_from_json(self):
self.assertEqual(self.const_a, self.MockConstant.from_json('a'))
self.assertRaises(
jose.DeserializationError, self.MockConstant.from_json, 'c')
assert self.const_a == self.MockConstant.from_json('a')
with pytest.raises(jose.DeserializationError):
self.MockConstant.from_json('c')
def test_from_json_hashable(self):
hash(self.MockConstant.from_json('a'))
def test_repr(self):
self.assertEqual('MockConstant(a)', repr(self.const_a))
self.assertEqual('MockConstant(b)', repr(self.const_b))
assert 'MockConstant(a)' == repr(self.const_a)
assert 'MockConstant(b)' == repr(self.const_b)
def test_equality(self):
const_a_prime = self.MockConstant('a')
self.assertNotEqual(self.const_a, self.const_b)
self.assertEqual(self.const_a, const_a_prime)
assert self.const_a != self.const_b
assert self.const_a == const_a_prime
self.assertNotEqual(self.const_a, self.const_b)
self.assertEqual(self.const_a, const_a_prime)
assert self.const_a != self.const_b
assert self.const_a == const_a_prime
class DirectoryTest(unittest.TestCase):
@ -149,19 +170,21 @@ class DirectoryTest(unittest.TestCase):
Directory({'foo': 'bar'})
def test_getitem(self):
self.assertEqual('reg', self.dir['newReg'])
assert 'reg' == self.dir['newReg']
def test_getitem_fails_with_key_error(self):
self.assertRaises(KeyError, self.dir.__getitem__, 'foo')
with pytest.raises(KeyError):
self.dir.__getitem__('foo')
def test_getattr(self):
self.assertEqual('reg', self.dir.newReg)
assert 'reg' == self.dir.newReg
def test_getattr_fails_with_attribute_error(self):
self.assertRaises(AttributeError, self.dir.__getattr__, 'foo')
with pytest.raises(AttributeError):
self.dir.__getattr__('foo')
def test_to_json(self):
self.assertEqual(self.dir.to_json(), {
assert self.dir.to_json() == {
'newReg': 'reg',
'newCert': 'cert',
'meta': {
@ -169,7 +192,7 @@ class DirectoryTest(unittest.TestCase):
'website': 'https://www.example.com/',
'caaIdentities': ['example.com'],
},
})
}
def test_from_json_deserialization_unknown_key_success(self): # pylint: disable=no-self-use
from acme.messages import Directory
@ -180,7 +203,7 @@ class DirectoryTest(unittest.TestCase):
for k in self.dir.meta:
if k == 'terms_of_service':
result = self.dir.meta[k] == 'https://example.com/acme/terms'
self.assertTrue(result)
assert result
class ExternalAccountBindingTest(unittest.TestCase):
@ -197,8 +220,8 @@ class ExternalAccountBindingTest(unittest.TestCase):
from acme.messages import ExternalAccountBinding
eab = ExternalAccountBinding.from_data(self.key, self.kid, self.hmac_key, self.dir)
self.assertEqual(len(eab), 3)
self.assertEqual(sorted(eab.keys()), sorted(['protected', 'payload', 'signature']))
assert len(eab) == 3
assert sorted(eab.keys()) == sorted(['protected', 'payload', 'signature'])
class RegistrationTest(unittest.TestCase):
@ -227,13 +250,15 @@ class RegistrationTest(unittest.TestCase):
def test_from_data(self):
from acme.messages import Registration
reg = Registration.from_data(phone='1234', email='admin@foo.com')
self.assertEqual(reg.contact, (
assert reg.contact == (
'tel:1234',
'mailto:admin@foo.com',
))
)
def test_new_registration_from_data_with_eab(self):
from acme.messages import NewRegistration, ExternalAccountBinding, Directory
from acme.messages import Directory
from acme.messages import ExternalAccountBinding
from acme.messages import NewRegistration
key = jose.jwk.JWKRSA(key=KEY.public_key())
kid = "kid-for-testing"
hmac_key = "hmac-key-for-testing"
@ -242,24 +267,24 @@ class RegistrationTest(unittest.TestCase):
})
eab = ExternalAccountBinding.from_data(key, kid, hmac_key, directory)
reg = NewRegistration.from_data(email='admin@foo.com', external_account_binding=eab)
self.assertEqual(reg.contact, (
assert reg.contact == (
'mailto:admin@foo.com',
))
self.assertEqual(sorted(reg.external_account_binding.keys()),
sorted(['protected', 'payload', 'signature']))
)
assert sorted(reg.external_account_binding.keys()) == \
sorted(['protected', 'payload', 'signature'])
def test_phones(self):
self.assertEqual(('1234',), self.reg.phones)
assert ('1234',) == self.reg.phones
def test_emails(self):
self.assertEqual(('admin@foo.com',), self.reg.emails)
assert ('admin@foo.com',) == self.reg.emails
def test_to_partial_json(self):
self.assertEqual(self.jobj_to, self.reg.to_partial_json())
assert self.jobj_to == self.reg.to_partial_json()
def test_from_json(self):
from acme.messages import Registration
self.assertEqual(self.reg, Registration.from_json(self.jobj_from))
assert self.reg == Registration.from_json(self.jobj_from)
def test_from_json_hashable(self):
from acme.messages import Registration
@ -270,13 +295,13 @@ class RegistrationTest(unittest.TestCase):
empty_new_reg = NewRegistration()
new_reg_with_contact = NewRegistration(contact=())
self.assertEqual(empty_new_reg.contact, ())
self.assertEqual(new_reg_with_contact.contact, ())
assert empty_new_reg.contact == ()
assert new_reg_with_contact.contact == ()
self.assertNotIn('contact', empty_new_reg.to_partial_json())
self.assertNotIn('contact', empty_new_reg.fields_to_partial_json())
self.assertIn('contact', new_reg_with_contact.to_partial_json())
self.assertIn('contact', new_reg_with_contact.fields_to_partial_json())
assert 'contact' not in empty_new_reg.to_partial_json()
assert 'contact' not in empty_new_reg.fields_to_partial_json()
assert 'contact' in new_reg_with_contact.to_partial_json()
assert 'contact' in new_reg_with_contact.fields_to_partial_json()
class UpdateRegistrationTest(unittest.TestCase):
@ -285,9 +310,8 @@ class UpdateRegistrationTest(unittest.TestCase):
def test_empty(self):
from acme.messages import UpdateRegistration
jstring = '{"resource": "reg"}'
self.assertEqual('{}', UpdateRegistration().json_dumps())
self.assertEqual(
UpdateRegistration(), UpdateRegistration.json_loads(jstring))
assert '{}' == UpdateRegistration().json_dumps()
assert UpdateRegistration() == UpdateRegistration.json_loads(jstring)
class RegistrationResourceTest(unittest.TestCase):
@ -300,11 +324,11 @@ class RegistrationResourceTest(unittest.TestCase):
terms_of_service=mock.sentinel.terms_of_service)
def test_to_partial_json(self):
self.assertEqual(self.regr.to_json(), {
assert self.regr.to_json() == {
'body': mock.sentinel.body,
'uri': mock.sentinel.uri,
'terms_of_service': mock.sentinel.terms_of_service,
})
}
class ChallengeResourceTest(unittest.TestCase):
@ -312,8 +336,8 @@ class ChallengeResourceTest(unittest.TestCase):
def test_uri(self):
from acme.messages import ChallengeResource
self.assertEqual('http://challb', ChallengeResource(body=mock.MagicMock(
uri='http://challb'), authzr_uri='http://authz').uri)
assert 'http://challb' == ChallengeResource(body=mock.MagicMock(
uri='http://challb'), authzr_uri='http://authz').uri
class ChallengeBodyTest(unittest.TestCase):
@ -347,22 +371,22 @@ class ChallengeBodyTest(unittest.TestCase):
}
def test_encode(self):
self.assertEqual(self.challb.encode('uri'), self.challb.uri)
assert self.challb.encode('uri') == self.challb.uri
def test_to_partial_json(self):
self.assertEqual(self.jobj_to, self.challb.to_partial_json())
assert self.jobj_to == self.challb.to_partial_json()
def test_from_json(self):
from acme.messages import ChallengeBody
self.assertEqual(self.challb, ChallengeBody.from_json(self.jobj_from))
assert self.challb == ChallengeBody.from_json(self.jobj_from)
def test_from_json_hashable(self):
from acme.messages import ChallengeBody
hash(ChallengeBody.from_json(self.jobj_from))
def test_proxy(self):
self.assertEqual(jose.b64decode(
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA'), self.challb.token)
assert jose.b64decode(
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA') == self.challb.token
class AuthorizationTest(unittest.TestCase):
@ -410,7 +434,7 @@ class AuthorizationResourceTest(unittest.TestCase):
authzr = AuthorizationResource(
uri=mock.sentinel.uri,
body=mock.sentinel.body)
self.assertIsInstance(authzr, jose.JSONDeSerializable)
assert isinstance(authzr, jose.JSONDeSerializable)
class CertificateRequestTest(unittest.TestCase):
@ -421,10 +445,9 @@ class CertificateRequestTest(unittest.TestCase):
self.req = CertificateRequest(csr=CSR)
def test_json_de_serializable(self):
self.assertIsInstance(self.req, jose.JSONDeSerializable)
assert isinstance(self.req, jose.JSONDeSerializable)
from acme.messages import CertificateRequest
self.assertEqual(
self.req, CertificateRequest.from_json(self.req.to_json()))
assert self.req == CertificateRequest.from_json(self.req.to_json())
class CertificateResourceTest(unittest.TestCase):
@ -437,10 +460,9 @@ class CertificateResourceTest(unittest.TestCase):
cert_chain_uri=mock.sentinel.cert_chain_uri)
def test_json_de_serializable(self):
self.assertIsInstance(self.certr, jose.JSONDeSerializable)
assert isinstance(self.certr, jose.JSONDeSerializable)
from acme.messages import CertificateResource
self.assertEqual(
self.certr, CertificateResource.from_json(self.certr.to_json()))
assert self.certr == CertificateResource.from_json(self.certr.to_json())
class RevocationTest(unittest.TestCase):
@ -464,11 +486,11 @@ class OrderResourceTest(unittest.TestCase):
body=mock.sentinel.body, uri=mock.sentinel.uri)
def test_to_partial_json(self):
self.assertEqual(self.regr.to_json(), {
assert self.regr.to_json() == {
'body': mock.sentinel.body,
'uri': mock.sentinel.uri,
'authorizations': None,
})
}
class NewOrderTest(unittest.TestCase):
@ -480,9 +502,9 @@ class NewOrderTest(unittest.TestCase):
identifiers=mock.sentinel.identifiers)
def test_to_partial_json(self):
self.assertEqual(self.reg.to_json(), {
assert self.reg.to_json() == {
'identifiers': mock.sentinel.identifiers,
})
}
class JWSPayloadRFC8555Compliant(unittest.TestCase):
@ -494,8 +516,8 @@ class JWSPayloadRFC8555Compliant(unittest.TestCase):
jobj = new_order.json_dumps(indent=2).encode()
# RFC8555 states that JWS bodies must not have a resource field.
self.assertEqual(jobj, b'{}')
assert jobj == b'{}'
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -2,18 +2,19 @@
import http.client as http_client
import socket
import socketserver
import sys
import threading
import unittest
from typing import Set
import unittest
from unittest import mock
import josepy as jose
import pytest
import requests
from acme import challenges
from acme import crypto_util
from acme import errors
import test_util
@ -58,14 +59,13 @@ class HTTP01ServerTest(unittest.TestCase):
def test_index(self):
response = requests.get(
'http://localhost:{0}'.format(self.port), verify=False)
self.assertEqual(
response.text, 'ACME client standalone challenge solver')
self.assertTrue(response.ok)
assert response.text == 'ACME client standalone challenge solver'
assert response.ok
def test_404(self):
response = requests.get(
'http://localhost:{0}/foo'.format(self.port), verify=False)
self.assertEqual(response.status_code, http_client.NOT_FOUND)
assert response.status_code == http_client.NOT_FOUND
def _test_http01(self, add):
chall = challenges.HTTP01(token=(b'x' * 16))
@ -81,10 +81,10 @@ class HTTP01ServerTest(unittest.TestCase):
port=self.port)
def test_http01_found(self):
self.assertTrue(self._test_http01(add=True))
assert self._test_http01(add=True)
def test_http01_not_found(self):
self.assertFalse(self._test_http01(add=False))
assert not self._test_http01(add=False)
def test_timely_shutdown(self):
from acme.standalone import HTTP01Server
@ -106,7 +106,7 @@ class HTTP01ServerTest(unittest.TestCase):
# may raise error because socket could already be closed
pass
self.assertFalse(is_hung, msg='Server shutdown should not be hung')
assert not is_hung, 'Server shutdown should not be hung'
@unittest.skipIf(not challenges.TLSALPN01.is_supported(), "pyOpenSSL too old")
@ -149,14 +149,12 @@ class TLSALPN01ServerTest(unittest.TestCase):
b'localhost', host=host, port=port, timeout=1,
alpn_protocols=[b"acme-tls/1"])
# Expect challenge cert when connecting with ALPN.
self.assertEqual(
jose.ComparableX509(cert),
assert jose.ComparableX509(cert) == \
jose.ComparableX509(self.challenge_certs[b'localhost'][1])
)
def test_bad_alpn(self):
host, port = self.server.socket.getsockname()[:2]
with self.assertRaises(errors.Error):
with pytest.raises(errors.Error):
crypto_util.probe_sni(
b'localhost', host=host, port=port, timeout=1,
alpn_protocols=[b"bad-alpn"])
@ -190,16 +188,17 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
@mock.patch("socket.socket.bind")
def test_fail_to_bind(self, mock_bind):
from errno import EADDRINUSE
from acme.standalone import BaseDualNetworkedServers
mock_bind.side_effect = socket.error(EADDRINUSE, "Fake addr in use error")
with self.assertRaises(socket.error) as em:
with pytest.raises(socket.error) as exc_info:
BaseDualNetworkedServers(
BaseDualNetworkedServersTest.SingleProtocolServer,
('', 0), socketserver.BaseRequestHandler)
self.assertEqual(em.exception.errno, EADDRINUSE)
assert exc_info.value.errno == EADDRINUSE
def test_ports_equal(self):
from acme.standalone import BaseDualNetworkedServers
@ -213,7 +212,7 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
for sockname in socknames:
port = sockname[1]
if prev_port:
self.assertEqual(prev_port, port)
assert prev_port == port
prev_port = port
@ -237,14 +236,13 @@ class HTTP01DualNetworkedServersTest(unittest.TestCase):
def test_index(self):
response = requests.get(
'http://localhost:{0}'.format(self.port), verify=False)
self.assertEqual(
response.text, 'ACME client standalone challenge solver')
self.assertTrue(response.ok)
assert response.text == 'ACME client standalone challenge solver'
assert response.ok
def test_404(self):
response = requests.get(
'http://localhost:{0}/foo'.format(self.port), verify=False)
self.assertEqual(response.status_code, http_client.NOT_FOUND)
assert response.status_code == http_client.NOT_FOUND
def _test_http01(self, add):
chall = challenges.HTTP01(token=(b'x' * 16))
@ -260,11 +258,11 @@ class HTTP01DualNetworkedServersTest(unittest.TestCase):
port=self.port)
def test_http01_found(self):
self.assertTrue(self._test_http01(add=True))
assert self._test_http01(add=True)
def test_http01_not_found(self):
self.assertFalse(self._test_http01(add=False))
assert not self._test_http01(add=False)
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -8,9 +8,9 @@ import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import josepy as jose
from josepy.util import ComparableECKey
from OpenSSL import crypto
import pkg_resources
from josepy.util import ComparableECKey
def load_vector(*names):

View file

@ -1,16 +1,16 @@
"""Tests for acme.util."""
import sys
import unittest
import pytest
class MapKeysTest(unittest.TestCase):
"""Tests for acme.util.map_keys."""
def test_it(self):
from acme.util import map_keys
self.assertEqual({'a': 'b', 'c': 'd'},
map_keys({'a': 'b', 'c': 'd'}, lambda key: key))
self.assertEqual({2: 2, 4: 4}, map_keys({1: 2, 3: 4}, lambda x: x + 1))
def test_it():
from acme.util import map_keys
assert {'a': 'b', 'c': 'd'} == \
map_keys({'a': 'b', 'c': 'd'}, lambda key: key)
assert {2: 2, 4: 4} == map_keys({1: 2, 3: 4}, lambda x: x + 1)
if __name__ == '__main__':
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -74,15 +74,14 @@ from typing import Set
from typing import Tuple
from typing import Union
from certbot import errors
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import assertions
from certbot_apache._internal import interfaces
from certbot_apache._internal import parser
from certbot_apache._internal import parsernode_util as util
from certbot import errors
from certbot.compat import os
class AugeasParserNode(interfaces.ParserNode):
""" Augeas implementation of ParserNode interface """

View file

@ -21,16 +21,6 @@ from typing import Tuple
from typing import Type
from typing import Union
from certbot_apache._internal import apache_util
from certbot_apache._internal import assertions
from certbot_apache._internal import constants
from certbot_apache._internal import display_ops
from certbot_apache._internal import dualparser
from certbot_apache._internal import http_01
from certbot_apache._internal import obj
from certbot_apache._internal import parser
from certbot_apache._internal.apacheparser import ApacheBlockNode
from acme import challenges
from certbot import achallenges
from certbot import errors
@ -42,6 +32,15 @@ from certbot.interfaces import RenewableCert
from certbot.plugins import common
from certbot.plugins.enhancements import AutoHSTSEnhancement
from certbot.plugins.util import path_surgery
from certbot_apache._internal import apache_util
from certbot_apache._internal import assertions
from certbot_apache._internal import constants
from certbot_apache._internal import display_ops
from certbot_apache._internal import dualparser
from certbot_apache._internal import http_01
from certbot_apache._internal import obj
from certbot_apache._internal import parser
from certbot_apache._internal.apacheparser import ApacheBlockNode
try:
import apacheconfig

View file

@ -6,11 +6,10 @@ from typing import Optional
from typing import Sequence
from typing import Tuple
from certbot_apache._internal.obj import VirtualHost
from certbot import errors
from certbot.compat import os
from certbot.display import util as display_util
from certbot_apache._internal.obj import VirtualHost
logger = logging.getLogger(__name__)

View file

@ -2,6 +2,7 @@
from typing import Dict
from typing import Type
from certbot import util
from certbot_apache._internal import configurator
from certbot_apache._internal import override_arch
from certbot_apache._internal import override_centos
@ -12,8 +13,6 @@ from certbot_apache._internal import override_gentoo
from certbot_apache._internal import override_suse
from certbot_apache._internal import override_void
from certbot import util
OVERRIDE_CLASSES: Dict[str, Type[configurator.ApacheConfigurator]] = {
"arch": override_arch.ArchConfigurator,
"cloudlinux": override_centos.CentOSConfigurator,

View file

@ -5,15 +5,14 @@ from typing import List
from typing import Set
from typing import TYPE_CHECKING
from certbot_apache._internal.obj import VirtualHost
from certbot_apache._internal.parser import get_aug_path
from acme.challenges import KeyAuthorizationChallengeResponse
from certbot import errors
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
from certbot.compat import filesystem
from certbot.compat import os
from certbot.plugins import common
from certbot_apache._internal.obj import VirtualHost
from certbot_apache._internal.parser import get_aug_path
if TYPE_CHECKING:
from certbot_apache._internal.configurator import ApacheConfigurator # pragma: no cover

View file

@ -7,12 +7,11 @@ from typing import Pattern
from typing import Set
from typing import Union
from certbot.plugins import common
from certbot_apache._internal.apacheparser import ApacheBlockNode
from certbot_apache._internal.augeasparser import AugeasBlockNode
from certbot_apache._internal.dualparser import DualBlockNode
from certbot.plugins import common
class Addr(common.Addr):
"""Represents an Apache address."""

View file

@ -2,14 +2,13 @@
import logging
from typing import Any
from certbot import errors
from certbot import util
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal import parser
from certbot_apache._internal.configurator import OsOptions
from certbot import errors
from certbot import util
logger = logging.getLogger(__name__)

View file

@ -1,15 +1,14 @@
""" Distribution specific override class for Debian family (Ubuntu/Debian) """
import logging
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal.configurator import OsOptions
from certbot_apache._internal.obj import VirtualHost
from certbot import errors
from certbot import util
from certbot.compat import filesystem
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal.configurator import OsOptions
from certbot_apache._internal.obj import VirtualHost
logger = logging.getLogger(__name__)

View file

@ -1,14 +1,13 @@
""" Distribution specific override class for Fedora 29+ """
from typing import Any
from certbot import errors
from certbot import util
from certbot_apache._internal import apache_util
from certbot_apache._internal import configurator
from certbot_apache._internal import parser
from certbot_apache._internal.configurator import OsOptions
from certbot import errors
from certbot import util
class FedoraConfigurator(configurator.ApacheConfigurator):
"""Fedora 29+ specific ApacheConfigurator override class"""

View file

@ -15,11 +15,10 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from certbot_apache._internal import apache_util
from certbot_apache._internal import constants
from certbot import errors
from certbot.compat import os
from certbot_apache._internal import apache_util
from certbot_apache._internal import constants
if TYPE_CHECKING:
from certbot_apache._internal.configurator import ApacheConfigurator # pragma: no cover

View file

@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
# We specify the minimum acme and certbot version as the current plugin
@ -17,6 +17,10 @@ dev_extras = [
'apacheconfig>=0.3.2',
]
test_extras = [
'pytest',
]
setup(
name='certbot-apache',
version=version,
@ -52,6 +56,7 @@ setup(
install_requires=install_requires,
extras_require={
'dev': dev_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,14 +1,13 @@
"""Tests for AugeasParserNode classes"""
from typing import List
import os
import util
from typing import List
from unittest import mock
from certbot import errors
from certbot_apache._internal import assertions
from certbot_apache._internal import augeasparser
import util
import pytest
def _get_augeasnode_mock(filepath):
@ -43,14 +42,14 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
def test_save(self):
with mock.patch('certbot_apache._internal.parser.ApacheParser.save') as mock_save:
self.config.parser_root.save("A save message")
self.assertIs(mock_save.called, True)
self.assertEqual(mock_save.call_args[0][0], "A save message")
assert mock_save.called is True
assert mock_save.call_args[0][0] == "A save message"
def test_unsaved_files(self):
with mock.patch('certbot_apache._internal.parser.ApacheParser.unsaved_files') as mock_uf:
mock_uf.return_value = ["first", "second"]
files = self.config.parser_root.unsaved_files()
self.assertEqual(files, ["first", "second"])
assert files == ["first", "second"]
def test_get_block_node_name(self):
from certbot_apache._internal.augeasparser import AugeasBlockNode
@ -69,26 +68,26 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
}
for test in testcases:
# pylint: disable=protected-access
self.assertEqual(block._aug_get_name(test), testcases[test])
assert block._aug_get_name(test) == testcases[test]
def test_find_blocks(self):
blocks = self.config.parser_root.find_blocks("VirtualHost", exclude=False)
self.assertEqual(len(blocks), 12)
assert len(blocks) == 12
def test_find_blocks_case_insensitive(self):
vhs = self.config.parser_root.find_blocks("VirtualHost")
vhs2 = self.config.parser_root.find_blocks("viRtuAlHoST")
self.assertEqual(len(vhs), len(vhs2))
assert len(vhs) == len(vhs2)
def test_find_directive_found(self):
directives = self.config.parser_root.find_directives("Listen")
self.assertEqual(len(directives), 1)
self.assertIs(directives[0].filepath.endswith("/apache2/ports.conf"), True)
self.assertEqual(directives[0].parameters, (u'80',))
assert len(directives) == 1
assert directives[0].filepath.endswith("/apache2/ports.conf") is True
assert directives[0].parameters == (u'80',)
def test_find_directive_notfound(self):
directives = self.config.parser_root.find_directives("Nonexistent")
self.assertEqual(len(directives), 0)
assert len(directives) == 0
def test_find_directive_from_block(self):
blocks = self.config.parser_root.find_blocks("virtualhost")
@ -96,31 +95,31 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
for vh in blocks:
if vh.filepath.endswith("sites-enabled/certbot.conf"):
servername = vh.find_directives("servername")
self.assertEqual(servername[0].parameters[0], "certbot.demo")
assert servername[0].parameters[0] == "certbot.demo"
found = True
self.assertIs(found, True)
assert found is True
def test_find_comments(self):
rootcomment = self.config.parser_root.find_comments(
"This is the main Apache server configuration file. "
)
self.assertEqual(len(rootcomment), 1)
self.assertIs(rootcomment[0].filepath.endswith(
assert len(rootcomment) == 1
assert rootcomment[0].filepath.endswith(
"debian_apache_2_4/multiple_vhosts/apache2/apache2.conf"
), True)
) is True
def test_set_parameters(self):
servernames = self.config.parser_root.find_directives("servername")
names: List[str] = []
for servername in servernames:
names += servername.parameters
self.assertNotIn("going_to_set_this", names)
assert "going_to_set_this" not in names
servernames[0].set_parameters(["something", "going_to_set_this"])
servernames = self.config.parser_root.find_directives("servername")
names = []
for servername in servernames:
names += servername.parameters
self.assertIn("going_to_set_this", names)
assert "going_to_set_this" in names
def test_set_parameters_atinit(self):
from certbot_apache._internal.augeasparser import AugeasDirectiveNode
@ -133,11 +132,9 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
ancestor=assertions.PASS,
metadata=servernames[0].metadata
)
self.assertIs(mock_set.called, True)
self.assertEqual(
mock_set.call_args_list[0][0][0],
assert mock_set.called is True
assert mock_set.call_args_list[0][0][0] == \
["test", "setting", "these"]
)
def test_set_parameters_delete(self):
# Set params
@ -150,48 +147,43 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
found = False
for servername in servernames:
if "thisshouldnotexistpreviously" in servername.parameters:
self.assertEqual(len(servername.parameters), 3)
assert len(servername.parameters) == 3
servername.set_parameters(["thisshouldnotexistpreviously"])
found = True
self.assertIs(found, True)
assert found is True
# Verify params
servernames = self.config.parser_root.find_directives("servername")
found = False
for servername in servernames:
if "thisshouldnotexistpreviously" in servername.parameters:
self.assertEqual(len(servername.parameters), 1)
assert len(servername.parameters) == 1
servername.set_parameters(["thisshouldnotexistpreviously"])
found = True
self.assertIs(found, True)
assert found is True
def test_add_child_comment(self):
newc = self.config.parser_root.add_child_comment("The content")
comments = self.config.parser_root.find_comments("The content")
self.assertEqual(len(comments), 1)
self.assertEqual(
newc.metadata["augeaspath"],
assert len(comments) == 1
assert newc.metadata["augeaspath"] == \
comments[0].metadata["augeaspath"]
)
self.assertEqual(newc.comment, comments[0].comment)
assert newc.comment == comments[0].comment
def test_delete_child(self):
listens = self.config.parser_root.find_directives("Listen")
self.assertEqual(len(listens), 1)
assert len(listens) == 1
self.config.parser_root.delete_child(listens[0])
listens = self.config.parser_root.find_directives("Listen")
self.assertEqual(len(listens), 0)
assert len(listens) == 0
def test_delete_child_not_found(self):
listen = self.config.parser_root.find_directives("Listen")[0]
listen.metadata["augeaspath"] = "/files/something/nonexistent"
self.assertRaises(
errors.PluginError,
self.config.parser_root.delete_child,
listen
)
with pytest.raises(errors.PluginError):
self.config.parser_root.delete_child(listen)
def test_add_child_block(self):
nb = self.config.parser_root.add_child_block(
@ -199,11 +191,9 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
["first", "second"]
)
rpath, _, directive = nb.metadata["augeaspath"].rpartition("/")
self.assertEqual(
rpath,
assert rpath == \
self.config.parser_root.metadata["augeaspath"]
)
self.assertIs(directive.startswith("NewBlock"), True)
assert directive.startswith("NewBlock") is True
def test_add_child_block_beginning(self):
self.config.parser_root.add_child_block(
@ -214,7 +204,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get first child
first = parser.aug.match("{}/*[1]".format(root_path))
self.assertIs(first[0].endswith("Beginning"), True)
assert first[0].endswith("Beginning") is True
def test_add_child_block_append(self):
self.config.parser_root.add_child_block(
@ -224,7 +214,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get last child
last = parser.aug.match("{}/*[last()]".format(root_path))
self.assertIs(last[0].endswith("VeryLast"), True)
assert last[0].endswith("VeryLast") is True
def test_add_child_block_append_alt(self):
self.config.parser_root.add_child_block(
@ -235,7 +225,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get last child
last = parser.aug.match("{}/*[last()]".format(root_path))
self.assertIs(last[0].endswith("VeryLastAlt"), True)
assert last[0].endswith("VeryLastAlt") is True
def test_add_child_block_middle(self):
self.config.parser_root.add_child_block(
@ -246,20 +236,20 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Augeas indices start at 1 :(
middle = parser.aug.match("{}/*[6]".format(root_path))
self.assertIs(middle[0].endswith("Middle"), True)
assert middle[0].endswith("Middle") is True
def test_add_child_block_existing_name(self):
parser = self.config.parser_root.parser
root_path = self.config.parser_root.metadata["augeaspath"]
# There already exists a single VirtualHost in the base config
new_block = parser.aug.match("{}/VirtualHost[2]".format(root_path))
self.assertEqual(len(new_block), 0)
assert len(new_block) == 0
vh = self.config.parser_root.add_child_block(
"VirtualHost",
)
new_block = parser.aug.match("{}/VirtualHost[2]".format(root_path))
self.assertEqual(len(new_block), 1)
self.assertIs(vh.metadata["augeaspath"].endswith("VirtualHost[2]"), True)
assert len(new_block) == 1
assert vh.metadata["augeaspath"].endswith("VirtualHost[2]") is True
def test_node_init_error_bad_augeaspath(self):
from certbot_apache._internal.augeasparser import AugeasBlockNode
@ -272,11 +262,8 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
"augeaspath": "/files/path/endswith/slash/"
}
}
self.assertRaises(
errors.PluginError,
AugeasBlockNode,
**parameters
)
with pytest.raises(errors.PluginError):
AugeasBlockNode(**parameters)
def test_node_init_error_missing_augeaspath(self):
from certbot_apache._internal.augeasparser import AugeasBlockNode
@ -288,11 +275,8 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
"augeasparser": mock.Mock(),
}
}
self.assertRaises(
errors.PluginError,
AugeasBlockNode,
**parameters
)
with pytest.raises(errors.PluginError):
AugeasBlockNode(**parameters)
def test_add_child_directive(self):
self.config.parser_root.add_child_directive(
@ -301,21 +285,18 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
position=0
)
dirs = self.config.parser_root.find_directives("ThisWasAdded")
self.assertEqual(len(dirs), 1)
self.assertEqual(dirs[0].parameters, ("with", "parameters"))
assert len(dirs) == 1
assert dirs[0].parameters == ("with", "parameters")
# The new directive was added to the very first line of the config
self.assertIs(dirs[0].metadata["augeaspath"].endswith("[1]"), True)
assert dirs[0].metadata["augeaspath"].endswith("[1]") is True
def test_add_child_directive_exception(self):
self.assertRaises(
errors.PluginError,
self.config.parser_root.add_child_directive,
"ThisRaisesErrorBecauseMissingParameters"
)
with pytest.raises(errors.PluginError):
self.config.parser_root.add_child_directive("ThisRaisesErrorBecauseMissingParameters")
def test_parsed_paths(self):
paths = self.config.parser_root.parsed_paths()
self.assertEqual(len(paths), 6)
assert len(paths) == 6
def test_find_ancestors(self):
vhsblocks = self.config.parser_root.find_blocks("VirtualHost")
@ -324,16 +305,16 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
for vh in vhsblocks:
if "/macro/" in vh.metadata["augeaspath"].lower():
ancs = vh.find_ancestors("Macro")
self.assertEqual(len(ancs), 1)
assert len(ancs) == 1
macro_test = True
else:
ancs = vh.find_ancestors("Macro")
self.assertEqual(len(ancs), 0)
assert len(ancs) == 0
nonmacro_test = True
self.assertIs(macro_test, True)
self.assertIs(nonmacro_test, True)
assert macro_test is True
assert nonmacro_test is True
def test_find_ancestors_bad_path(self):
self.config.parser_root.metadata["augeaspath"] = ""
ancs = self.config.parser_root.find_ancestors("Anything")
self.assertEqual(len(ancs), 0)
assert len(ancs) == 0

View file

@ -1,9 +1,12 @@
# pylint: disable=too-many-lines
"""Test for certbot_apache._internal.configurator AutoHSTS functionality"""
import re
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot_apache._internal import constants
import util
@ -43,14 +46,13 @@ class AutoHSTSTest(util.ApacheTest):
self.config.parser.modules.pop("headers_module", None)
self.config.parser.modules.pop("mod_header.c", None)
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
self.assertIs(mock_enable.called, True)
assert mock_enable.called is True
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
def test_autohsts_deploy_already_exists(self, _restart):
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
self.assertRaises(errors.PluginEnhancementAlreadyPresent,
self.config.enable_autohsts,
mock.MagicMock(), ["ocspvhost.com"])
with pytest.raises(errors.PluginEnhancementAlreadyPresent):
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
@mock.patch("certbot_apache._internal.constants.AUTOHSTS_FREQ", 0)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@ -63,14 +65,14 @@ class AutoHSTSTest(util.ApacheTest):
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
# Verify initial value
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
initial_val)
assert self.get_autohsts_value(self.vh_truth[7].path) == \
initial_val
# Increase
self.config.update_autohsts(mock.MagicMock())
# Verify increased value
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
inc_val)
self.assertIs(mock_prepare.called, True)
assert self.get_autohsts_value(self.vh_truth[7].path) == \
inc_val
assert mock_prepare.called is True
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator._autohsts_increase")
@ -79,12 +81,12 @@ class AutoHSTSTest(util.ApacheTest):
initial_val = maxage.format(constants.AUTOHSTS_STEPS[0])
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
# Verify initial value
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
initial_val)
assert self.get_autohsts_value(self.vh_truth[7].path) == \
initial_val
self.config.update_autohsts(mock.MagicMock())
# Freq not patched, so value shouldn't increase
self.assertIs(mock_increase.called, False)
assert mock_increase.called is False
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@ -96,9 +98,8 @@ class AutoHSTSTest(util.ApacheTest):
self.vh_truth[7].path)
dir_loc = "/".join(dir_locs[0].split("/")[:-1])
self.config.parser.aug.remove(dir_loc)
self.assertRaises(errors.PluginError,
self.config.update_autohsts,
mock.MagicMock())
with pytest.raises(errors.PluginError):
self.config.update_autohsts(mock.MagicMock())
@mock.patch("certbot_apache._internal.constants.AUTOHSTS_FREQ", 0)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@ -111,49 +112,48 @@ class AutoHSTSTest(util.ApacheTest):
for i in range(len(constants.AUTOHSTS_STEPS)-1):
# Ensure that value is not made permanent prematurely
self.config.deploy_autohsts(mock_lineage)
self.assertNotEqual(self.get_autohsts_value(self.vh_truth[7].path),
max_val)
assert self.get_autohsts_value(self.vh_truth[7].path) != \
max_val
self.config.update_autohsts(mock.MagicMock())
# Value should match pre-permanent increment step
cur_val = maxage.format(constants.AUTOHSTS_STEPS[i+1])
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
cur_val)
assert self.get_autohsts_value(self.vh_truth[7].path) == \
cur_val
# Ensure that the value is raised to max
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
maxage.format(constants.AUTOHSTS_STEPS[-1]))
assert self.get_autohsts_value(self.vh_truth[7].path) == \
maxage.format(constants.AUTOHSTS_STEPS[-1])
# Make permanent
self.config.deploy_autohsts(mock_lineage)
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
max_val)
assert self.get_autohsts_value(self.vh_truth[7].path) == \
max_val
def test_autohsts_update_noop(self):
with mock.patch("time.time") as mock_time:
# Time mock is used to make sure that the execution does not
# continue when no autohsts entries exist in pluginstorage
self.config.update_autohsts(mock.MagicMock())
self.assertIs(mock_time.called, False)
assert mock_time.called is False
def test_autohsts_make_permanent_noop(self):
self.config.storage.put = mock.MagicMock()
self.config.deploy_autohsts(mock.MagicMock())
# Make sure that the execution does not continue when no entries in store
self.assertIs(self.config.storage.put.called, False)
assert self.config.storage.put.called is False
@mock.patch("certbot_apache._internal.display_ops.select_vhost")
def test_autohsts_no_ssl_vhost(self, mock_select):
mock_select.return_value = self.vh_truth[0]
with mock.patch("certbot_apache._internal.configurator.logger.error") as mock_log:
self.assertRaises(errors.PluginError,
self.config.enable_autohsts,
mock.MagicMock(), "invalid.example.com")
self.assertIn("Certbot was not able to find SSL", mock_log.call_args[0][0])
with pytest.raises(errors.PluginError):
self.config.enable_autohsts(mock.MagicMock(), "invalid.example.com")
assert "Certbot was not able to find SSL" in mock_log.call_args[0][0]
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.add_vhost_id")
def test_autohsts_dont_enhance_twice(self, mock_id, _restart):
mock_id.return_value = "1234567"
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com", "ocspvhost.com"])
self.assertEqual(mock_id.call_count, 1)
assert mock_id.call_count == 1
def test_autohsts_remove_orphaned(self):
# pylint: disable=protected-access
@ -162,11 +162,11 @@ class AutoHSTSTest(util.ApacheTest):
self.config._autohsts_save_state()
self.config.update_autohsts(mock.MagicMock())
self.assertNotIn("orphan_id", self.config._autohsts)
assert "orphan_id" not in self.config._autohsts
# Make sure it's removed from the pluginstorage file as well
self.config._autohsts = None
self.config._autohsts_fetch_state()
self.assertFalse(self.config._autohsts)
assert not self.config._autohsts
def test_autohsts_make_permanent_vhost_not_found(self):
# pylint: disable=protected-access
@ -175,9 +175,9 @@ class AutoHSTSTest(util.ApacheTest):
self.config._autohsts_save_state()
with mock.patch("certbot_apache._internal.configurator.logger.error") as mock_log:
self.config.deploy_autohsts(mock.MagicMock())
self.assertIs(mock_log.called, True)
self.assertIn("VirtualHost with id orphan_id was not", mock_log.call_args[0][0])
assert mock_log.called is True
assert "VirtualHost with id orphan_id was not" in mock_log.call_args[0][0]
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,12 +1,15 @@
"""Test for certbot_apache._internal.configurator for Centos overrides"""
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
from certbot_apache._internal import override_centos
from certbot_apache._internal import obj
from certbot_apache._internal import override_centos
import util
@ -48,7 +51,7 @@ class FedoraRestartTest(util.ApacheTest):
self.temp_dir, "centos7_apache/apache")
def _run_fedora_test(self):
self.assertIsInstance(self.config, override_centos.CentOSConfigurator)
assert isinstance(self.config, override_centos.CentOSConfigurator)
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ["fedora", "28"]
self.config.config_test()
@ -59,8 +62,8 @@ class FedoraRestartTest(util.ApacheTest):
mock_test.side_effect = errors.MisconfigurationError
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ["not_fedora"]
self.assertRaises(errors.MisconfigurationError,
self.config.config_test)
with pytest.raises(errors.MisconfigurationError):
self.config.config_test()
def test_fedora_restart_error(self):
c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
@ -69,8 +72,8 @@ class FedoraRestartTest(util.ApacheTest):
mock_test.side_effect = [errors.MisconfigurationError, '']
with mock.patch("certbot.util.run_script") as mock_run:
mock_run.side_effect = errors.SubprocessError
self.assertRaises(errors.MisconfigurationError,
self._run_fedora_test)
with pytest.raises(errors.MisconfigurationError):
self._run_fedora_test()
def test_fedora_restart(self):
c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
@ -79,9 +82,9 @@ class FedoraRestartTest(util.ApacheTest):
# First call raises error, second doesn't
mock_test.side_effect = [errors.MisconfigurationError, '']
self._run_fedora_test()
self.assertEqual(mock_test.call_count, 2)
self.assertEqual(mock_run.call_args[0][0],
['systemctl', 'restart', 'httpd'])
assert mock_test.call_count == 2
assert mock_run.call_args[0][0] == \
['systemctl', 'restart', 'httpd']
class UseCorrectApacheExecutableTest(util.ApacheTest):
@ -102,15 +105,15 @@ class UseCorrectApacheExecutableTest(util.ApacheTest):
config = util.get_apache_configurator(
self.config_path, self.vhost_path, self.config_dir, self.work_dir,
os_info="centos")
self.assertEqual(config.options.ctl, "apachectl")
self.assertEqual(config.options.bin, "httpd")
self.assertEqual(config.options.version_cmd, ["apachectl", "-v"])
self.assertEqual(config.options.restart_cmd, ["apachectl", "graceful"])
self.assertEqual(config.options.restart_cmd_alt, ["apachectl", "restart"])
self.assertEqual(config.options.conftest_cmd, ["apachectl", "configtest"])
self.assertEqual(config.options.get_defines_cmd, ["apachectl", "-t", "-D", "DUMP_RUN_CFG"])
self.assertEqual(config.options.get_includes_cmd, ["apachectl", "-t", "-D", "DUMP_INCLUDES"])
self.assertEqual(config.options.get_modules_cmd, ["apachectl", "-t", "-D", "DUMP_MODULES"])
assert config.options.ctl == "apachectl"
assert config.options.bin == "httpd"
assert config.options.version_cmd == ["apachectl", "-v"]
assert config.options.restart_cmd == ["apachectl", "graceful"]
assert config.options.restart_cmd_alt == ["apachectl", "restart"]
assert config.options.conftest_cmd == ["apachectl", "configtest"]
assert config.options.get_defines_cmd == ["apachectl", "-t", "-D", "DUMP_RUN_CFG"]
assert config.options.get_includes_cmd == ["apachectl", "-t", "-D", "DUMP_INCLUDES"]
assert config.options.get_modules_cmd == ["apachectl", "-t", "-D", "DUMP_MODULES"]
@mock.patch("certbot.util.get_os_info")
def test_new_rhel_derived(self, mock_get_os_info):
@ -119,22 +122,20 @@ class UseCorrectApacheExecutableTest(util.ApacheTest):
config = util.get_apache_configurator(
self.config_path, self.vhost_path, self.config_dir, self.work_dir,
os_info=os_info[0])
self.assertEqual(config.options.ctl, "apachectl")
self.assertEqual(config.options.bin, "httpd")
self.assertEqual(config.options.version_cmd, ["httpd", "-v"])
self.assertEqual(config.options.restart_cmd, ["apachectl", "graceful"])
self.assertEqual(config.options.restart_cmd_alt, ["apachectl", "restart"])
self.assertEqual(config.options.conftest_cmd, ["apachectl", "configtest"])
self.assertEqual(config.options.get_defines_cmd, ["httpd", "-t", "-D", "DUMP_RUN_CFG"])
self.assertEqual(config.options.get_includes_cmd, ["httpd", "-t", "-D", "DUMP_INCLUDES"])
self.assertEqual(config.options.get_modules_cmd, ["httpd", "-t", "-D", "DUMP_MODULES"])
assert config.options.ctl == "apachectl"
assert config.options.bin == "httpd"
assert config.options.version_cmd == ["httpd", "-v"]
assert config.options.restart_cmd == ["apachectl", "graceful"]
assert config.options.restart_cmd_alt == ["apachectl", "restart"]
assert config.options.conftest_cmd == ["apachectl", "configtest"]
assert config.options.get_defines_cmd == ["httpd", "-t", "-D", "DUMP_RUN_CFG"]
assert config.options.get_includes_cmd == ["httpd", "-t", "-D", "DUMP_INCLUDES"]
assert config.options.get_modules_cmd == ["httpd", "-t", "-D", "DUMP_MODULES"]
class MultipleVhostsTestCentOS(util.ApacheTest):
"""Multiple vhost tests for CentOS / RHEL family of distros"""
_multiprocess_can_split_ = True
@mock.patch("certbot.util.get_os_info")
def setUp(self, mock_get_os_info): # pylint: disable=arguments-differ
test_dir = "centos7_apache/apache"
@ -151,7 +152,7 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
self.temp_dir, "centos7_apache/apache")
def test_get_parser(self):
self.assertIsInstance(self.config.parser, override_centos.CentOSParser)
assert isinstance(self.config.parser, override_centos.CentOSParser)
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_opportunistic_httpd_runtime_parsing(self, mock_get):
@ -181,16 +182,16 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
mock_osi.return_value = ("centos", "9")
self.config.parser.update_runtime_variables()
self.assertEqual(mock_get.call_count, 3)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertEqual(len(self.config.parser.variables), 2)
self.assertIn("TEST2", self.config.parser.variables)
self.assertIn("mod_another.c", self.config.parser.modules)
assert mock_get.call_count == 3
assert len(self.config.parser.modules) == 4
assert len(self.config.parser.variables) == 2
assert "TEST2" in self.config.parser.variables
assert "mod_another.c" in self.config.parser.modules
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 2)
assert len(vhs) == 2
found = 0
for vhost in vhs:
@ -200,7 +201,7 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
break
else:
raise Exception("Missed: %s" % vhost) # pragma: no cover
self.assertEqual(found, 2)
assert found == 2
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_get_sysconfig_vars(self, mock_cfg):
@ -216,26 +217,27 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
mock_osi.return_value = ("centos", "9")
self.config.parser.update_runtime_variables()
self.assertIn("mock_define", self.config.parser.variables)
self.assertIn("mock_define_too", self.config.parser.variables)
self.assertIn("mock_value", self.config.parser.variables)
self.assertEqual("TRUE", self.config.parser.variables["mock_value"])
self.assertIn("MOCK_NOSEP", self.config.parser.variables)
self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"])
assert "mock_define" in self.config.parser.variables
assert "mock_define_too" in self.config.parser.variables
assert "mock_value" in self.config.parser.variables
assert "TRUE" == self.config.parser.variables["mock_value"]
assert "MOCK_NOSEP" in self.config.parser.variables
assert "NOSEP_VAL" == self.config.parser.variables["NOSEP_TWO"]
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_works(self, mock_run_script):
mock_run_script.side_effect = [None, errors.SubprocessError, None]
self.config.restart()
self.assertEqual(mock_run_script.call_count, 3)
assert mock_run_script.call_count == 3
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_errors(self, mock_run_script):
mock_run_script.side_effect = [None,
errors.SubprocessError,
errors.SubprocessError]
self.assertRaises(errors.MisconfigurationError, self.config.restart)
with pytest.raises(errors.MisconfigurationError):
self.config.restart()
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Tests for certbot_apache._internal.parser."""
import shutil
import sys
import unittest
import pytest
from certbot import errors
from certbot.compat import os
import util
@ -38,51 +41,49 @@ class ComplexParserTest(util.ParserTest):
"""Note: This may also fail do to Include conf-enabled/ syntax."""
matches = self.parser.find_dir("TestArgsDirective")
self.assertEqual(len(self.parser.filter_args_num(matches, 1)), 3)
self.assertEqual(len(self.parser.filter_args_num(matches, 2)), 2)
self.assertEqual(len(self.parser.filter_args_num(matches, 3)), 1)
assert len(self.parser.filter_args_num(matches, 1)) == 3
assert len(self.parser.filter_args_num(matches, 2)) == 2
assert len(self.parser.filter_args_num(matches, 3)) == 1
def test_basic_variable_parsing(self):
matches = self.parser.find_dir("TestVariablePort")
self.assertEqual(len(matches), 1)
self.assertEqual(self.parser.get_arg(matches[0]), "1234")
assert len(matches) == 1
assert self.parser.get_arg(matches[0]) == "1234"
def test_basic_variable_parsing_quotes(self):
matches = self.parser.find_dir("TestVariablePortStr")
self.assertEqual(len(matches), 1)
self.assertEqual(self.parser.get_arg(matches[0]), "1234")
assert len(matches) == 1
assert self.parser.get_arg(matches[0]) == "1234"
def test_invalid_variable_parsing(self):
del self.parser.variables["tls_port"]
matches = self.parser.find_dir("TestVariablePort")
self.assertRaises(
errors.PluginError, self.parser.get_arg, matches[0])
with pytest.raises(errors.PluginError):
self.parser.get_arg(matches[0])
def test_basic_ifdefine(self):
self.assertEqual(len(self.parser.find_dir("VAR_DIRECTIVE")), 2)
self.assertEqual(len(self.parser.find_dir("INVALID_VAR_DIRECTIVE")), 0)
assert len(self.parser.find_dir("VAR_DIRECTIVE")) == 2
assert len(self.parser.find_dir("INVALID_VAR_DIRECTIVE")) == 0
def test_basic_ifmodule(self):
self.assertEqual(len(self.parser.find_dir("MOD_DIRECTIVE")), 2)
self.assertEqual(
len(self.parser.find_dir("INVALID_MOD_DIRECTIVE")), 0)
assert len(self.parser.find_dir("MOD_DIRECTIVE")) == 2
assert len(self.parser.find_dir("INVALID_MOD_DIRECTIVE")) == 0
def test_nested(self):
self.assertEqual(len(self.parser.find_dir("NESTED_DIRECTIVE")), 3)
self.assertEqual(
len(self.parser.find_dir("INVALID_NESTED_DIRECTIVE")), 0)
assert len(self.parser.find_dir("NESTED_DIRECTIVE")) == 3
assert len(self.parser.find_dir("INVALID_NESTED_DIRECTIVE")) == 0
def test_load_modules(self):
"""If only first is found, there is bad variable parsing."""
self.assertIn("status_module", self.parser.modules)
self.assertIn("mod_status.c", self.parser.modules)
assert "status_module" in self.parser.modules
assert "mod_status.c" in self.parser.modules
# This is in an IfDefine
self.assertIn("ssl_module", self.parser.modules)
self.assertIn("mod_ssl.c", self.parser.modules)
assert "ssl_module" in self.parser.modules
assert "mod_ssl.c" in self.parser.modules
def verify_fnmatch(self, arg, hit=True):
"""Test if Include was correctly parsed."""
@ -90,9 +91,9 @@ class ComplexParserTest(util.ParserTest):
self.parser.add_dir(parser.get_aug_path(self.parser.loc["default"]),
"Include", [arg])
if hit:
self.assertTrue(self.parser.find_dir("FNMATCH_DIRECTIVE"))
assert self.parser.find_dir("FNMATCH_DIRECTIVE")
else:
self.assertFalse(self.parser.find_dir("FNMATCH_DIRECTIVE"))
assert not self.parser.find_dir("FNMATCH_DIRECTIVE")
# NOTE: Only run one test per function otherwise you will have
# inf recursion
@ -124,4 +125,4 @@ class ComplexParserTest(util.ParserTest):
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,8 +1,11 @@
"""Test for certbot_apache._internal.configurator implementations of reverter"""
import shutil
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
import util
@ -26,51 +29,54 @@ class ConfiguratorReverterTest(util.ApacheTest):
def test_bad_save_checkpoint(self):
self.config.reverter.add_to_checkpoint = mock.Mock(side_effect=errors.ReverterError)
self.config.parser.add_dir(self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.assertRaises(errors.PluginError, self.config.save)
with pytest.raises(errors.PluginError):
self.config.save()
def test_bad_save_finalize_checkpoint(self):
self.config.reverter.finalize_checkpoint = mock.Mock(side_effect=errors.ReverterError)
self.config.parser.add_dir(self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.assertRaises(errors.PluginError, self.config.save, "Title")
with pytest.raises(errors.PluginError):
self.config.save("Title")
def test_finalize_save(self):
mock_finalize = mock.Mock()
self.config.reverter = mock_finalize
self.config.save("Example Title")
self.assertTrue(mock_finalize.is_called)
assert mock_finalize.is_called
def test_revert_challenge_config(self):
mock_load = mock.Mock()
self.config.parser.aug.load = mock_load
self.config.revert_challenge_config()
self.assertEqual(mock_load.call_count, 1)
assert mock_load.call_count == 1
def test_revert_challenge_config_error(self):
self.config.reverter.revert_temporary_config = mock.Mock(
side_effect=errors.ReverterError)
self.assertRaises(
errors.PluginError, self.config.revert_challenge_config)
with pytest.raises(errors.PluginError):
self.config.revert_challenge_config()
def test_rollback_checkpoints(self):
mock_load = mock.Mock()
self.config.parser.aug.load = mock_load
self.config.rollback_checkpoints()
self.assertEqual(mock_load.call_count, 1)
assert mock_load.call_count == 1
def test_rollback_error(self):
self.config.reverter.rollback_checkpoints = mock.Mock(side_effect=errors.ReverterError)
self.assertRaises(errors.PluginError, self.config.rollback_checkpoints)
with pytest.raises(errors.PluginError):
self.config.rollback_checkpoints()
def test_recovery_routine_reload(self):
mock_load = mock.Mock()
self.config.parser.aug.load = mock_load
self.config.recovery_routine()
self.assertEqual(mock_load.call_count, 1)
assert mock_load.call_count == 1
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,11 @@
"""Test for certbot_apache._internal.configurator for Debian overrides"""
import shutil
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import os
from certbot.tests import util as certbot_util
@ -14,8 +17,6 @@ import util
class MultipleVhostsTestDebian(util.ApacheTest):
"""Multiple vhost tests for Debian family of distros"""
_multiprocess_can_split_ = True
def setUp(self): # pylint: disable=arguments-differ
super().setUp()
self.config = util.get_apache_configurator(
@ -41,7 +42,8 @@ class MultipleVhostsTestDebian(util.ApacheTest):
def test_enable_mod_unsupported_dirs(self):
shutil.rmtree(os.path.join(self.config.parser.root, "mods-enabled"))
self.assertRaises(errors.NotSupportedError, self.config.enable_mod, "ssl")
with pytest.raises(errors.NotSupportedError):
self.config.enable_mod("ssl")
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -53,29 +55,29 @@ class MultipleVhostsTestDebian(util.ApacheTest):
mock_exe_exists.return_value = True
self.config.enable_mod("ssl")
self.assertIn("ssl_module", self.config.parser.modules)
self.assertIn("mod_ssl.c", self.config.parser.modules)
assert "ssl_module" in self.config.parser.modules
assert "mod_ssl.c" in self.config.parser.modules
self.assertIs(mock_run_script.called, True)
assert mock_run_script.called is True
def test_deploy_cert_enable_new_vhost(self):
# Create
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
self.config.parser.modules["ssl_module"] = None
self.config.parser.modules["mod_ssl.c"] = None
self.assertIs(ssl_vhost.enabled, False)
assert ssl_vhost.enabled is False
with certbot_util.patch_display_util():
self.config.deploy_cert(
"encryption-example.demo", "example/cert.pem", "example/key.pem",
"example/cert_chain.pem", "example/fullchain.pem")
self.assertIs(ssl_vhost.enabled, True)
assert ssl_vhost.enabled is True
# Make sure that we don't error out if symlink already exists
ssl_vhost.enabled = False
self.assertIs(ssl_vhost.enabled, False)
assert ssl_vhost.enabled is False
self.config.deploy_cert(
"encryption-example.demo", "example/cert.pem", "example/key.pem",
"example/cert_chain.pem", "example/fullchain.pem")
self.assertIs(ssl_vhost.enabled, True)
assert ssl_vhost.enabled is True
def test_enable_site_failure(self):
self.config.parser.root = "/tmp/nonexistent"
@ -83,10 +85,8 @@ class MultipleVhostsTestDebian(util.ApacheTest):
mock_dir.return_value = True
with mock.patch("certbot.compat.os.path.islink") as mock_link:
mock_link.return_value = False
self.assertRaises(
errors.NotSupportedError,
self.config.enable_site,
obj.VirtualHost("asdf", "afsaf", set(), False, False))
with pytest.raises(errors.NotSupportedError):
self.config.enable_site(obj.VirtualHost("asdf", "afsaf", set(), False, False))
def test_deploy_cert_newssl(self):
self.config = util.get_apache_configurator(
@ -105,8 +105,8 @@ class MultipleVhostsTestDebian(util.ApacheTest):
self.config.save()
# Verify ssl_module was enabled.
self.assertIs(self.vh_truth[1].enabled, True)
self.assertIn("ssl_module", self.config.parser.modules)
assert self.vh_truth[1].enabled is True
assert "ssl_module" in self.config.parser.modules
loc_cert = self.config.parser.find_dir(
"sslcertificatefile", "example/fullchain.pem",
@ -115,15 +115,13 @@ class MultipleVhostsTestDebian(util.ApacheTest):
"sslcertificateKeyfile", "example/key.pem", self.vh_truth[1].path)
# Verify one directive was found in the correct file
self.assertEqual(len(loc_cert), 1)
self.assertEqual(
apache_util.get_file_path(loc_cert[0]),
self.vh_truth[1].filep)
assert len(loc_cert) == 1
assert apache_util.get_file_path(loc_cert[0]) == \
self.vh_truth[1].filep
self.assertEqual(len(loc_key), 1)
self.assertEqual(
apache_util.get_file_path(loc_key[0]),
self.vh_truth[1].filep)
assert len(loc_key) == 1
assert apache_util.get_file_path(loc_key[0]) == \
self.vh_truth[1].filep
def test_deploy_cert_newssl_no_fullchain(self):
self.config = util.get_apache_configurator(
@ -135,10 +133,10 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# Get the default 443 vhost
self.config.assoc["random.demo"] = self.vh_truth[1]
self.assertRaises(errors.PluginError,
lambda: self.config.deploy_cert(
with pytest.raises(errors.PluginError):
self.config.deploy_cert(
"random.demo", "example/cert.pem",
"example/key.pem"))
"example/key.pem")
def test_deploy_cert_old_apache_no_chain(self):
self.config = util.get_apache_configurator(
@ -150,10 +148,10 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# Get the default 443 vhost
self.config.assoc["random.demo"] = self.vh_truth[1]
self.assertRaises(errors.PluginError,
lambda: self.config.deploy_cert(
with pytest.raises(errors.PluginError):
self.config.deploy_cert(
"random.demo", "example/cert.pem",
"example/key.pem"))
"example/key.pem")
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -165,7 +163,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# This will create an ssl vhost for certbot.demo
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "staple-ocsp")
self.assertIn("socache_shmcb_module", self.config.parser.modules)
assert "socache_shmcb_module" in self.config.parser.modules
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -178,7 +176,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "ensure-http-header",
"Strict-Transport-Security")
self.assertIn("headers_module", self.config.parser.modules)
assert "headers_module" in self.config.parser.modules
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -189,10 +187,10 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# This will create an ssl vhost for certbot.demo
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "redirect")
self.assertIn("rewrite_module", self.config.parser.modules)
assert "rewrite_module" in self.config.parser.modules
def test_enable_site_already_enabled(self):
self.assertIs(self.vh_truth[1].enabled, True)
assert self.vh_truth[1].enabled is True
self.config.enable_site(self.vh_truth[1])
def test_enable_site_call_parent(self):
@ -202,13 +200,13 @@ class MultipleVhostsTestDebian(util.ApacheTest):
vh = self.vh_truth[0]
vh.enabled = False
self.config.enable_site(vh)
self.assertIs(e_s.called, True)
assert e_s.called is True
@mock.patch("certbot.util.exe_exists")
def test_enable_mod_no_disable(self, mock_exe_exists):
mock_exe_exists.return_value = False
self.assertRaises(
errors.MisconfigurationError, self.config.enable_mod, "ssl")
with pytest.raises(errors.MisconfigurationError):
self.config.enable_mod("ssl")
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Test certbot_apache._internal.display_ops."""
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.display import util as display_util
from certbot.tests import util as certbot_util
@ -19,7 +22,7 @@ class SelectVhostMultiTest(unittest.TestCase):
self.base_dir, "debian_apache_2_4/multiple_vhosts")
def test_select_no_input(self):
self.assertEqual(len(select_vhost_multiple([])), 0)
assert len(select_vhost_multiple([])) == 0
@certbot_util.patch_display_util()
def test_select_correct(self, mock_util):
@ -29,15 +32,15 @@ class SelectVhostMultiTest(unittest.TestCase):
vhs = select_vhost_multiple([self.vhosts[3],
self.vhosts[2],
self.vhosts[1]])
self.assertIn(self.vhosts[2], vhs)
self.assertIn(self.vhosts[3], vhs)
self.assertNotIn(self.vhosts[1], vhs)
assert self.vhosts[2] in vhs
assert self.vhosts[3] in vhs
assert self.vhosts[1] not in vhs
@certbot_util.patch_display_util()
def test_select_cancel(self, mock_util):
mock_util().checklist.return_value = (display_util.CANCEL, "whatever")
vhs = select_vhost_multiple([self.vhosts[2], self.vhosts[3]])
self.assertEqual(vhs, [])
assert vhs == []
class SelectVhostTest(unittest.TestCase):
@ -56,7 +59,7 @@ class SelectVhostTest(unittest.TestCase):
@certbot_util.patch_display_util()
def test_successful_choice(self, mock_util):
mock_util().menu.return_value = (display_util.OK, 3)
self.assertEqual(self.vhosts[3], self._call(self.vhosts))
assert self.vhosts[3] == self._call(self.vhosts)
@certbot_util.patch_display_util()
def test_noninteractive(self, mock_util):
@ -64,7 +67,7 @@ class SelectVhostTest(unittest.TestCase):
try:
self._call(self.vhosts)
except errors.MissingCommandlineFlag as e:
self.assertIn("vhost ambiguity", str(e))
assert "vhost ambiguity" in str(e)
@certbot_util.patch_display_util()
def test_more_info_cancel(self, mock_util):
@ -72,10 +75,10 @@ class SelectVhostTest(unittest.TestCase):
(display_util.CANCEL, -1),
]
self.assertIsNone(self._call(self.vhosts))
assert self._call(self.vhosts) is None
def test_no_vhosts(self):
self.assertIsNone(self._call([]))
assert self._call([]) is None
@mock.patch("certbot_apache._internal.display_ops.display_util")
@mock.patch("certbot_apache._internal.display_ops.logger")
@ -84,7 +87,7 @@ class SelectVhostTest(unittest.TestCase):
mock_display_util.menu.return_value = (display_util.OK, 0)
self._call(self.vhosts)
self.assertEqual(mock_logger.debug.call_count, 1)
assert mock_logger.debug.call_count == 1
@certbot_util.patch_display_util()
def test_multiple_names(self, mock_util):
@ -96,8 +99,8 @@ class SelectVhostTest(unittest.TestCase):
False, False,
"wildcard.com", {"*.wildcard.com"}))
self.assertEqual(self.vhosts[5], self._call(self.vhosts))
assert self.vhosts[5] == self._call(self.vhosts)
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -5,6 +5,7 @@ from unittest import mock
from certbot_apache._internal import assertions
from certbot_apache._internal import augeasparser
from certbot_apache._internal import dualparser
import pytest
class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-methods
@ -49,20 +50,20 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
primary=self.block.secondary,
secondary=self.block.primary)
# Switched around
self.assertEqual(cnode.primary, self.comment.secondary)
self.assertEqual(cnode.secondary, self.comment.primary)
self.assertEqual(dnode.primary, self.directive.secondary)
self.assertEqual(dnode.secondary, self.directive.primary)
self.assertEqual(bnode.primary, self.block.secondary)
self.assertEqual(bnode.secondary, self.block.primary)
assert cnode.primary == self.comment.secondary
assert cnode.secondary == self.comment.primary
assert dnode.primary == self.directive.secondary
assert dnode.secondary == self.directive.primary
assert bnode.primary == self.block.secondary
assert bnode.secondary == self.block.primary
def test_set_params(self):
params = ("first", "second")
self.directive.primary.set_parameters = mock.Mock()
self.directive.secondary.set_parameters = mock.Mock()
self.directive.set_parameters(params)
self.assertIs(self.directive.primary.set_parameters.called, True)
self.assertIs(self.directive.secondary.set_parameters.called, True)
assert self.directive.primary.set_parameters.called is True
assert self.directive.secondary.set_parameters.called is True
def test_set_parameters(self):
pparams = mock.MagicMock()
@ -72,8 +73,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.directive.primary.set_parameters = pparams
self.directive.secondary.set_parameters = sparams
self.directive.set_parameters(("param", "seq"))
self.assertIs(pparams.called, True)
self.assertIs(sparams.called, True)
assert pparams.called is True
assert sparams.called is True
def test_delete_child(self):
pdel = mock.MagicMock()
@ -81,8 +82,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.delete_child = pdel
self.block.secondary.delete_child = sdel
self.block.delete_child(self.comment)
self.assertIs(pdel.called, True)
self.assertIs(sdel.called, True)
assert pdel.called is True
assert sdel.called is True
def test_unsaved_files(self):
puns = mock.MagicMock()
@ -92,13 +93,13 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.unsaved_files = puns
self.block.secondary.unsaved_files = suns
self.block.unsaved_files()
self.assertIs(puns.called, True)
self.assertIs(suns.called, True)
assert puns.called is True
assert suns.called is True
def test_getattr_equality(self):
self.directive.primary.variableexception = "value"
self.directive.secondary.variableexception = "not_value"
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
_ = self.directive.variableexception
self.directive.primary.variable = "value"
@ -117,7 +118,7 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.comment.primary.dirty = False
self.comment.secondary.dirty = True
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
assertions.assertEqual(self.comment.primary, self.comment.secondary)
def test_parsernode_filepath_assert(self):
@ -127,7 +128,7 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.comment.primary.filepath = "first"
self.comment.secondary.filepath = "second"
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
assertions.assertEqual(self.comment.primary, self.comment.secondary)
def test_add_child_block(self):
@ -136,8 +137,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_block = mock_first
self.block.secondary.add_child_block = mock_second
self.block.add_child_block("Block")
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
assert mock_first.called is True
assert mock_second.called is True
def test_add_child_directive(self):
mock_first = mock.MagicMock(return_value=self.directive.primary)
@ -145,8 +146,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_directive = mock_first
self.block.secondary.add_child_directive = mock_second
self.block.add_child_directive("Directive")
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
assert mock_first.called is True
assert mock_second.called is True
def test_add_child_comment(self):
mock_first = mock.MagicMock(return_value=self.comment.primary)
@ -154,8 +155,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_comment = mock_first
self.block.secondary.add_child_comment = mock_second
self.block.add_child_comment("Comment")
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
assert mock_first.called is True
assert mock_second.called is True
def test_find_comments(self):
pri_comments = [augeasparser.AugeasCommentNode(comment="some comment",
@ -179,9 +180,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
# Check that every comment response is represented in the list of
# DualParserNode instances.
for p in p_dcoms:
self.assertIn(p, p_coms)
assert p in p_coms
for s in s_dcoms:
self.assertIn(s, s_coms)
assert s in s_coms
def test_find_blocks_first_passing(self):
youshallnotpass = [augeasparser.AugeasBlockNode(name="notpassing",
@ -203,8 +204,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(block.primary, block.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassDirective(block.primary), True)
self.assertIs(assertions.isPassDirective(block.secondary), False)
assert assertions.isPassDirective(block.primary) is True
assert assertions.isPassDirective(block.secondary) is False
def test_find_blocks_second_passing(self):
youshallnotpass = [augeasparser.AugeasBlockNode(name="notpassing",
@ -226,8 +227,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(block.primary, block.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassDirective(block.primary), False)
self.assertIs(assertions.isPassDirective(block.secondary), True)
assert assertions.isPassDirective(block.primary) is False
assert assertions.isPassDirective(block.secondary) is True
def test_find_dirs_first_passing(self):
notpassing = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -249,8 +250,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(directive.primary, directive.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassDirective(directive.primary), True)
self.assertIs(assertions.isPassDirective(directive.secondary), False)
assert assertions.isPassDirective(directive.primary) is True
assert assertions.isPassDirective(directive.secondary) is False
def test_find_dirs_second_passing(self):
notpassing = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -272,8 +273,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(directive.primary, directive.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassDirective(directive.primary), False)
self.assertIs(assertions.isPassDirective(directive.secondary), True)
assert assertions.isPassDirective(directive.primary) is False
assert assertions.isPassDirective(directive.secondary) is True
def test_find_coms_first_passing(self):
notpassing = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -295,8 +296,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(comment.primary, comment.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassComment(comment.primary), True)
self.assertIs(assertions.isPassComment(comment.secondary), False)
assert assertions.isPassComment(comment.primary) is True
assert assertions.isPassComment(comment.secondary) is False
def test_find_coms_second_passing(self):
notpassing = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -318,8 +319,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(comment.primary, comment.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertIs(assertions.isPassComment(comment.primary), False)
self.assertIs(assertions.isPassComment(comment.secondary), True)
assert assertions.isPassComment(comment.primary) is False
assert assertions.isPassComment(comment.secondary) is True
def test_find_blocks_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasBlockNode(name="notpassing",
@ -338,8 +339,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
blocks = self.block.find_blocks("anything")
for block in blocks:
with self.subTest(block=block):
self.assertEqual(block.primary, block.secondary)
self.assertIsNot(block.primary, block.secondary)
assert block.primary == block.secondary
assert block.primary is not block.secondary
def test_find_dirs_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -358,8 +359,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
directives = self.block.find_directives("anything")
for directive in directives:
with self.subTest(directive=directive):
self.assertEqual(directive.primary, directive.secondary)
self.assertIsNot(directive.primary, directive.secondary)
assert directive.primary == directive.secondary
assert directive.primary is not directive.secondary
def test_find_comments_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -378,8 +379,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
comments = self.block.find_comments("anything")
for comment in comments:
with self.subTest(comment=comment):
self.assertEqual(comment.primary, comment.secondary)
self.assertIsNot(comment.primary, comment.secondary)
assert comment.primary == comment.secondary
assert comment.primary is not comment.secondary
def test_find_blocks_no_pass_notequal(self):
notpassing1 = [augeasparser.AugeasBlockNode(name="notpassing",
@ -395,7 +396,7 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.find_blocks = find_blocks_primary
self.block.secondary.find_blocks = find_blocks_secondary
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
_ = self.block.find_blocks("anything")
def test_parsernode_notequal(self):
@ -411,9 +412,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
ancestor=self.block,
filepath="/path/to/whatever",
metadata=self.metadata)
self.assertNotEqual(self.block, ne_block)
self.assertNotEqual(self.directive, ne_directive)
self.assertNotEqual(self.comment, ne_comment)
assert self.block != ne_block
assert self.directive != ne_directive
assert self.comment != ne_comment
def test_parsed_paths(self):
mock_p = mock.MagicMock(return_value=['/path/file.conf',
@ -423,15 +424,15 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.parsed_paths = mock_p
self.block.secondary.parsed_paths = mock_s
self.block.parsed_paths()
self.assertIs(mock_p.called, True)
self.assertIs(mock_s.called, True)
assert mock_p.called is True
assert mock_s.called is True
def test_parsed_paths_error(self):
mock_p = mock.MagicMock(return_value=['/path/file.conf'])
mock_s = mock.MagicMock(return_value=['/path/*.conf', '/another/path'])
self.block.primary.parsed_paths = mock_p
self.block.secondary.parsed_paths = mock_s
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
self.block.parsed_paths()
def test_find_ancestors(self):
@ -440,5 +441,5 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.find_ancestors = primarymock
self.block.secondary.find_ancestors = secondarymock
self.block.find_ancestors("anything")
self.assertIs(primarymock.called, True)
self.assertIs(secondarymock.called, True)
assert primarymock.called is True
assert secondarymock.called is True

View file

@ -1,46 +1,42 @@
"""Test for certbot_apache._internal.entrypoint for override class resolution"""
import unittest
import sys
from unittest import mock
import pytest
from certbot_apache._internal import configurator
from certbot_apache._internal import entrypoint
class EntryPointTest(unittest.TestCase):
"""Entrypoint tests"""
def test_get_configurator():
with mock.patch("certbot.util.get_os_info") as mock_info:
for distro in entrypoint.OVERRIDE_CLASSES:
return_value = (distro, "whatever")
if distro == 'fedora_old':
return_value = ('fedora', '28')
elif distro == 'fedora':
return_value = ('fedora', '29')
mock_info.return_value = return_value
assert entrypoint.get_configurator() == \
entrypoint.OVERRIDE_CLASSES[distro]
_multiprocess_can_split_ = True
def test_nonexistent_like():
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ("nonexistent", "irrelevant")
with mock.patch("certbot.util.get_systemd_os_like") as mock_like:
for like in entrypoint.OVERRIDE_CLASSES:
mock_like.return_value = [like]
assert entrypoint.get_configurator() == \
entrypoint.OVERRIDE_CLASSES[like]
def test_get_configurator(self):
with mock.patch("certbot.util.get_os_info") as mock_info:
for distro in entrypoint.OVERRIDE_CLASSES:
return_value = (distro, "whatever")
if distro == 'fedora_old':
return_value = ('fedora', '28')
elif distro == 'fedora':
return_value = ('fedora', '29')
mock_info.return_value = return_value
self.assertEqual(entrypoint.get_configurator(),
entrypoint.OVERRIDE_CLASSES[distro])
def test_nonexistent_like(self):
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ("nonexistent", "irrelevant")
with mock.patch("certbot.util.get_systemd_os_like") as mock_like:
for like in entrypoint.OVERRIDE_CLASSES:
mock_like.return_value = [like]
self.assertEqual(entrypoint.get_configurator(),
entrypoint.OVERRIDE_CLASSES[like])
def test_nonexistent_generic(self):
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ("nonexistent", "irrelevant")
with mock.patch("certbot.util.get_systemd_os_like") as mock_like:
mock_like.return_value = ["unknown"]
self.assertEqual(entrypoint.get_configurator(),
configurator.ApacheConfigurator)
def test_nonexistent_generic():
with mock.patch("certbot.util.get_os_info") as mock_info:
mock_info.return_value = ("nonexistent", "irrelevant")
with mock.patch("certbot.util.get_systemd_os_like") as mock_like:
mock_like.return_value = ["unknown"]
assert entrypoint.get_configurator() == \
configurator.ApacheConfigurator
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Test for certbot_apache._internal.configurator for Fedora 29+ overrides"""
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
@ -52,7 +55,7 @@ class FedoraRestartTest(util.ApacheTest):
self.temp_dir, "centos7_apache/apache")
def _run_fedora_test(self):
self.assertIsInstance(self.config, override_fedora.FedoraConfigurator)
assert isinstance(self.config, override_fedora.FedoraConfigurator)
self.config.config_test()
def test_fedora_restart_error(self):
@ -62,8 +65,8 @@ class FedoraRestartTest(util.ApacheTest):
mock_test.side_effect = [errors.MisconfigurationError, '']
with mock.patch("certbot.util.run_script") as mock_run:
mock_run.side_effect = errors.SubprocessError
self.assertRaises(errors.MisconfigurationError,
self._run_fedora_test)
with pytest.raises(errors.MisconfigurationError):
self._run_fedora_test()
def test_fedora_restart(self):
c_test = "certbot_apache._internal.configurator.ApacheConfigurator.config_test"
@ -72,16 +75,14 @@ class FedoraRestartTest(util.ApacheTest):
# First call raises error, second doesn't
mock_test.side_effect = [errors.MisconfigurationError, '']
self._run_fedora_test()
self.assertEqual(mock_test.call_count, 2)
self.assertEqual(mock_run.call_args[0][0],
['systemctl', 'restart', 'httpd'])
assert mock_test.call_count == 2
assert mock_run.call_args[0][0] == \
['systemctl', 'restart', 'httpd']
class MultipleVhostsTestFedora(util.ApacheTest):
"""Multiple vhost tests for CentOS / RHEL family of distros"""
_multiprocess_can_split_ = True
def setUp(self): # pylint: disable=arguments-differ
test_dir = "centos7_apache/apache"
config_root = "centos7_apache/apache/httpd"
@ -97,7 +98,7 @@ class MultipleVhostsTestFedora(util.ApacheTest):
self.temp_dir, "centos7_apache/apache")
def test_get_parser(self):
self.assertIsInstance(self.config.parser, override_fedora.FedoraParser)
assert isinstance(self.config.parser, override_fedora.FedoraParser)
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_opportunistic_httpd_runtime_parsing(self, mock_get):
@ -127,22 +128,23 @@ class MultipleVhostsTestFedora(util.ApacheTest):
mock_osi.return_value = ("fedora", "29")
self.config.parser.update_runtime_variables()
self.assertEqual(mock_get.call_count, 3)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertEqual(len(self.config.parser.variables), 2)
self.assertIn("TEST2", self.config.parser.variables)
self.assertIn("mod_another.c", self.config.parser.modules)
assert mock_get.call_count == 3
assert len(self.config.parser.modules) == 4
assert len(self.config.parser.variables) == 2
assert "TEST2" in self.config.parser.variables
assert "mod_another.c" in self.config.parser.modules
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_get_version(self, mock_run_script):
mock_run_script.return_value = ('', None)
self.assertRaises(errors.PluginError, self.config.get_version)
self.assertEqual(mock_run_script.call_args[0][0][0], 'httpd')
with pytest.raises(errors.PluginError):
self.config.get_version()
assert mock_run_script.call_args[0][0][0] == 'httpd'
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 2)
assert len(vhs) == 2
found = 0
for vhost in vhs:
@ -152,7 +154,7 @@ class MultipleVhostsTestFedora(util.ApacheTest):
break
else:
raise Exception("Missed: %s" % vhost) # pragma: no cover
self.assertEqual(found, 2)
assert found == 2
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_get_sysconfig_vars(self, mock_cfg):
@ -168,26 +170,27 @@ class MultipleVhostsTestFedora(util.ApacheTest):
mock_osi.return_value = ("fedora", "29")
self.config.parser.update_runtime_variables()
self.assertIn("mock_define", self.config.parser.variables)
self.assertIn("mock_define_too", self.config.parser.variables)
self.assertIn("mock_value", self.config.parser.variables)
self.assertEqual("TRUE", self.config.parser.variables["mock_value"])
self.assertIn("MOCK_NOSEP", self.config.parser.variables)
self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"])
assert "mock_define" in self.config.parser.variables
assert "mock_define_too" in self.config.parser.variables
assert "mock_value" in self.config.parser.variables
assert "TRUE" == self.config.parser.variables["mock_value"]
assert "MOCK_NOSEP" in self.config.parser.variables
assert "NOSEP_VAL" == self.config.parser.variables["NOSEP_TWO"]
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_works(self, mock_run_script):
mock_run_script.side_effect = [None, errors.SubprocessError, None]
self.config.restart()
self.assertEqual(mock_run_script.call_count, 3)
assert mock_run_script.call_count == 3
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_errors(self, mock_run_script):
mock_run_script.side_effect = [None,
errors.SubprocessError,
errors.SubprocessError]
self.assertRaises(errors.MisconfigurationError, self.config.restart)
with pytest.raises(errors.MisconfigurationError):
self.config.restart()
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Test for certbot_apache._internal.configurator for Gentoo overrides"""
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
@ -40,8 +43,6 @@ def get_vh_truth(temp_dir, config_name):
class MultipleVhostsTestGentoo(util.ApacheTest):
"""Multiple vhost tests for non-debian distro"""
_multiprocess_can_split_ = True
def setUp(self): # pylint: disable=arguments-differ
test_dir = "gentoo_apache/apache"
config_root = "gentoo_apache/apache/apache2"
@ -59,12 +60,12 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
self.temp_dir, "gentoo_apache/apache")
def test_get_parser(self):
self.assertIsInstance(self.config.parser, override_gentoo.GentooParser)
assert isinstance(self.config.parser, override_gentoo.GentooParser)
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 3)
assert len(vhs) == 3
found = 0
for vhost in vhs:
@ -74,7 +75,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
break
else:
raise Exception("Missed: %s" % vhost) # pragma: no cover
self.assertEqual(found, 3)
assert found == 3
def test_get_sysconfig_vars(self):
"""Make sure we read the Gentoo APACHE2_OPTS variable correctly"""
@ -86,7 +87,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
with mock.patch("certbot_apache._internal.override_gentoo.GentooParser.update_modules"):
self.config.parser.update_runtime_variables()
for define in defines:
self.assertIn(define, self.config.parser.variables)
assert define in self.config.parser.variables
@mock.patch("certbot_apache._internal.apache_util.parse_from_subprocess")
def test_no_binary_configdump(self, mock_subprocess):
@ -96,11 +97,11 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
with mock.patch("certbot_apache._internal.override_gentoo.GentooParser.update_modules"):
self.config.parser.update_runtime_variables()
self.config.parser.reset_modules()
self.assertIs(mock_subprocess.called, False)
assert mock_subprocess.called is False
self.config.parser.update_runtime_variables()
self.config.parser.reset_modules()
self.assertIs(mock_subprocess.called, True)
assert mock_subprocess.called is True
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_opportunistic_httpd_runtime_parsing(self, mock_get):
@ -122,15 +123,15 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
mock_osi.return_value = ("gentoo", "123")
self.config.parser.update_runtime_variables()
self.assertEqual(mock_get.call_count, 1)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertIn("mod_another.c", self.config.parser.modules)
assert mock_get.call_count == 1
assert len(self.config.parser.modules) == 4
assert "mod_another.c" in self.config.parser.modules
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_works(self, mock_run_script):
mock_run_script.side_effect = [None, errors.SubprocessError, None]
self.config.restart()
self.assertEqual(mock_run_script.call_count, 3)
assert mock_run_script.call_count == 3
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,9 +1,12 @@
"""Test for certbot_apache._internal.http_01."""
import unittest
import errno
import sys
from typing import List
import unittest
from unittest import mock
import pytest
from acme import challenges
from certbot import achallenges
from certbot import errors
@ -47,7 +50,7 @@ class ApacheHttp01Test(util.ApacheTest):
self.http = ApacheHttp01(self.config)
def test_empty_perform(self):
self.assertEqual(len(self.http.perform()), 0)
assert len(self.http.perform()) == 0
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.enable_mod")
def test_enable_modules_apache_2_4(self, mock_enmod):
@ -55,7 +58,7 @@ class ApacheHttp01Test(util.ApacheTest):
del self.config.parser.modules["mod_authz_host.c"]
enmod_calls = self.common_enable_modules_test(mock_enmod)
self.assertEqual(enmod_calls[0][0][0], "authz_core")
assert enmod_calls[0][0][0] == "authz_core"
def common_enable_modules_test(self, mock_enmod):
"""Tests enabling mod_rewrite and other modules."""
@ -64,7 +67,7 @@ class ApacheHttp01Test(util.ApacheTest):
self.http.prepare_http01_modules()
self.assertIs(mock_enmod.called, True)
assert mock_enmod.called is True
calls = mock_enmod.call_args_list
other_calls = []
for call in calls:
@ -72,7 +75,7 @@ class ApacheHttp01Test(util.ApacheTest):
other_calls.append(call)
# If these lists are equal, we never enabled mod_rewrite
self.assertNotEqual(calls, other_calls)
assert calls != other_calls
return other_calls
def test_same_vhost(self):
@ -103,7 +106,7 @@ class ApacheHttp01Test(util.ApacheTest):
def test_configure_multiple_vhosts(self):
vhosts = [v for v in self.config.vhosts if "duplicate.example.com" in v.get_names()]
self.assertEqual(len(vhosts), 2)
assert len(vhosts) == 2
achalls = [
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
@ -128,7 +131,8 @@ class ApacheHttp01Test(util.ApacheTest):
for achall in self.achalls:
self.http.add_chall(achall)
self.config.config.http01_port = 12345
self.assertRaises(errors.PluginError, self.http.perform)
with pytest.raises(errors.PluginError):
self.http.perform()
def test_perform_1_achall_apache_2_4(self):
self.combinations_perform_test(num_achalls=1, minor_version=4)
@ -152,7 +156,7 @@ class ApacheHttp01Test(util.ApacheTest):
matches = self.config.parser.find_dir(
"Include", vhosts[0].filep,
get_aug_path(self.config.parser.loc["default"]))
self.assertEqual(len(matches), 1)
assert len(matches) == 1
def combinations_perform_test(self, num_achalls, minor_version):
"""Test perform with the given achall count and Apache version."""
@ -164,16 +168,16 @@ class ApacheHttp01Test(util.ApacheTest):
def common_perform_test(self, achalls, vhosts):
"""Tests perform with the given achalls."""
challenge_dir = self.http.challenge_dir
self.assertIs(os.path.exists(challenge_dir), False)
assert os.path.exists(challenge_dir) is False
for achall in achalls:
self.http.add_chall(achall)
expected_response = [
achall.response(self.account_key) for achall in achalls]
self.assertEqual(self.http.perform(), expected_response)
assert self.http.perform() == expected_response
self.assertIs(os.path.isdir(self.http.challenge_dir), True)
self.assertIs(filesystem.has_min_permissions(self.http.challenge_dir, 0o755), True)
assert os.path.isdir(self.http.challenge_dir) is True
assert filesystem.has_min_permissions(self.http.challenge_dir, 0o755) is True
self._test_challenge_conf()
for achall in achalls:
@ -183,19 +187,20 @@ class ApacheHttp01Test(util.ApacheTest):
matches = self.config.parser.find_dir("Include",
self.http.challenge_conf_pre,
vhost.path)
self.assertEqual(len(matches), 1)
assert len(matches) == 1
matches = self.config.parser.find_dir("Include",
self.http.challenge_conf_post,
vhost.path)
self.assertEqual(len(matches), 1)
assert len(matches) == 1
self.assertIs(os.path.exists(challenge_dir), True)
assert os.path.exists(challenge_dir) is True
@mock.patch("certbot_apache._internal.http_01.filesystem.makedirs")
def test_failed_makedirs(self, mock_makedirs):
mock_makedirs.side_effect = OSError(errno.EACCES, "msg")
self.http.add_chall(self.achalls[0])
self.assertRaises(errors.PluginError, self.http.perform)
with pytest.raises(errors.PluginError):
self.http.perform()
def _test_challenge_conf(self):
with open(self.http.challenge_conf_pre) as f:
@ -204,20 +209,20 @@ class ApacheHttp01Test(util.ApacheTest):
with open(self.http.challenge_conf_post) as f:
post_conf_contents = f.read()
self.assertIn("RewriteEngine on", pre_conf_contents)
self.assertIn("RewriteRule", pre_conf_contents)
assert "RewriteEngine on" in pre_conf_contents
assert "RewriteRule" in pre_conf_contents
self.assertIn(self.http.challenge_dir, post_conf_contents)
self.assertIn("Require all granted", post_conf_contents)
assert self.http.challenge_dir in post_conf_contents
assert "Require all granted" in post_conf_contents
def _test_challenge_file(self, achall):
name = os.path.join(self.http.challenge_dir, achall.chall.encode("token"))
validation = achall.validation(self.account_key)
self.assertIs(filesystem.has_min_permissions(name, 0o644), True)
assert filesystem.has_min_permissions(name, 0o644) is True
with open(name, 'rb') as f:
self.assertEqual(f.read(), validation.encode())
assert f.read() == validation.encode()
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,6 +1,9 @@
"""Tests for certbot_apache._internal.obj."""
import sys
import unittest
import pytest
class VirtualHostTest(unittest.TestCase):
"""Test the VirtualHost class."""
@ -23,18 +26,18 @@ class VirtualHostTest(unittest.TestCase):
"fp", "vhp", {self.addr2}, False, False, "localhost")
def test_repr(self):
self.assertEqual(repr(self.addr2),
"certbot_apache._internal.obj.Addr(('127.0.0.1', '443'))")
assert repr(self.addr2) == \
"certbot_apache._internal.obj.Addr(('127.0.0.1', '443'))"
def test_eq(self):
self.assertEqual(self.vhost1b, self.vhost1)
self.assertNotEqual(self.vhost1, self.vhost2)
self.assertEqual(str(self.vhost1b), str(self.vhost1))
self.assertNotEqual(self.vhost1b, 1234)
assert self.vhost1b == self.vhost1
assert self.vhost1 != self.vhost2
assert str(self.vhost1b) == str(self.vhost1)
assert self.vhost1b != 1234
def test_ne(self):
self.assertNotEqual(self.vhost1, self.vhost2)
self.assertEqual(self.vhost1, self.vhost1b)
assert self.vhost1 != self.vhost2
assert self.vhost1 == self.vhost1b
def test_conflicts(self):
from certbot_apache._internal.obj import Addr
@ -44,14 +47,14 @@ class VirtualHostTest(unittest.TestCase):
"fp", "vhp",
{Addr.fromstring("*:443"), Addr.fromstring("1.2.3.4:443")},
False, False)
self.assertIs(complex_vh.conflicts([self.addr1]), True)
self.assertIs(complex_vh.conflicts([self.addr2]), True)
self.assertIs(complex_vh.conflicts([self.addr_default]), False)
assert complex_vh.conflicts([self.addr1]) is True
assert complex_vh.conflicts([self.addr2]) is True
assert complex_vh.conflicts([self.addr_default]) is False
self.assertIs(self.vhost1.conflicts([self.addr2]), True)
self.assertIs(self.vhost1.conflicts([self.addr_default]), False)
assert self.vhost1.conflicts([self.addr2]) is True
assert self.vhost1.conflicts([self.addr_default]) is False
self.assertIs(self.vhost2.conflicts([self.addr1, self.addr_default]), False)
assert self.vhost2.conflicts([self.addr1, self.addr_default]) is False
def test_same_server(self):
from certbot_apache._internal.obj import VirtualHost
@ -66,12 +69,12 @@ class VirtualHostTest(unittest.TestCase):
"fp", "vhp", {self.addr2, self.addr_default},
False, False, None)
self.assertIs(self.vhost1.same_server(self.vhost2), True)
self.assertIs(no_name1.same_server(no_name2), True)
assert self.vhost1.same_server(self.vhost2) is True
assert no_name1.same_server(no_name2) is True
self.assertIs(self.vhost1.same_server(no_name1), False)
self.assertIs(no_name1.same_server(no_name3), False)
self.assertIs(no_name1.same_server(no_name4), False)
assert self.vhost1.same_server(no_name1) is False
assert no_name1.same_server(no_name3) is False
assert no_name1.same_server(no_name4) is False
class AddrTest(unittest.TestCase):
@ -87,54 +90,51 @@ class AddrTest(unittest.TestCase):
self.addr_default = Addr.fromstring("_default_:443")
def test_wildcard(self):
self.assertIs(self.addr.is_wildcard(), False)
self.assertIs(self.addr1.is_wildcard(), True)
self.assertIs(self.addr2.is_wildcard(), True)
assert self.addr.is_wildcard() is False
assert self.addr1.is_wildcard() is True
assert self.addr2.is_wildcard() is True
def test_get_sni_addr(self):
from certbot_apache._internal.obj import Addr
self.assertEqual(
self.addr.get_sni_addr("443"), Addr.fromstring("*:443"))
self.assertEqual(
self.addr.get_sni_addr("225"), Addr.fromstring("*:225"))
self.assertEqual(
self.addr1.get_sni_addr("443"), Addr.fromstring("127.0.0.1"))
assert self.addr.get_sni_addr("443") == Addr.fromstring("*:443")
assert self.addr.get_sni_addr("225") == Addr.fromstring("*:225")
assert self.addr1.get_sni_addr("443") == Addr.fromstring("127.0.0.1")
def test_conflicts(self):
# Note: Defined IP is more important than defined port in match
self.assertIs(self.addr.conflicts(self.addr1), True)
self.assertIs(self.addr.conflicts(self.addr2), True)
self.assertIs(self.addr.conflicts(self.addr_defined), True)
self.assertIs(self.addr.conflicts(self.addr_default), False)
assert self.addr.conflicts(self.addr1) is True
assert self.addr.conflicts(self.addr2) is True
assert self.addr.conflicts(self.addr_defined) is True
assert self.addr.conflicts(self.addr_default) is False
self.assertIs(self.addr1.conflicts(self.addr), False)
self.assertIs(self.addr1.conflicts(self.addr_defined), True)
self.assertIs(self.addr1.conflicts(self.addr_default), False)
assert self.addr1.conflicts(self.addr) is False
assert self.addr1.conflicts(self.addr_defined) is True
assert self.addr1.conflicts(self.addr_default) is False
self.assertIs(self.addr_defined.conflicts(self.addr1), False)
self.assertIs(self.addr_defined.conflicts(self.addr2), False)
self.assertIs(self.addr_defined.conflicts(self.addr), False)
self.assertIs(self.addr_defined.conflicts(self.addr_default), False)
assert self.addr_defined.conflicts(self.addr1) is False
assert self.addr_defined.conflicts(self.addr2) is False
assert self.addr_defined.conflicts(self.addr) is False
assert self.addr_defined.conflicts(self.addr_default) is False
self.assertIs(self.addr_default.conflicts(self.addr), True)
self.assertIs(self.addr_default.conflicts(self.addr1), True)
self.assertIs(self.addr_default.conflicts(self.addr_defined), True)
assert self.addr_default.conflicts(self.addr) is True
assert self.addr_default.conflicts(self.addr1) is True
assert self.addr_default.conflicts(self.addr_defined) is True
# Self test
self.assertIs(self.addr.conflicts(self.addr), True)
self.assertIs(self.addr1.conflicts(self.addr1), True)
assert self.addr.conflicts(self.addr) is True
assert self.addr1.conflicts(self.addr1) is True
# This is a tricky one...
self.assertIs(self.addr1.conflicts(self.addr2), True)
assert self.addr1.conflicts(self.addr2) is True
def test_equal(self):
self.assertEqual(self.addr1, self.addr2)
self.assertNotEqual(self.addr, self.addr1)
self.assertNotEqual(self.addr, 123)
assert self.addr1 == self.addr2
assert self.addr != self.addr1
assert self.addr != 123
def test_not_equal(self):
self.assertEqual(self.addr1, self.addr2)
self.assertNotEqual(self.addr, self.addr1)
assert self.addr1 == self.addr2
assert self.addr != self.addr1
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,8 +1,11 @@
"""Tests for certbot_apache._internal.parser."""
import shutil
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import os
import util
@ -22,14 +25,15 @@ class BasicParserTest(util.ParserTest):
def test_bad_parse(self):
self.parser.parse_file(os.path.join(self.parser.root,
"conf-available", "bad_conf_file.conf"))
self.assertRaises(
errors.PluginError, self.parser.check_parsing_errors, "httpd.aug")
with pytest.raises(errors.PluginError):
self.parser.check_parsing_errors("httpd.aug")
def test_bad_save(self):
mock_save = mock.Mock()
mock_save.side_effect = IOError
self.parser.aug.save = mock_save
self.assertRaises(errors.PluginError, self.parser.unsaved_files)
with pytest.raises(errors.PluginError):
self.parser.unsaved_files()
@mock.patch("certbot_apache._internal.parser.logger")
def test_bad_save_errors(self, mock_logger):
@ -37,7 +41,8 @@ class BasicParserTest(util.ParserTest):
self.parser.aug.set("/augeas/load/Httpd/incl[last()]", nx_path)
self.parser.add_dir(f"/files{nx_path}", "AddDirective", "test")
self.assertRaises(IOError, self.parser.save, {})
with pytest.raises(IOError):
self.parser.save({})
mock_logger.error.assert_called_with(
'Unable to save files: %s.%s', '/non/existent/path.conf', mock.ANY)
mock_logger.debug.assert_called_with(
@ -48,16 +53,16 @@ class BasicParserTest(util.ParserTest):
mock_match = mock.Mock(return_value=["something"])
self.parser.aug.match = mock_match
# pylint: disable=protected-access
self.assertEqual(self.parser.check_aug_version(),
["something"])
assert self.parser.check_aug_version() == \
["something"]
self.parser.aug.match.side_effect = RuntimeError
self.assertIs(self.parser.check_aug_version(), False)
assert self.parser.check_aug_version() is False
def test_find_config_root_no_root(self):
# pylint: disable=protected-access
os.remove(self.parser.loc["root"])
self.assertRaises(
errors.NoInstallationError, self.parser._find_config_root)
with pytest.raises(errors.NoInstallationError):
self.parser._find_config_root()
def test_parse_file(self):
"""Test parse_file.
@ -75,26 +80,26 @@ class BasicParserTest(util.ParserTest):
matches = self.parser.aug.match(
"/augeas/load/Httpd/incl [. ='%s']" % file_path)
self.assertTrue(matches)
assert matches
def test_find_dir(self):
test = self.parser.find_dir("Listen", "80")
# This will only look in enabled hosts
test2 = self.parser.find_dir("documentroot")
self.assertEqual(len(test), 1)
self.assertEqual(len(test2), 8)
assert len(test) == 1
assert len(test2) == 8
def test_add_dir(self):
aug_default = "/files" + self.parser.loc["default"]
self.parser.add_dir(aug_default, "AddDirective", "test")
self.assertTrue(self.parser.find_dir("AddDirective", "test", aug_default))
assert self.parser.find_dir("AddDirective", "test", aug_default)
self.parser.add_dir(aug_default, "AddList", ["1", "2", "3", "4"])
matches = self.parser.find_dir("AddList", None, aug_default)
for i, match in enumerate(matches):
self.assertEqual(self.parser.aug.get(match), str(i + 1))
assert self.parser.aug.get(match) == str(i + 1)
def test_add_dir_beginning(self):
aug_default = "/files" + self.parser.loc["default"]
@ -102,24 +107,22 @@ class BasicParserTest(util.ParserTest):
"AddDirectiveBeginning",
"testBegin")
self.assertTrue(self.parser.find_dir("AddDirectiveBeginning", "testBegin", aug_default))
assert self.parser.find_dir("AddDirectiveBeginning", "testBegin", aug_default)
self.assertEqual(self.parser.aug.get(aug_default+"/directive[1]"), "AddDirectiveBeginning")
assert self.parser.aug.get(aug_default+"/directive[1]") == "AddDirectiveBeginning"
self.parser.add_dir_beginning(aug_default, "AddList", ["1", "2", "3", "4"])
matches = self.parser.find_dir("AddList", None, aug_default)
for i, match in enumerate(matches):
self.assertEqual(self.parser.aug.get(match), str(i + 1))
assert self.parser.aug.get(match) == str(i + 1)
for name in ("empty.conf", "no-directives.conf"):
conf = "/files" + os.path.join(self.parser.root, "sites-available", name)
self.parser.add_dir_beginning(conf, "AddDirectiveBeginning", "testBegin")
self.assertGreater(
len(self.parser.find_dir("AddDirectiveBeginning", "testBegin", conf)),
assert len(self.parser.find_dir("AddDirectiveBeginning", "testBegin", conf)) > \
0
)
def test_empty_arg(self):
self.assertIsNone(self.parser.get_arg("/files/whatever/nonexistent"))
assert self.parser.get_arg("/files/whatever/nonexistent") is None
def test_add_dir_to_ifmodssl(self):
"""test add_dir_to_ifmodssl.
@ -128,6 +131,7 @@ class BasicParserTest(util.ParserTest):
"""
from certbot_apache._internal.parser import get_aug_path
# This makes sure that find_dir will work
self.parser.modules["mod_ssl.c"] = "/fake/path"
@ -137,11 +141,12 @@ class BasicParserTest(util.ParserTest):
matches = self.parser.find_dir("FakeDirective", "123")
self.assertEqual(len(matches), 1)
self.assertIn("IfModule", matches[0])
assert len(matches) == 1
assert "IfModule" in matches[0]
def test_add_dir_to_ifmodssl_multiple(self):
from certbot_apache._internal.parser import get_aug_path
# This makes sure that find_dir will work
self.parser.modules["mod_ssl.c"] = "/fake/path"
@ -151,12 +156,12 @@ class BasicParserTest(util.ParserTest):
matches = self.parser.find_dir("FakeDirective")
self.assertEqual(len(matches), 3)
self.assertIn("IfModule", matches[0])
assert len(matches) == 3
assert "IfModule" in matches[0]
def test_get_aug_path(self):
from certbot_apache._internal.parser import get_aug_path
self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache"))
assert "/files/etc/apache" == get_aug_path("/etc/apache")
def test_set_locations(self):
with mock.patch("certbot_apache._internal.parser.os.path") as mock_path:
@ -166,8 +171,8 @@ class BasicParserTest(util.ParserTest):
# pylint: disable=protected-access
results = self.parser._set_locations()
self.assertEqual(results["default"], results["listen"])
self.assertEqual(results["default"], results["name"])
assert results["default"] == results["listen"]
assert results["default"] == results["name"]
@mock.patch("certbot_apache._internal.parser.ApacheParser.find_dir")
@mock.patch("certbot_apache._internal.parser.ApacheParser.get_arg")
@ -177,7 +182,7 @@ class BasicParserTest(util.ParserTest):
with mock.patch("certbot_apache._internal.parser.logger") as mock_logger:
self.parser.parse_modules()
# Make sure that we got None return value and logged the file
self.assertIs(mock_logger.debug.called, True)
assert mock_logger.debug.called is True
@mock.patch("certbot_apache._internal.parser.ApacheParser.find_dir")
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
@ -284,11 +289,11 @@ class BasicParserTest(util.ParserTest):
with mock.patch(
"certbot_apache._internal.parser.ApacheParser.parse_file") as mock_parse:
self.parser.update_runtime_variables()
self.assertEqual(self.parser.variables, expected_vars)
self.assertEqual(len(self.parser.modules), 58)
assert self.parser.variables == expected_vars
assert len(self.parser.modules) == 58
# None of the includes in inc_val should be in parsed paths.
# Make sure we tried to include them all.
self.assertEqual(mock_parse.call_count, 25)
assert mock_parse.call_count == 25
@mock.patch("certbot_apache._internal.parser.ApacheParser.find_dir")
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
@ -308,17 +313,16 @@ class BasicParserTest(util.ParserTest):
"certbot_apache._internal.parser.ApacheParser.parse_file") as mock_parse:
self.parser.update_runtime_variables()
# No matching modules should have been found
self.assertEqual(len(self.parser.modules), 0)
assert len(self.parser.modules) == 0
# Only one of the three includes do not exist in already parsed
# path derived from root configuration Include statements
self.assertEqual(mock_parse.call_count, 1)
assert mock_parse.call_count == 1
@mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_ctl(self, mock_run):
mock_run.side_effect = OSError
self.assertRaises(
errors.MisconfigurationError,
self.parser.update_runtime_variables)
with pytest.raises(errors.MisconfigurationError):
self.parser.update_runtime_variables()
@mock.patch("certbot_apache._internal.apache_util.subprocess.run")
def test_update_runtime_vars_bad_exit(self, mock_run):
@ -326,16 +330,15 @@ class BasicParserTest(util.ParserTest):
mock_proc.stdout = ""
mock_proc.stderr = ""
mock_proc.returncode = -1
self.assertRaises(
errors.MisconfigurationError,
self.parser.update_runtime_variables)
with pytest.raises(errors.MisconfigurationError):
self.parser.update_runtime_variables()
def test_add_comment(self):
from certbot_apache._internal.parser import get_aug_path
self.parser.add_comment(get_aug_path(self.parser.loc["name"]), "123456")
comm = self.parser.find_comments("123456")
self.assertEqual(len(comm), 1)
self.assertIn(self.parser.loc["name"], comm[0])
assert len(comm) == 1
assert self.parser.loc["name"] in comm[0]
class ParserInitTest(util.ApacheTest):
@ -352,18 +355,16 @@ class ParserInitTest(util.ApacheTest):
from certbot_apache._internal.parser import ApacheParser
mock_init_augeas.side_effect = errors.NoInstallationError
self.config.config_test = mock.Mock()
self.assertRaises(
errors.NoInstallationError, ApacheParser,
os.path.relpath(self.config_path), self.config,
with pytest.raises(errors.NoInstallationError):
ApacheParser(os.path.relpath(self.config_path), self.config,
"/dummy/vhostpath", version=(2, 4, 22))
def test_init_old_aug(self):
from certbot_apache._internal.parser import ApacheParser
with mock.patch("certbot_apache._internal.parser.ApacheParser.check_aug_version") as mock_c:
mock_c.return_value = False
self.assertRaises(
errors.NotSupportedError,
ApacheParser, os.path.relpath(self.config_path), self.config,
with pytest.raises(errors.NotSupportedError):
ApacheParser(os.path.relpath(self.config_path), self.config,
"/dummy/vhostpath", version=(2, 4, 22))
def test_root_normalized(self):
@ -377,7 +378,7 @@ class ParserInitTest(util.ApacheTest):
parser = ApacheParser(path, self.config, "/dummy/vhostpath")
self.assertEqual(parser.root, self.config_path)
assert parser.root == self.config_path
def test_root_absolute(self):
from certbot_apache._internal.parser import ApacheParser
@ -386,7 +387,7 @@ class ParserInitTest(util.ApacheTest):
parser = ApacheParser(
os.path.relpath(self.config_path), self.config, "/dummy/vhostpath")
self.assertEqual(parser.root, self.config_path)
assert parser.root == self.config_path
def test_root_no_trailing_slash(self):
from certbot_apache._internal.parser import ApacheParser
@ -394,8 +395,8 @@ class ParserInitTest(util.ApacheTest):
"update_runtime_variables"):
parser = ApacheParser(
self.config_path + os.path.sep, self.config, "/dummy/vhostpath")
self.assertEqual(parser.root, self.config_path)
assert parser.root == self.config_path
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,7 +1,10 @@
"""Tests for ApacheConfigurator for AugeasParserNode classes"""
import sys
import unittest
from unittest import mock
import pytest
import util
try:
@ -28,16 +31,16 @@ class ConfiguratorParserNodeTest(util.ApacheTest): # pylint: disable=too-many-p
self.config.USE_PARSERNODE = True
vhosts = self.config.get_virtual_hosts()
# Legacy get_virtual_hosts() do not set the node
self.assertIsNotNone(vhosts[0].node)
assert vhosts[0].node is not None
def test_parsernode_get_vhosts_mismatch(self):
vhosts = self.config.get_virtual_hosts_v2()
# One of the returned VirtualHost objects differs
vhosts[0].name = "IdidntExpectThat"
self.config.get_virtual_hosts_v2 = mock.MagicMock(return_value=vhosts)
with self.assertRaises(AssertionError):
with pytest.raises(AssertionError):
_ = self.config.get_virtual_hosts()
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,6 +1,8 @@
""" Tests for ParserNode interface """
import unittest
import sys
import pytest
from certbot_apache._internal import interfaces
from certbot_apache._internal import parsernode_util as util
@ -101,28 +103,26 @@ interfaces.CommentNode.register(DummyCommentNode)
interfaces.DirectiveNode.register(DummyDirectiveNode)
interfaces.BlockNode.register(DummyBlockNode)
class ParserNodeTest(unittest.TestCase):
def test_dummy():
"""Dummy placeholder test case for ParserNode interfaces"""
def test_dummy(self):
dummyblock = DummyBlockNode(
name="None",
parameters=(),
ancestor=None,
dirty=False,
filepath="/some/random/path"
)
dummydirective = DummyDirectiveNode(
name="Name",
ancestor=None,
filepath="/another/path"
)
dummycomment = DummyCommentNode(
comment="Comment",
ancestor=dummyblock,
filepath="/some/file"
)
dummyblock = DummyBlockNode(
name="None",
parameters=(),
ancestor=None,
dirty=False,
filepath="/some/random/path"
)
dummydirective = DummyDirectiveNode(
name="Name",
ancestor=None,
filepath="/another/path"
)
dummycomment = DummyCommentNode(
comment="Comment",
ancestor=dummyblock,
filepath="/some/file"
)
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,115 +1,120 @@
""" Tests for ParserNode utils """
import unittest
import sys
import pytest
from certbot_apache._internal import parsernode_util as util
class ParserNodeUtilTest(unittest.TestCase):
"""Tests for ParserNode utils"""
def _setup_parsernode():
""" Sets up kwargs dict for ParserNode """
return {
"ancestor": None,
"dirty": False,
"filepath": "/tmp",
}
def _setup_parsernode(self):
""" Sets up kwargs dict for ParserNode """
return {
"ancestor": None,
"dirty": False,
"filepath": "/tmp",
}
def _setup_commentnode():
""" Sets up kwargs dict for CommentNode """
def _setup_commentnode(self):
""" Sets up kwargs dict for CommentNode """
pn = _setup_parsernode()
pn["comment"] = "x"
return pn
pn = self._setup_parsernode()
pn["comment"] = "x"
return pn
def _setup_directivenode():
""" Sets up kwargs dict for DirectiveNode """
def _setup_directivenode(self):
""" Sets up kwargs dict for DirectiveNode """
pn = _setup_parsernode()
pn["name"] = "Name"
pn["parameters"] = ("first",)
pn["enabled"] = True
return pn
pn = self._setup_parsernode()
pn["name"] = "Name"
pn["parameters"] = ("first",)
pn["enabled"] = True
return pn
def test_unknown_parameter():
params = _setup_parsernode()
params["unknown"] = "unknown"
with pytest.raises(TypeError):
util.parsernode_kwargs(params)
def test_unknown_parameter(self):
params = self._setup_parsernode()
params["unknown"] = "unknown"
self.assertRaises(TypeError, util.parsernode_kwargs, params)
params = self._setup_commentnode()
params["unknown"] = "unknown"
self.assertRaises(TypeError, util.commentnode_kwargs, params)
params = self._setup_directivenode()
params["unknown"] = "unknown"
self.assertRaises(TypeError, util.directivenode_kwargs, params)
def test_parsernode(self):
params = self._setup_parsernode()
ctrl = self._setup_parsernode()
ancestor, dirty, filepath, metadata = util.parsernode_kwargs(params)
self.assertEqual(ancestor, ctrl["ancestor"])
self.assertEqual(dirty, ctrl["dirty"])
self.assertEqual(filepath, ctrl["filepath"])
self.assertEqual(metadata, {})
def test_parsernode_from_metadata(self):
params = self._setup_parsernode()
params.pop("filepath")
md = {"some": "value"}
params["metadata"] = md
# Just testing that error from missing required parameters is not raised
_, _, _, metadata = util.parsernode_kwargs(params)
self.assertEqual(metadata, md)
def test_commentnode(self):
params = self._setup_commentnode()
ctrl = self._setup_commentnode()
comment, _ = util.commentnode_kwargs(params)
self.assertEqual(comment, ctrl["comment"])
def test_commentnode_from_metadata(self):
params = self._setup_commentnode()
params.pop("comment")
params["metadata"] = {}
# Just testing that error from missing required parameters is not raised
params = _setup_commentnode()
params["unknown"] = "unknown"
with pytest.raises(TypeError):
util.commentnode_kwargs(params)
def test_directivenode(self):
params = self._setup_directivenode()
ctrl = self._setup_directivenode()
name, parameters, enabled, _ = util.directivenode_kwargs(params)
self.assertEqual(name, ctrl["name"])
self.assertEqual(parameters, ctrl["parameters"])
self.assertEqual(enabled, ctrl["enabled"])
def test_directivenode_from_metadata(self):
params = self._setup_directivenode()
params.pop("filepath")
params.pop("name")
params["metadata"] = {"irrelevant": "value"}
# Just testing that error from missing required parameters is not raised
params = _setup_directivenode()
params["unknown"] = "unknown"
with pytest.raises(TypeError):
util.directivenode_kwargs(params)
def test_missing_required(self):
c_params = self._setup_commentnode()
c_params.pop("comment")
self.assertRaises(TypeError, util.commentnode_kwargs, c_params)
def test_parsernode():
params = _setup_parsernode()
ctrl = _setup_parsernode()
d_params = self._setup_directivenode()
d_params.pop("ancestor")
self.assertRaises(TypeError, util.directivenode_kwargs, d_params)
ancestor, dirty, filepath, metadata = util.parsernode_kwargs(params)
assert ancestor == ctrl["ancestor"]
assert dirty == ctrl["dirty"]
assert filepath == ctrl["filepath"]
assert metadata == {}
p_params = self._setup_parsernode()
p_params.pop("filepath")
self.assertRaises(TypeError, util.parsernode_kwargs, p_params)
def test_parsernode_from_metadata():
params = _setup_parsernode()
params.pop("filepath")
md = {"some": "value"}
params["metadata"] = md
# Just testing that error from missing required parameters is not raised
_, _, _, metadata = util.parsernode_kwargs(params)
assert metadata == md
def test_commentnode():
params = _setup_commentnode()
ctrl = _setup_commentnode()
comment, _ = util.commentnode_kwargs(params)
assert comment == ctrl["comment"]
def test_commentnode_from_metadata():
params = _setup_commentnode()
params.pop("comment")
params["metadata"] = {}
# Just testing that error from missing required parameters is not raised
util.commentnode_kwargs(params)
def test_directivenode():
params = _setup_directivenode()
ctrl = _setup_directivenode()
name, parameters, enabled, _ = util.directivenode_kwargs(params)
assert name == ctrl["name"]
assert parameters == ctrl["parameters"]
assert enabled == ctrl["enabled"]
def test_directivenode_from_metadata():
params = _setup_directivenode()
params.pop("filepath")
params.pop("name")
params["metadata"] = {"irrelevant": "value"}
# Just testing that error from missing required parameters is not raised
util.directivenode_kwargs(params)
def test_missing_required():
c_params = _setup_commentnode()
c_params.pop("comment")
with pytest.raises(TypeError):
util.commentnode_kwargs(c_params)
d_params = _setup_directivenode()
d_params.pop("ancestor")
with pytest.raises(TypeError):
util.directivenode_kwargs(d_params)
p_params = _setup_parsernode()
p_params.pop("filepath")
with pytest.raises(TypeError):
util.parsernode_kwargs(p_params)
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -1,10 +1,10 @@
"""Common utilities for certbot_apache."""
import shutil
import unittest
from unittest import mock
import augeas
import josepy as jose
from unittest import mock
from certbot.compat import os
from certbot.plugins import common

View file

@ -5,7 +5,8 @@ from typing import Optional
from typing import Type
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey, EllipticCurve
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
@ -13,8 +14,8 @@ try:
import grp
POSIX_MODE = True
except ImportError:
import win32security
import ntsecuritycon
import win32security
POSIX_MODE = False
EVERYBODY_SID = 'S-1-1-0'
@ -22,13 +23,13 @@ SYSTEM_SID = 'S-1-5-18'
ADMINS_SID = 'S-1-5-32-544'
def assert_elliptic_key(key: str, curve: Type[EllipticCurve]) -> None:
def assert_elliptic_key(key_path: str, curve: Type[EllipticCurve]) -> None:
"""
Asserts that the key at the given path is an EC key using the given curve.
:param key: path to key
:param key_path: path to key
:param EllipticCurve curve: name of the expected elliptic curve
"""
with open(key, 'rb') as file:
with open(key_path, 'rb') as file:
privkey1 = file.read()
key = load_pem_private_key(data=privkey1, password=None, backend=default_backend())
@ -37,13 +38,13 @@ def assert_elliptic_key(key: str, curve: Type[EllipticCurve]) -> None:
assert isinstance(key.curve, curve), f"should have curve {curve} but was {key.curve}"
def assert_rsa_key(key: str, key_size: Optional[int] = None) -> None:
def assert_rsa_key(key_path: str, key_size: Optional[int] = None) -> None:
"""
Asserts that the key at the given path is an RSA key.
:param str key: path to key
:param str key_path: path to key
:param int key_size: if provided, assert that the RSA key is of this size
"""
with open(key, 'rb') as file:
with open(key_path, 'rb') as file:
privkey1 = file.read()
key = load_pem_private_key(data=privkey1, password=None, backend=default_backend())

View file

@ -6,8 +6,8 @@ import re
import shutil
import subprocess
import time
from typing import Iterable
from typing import Generator
from typing import Iterable
from typing import Tuple
from typing import Type
@ -18,7 +18,6 @@ from cryptography.hazmat.primitives.asymmetric.ec import SECP521R1
from cryptography.x509 import NameOID
import pytest
from certbot_integration_tests.certbot_tests.context import IntegrationTestsContext
from certbot_integration_tests.certbot_tests.assertions import assert_cert_count_for_lineage
from certbot_integration_tests.certbot_tests.assertions import assert_elliptic_key
from certbot_integration_tests.certbot_tests.assertions import assert_equals_group_owner
@ -31,6 +30,7 @@ from certbot_integration_tests.certbot_tests.assertions import assert_saved_rene
from certbot_integration_tests.certbot_tests.assertions import assert_world_no_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_world_read_permissions
from certbot_integration_tests.certbot_tests.assertions import EVERYBODY_SID
from certbot_integration_tests.certbot_tests.context import IntegrationTestsContext
from certbot_integration_tests.utils import misc
@ -118,7 +118,7 @@ def test_http_01(context: IntegrationTestsContext) -> None:
def test_manual_http_auth(context: IntegrationTestsContext) -> None:
"""Test the HTTP-01 challenge using manual plugin."""
with misc.create_http_server(context.http_01_port) as webroot,\
misc.manual_http_hooks(webroot, context.http_01_port) as scripts:
misc.manual_http_hooks(webroot) as scripts:
certname = context.get_domain()
context.certbot([
@ -248,8 +248,9 @@ def test_renew_files_propagate_permissions(context: IntegrationTestsContext) ->
if os.name != 'nt':
os.chmod(privkey1, 0o444)
else:
import win32security # pylint: disable=import-error
import ntsecuritycon # pylint: disable=import-error
import win32security # pylint: disable=import-error
# Get the current DACL of the private key
security = win32security.GetFileSecurity(privkey1, win32security.DACL_SECURITY_INFORMATION)
dacl = security.GetSecurityDescriptorDacl()
@ -329,7 +330,6 @@ def test_renew_with_changed_private_key_complexity(context: IntegrationTestsCont
context.certbot(['renew', '--rsa-key-size', '2048'])
assert_cert_count_for_lineage(context.config_dir, certname, 3)
key3 = join(context.config_dir, 'archive', certname, 'privkey3.pem')
assert_rsa_key(key3, 2048)
@ -437,38 +437,37 @@ def test_reuse_key(context: IntegrationTestsContext) -> None:
with open(join(context.config_dir, 'archive/{0}/privkey1.pem').format(certname), 'r') as file:
privkey1 = file.read()
with open(join(context.config_dir, 'archive/{0}/cert1.pem').format(certname), 'r') as file:
cert1 = file.read()
with open(join(context.config_dir, 'archive/{0}/privkey2.pem').format(certname), 'r') as file:
privkey2 = file.read()
with open(join(context.config_dir, 'archive/{0}/cert2.pem').format(certname), 'r') as file:
cert2 = file.read()
assert privkey1 == privkey2
context.certbot(['--cert-name', certname, '--domains', certname, '--force-renewal'])
with open(join(context.config_dir, 'archive/{0}/privkey3.pem').format(certname), 'r') as file:
privkey3 = file.read()
with open(join(context.config_dir, 'archive/{0}/cert3.pem').format(certname), 'r') as file:
cert3 = file.read()
assert privkey2 != privkey3
context.certbot(['--cert-name', certname, '--domains', certname,
'--reuse-key','--force-renewal'])
context.certbot(['renew', '--cert-name', certname, '--no-reuse-key', '--force-renewal'])
context.certbot(['renew', '--cert-name', certname, '--force-renewal'])
with open(join(context.config_dir, 'archive/{0}/privkey4.pem').format(certname), 'r') as file:
privkey4 = file.read()
context.certbot(['renew', '--cert-name', certname, '--no-reuse-key', '--force-renewal'])
with open(join(context.config_dir, 'archive/{0}/privkey5.pem').format(certname), 'r') as file:
privkey5 = file.read()
context.certbot(['renew', '--cert-name', certname, '--force-renewal'])
with open(join(context.config_dir, 'archive/{0}/privkey6.pem').format(certname), 'r') as file:
privkey6 = file.read()
assert privkey3 == privkey4
assert privkey4 != privkey5
assert privkey5 != privkey6
with open(join(context.config_dir, 'archive/{0}/cert1.pem').format(certname), 'r') as file:
cert1 = file.read()
with open(join(context.config_dir, 'archive/{0}/cert2.pem').format(certname), 'r') as file:
cert2 = file.read()
with open(join(context.config_dir, 'archive/{0}/cert3.pem').format(certname), 'r') as file:
cert3 = file.read()
assert len({cert1, cert2, cert3}) == 3
@ -615,7 +614,6 @@ def test_renew_with_ec_keys(context: IntegrationTestsContext) -> None:
# to the lineage key type, Certbot should keep the lineage key type. The curve will still
# change to the default value, in order to stay consistent with the behavior of certonly.
context.certbot(['certonly', '--force-renewal', '-d', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 3)
key3 = join(context.config_dir, 'archive', certname, 'privkey3.pem')
assert 200 < os.stat(key3).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key3, SECP256R1)
@ -629,14 +627,12 @@ def test_renew_with_ec_keys(context: IntegrationTestsContext) -> None:
context.certbot(['certonly', '--force-renewal', '-d', certname,
'--key-type', 'rsa', '--cert-name', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 4)
key4 = join(context.config_dir, 'archive', certname, 'privkey4.pem')
assert_rsa_key(key4)
# We expect that the previous behavior of requiring both --cert-name and
# --key-type to be set to not apply to the renew subcommand.
context.certbot(['renew', '--force-renewal', '--key-type', 'ecdsa'])
assert_cert_count_for_lineage(context.config_dir, certname, 5)
key5 = join(context.config_dir, 'archive', certname, 'privkey5.pem')
assert 200 < os.stat(key5).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key5, SECP256R1)
@ -813,6 +809,25 @@ def test_revoke_multiple_lineages(context: IntegrationTestsContext) -> None:
assert 'Not deleting revoked certificates due to overlapping archive dirs' in f.read()
def test_reconfigure(context: IntegrationTestsContext) -> None:
"""Test the reconfigure verb"""
certname = context.get_domain()
context.certbot(['-d', certname])
conf_path = join(context.config_dir, 'renewal', '{}.conf'.format(certname))
with misc.create_http_server(context.http_01_port) as webroot:
context.certbot(['reconfigure', '--cert-name', certname,
'-a', 'webroot', '--webroot-path', webroot])
with open(conf_path, 'r') as f:
file_contents = f.read()
# Check changed value
assert 'authenticator = webroot' in file_contents, \
'Expected authenticator to be changed to webroot in renewal config'
# Check added value
assert f'webroot_path = {webroot}' in file_contents, \
'Expected new webroot path to be added to renewal config'
def test_wildcard_certificates(context: IntegrationTestsContext) -> None:
"""Test wildcard certificate issuance."""
certname = context.get_domain('wild')
@ -902,7 +917,7 @@ def test_preferred_chain(context: IntegrationTestsContext) -> None:
except NotImplementedError:
pytest.skip('This ACME server does not support alternative issuers.')
names = [i.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value \
names = [str(i.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value) \
for i in issuers]
domain = context.get_domain('preferred-chain')
@ -915,9 +930,9 @@ def test_preferred_chain(context: IntegrationTestsContext) -> None:
context.certbot(args)
dumped = misc.read_certificate(cert_path)
assert 'Issuer: CN={}'.format(expected) in dumped, \
'Expected chain issuer to be {} when preferring {}'.format(expected, requested)
assert f'Issuer: CN={expected}'in dumped, \
f'Expected chain issuer to be {expected} when preferring {requested}'
with open(conf_path, 'r') as f:
assert 'preferred_chain = {}'.format(requested) in f.read(), \
assert f'preferred_chain = {requested}' in f.read(), \
'Expected preferred_chain to be set in renewal config'

View file

@ -44,7 +44,7 @@ class ACMEServer:
"""
def __init__(self, acme_server: str, nodes: List[str], http_proxy: bool = True,
stdout: bool = False, dns_server: Optional[str] = None,
http_01_port: int = DEFAULT_HTTP_01_PORT) -> None:
http_01_port: Optional[int] = None) -> None:
"""
Create an ACMEServer instance.
:param str acme_server: the type of acme server used (boulder-v2 or pebble)
@ -63,12 +63,14 @@ class ACMEServer:
self._processes: List[subprocess.Popen] = []
self._stdout = sys.stdout if stdout else open(os.devnull, 'w') # pylint: disable=consider-using-with
self._dns_server = dns_server
self._http_01_port = http_01_port
self._preterminate_cmds_args: List[Tuple[Tuple[Any, ...], Dict[str, Any]]] = []
if http_01_port != DEFAULT_HTTP_01_PORT:
if self._acme_type != 'pebble' or self._proxy:
raise ValueError('setting http_01_port is not currently supported '
'with boulder or the HTTP proxy')
self._http_01_port = BOULDER_HTTP_01_PORT if self._acme_type == 'boulder' \
else DEFAULT_HTTP_01_PORT
if http_01_port:
if (self._acme_type == 'pebble' and self._proxy) or self._acme_type == 'boulder':
raise ValueError('Setting http_01_port is not currently supported when '
'using Boulder or the HTTP proxy')
self._http_01_port = http_01_port
def start(self) -> None:
"""Start the test stack"""
@ -236,11 +238,11 @@ class ACMEServer:
def _prepare_http_proxy(self) -> None:
"""Configure and launch an HTTP proxy"""
print('=> Configuring the HTTP proxy...')
print(f'=> Configuring the HTTP proxy on port {self._http_01_port}...')
http_port_map = cast(Dict[str, int], self.acme_xdist['http_port'])
mapping = {r'.+\.{0}\.wtf'.format(node): 'http://127.0.0.1:{0}'.format(port)
for node, port in http_port_map.items()}
command = [sys.executable, proxy.__file__, str(DEFAULT_HTTP_01_PORT), json.dumps(mapping)]
command = [sys.executable, proxy.__file__, str(self._http_01_port), json.dumps(mapping)]
self._launch_process(command)
print('=> Finished configuring the HTTP proxy.')

View file

@ -2,7 +2,6 @@
"""Module to call certbot in test mode"""
import os
import pkg_resources
import subprocess
import sys
from typing import Dict
@ -10,6 +9,8 @@ from typing import List
from typing import Mapping
from typing import Tuple
import pkg_resources
import certbot_integration_tests
# pylint: disable=wildcard-import,unused-wildcard-import
from certbot_integration_tests.utils.constants import *

View file

@ -1,5 +1,6 @@
"""Some useful constants to use throughout certbot-ci integration tests"""
DEFAULT_HTTP_01_PORT = 5002
BOULDER_HTTP_01_PORT = 80
TLS_ALPN_01_PORT = 5001
CHALLTESTSRV_PORT = 8055
BOULDER_V2_CHALLTESTSRV_URL = f'http://10.77.77.77:{CHALLTESTSRV_PORT}'

View file

@ -15,20 +15,20 @@ import sys
import tempfile
import threading
import time
import warnings
from typing import Generator
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple
import warnings
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import NoEncryption
from cryptography.hazmat.primitives.serialization import PrivateFormat
from cryptography.x509 import load_pem_x509_certificate
from cryptography.x509 import Certificate
from cryptography.x509 import load_pem_x509_certificate
from OpenSSL import crypto
import pkg_resources
import requests
@ -158,14 +158,12 @@ set -e
@contextlib.contextmanager
def manual_http_hooks(http_server_root: str,
http_port: int) -> Generator[Tuple[str, str], None, None]:
def manual_http_hooks(http_server_root: str) -> Generator[Tuple[str, str], None, None]:
"""
Generate suitable http-01 hooks command for test purpose in the given HTTP
server webroot directory. These hooks command use temporary python scripts
that are deleted upon context exit.
:param str http_server_root: path to the HTTP server configured to serve http-01 challenges
:param int http_port: HTTP port that the HTTP server listen on
:return (str, str): a tuple containing the authentication hook and cleanup hook commands
"""
tempdir = tempfile.mkdtemp()
@ -175,24 +173,12 @@ def manual_http_hooks(http_server_root: str,
file_h.write('''\
#!/usr/bin/env python
import os
import requests
import time
import sys
challenge_dir = os.path.join('{0}', '.well-known', 'acme-challenge')
os.makedirs(challenge_dir)
challenge_file = os.path.join(challenge_dir, os.environ.get('CERTBOT_TOKEN'))
with open(challenge_file, 'w') as file_h:
file_h.write(os.environ.get('CERTBOT_VALIDATION'))
url = 'http://localhost:{1}/.well-known/acme-challenge/' + os.environ.get('CERTBOT_TOKEN')
for _ in range(0, 10):
time.sleep(1)
try:
if request.get(url).status_code == 200:
sys.exit(0)
except requests.exceptions.ConnectionError:
pass
raise ValueError('Error, url did not respond after 10 attempts: {{0}}'.format(url))
'''.format(http_server_root.replace('\\', '\\\\'), http_port))
'''.format(http_server_root.replace('\\', '\\\\')))
os.chmod(auth_script_path, 0o755)
cleanup_script_path = os.path.join(tempdir, 'cleanup.py')
@ -230,11 +216,7 @@ def generate_csr(domains: Iterable[str], key_path: str, csr_path: str,
# Ignore a warning on some old versions of cryptography
warnings.simplefilter('ignore', category=PendingDeprecationWarning)
_key = ec.generate_private_key(ec.SECP384R1(), default_backend())
# This type ignore directive is required due to an outdated version of types-cryptography.
# It can be removed once package types-pyOpenSSL depends on cryptography instead of
# types-cryptography and so types-cryptography is not installed anymore.
# See https://github.com/python/typeshed/issues/5618
_bytes = _key.private_bytes(encoding=Encoding.PEM, # type: ignore
_bytes = _key.private_bytes(encoding=Encoding.PEM,
format=PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=NoEncryption())
key = crypto.load_privatekey(crypto.FILETYPE_PEM, _bytes)

View file

@ -6,11 +6,15 @@ to serve a mock OCSP responder during integration tests against Pebble.
import datetime
import http.server as BaseHTTPServer
import re
from typing import cast
from typing import Union
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.x509 import ocsp
from dateutil import parser
import requests
@ -25,7 +29,9 @@ class _ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(self) -> None:
request = requests.get(PEBBLE_MANAGEMENT_URL + '/intermediate-keys/0',
verify=False, timeout=10)
issuer_key = serialization.load_pem_private_key(request.content, None, default_backend())
issuer_key = cast(
Union[RSAPrivateKey, EllipticCurvePrivateKey],
serialization.load_pem_private_key(request.content, None, default_backend()))
request = requests.get(PEBBLE_MANAGEMENT_URL + '/intermediates/0',
verify=False, timeout=10)

View file

@ -3,13 +3,12 @@ import os
import re
import subprocess
import time
import unittest
from typing import Any
import pytest
@unittest.skipIf(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
@pytest.mark.skipif(os.name != 'nt', reason='Windows installer tests must be run on Windows.')
def test_it(request: pytest.FixtureRequest) -> None:
try:
subprocess.check_call(['certbot', '--version'])

View file

@ -7,15 +7,14 @@ from typing import Set
from typing import Tuple
from unittest import mock
from certbot import configuration
from certbot import errors as le_errors
from certbot import util as certbot_util
from certbot_apache._internal import entrypoint
from certbot_compatibility_test import errors
from certbot_compatibility_test import util
from certbot_compatibility_test.configurators import common as configurators_common
from certbot import configuration
from certbot import errors as le_errors
from certbot import util as certbot_util
class Proxy(configurators_common.Proxy):
"""A common base for Apache test configurators"""

View file

@ -8,21 +8,20 @@ import tempfile
from typing import Iterable
from typing import List
from typing import Optional
from typing import Union
from typing import overload
from typing import Set
from typing import Tuple
from typing import Type
from certbot_compatibility_test import errors
from certbot_compatibility_test import interfaces
from certbot_compatibility_test import util
from typing import Union
from acme import challenges
from acme.challenges import Challenge
from certbot._internal import constants
from certbot.plugins import common
from certbot.achallenges import AnnotatedChallenge
from certbot.plugins import common
from certbot_compatibility_test import errors
from certbot_compatibility_test import interfaces
from certbot_compatibility_test import util
logger = logging.getLogger(__name__)

View file

@ -5,14 +5,13 @@ import subprocess
from typing import Set
from typing import Tuple
from certbot import configuration
from certbot_compatibility_test import errors
from certbot_compatibility_test import util
from certbot_compatibility_test.configurators import common as configurators_common
from certbot_nginx._internal import configurator
from certbot_nginx._internal import constants
from certbot import configuration
class Proxy(configurators_common.Proxy):
"""A common base for Nginx test configurators"""

View file

@ -18,12 +18,6 @@ from typing import Optional
from typing import Tuple
from typing import Type
from certbot_compatibility_test import errors
from certbot_compatibility_test import util
from certbot_compatibility_test import validator
from certbot_compatibility_test.configurators import common
from certbot_compatibility_test.configurators.apache import common as a_common
from certbot_compatibility_test.configurators.nginx import common as n_common
from OpenSSL import crypto
from urllib3.util import connection
@ -34,6 +28,12 @@ from certbot import achallenges
from certbot import errors as le_errors
from certbot._internal.display import obj as display_obj
from certbot.tests import acme_util
from certbot_compatibility_test import errors
from certbot_compatibility_test import util
from certbot_compatibility_test import validator
from certbot_compatibility_test.configurators import common
from certbot_compatibility_test.configurators.apache import common as a_common
from certbot_compatibility_test.configurators.nginx import common as n_common
DESCRIPTION = """
Tests Certbot plugins against different server configurations. It is

View file

@ -6,11 +6,11 @@ import re
import shutil
import tarfile
from certbot_compatibility_test import errors
import josepy as jose
from certbot._internal import constants
from certbot.tests import util as test_util
from certbot_compatibility_test import errors
_KEY_BASE = "rsa2048_key.pem"
KEY_PATH = test_util.vector_path(_KEY_BASE)

View file

@ -1,129 +0,0 @@
"""Tests for certbot_compatibility_test.validator."""
from typing import cast
from typing import Mapping
from typing import Optional
import unittest
from unittest import mock
from certbot_compatibility_test import validator
from OpenSSL import crypto
import requests
from acme import errors as acme_errors
class ValidatorTest(unittest.TestCase):
def setUp(self) -> None:
self.validator = validator.Validator()
@mock.patch(
"certbot_compatibility_test.validator.crypto_util.probe_sni")
def test_certificate_success(self, mock_probe_sni: mock.MagicMock) -> None:
cert = crypto.X509()
mock_probe_sni.return_value = cert
self.assertTrue(self.validator.certificate(
cert, "test.com", "127.0.0.1"))
@mock.patch(
"certbot_compatibility_test.validator.crypto_util.probe_sni")
def test_certificate_error(self, mock_probe_sni: mock.MagicMock) -> None:
cert = crypto.X509()
mock_probe_sni.side_effect = [acme_errors.Error]
self.assertFalse(self.validator.certificate(
cert, "test.com", "127.0.0.1"))
@mock.patch(
"certbot_compatibility_test.validator.crypto_util.probe_sni")
def test_certificate_failure(self, mock_probe_sni: mock.MagicMock) -> None:
cert = crypto.X509()
cert.set_serial_number(1337)
mock_probe_sni.return_value = crypto.X509()
self.assertFalse(self.validator.certificate(
cert, "test.com", "127.0.0.1"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_successful_redirect(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
301, {"location": "https://test.com"})
self.assertTrue(self.validator.redirect("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_redirect_with_headers(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
301, {"location": "https://test.com"})
self.assertTrue(self.validator.redirect(
"test.com", headers={"Host": "test.com"}))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_redirect_missing_location(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(301)
self.assertFalse(self.validator.redirect("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_redirect_wrong_status_code(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
201, {"location": "https://test.com"})
self.assertFalse(self.validator.redirect("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_redirect_wrong_redirect_code(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
303, {"location": "https://test.com"})
self.assertFalse(self.validator.redirect("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts_empty(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security": ""})
self.assertFalse(self.validator.hsts("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts_malformed(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security": "sdfal"})
self.assertFalse(self.validator.hsts("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts_bad_max_age(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security": "max-age=not-an-int"})
self.assertFalse(self.validator.hsts("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts_expire(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security": "max-age=3600"})
self.assertFalse(self.validator.hsts("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security": "max-age=31536000"})
self.assertTrue(self.validator.hsts("test.com"))
@mock.patch("certbot_compatibility_test.validator.requests.get")
def test_hsts_include_subdomains(self, mock_get_request: mock.MagicMock) -> None:
mock_get_request.return_value = create_response(
headers={"strict-transport-security":
"max-age=31536000;includeSubDomains"})
self.assertTrue(self.validator.hsts("test.com"))
def test_ocsp_stapling(self) -> None:
self.assertRaises(
NotImplementedError, self.validator.ocsp_stapling, "test.com")
def create_response(status_code: int = 200,
headers: Optional[Mapping[str, str]] = None) -> requests.Response:
"""Creates a requests.Response object for testing"""
response = requests.Response()
response.status_code = status_code
if headers:
response.headers = cast(requests.models.CaseInsensitiveDict, headers)
return response
if __name__ == '__main__':
unittest.main() # pragma: no cover

View file

@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'certbot',

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'cloudflare>=1.5.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-cloudflare',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,9 +1,11 @@
"""Tests for certbot_dns_cloudflare._internal.dns_cloudflare."""
import sys
import unittest
from unittest import mock
import CloudFlare
import pytest
from certbot import errors
from certbot.compat import os
@ -43,7 +45,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_cleanup(self):
# _attempt_cleanup | pylint: disable=protected-access
@ -51,7 +53,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.cleanup([self.achall])
expected = [mock.call.del_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
@test_util.patch_display_util()
def test_api_token(self, unused_mock_get_utility):
@ -60,43 +62,37 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_no_creds(self):
dns_test_common.write({}, self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
def test_missing_email_or_key(self):
dns_test_common.write({"cloudflare_api_key": API_KEY}, self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
dns_test_common.write({"cloudflare_email": EMAIL}, self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
def test_email_or_key_with_token(self):
dns_test_common.write({"cloudflare_api_token": API_TOKEN, "cloudflare_email": EMAIL},
self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
dns_test_common.write({"cloudflare_api_token": API_TOKEN, "cloudflare_api_key": API_KEY},
self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
dns_test_common.write({"cloudflare_api_token": API_TOKEN, "cloudflare_email": EMAIL,
"cloudflare_api_key": API_KEY}, self.config.cloudflare_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
class CloudflareClientTest(unittest.TestCase):
@ -124,61 +120,47 @@ class CloudflareClientTest(unittest.TestCase):
post_data = self.cf.zones.dns_records.post.call_args[1]['data']
self.assertEqual('TXT', post_data['type'])
self.assertEqual(self.record_name, post_data['name'])
self.assertEqual(self.record_content, post_data['content'])
self.assertEqual(self.record_ttl, post_data['ttl'])
assert 'TXT' == post_data['type']
assert self.record_name == post_data['name']
assert self.record_content == post_data['content']
assert self.record_ttl == post_data['ttl']
def test_add_txt_record_error(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
self.cf.zones.dns_records.post.side_effect = CloudFlare.exceptions.CloudFlareAPIError(1009, '', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_error_during_zone_lookup(self):
self.cf.zones.get.side_effect = API_ERROR
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_zone_not_found(self):
self.cf.zones.get.return_value = []
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_bad_creds(self):
self.cf.zones.get.side_effect = CloudFlare.exceptions.CloudFlareAPIError(6003, '', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.cf.zones.get.side_effect = CloudFlare.exceptions.CloudFlareAPIError(9103, '', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.cf.zones.get.side_effect = CloudFlare.exceptions.CloudFlareAPIError(9109, '', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.cf.zones.get.side_effect = CloudFlare.exceptions.CloudFlareAPIError(0, 'com.cloudflare.api.account.zone.list', '')
self.assertRaises(
errors.PluginError,
self.cloudflare_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_del_txt_record(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
@ -190,13 +172,13 @@ class CloudflareClientTest(unittest.TestCase):
mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY),
mock.call.zones.dns_records.delete(self.zone_id, self.record_id)]
self.assertEqual(expected, self.cf.mock_calls)
assert expected == self.cf.mock_calls
get_data = self.cf.zones.dns_records.get.call_args[1]['params']
self.assertEqual('TXT', get_data['type'])
self.assertEqual(self.record_name, get_data['name'])
self.assertEqual(self.record_content, get_data['content'])
assert 'TXT' == get_data['type']
assert self.record_name == get_data['name']
assert self.record_content == get_data['content']
def test_del_txt_record_error_during_zone_lookup(self):
self.cf.zones.get.side_effect = API_ERROR
@ -213,7 +195,7 @@ class CloudflareClientTest(unittest.TestCase):
mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY),
mock.call.zones.dns_records.delete(self.zone_id, self.record_id)]
self.assertEqual(expected, self.cf.mock_calls)
assert expected == self.cf.mock_calls
def test_del_txt_record_error_during_get(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
@ -223,7 +205,7 @@ class CloudflareClientTest(unittest.TestCase):
expected = [mock.call.zones.get(params=mock.ANY),
mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY)]
self.assertEqual(expected, self.cf.mock_calls)
assert expected == self.cf.mock_calls
def test_del_txt_record_no_record(self):
self.cf.zones.get.return_value = [{'id': self.zone_id}]
@ -233,7 +215,7 @@ class CloudflareClientTest(unittest.TestCase):
expected = [mock.call.zones.get(params=mock.ANY),
mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY)]
self.assertEqual(expected, self.cf.mock_calls)
assert expected == self.cf.mock_calls
def test_del_txt_record_no_zone(self):
self.cf.zones.get.return_value = [{'id': None}]
@ -241,8 +223,8 @@ class CloudflareClientTest(unittest.TestCase):
self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content)
expected = [mock.call.zones.get(params=mock.ANY)]
self.assertEqual(expected, self.cf.mock_calls)
assert expected == self.cf.mock_calls
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-digitalocean',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,9 +1,11 @@
"""Tests for certbot_dns_digitalocean._internal.dns_digitalocean."""
import sys
import unittest
from unittest import mock
import digitalocean
import pytest
from certbot import errors
from certbot.compat import os
@ -39,7 +41,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, 30)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_cleanup(self):
# _attempt_cleanup | pylint: disable=protected-access
@ -47,7 +49,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.cleanup([self.achall])
expected = [mock.call.del_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
class DigitalOceanClientTest(unittest.TestCase):
@ -88,16 +90,14 @@ class DigitalOceanClientTest(unittest.TestCase):
def test_add_txt_record_fail_to_find_domain(self):
self.manager.get_all_domains.return_value = []
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_error_finding_domain(self):
self.manager.get_all_domains.side_effect = API_ERROR
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_add_txt_record_error_creating_record(self):
domain_mock = mock.MagicMock()
@ -106,9 +106,8 @@ class DigitalOceanClientTest(unittest.TestCase):
self.manager.get_all_domains.return_value = [domain_mock]
self.assertRaises(errors.PluginError,
self.digitalocean_client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
self.digitalocean_client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
def test_del_txt_record(self):
first_record_mock = mock.MagicMock()
@ -136,10 +135,10 @@ class DigitalOceanClientTest(unittest.TestCase):
self.digitalocean_client.del_txt_record(DOMAIN, self.record_name, self.record_content)
self.assertTrue(correct_record_mock.destroy.called)
assert correct_record_mock.destroy.called
self.assertFalse(first_record_mock.destroy.call_args_list)
self.assertFalse(last_record_mock.destroy.call_args_list)
assert not first_record_mock.destroy.call_args_list
assert not last_record_mock.destroy.call_args_list
def test_del_txt_record_error_finding_domain(self):
self.manager.get_all_domains.side_effect = API_ERROR
@ -172,4 +171,4 @@ class DigitalOceanClientTest(unittest.TestCase):
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
# This version of lexicon is required to address the problem described in
@ -32,6 +32,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-dnsimple',
version=version,
@ -67,6 +71,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_dnsimple._internal.dns_dnsimple."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -48,4 +50,4 @@ class DNSimpleLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseL
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-dnsmadeeasy',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_dnsmadeeasy._internal.dns_dnsmadeeasy."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -53,4 +55,4 @@ class DNSMadeEasyLexiconClientTest(unittest.TestCase,
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-gehirn',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_gehirn._internal.dns_gehirn."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -52,4 +54,4 @@ class GehirnLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLex
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -38,6 +38,19 @@ for an account with the following permissions:
* ``dns.resourceRecordSets.list``
* ``dns.resourceRecordSets.update``
(The closest role is `dns.admin <https://cloud.google.com/dns/docs/
access-control#dns.admin>`_).
If the above permissions are assigned at the `resource level <https://cloud
.google.com/dns/docs/zones/iam-per-resource-zones>`_, the same user must
have, at the PROJECT level, the following permissions:
* ``dns.managedZones.get``
* ``dns.managedZones.list``
(The closest role is `dns.reader <https://cloud.google.com/dns/docs/
access-control#dns.reader>`_).
Google provides instructions for `creating a service account <https://developers
.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount>`_ and
`information about the required permissions <https://cloud.google.com/dns/access

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'google-api-python-client>=1.5.5',
@ -33,6 +33,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-google',
version=version,
@ -68,6 +72,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,13 +1,14 @@
"""Tests for certbot_dns_google._internal.dns_google."""
import sys
import unittest
from unittest import mock
from googleapiclient import discovery
from googleapiclient.errors import Error
from googleapiclient.http import HttpMock
from httplib2 import ServerNotFoundError
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import os
@ -46,7 +47,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_cleanup(self):
# _attempt_cleanup | pylint: disable=protected-access
@ -54,13 +55,14 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.cleanup([self.achall])
expected = [mock.call.del_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
@mock.patch('httplib2.Http.request', side_effect=ServerNotFoundError)
@test_util.patch_display_util()
def test_without_auth(self, unused_mock_get_utility, unused_mock):
self.config.google_credentials = None
self.assertRaises(PluginError, self.auth.perform, [self.achall])
with pytest.raises(PluginError):
self.auth.perform([self.achall])
class GoogleClientTest(unittest.TestCase):
@ -111,19 +113,17 @@ class GoogleClientTest(unittest.TestCase):
unused_discovery_mock):
from certbot_dns_google._internal.dns_google import _GoogleClient
_GoogleClient(None)
self.assertFalse(credential_mock.called)
self.assertTrue(get_project_id_mock.called)
assert not credential_mock.called
assert get_project_id_mock.called
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
def test_client_bad_credentials_file(self, credential_mock):
credential_mock.side_effect = ValueError('Some exception buried in oauth2client')
with self.assertRaises(errors.PluginError) as cm:
with pytest.raises(errors.PluginError) as exc_info:
self._setUp_client_with_mock([])
self.assertEqual(
str(cm.exception),
"Error parsing credentials file '/not/a/real/path.json': "
assert str(exc_info.value) == \
"Error parsing credentials file '/not/a/real/path.json': " \
"Some exception buried in oauth2client"
)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -132,7 +132,7 @@ class GoogleClientTest(unittest.TestCase):
def test_add_txt_record(self, get_project_id_mock, credential_mock):
client, changes = self._setUp_client_with_mock([{'managedZones': [{'id': self.zone}]}])
credential_mock.assert_called_once_with('/not/a/real/path.json', mock.ANY)
self.assertFalse(get_project_id_mock.called)
assert not get_project_id_mock.called
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
@ -182,10 +182,10 @@ class GoogleClientTest(unittest.TestCase):
with mock.patch(mock_get_rrs) as mock_rrs:
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": self.record_ttl}
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertIs(changes.create.called, True)
assert changes.create.called is True
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertIn("sample-txt-contents", deletions["rrdatas"])
self.assertEqual(self.record_ttl, deletions["ttl"])
assert "sample-txt-contents" in deletions["rrdatas"]
assert self.record_ttl == deletions["ttl"]
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -199,10 +199,10 @@ class GoogleClientTest(unittest.TestCase):
custom_ttl = 300
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": custom_ttl}
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertIs(changes.create.called, True)
assert changes.create.called is True
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertIn("sample-txt-contents", deletions["rrdatas"])
self.assertEqual(custom_ttl, deletions["ttl"]) #otherwise HTTP 412
assert "sample-txt-contents" in deletions["rrdatas"]
assert custom_ttl == deletions["ttl"] #otherwise HTTP 412
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -212,7 +212,7 @@ class GoogleClientTest(unittest.TestCase):
[{'managedZones': [{'id': self.zone}]}])
client.add_txt_record(DOMAIN, "_acme-challenge.example.org",
"example-txt-contents", self.record_ttl)
self.assertIs(changes.create.called, False)
assert changes.create.called is False
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -220,8 +220,8 @@ class GoogleClientTest(unittest.TestCase):
def test_add_txt_record_error_during_zone_lookup(self, unused_credential_mock):
client, unused_changes = self._setUp_client_with_mock(API_ERROR)
self.assertRaises(errors.PluginError, client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -230,8 +230,8 @@ class GoogleClientTest(unittest.TestCase):
client, unused_changes = self._setUp_client_with_mock([{'managedZones': []},
{'managedZones': []}])
self.assertRaises(errors.PluginError, client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -240,8 +240,8 @@ class GoogleClientTest(unittest.TestCase):
client, changes = self._setUp_client_with_mock([{'managedZones': [{'id': self.zone}]}])
changes.create.side_effect = API_ERROR
self.assertRaises(errors.PluginError, client.add_txt_record,
DOMAIN, self.record_name, self.record_content, self.record_ttl)
with pytest.raises(errors.PluginError):
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -345,8 +345,8 @@ class GoogleClientTest(unittest.TestCase):
[{'managedZones': [{'id': self.zone}]}])
# Record name mocked in setUp
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertEqual(found["rrdatas"], ["\"example-txt-contents\""])
self.assertEqual(found["ttl"], 60)
assert found["rrdatas"] == ["\"example-txt-contents\""]
assert found["ttl"] == 60
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -355,7 +355,7 @@ class GoogleClientTest(unittest.TestCase):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}])
not_found = client.get_existing_txt_rrset(self.zone, "nonexistent.tld")
self.assertIsNone(not_found)
assert not_found is None
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -365,7 +365,7 @@ class GoogleClientTest(unittest.TestCase):
[{'managedZones': [{'id': self.zone}]}], API_ERROR)
# Record name mocked in setUp
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertIsNone(found)
assert found is None
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -374,7 +374,7 @@ class GoogleClientTest(unittest.TestCase):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}], API_ERROR)
rrset = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertFalse(rrset)
assert not rrset
def test_get_project_id(self):
from certbot_dns_google._internal.dns_google import _GoogleClient
@ -384,21 +384,23 @@ class GoogleClientTest(unittest.TestCase):
with mock.patch('httplib2.Http.request', return_value=(response, 'test-test-1')):
project_id = _GoogleClient.get_project_id()
self.assertEqual(project_id, 'test-test-1')
assert project_id == 'test-test-1'
with mock.patch('httplib2.Http.request', return_value=(response, b'test-test-1')):
project_id = _GoogleClient.get_project_id()
self.assertEqual(project_id, 'test-test-1')
assert project_id == 'test-test-1'
failed_response = DummyResponse()
failed_response.status = 404
with mock.patch('httplib2.Http.request',
return_value=(failed_response, "some detailed http error response")):
self.assertRaises(ValueError, _GoogleClient.get_project_id)
with pytest.raises(ValueError):
_GoogleClient.get_project_id()
with mock.patch('httplib2.Http.request', side_effect=ServerNotFoundError):
self.assertRaises(ServerNotFoundError, _GoogleClient.get_project_id)
with pytest.raises(ServerNotFoundError):
_GoogleClient.get_project_id()
class DummyResponse:
@ -411,4 +413,4 @@ class DummyResponse:
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-linode',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,11 @@
"""Tests for certbot_dns_linode._internal.dns_linode."""
import sys
import unittest
from unittest import mock
import pytest
from certbot import errors
from certbot.compat import os
from certbot.plugins import dns_test_common
@ -43,7 +46,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(3, client.api_version)
assert 3 == client.api_version
# pylint: disable=protected-access
def test_api_version_4_detection(self):
@ -55,7 +58,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(4, client.api_version)
assert 4 == client.api_version
# pylint: disable=protected-access
def test_api_version_3_detection_empty_version(self):
@ -67,7 +70,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(3, client.api_version)
assert 3 == client.api_version
# pylint: disable=protected-access
def test_api_version_4_detection_empty_version(self):
@ -79,7 +82,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(4, client.api_version)
assert 4 == client.api_version
# pylint: disable=protected-access
def test_api_version_3_manual(self):
@ -91,7 +94,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(3, client.api_version)
assert 3 == client.api_version
# pylint: disable=protected-access
def test_api_version_4_manual(self):
@ -103,7 +106,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth = Authenticator(config, "linode")
auth._setup_credentials()
client = auth._get_linode_client()
self.assertEqual(4, client.api_version)
assert 4 == client.api_version
# pylint: disable=protected-access
def test_api_version_error(self):
@ -114,7 +117,8 @@ class AuthenticatorTest(test_util.TempDirTestCase,
linode_propagation_seconds=0)
auth = Authenticator(config, "linode")
auth._setup_credentials()
self.assertRaises(errors.PluginError, auth._get_linode_client)
with pytest.raises(errors.PluginError):
auth._get_linode_client()
class LinodeLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
@ -144,4 +148,4 @@ class Linode4LexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLe
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-luadns',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_luadns._internal.dns_luadns."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -49,4 +51,4 @@ class LuaDNSLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLex
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-nsone',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_nsone._internal.dns_nsone."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -49,4 +51,4 @@ class NS1LexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexico
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-ovh',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,8 +1,10 @@
"""Tests for certbot_dns_ovh._internal.dns_ovh."""
import sys
import unittest
from unittest import mock
import pytest
from requests.exceptions import HTTPError
from certbot.compat import os
@ -59,4 +61,4 @@ class OVHLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexico
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -138,7 +138,7 @@ class _RFC2136Client:
except Exception as e:
raise errors.PluginError('Encountered error adding TXT record: {0}'
.format(e))
rcode = response.rcode() # type: ignore[attr-defined]
rcode = response.rcode()
if rcode == dns.rcode.NOERROR:
logger.debug('Successfully added TXT record %s', record_name)
@ -173,7 +173,7 @@ class _RFC2136Client:
except Exception as e:
raise errors.PluginError('Encountered error deleting TXT record: {0}'
.format(e))
rcode = response.rcode() # type: ignore[attr-defined]
rcode = response.rcode()
if rcode == dns.rcode.NOERROR:
logger.debug('Successfully deleted TXT record %s', record_name)
@ -217,7 +217,7 @@ class _RFC2136Client:
# Turn off Recursion Desired bit in query
request.flags ^= dns.flags.RD
# Use our TSIG keyring
request.use_tsig(self.keyring, algorithm=self.algorithm) # type: ignore[attr-defined]
request.use_tsig(self.keyring, algorithm=self.algorithm)
try:
try:
@ -225,11 +225,11 @@ class _RFC2136Client:
except (OSError, dns.exception.Timeout) as e:
logger.debug('TCP query failed, fallback to UDP: %s', e)
response = dns.query.udp(request, self.server, self._default_timeout, self.port)
rcode = response.rcode() # type: ignore[attr-defined]
rcode = response.rcode()
# Authoritative Answer bit should be set
if (rcode == dns.rcode.NOERROR
and response.get_rrset(response.answer, # type: ignore[attr-defined]
and response.get_rrset(response.answer,
domain, dns.rdataclass.IN, dns.rdatatype.SOA)
and response.flags & dns.flags.AA):
logger.debug('Received authoritative SOA response for %s', domain_name)

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'dnspython>=1.15.0',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-rfc2136',
version=version,
@ -65,6 +69,7 @@ setup(
install_requires=install_requires,
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,11 +1,13 @@
"""Tests for certbot_dns_rfc2136._internal.dns_rfc2136."""
import sys
import unittest
from unittest import mock
import dns.flags
import dns.rcode
import dns.tsig
import pytest
from certbot import errors
from certbot.compat import os
@ -45,7 +47,7 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.perform([self.achall])
expected = [mock.call.add_txt_record('_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_cleanup(self):
# _attempt_cleanup | pylint: disable=protected-access
@ -53,16 +55,15 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
self.auth.cleanup([self.achall])
expected = [mock.call.del_txt_record('_acme-challenge.'+DOMAIN, mock.ANY)]
self.assertEqual(expected, self.mock_client.mock_calls)
assert expected == self.mock_client.mock_calls
def test_invalid_algorithm_raises(self):
config = VALID_CONFIG.copy()
config["rfc2136_algorithm"] = "INVALID"
dns_test_common.write(config, self.config.rfc2136_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
@test_util.patch_display_util()
def test_valid_algorithm_passes(self, unused_mock_get_utility):
@ -77,9 +78,8 @@ class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthentic
config["rfc2136_server"] = "example.com"
dns_test_common.write(config, self.config.rfc2136_credentials)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
@test_util.patch_display_util()
def test_valid_server_passes(self, unused_mock_get_utility):
@ -111,7 +111,7 @@ class RFC2136ClientTest(unittest.TestCase):
self.rfc2136_client.add_txt_record("bar", "baz", 42)
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertIn('bar. 42 IN TXT "baz"', str(query_mock.call_args[0][0]))
assert 'bar. 42 IN TXT "baz"' in str(query_mock.call_args[0][0])
@mock.patch("dns.query.tcp")
def test_add_txt_record_wraps_errors(self, query_mock):
@ -119,10 +119,8 @@ class RFC2136ClientTest(unittest.TestCase):
# _find_domain | pylint: disable=protected-access
self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com")
self.assertRaises(
errors.PluginError,
self.rfc2136_client.add_txt_record,
"bar", "baz", 42)
with pytest.raises(errors.PluginError):
self.rfc2136_client.add_txt_record("bar", "baz", 42)
@mock.patch("dns.query.tcp")
def test_add_txt_record_server_error(self, query_mock):
@ -130,10 +128,8 @@ class RFC2136ClientTest(unittest.TestCase):
# _find_domain | pylint: disable=protected-access
self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com")
self.assertRaises(
errors.PluginError,
self.rfc2136_client.add_txt_record,
"bar", "baz", 42)
with pytest.raises(errors.PluginError):
self.rfc2136_client.add_txt_record("bar", "baz", 42)
@mock.patch("dns.query.tcp")
def test_del_txt_record(self, query_mock):
@ -144,7 +140,7 @@ class RFC2136ClientTest(unittest.TestCase):
self.rfc2136_client.del_txt_record("bar", "baz")
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertIn('bar. 0 NONE TXT "baz"', str(query_mock.call_args[0][0]))
assert 'bar. 0 NONE TXT "baz"' in str(query_mock.call_args[0][0])
@mock.patch("dns.query.tcp")
def test_del_txt_record_wraps_errors(self, query_mock):
@ -152,10 +148,8 @@ class RFC2136ClientTest(unittest.TestCase):
# _find_domain | pylint: disable=protected-access
self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com")
self.assertRaises(
errors.PluginError,
self.rfc2136_client.del_txt_record,
"bar", "baz")
with pytest.raises(errors.PluginError):
self.rfc2136_client.del_txt_record("bar", "baz")
@mock.patch("dns.query.tcp")
def test_del_txt_record_server_error(self, query_mock):
@ -163,10 +157,8 @@ class RFC2136ClientTest(unittest.TestCase):
# _find_domain | pylint: disable=protected-access
self.rfc2136_client._find_domain = mock.MagicMock(return_value="example.com")
self.assertRaises(
errors.PluginError,
self.rfc2136_client.del_txt_record,
"bar", "baz")
with pytest.raises(errors.PluginError):
self.rfc2136_client.del_txt_record("bar", "baz")
def test_find_domain(self):
# _query_soa | pylint: disable=protected-access
@ -175,17 +167,14 @@ class RFC2136ClientTest(unittest.TestCase):
# _find_domain | pylint: disable=protected-access
domain = self.rfc2136_client._find_domain('foo.bar.'+DOMAIN)
self.assertEqual(domain, DOMAIN)
assert domain == DOMAIN
def test_find_domain_wraps_errors(self):
# _query_soa | pylint: disable=protected-access
self.rfc2136_client._query_soa = mock.MagicMock(return_value=False)
self.assertRaises(
errors.PluginError,
# _find_domain | pylint: disable=protected-access
self.rfc2136_client._find_domain,
'foo.bar.'+DOMAIN)
with pytest.raises(errors.PluginError):
self.rfc2136_client._find_domain('foo.bar.'+DOMAIN)
@mock.patch("dns.query.tcp")
def test_query_soa_found(self, query_mock):
@ -196,7 +185,7 @@ class RFC2136ClientTest(unittest.TestCase):
result = self.rfc2136_client._query_soa(DOMAIN)
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertTrue(result)
assert result
@mock.patch("dns.query.tcp")
def test_query_soa_not_found(self, query_mock):
@ -206,17 +195,14 @@ class RFC2136ClientTest(unittest.TestCase):
result = self.rfc2136_client._query_soa(DOMAIN)
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertFalse(result)
assert not result
@mock.patch("dns.query.tcp")
def test_query_soa_wraps_errors(self, query_mock):
query_mock.side_effect = Exception
self.assertRaises(
errors.PluginError,
# _query_soa | pylint: disable=protected-access
self.rfc2136_client._query_soa,
DOMAIN)
with pytest.raises(errors.PluginError):
self.rfc2136_client._query_soa(DOMAIN)
@mock.patch("dns.query.udp")
@mock.patch("dns.query.tcp")
@ -230,8 +216,8 @@ class RFC2136ClientTest(unittest.TestCase):
tcp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
udp_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertTrue(result)
assert result
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

View file

@ -4,7 +4,7 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '2.3.0.dev0'
version = '2.4.0.dev0'
install_requires = [
'boto3>=1.15.15',
@ -30,6 +30,10 @@ docs_extras = [
'sphinx_rtd_theme',
]
test_extras = [
'pytest',
]
setup(
name='certbot-dns-route53',
version=version,
@ -65,6 +69,7 @@ setup(
keywords=['certbot', 'route53', 'aws'],
extras_require={
'docs': docs_extras,
'test': test_extras,
},
entry_points={
'certbot.plugins': [

View file

@ -1,10 +1,12 @@
"""Tests for certbot_dns_route53._internal.dns_route53.Authenticator"""
import sys
import unittest
from unittest import mock
from botocore.exceptions import ClientError
from botocore.exceptions import NoCredentialsError
import pytest
from certbot import errors
from certbot.compat import os
@ -42,22 +44,20 @@ class AuthenticatorTest(unittest.TestCase, dns_test_common.BaseAuthenticatorTest
self.auth._change_txt_record.assert_called_once_with("UPSERT",
'_acme-challenge.' + DOMAIN,
mock.ANY)
self.assertEqual(self.auth._wait_for_change.call_count, 1)
assert self.auth._wait_for_change.call_count == 1
def test_perform_no_credentials_error(self):
self.auth._change_txt_record = mock.MagicMock(side_effect=NoCredentialsError)
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
def test_perform_client_error(self):
self.auth._change_txt_record = mock.MagicMock(
side_effect=ClientError({"Error": {"Code": "foo"}}, "bar"))
self.assertRaises(errors.PluginError,
self.auth.perform,
[self.achall])
with pytest.raises(errors.PluginError):
self.auth.perform([self.achall])
def test_cleanup(self):
self.auth._attempt_cleanup = True
@ -149,7 +149,7 @@ class ClientTest(unittest.TestCase):
]
result = self.client._find_zone_id_for_domain("foo.example.com")
self.assertEqual(result, "EXAMPLE")
assert result == "EXAMPLE"
def test_find_zone_id_for_domain_pagination(self):
self.client.r53.get_paginator = mock.MagicMock()
@ -169,15 +169,14 @@ class ClientTest(unittest.TestCase):
]
result = self.client._find_zone_id_for_domain("foo.example.com")
self.assertEqual(result, "FOO")
assert result == "FOO"
def test_find_zone_id_for_domain_no_results(self):
self.client.r53.get_paginator = mock.MagicMock()
self.client.r53.get_paginator().paginate.return_value = []
self.assertRaises(errors.PluginError,
self.client._find_zone_id_for_domain,
"foo.example.com")
with pytest.raises(errors.PluginError):
self.client._find_zone_id_for_domain("foo.example.com")
def test_find_zone_id_for_domain_no_correct_results(self):
self.client.r53.get_paginator = mock.MagicMock()
@ -190,9 +189,8 @@ class ClientTest(unittest.TestCase):
},
]
self.assertRaises(errors.PluginError,
self.client._find_zone_id_for_domain,
"foo.example.com")
with pytest.raises(errors.PluginError):
self.client._find_zone_id_for_domain("foo.example.com")
def test_change_txt_record(self):
self.client._find_zone_id_for_domain = mock.MagicMock()
@ -202,7 +200,7 @@ class ClientTest(unittest.TestCase):
self.client._change_txt_record("FOO", DOMAIN, "foo")
call_count = self.client.r53.change_resource_record_sets.call_count
self.assertEqual(call_count, 1)
assert call_count == 1
def test_change_txt_record_delete(self):
self.client._find_zone_id_for_domain = mock.MagicMock()
@ -216,13 +214,12 @@ class ClientTest(unittest.TestCase):
self.client._change_txt_record("DELETE", DOMAIN, validation)
call_count = self.client.r53.change_resource_record_sets.call_count
self.assertEqual(call_count, 1)
assert call_count == 1
call_args = self.client.r53.change_resource_record_sets.call_args_list[0][1]
call_args_batch = call_args["ChangeBatch"]["Changes"][0]
self.assertEqual(call_args_batch["Action"], "DELETE")
self.assertEqual(
call_args_batch["ResourceRecordSet"]["ResourceRecords"],
[validation_record])
assert call_args_batch["Action"] == "DELETE"
assert call_args_batch["ResourceRecordSet"]["ResourceRecords"] == \
[validation_record]
def test_change_txt_record_multirecord(self):
self.client._find_zone_id_for_domain = mock.MagicMock()
@ -239,12 +236,11 @@ class ClientTest(unittest.TestCase):
call_count = self.client.r53.change_resource_record_sets.call_count
call_args = self.client.r53.change_resource_record_sets.call_args_list[0][1]
call_args_batch = call_args["ChangeBatch"]["Changes"][0]
self.assertEqual(call_args_batch["Action"], "UPSERT")
self.assertEqual(
call_args_batch["ResourceRecordSet"]["ResourceRecords"],
[{"Value": "\"pre-existing-value-two\""}])
assert call_args_batch["Action"] == "UPSERT"
assert call_args_batch["ResourceRecordSet"]["ResourceRecords"] == \
[{"Value": "\"pre-existing-value-two\""}]
self.assertEqual(call_count, 1)
assert call_count == 1
def test_wait_for_change(self):
self.client.r53.get_change = mock.MagicMock(
@ -253,8 +249,8 @@ class ClientTest(unittest.TestCase):
self.client._wait_for_change(1)
self.assertTrue(self.client.r53.get_change.called)
assert self.client.r53.get_change.called
if __name__ == "__main__":
unittest.main() # pragma: no cover
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover

Some files were not shown because too many files have changed in this diff Show more