mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 14:26:10 -04:00
Merge branch 'master' of https://github.com/certbot/certbot into docs-rewrite/renewal
This commit is contained in:
commit
26d951fc7d
202 changed files with 5854 additions and 5632 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
7
.github/codecov.yml
vendored
Normal 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
35
.github/stale.yml
vendored
|
|
@ -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
25
.github/workflows/notify_weekly.yaml
vendored
Normal 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
45
.github/workflows/stale.yml
vendored
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
5
SECURITY.md
Normal 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).
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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.')
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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}'
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue