Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/qa/1.x' into qa/2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
sevein committed Nov 5, 2024
2 parents b862b22 + bade7ad commit 36e6893
Show file tree
Hide file tree
Showing 18 changed files with 734 additions and 592 deletions.
1 change: 0 additions & 1 deletion worker/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ dev-dependencies = [
"pytest>=8.2.2",
"pytest-cov>=5.0.0",
"pytest-django>=4.8.0",
"pytest-mock>=3.14.0",
"pytest-randomly>=3.15.0",
]

Expand Down
52 changes: 36 additions & 16 deletions worker/tests/test_antivirus.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from collections import OrderedDict
from collections import namedtuple
from unittest import mock

import pytest

Expand Down Expand Up @@ -79,33 +80,39 @@ def version_attrs(self):


def setup_test_scan_file_mocks(
mocker,
get_scanner,
file_already_scanned_mock,
file_objects_get,
file_already_scanned=False,
file_size=1024,
scanner_should_except=False,
scanner_passed=False,
):
file_already_scanned_mock.return_value = file_already_scanned
file_objects_get.return_value = FileMock(size=file_size)
deps = namedtuple("deps", ["file_already_scanned", "file_get", "scanner"])(
file_already_scanned=mocker.patch(
"worker.clientScripts.archivematica_clamscan.file_already_scanned",
return_value=file_already_scanned,
),
file_get=mocker.patch(
"worker.main.models.File.objects.get", return_value=FileMock(size=file_size)
),
file_already_scanned=file_already_scanned_mock,
file_get=file_objects_get,
scanner=ScannerMock(should_except=scanner_should_except, passed=scanner_passed),
)

mocker.patch(
"worker.clientScripts.archivematica_clamscan.get_scanner",
return_value=deps.scanner,
)
get_scanner.return_value = deps.scanner

return deps


def test_scan_file_already_scanned(mocker):
deps = setup_test_scan_file_mocks(mocker, file_already_scanned=True)
@mock.patch("worker.clientScripts.archivematica_clamscan.file_already_scanned")
@mock.patch("worker.main.models.File.objects.get")
@mock.patch("worker.clientScripts.archivematica_clamscan.get_scanner")
def test_scan_file_already_scanned(
get_scanner, file_objects_get, file_already_scanned_mock
):
deps = setup_test_scan_file_mocks(
get_scanner,
file_already_scanned_mock,
file_objects_get,
file_already_scanned=True,
)

exit_code = archivematica_clamscan.scan_file([], **dict(args))

Expand Down Expand Up @@ -163,8 +170,21 @@ def test_scan_file_already_scanned(mocker):
),
],
)
def test_scan_file(mocker, setup_kwargs, exit_code, queue_event_params, settings):
setup_test_scan_file_mocks(mocker, **setup_kwargs)
@mock.patch("worker.clientScripts.archivematica_clamscan.file_already_scanned")
@mock.patch("worker.main.models.File.objects.get")
@mock.patch("worker.clientScripts.archivematica_clamscan.get_scanner")
def test_scan_file(
get_scanner,
file_objects_get,
file_already_scanned_mock,
setup_kwargs,
exit_code,
queue_event_params,
settings,
):
setup_test_scan_file_mocks(
get_scanner, file_already_scanned_mock, file_objects_get, **setup_kwargs
)

# Here the user configurable thresholds for maimum file size, and maximum
# scan size are being tested. The scan size is offset so as to enable the
Expand Down
249 changes: 158 additions & 91 deletions worker/tests/test_antivirus_clamdscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import errno
from collections import namedtuple
from unittest import mock

from clamd import BufferTooLongError
from clamd import ClamdNetworkSocket
Expand All @@ -21,32 +22,30 @@ def setup_clamdscanner(
return archivematica_clamscan.ClamdScanner()


def test_clamdscanner_version_props(mocker, settings):
def test_clamdscanner_version_props(settings):
scanner = setup_clamdscanner(settings)
mocker.patch.object(
with mock.patch.object(
scanner,
"version_attrs",
return_value=("ClamAV 0.99.2", "23992/Fri Oct 27 05:04:12 2017"),
)
):
assert scanner.program() == "ClamAV (clamd)"
assert scanner.version() == "ClamAV 0.99.2"
assert scanner.virus_definitions() == "23992/Fri Oct 27 05:04:12 2017"

assert scanner.program() == "ClamAV (clamd)"
assert scanner.version() == "ClamAV 0.99.2"
assert scanner.virus_definitions() == "23992/Fri Oct 27 05:04:12 2017"


def test_clamdscanner_version_attrs(mocker, settings):
def test_clamdscanner_version_attrs(settings):
scanner = setup_clamdscanner(settings, addr="/var/run/clamav/clamd.ctl")
version = mocker.patch.object(
with mock.patch.object(
scanner.client,
"version",
return_value="ClamAV 0.99.2/23992/Fri Oct 27 05:04:12 2017",
)

assert scanner.version_attrs() == (
"ClamAV 0.99.2",
"23992/Fri Oct 27 05:04:12 2017",
)
version.assert_called_once()
) as version:
assert scanner.version_attrs() == (
"ClamAV 0.99.2",
"23992/Fri Oct 27 05:04:12 2017",
)
version.assert_called_once()


def test_clamdscanner_get_client(settings):
Expand All @@ -60,24 +59,22 @@ def test_clamdscanner_get_client(settings):
assert scanner.client.timeout == 15.5


def test_clamdscanner_scan(mocker, settings):
def test_clamdscanner_scan(settings):
OKAY_RET = ("OK", None)
ERROR_RET = ("ERROR", "Permission denied")
FOUND_RET = ("FOUND", "Eicar-Test-Signature")

def patch(scanner, ret=OKAY_RET, excepts=False):
def patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET, excepts=False):
"""Patch the scanner function and enable testing of exceptions raised
by clamdscanner that we want to control. excepts can take an argument
of True to pass a generic exception. excepts can also take an exception
as an argument for better granularity.
"""
pass_by_stream.return_value = {"stream": ret}
pass_by_reference.return_value = {"/file": ret}
deps = namedtuple("deps", ["pass_by_stream", "pass_by_reference"])(
pass_by_stream=mocker.patch.object(
scanner, "pass_by_stream", return_value={"stream": ret}
),
pass_by_reference=mocker.patch.object(
scanner, "pass_by_reference", return_value={"/file": ret}
),
pass_by_stream=pass_by_stream,
pass_by_reference=pass_by_reference,
)
if excepts is not False:
e = excepts
Expand All @@ -88,72 +85,142 @@ def patch(scanner, ret=OKAY_RET, excepts=False):
return deps

scanner = setup_clamdscanner(settings, stream=False)
deps = patch(scanner, ret=OKAY_RET)
passed, state, details = scanner.scan("/file")
assert passed is True
assert state == "OK"
assert details is None
deps.pass_by_stream.assert_not_called()
deps.pass_by_reference.assert_called_once()

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
deps = patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET)
passed, state, details = scanner.scan("/file")
assert passed is True
assert state == "OK"
assert details is None
deps.pass_by_stream.assert_not_called()
deps.pass_by_reference.assert_called_once()

scanner = setup_clamdscanner(settings, stream=True)
deps = patch(scanner, ret=OKAY_RET)
passed, state, details = scanner.scan("/file")
assert passed is True
assert state == "OK"
assert details is None
deps.pass_by_stream.assert_called_once()
deps.pass_by_reference.assert_not_called()

patch(scanner, ret=ERROR_RET)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state == "ERROR"
assert details == "Permission denied"

patch(scanner, ret=FOUND_RET)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state == "FOUND"
assert details == "Eicar-Test-Signature"

# Testing a generic Exception returned by the clamdscan micorservice.
patch(scanner, ret=OKAY_RET, excepts=True)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state is None
assert details is None

# Testing a generic IOError that is not a broken pipe error that we're
# expecting to be able to manage from clamdscan.
patch(scanner, ret=OKAY_RET, excepts=OSError("Testing a generic IO Error"))
passed, state, details = scanner.scan("/file")
assert passed is False
assert state is None
assert details is None

# Broken pipe is a known error from the clamd library.
brokenpipe_error = OSError("Testing a broken pipe error")
brokenpipe_error.errno = errno.EPIPE
patch(scanner, ret=OKAY_RET, excepts=brokenpipe_error)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None

# The INSTREAM size limit error is known to us; test it here.
instream_error = BufferTooLongError("INSTREAM size limit exceeded. ERROR.")
patch(scanner, ret=OKAY_RET, excepts=instream_error)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None

# The clamd library can return a further error code here, and we we test it
# to make sure that if it does, it is managed.
connection_error = ConnectionError("Error while reading from socket.")
patch(scanner, ret=OKAY_RET, excepts=connection_error)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None
with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
deps = patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET)
passed, state, details = scanner.scan("/file")
assert passed is True
assert state == "OK"
assert details is None
deps.pass_by_stream.assert_called_once()
deps.pass_by_reference.assert_not_called()

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
patch(pass_by_stream, pass_by_reference, scanner, ret=ERROR_RET)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state == "ERROR"
assert details == "Permission denied"

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
patch(pass_by_stream, pass_by_reference, scanner, ret=FOUND_RET)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state == "FOUND"
assert details == "Eicar-Test-Signature"

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
# Testing a generic Exception returned by the clamdscan micorservice.
patch(pass_by_stream, pass_by_reference, scanner, ret=OKAY_RET, excepts=True)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state is None
assert details is None

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
# Testing a generic IOError that is not a broken pipe error that we're
# expecting to be able to manage from clamdscan.
patch(
pass_by_stream,
pass_by_reference,
scanner,
ret=OKAY_RET,
excepts=OSError("Testing a generic IO Error"),
)
passed, state, details = scanner.scan("/file")
assert passed is False
assert state is None
assert details is None

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
# Broken pipe is a known error from the clamd library.
brokenpipe_error = OSError("Testing a broken pipe error")
brokenpipe_error.errno = errno.EPIPE
patch(
pass_by_stream,
pass_by_reference,
scanner,
ret=OKAY_RET,
excepts=brokenpipe_error,
)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
# The INSTREAM size limit error is known to us; test it here.
instream_error = BufferTooLongError("INSTREAM size limit exceeded. ERROR.")
patch(
pass_by_stream,
pass_by_reference,
scanner,
ret=OKAY_RET,
excepts=instream_error,
)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None

with mock.patch.object(
scanner, "pass_by_stream"
) as pass_by_stream, mock.patch.object(
scanner, "pass_by_reference"
) as pass_by_reference:
# The clamd library can return a further error code here, and we we test it
# to make sure that if it does, it is managed.
connection_error = ConnectionError("Error while reading from socket.")
patch(
pass_by_stream,
pass_by_reference,
scanner,
ret=OKAY_RET,
excepts=connection_error,
)
passed, state, details = scanner.scan("/file")
assert passed is None
assert state is None
assert details is None
Loading

0 comments on commit 36e6893

Please sign in to comment.