mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-14 12:32:44 +00:00
code coverage updates (#13245)
Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
1 parent
2c8ec53ed2
commit
16309370ba
7 files changed
+71
-48
No files matched your search
@@ -366,13 +366,12 @@ jobs:
|
||||
mode: walltime
|
||||
run: pytest ./src/performance --codspeed
|
||||
|
||||
coverage:
|
||||
name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
|
||||
sqlite:
|
||||
name: Tests - DB [SQLite] ${{ matrix.python_version }}
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
continue-on-error: true # continue if a step fails so that coverage gets pushed
|
||||
strategy:
|
||||
matrix:
|
||||
python_version: [3.12, 3.14]
|
||||
@@ -404,21 +403,8 @@ jobs:
|
||||
run: |
|
||||
invoke migrate --detect
|
||||
python3 .github/scripts/check_migration_files.py
|
||||
- name: Coverage Tests
|
||||
run: invoke dev.test --check --coverage --translations
|
||||
- name: Upload raw coverage to artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: coverage
|
||||
path: .coverage
|
||||
retention-days: 14
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: inventree/InvenTree
|
||||
flags: backend
|
||||
- name: Run Tests
|
||||
run: invoke dev.test --check --translations
|
||||
|
||||
performance:
|
||||
name: Tests - Performance
|
||||
@@ -458,7 +444,7 @@ jobs:
|
||||
run: inv dev.test --pytest
|
||||
|
||||
postgres:
|
||||
name: Tests - DB [PostgreSQL]
|
||||
name: Tests - DB [PostgreSQL] + Coverage
|
||||
runs-on: ubuntu-24.04
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
@@ -501,8 +487,25 @@ jobs:
|
||||
dev-install: true
|
||||
update: true
|
||||
static: true
|
||||
- name: Run Tests
|
||||
run: invoke dev.test --check --translations
|
||||
# Coverage is collected here (not on SQLite) so that tests which require
|
||||
# select_for_update (concurrency / locking tests) contribute to coverage
|
||||
- name: Coverage Tests
|
||||
run: invoke dev.test --check --coverage --translations
|
||||
- name: Upload raw coverage to artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
if: always()
|
||||
with:
|
||||
name: coverage
|
||||
path: .coverage
|
||||
include-hidden-files: true
|
||||
retention-days: 14
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: inventree/InvenTree
|
||||
flags: backend
|
||||
- name: Data Export Test
|
||||
uses: $/.github/actions/migration
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,6 @@
|
||||
ignore:
|
||||
- "src/backend/InvenTree/**/test_migrations.py"
|
||||
- "src/backend/InvenTree/**/migrations/**"
|
||||
|
||||
coverage:
|
||||
status:
|
||||
@@ -20,11 +21,10 @@ flag_management:
|
||||
statuses:
|
||||
- type: project
|
||||
target: 85%
|
||||
# Legacy flag - no longer uploaded (replaced by migrations-sqlite / -mysql / -postgresql).
|
||||
# Disable carryforward so its stale report (incl. files since deleted) is dropped.
|
||||
- name: migrations
|
||||
carryforward: true
|
||||
statuses:
|
||||
- type: project
|
||||
target: 38%
|
||||
carryforward: false
|
||||
- name: web
|
||||
carryforward: true
|
||||
statuses:
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ unused-ignore-comment = "error"
|
||||
[tool.coverage.run]
|
||||
source = ["src/backend/InvenTree", "InvenTree"]
|
||||
dynamic_context = "test_function"
|
||||
omit = ["*/test_migrations.py"]
|
||||
omit = ["*/test_migrations.py", "*/migrations/*"]
|
||||
|
||||
[tool.coverage.html]
|
||||
show_contexts = true
|
||||
|
||||
@@ -454,6 +454,14 @@ MIDDLEWARE = ensure_removed(
|
||||
MIDDLEWARE, 'oauth2_provider.middleware.OAuth2TokenMiddleware', 'MIDDLEWARE'
|
||||
)
|
||||
|
||||
if TESTING:
|
||||
# WhiteNoise scans the entire STATIC_ROOT whenever the middleware chain is loaded,
|
||||
# which happens for every new test client (i.e. every test). No unit test relies on
|
||||
# static files being served via HTTP, so the middleware is not loaded during testing.
|
||||
MIDDLEWARE = [
|
||||
m for m in MIDDLEWARE if m != 'whitenoise.middleware.WhiteNoiseMiddleware'
|
||||
]
|
||||
|
||||
# Audit log configuration (django-auditlog)
|
||||
# Ref: https://django-auditlog.readthedocs.io/en/latest/usage.html#settings
|
||||
# Note: AUDITLOG_LOGENTRY_MODEL is a swappable model, and must not be changed once migrations have been applied
|
||||
|
||||
@@ -296,9 +296,11 @@ def resolve_cid() -> Optional[str]:
|
||||
"""Resolve the current correlation ID.
|
||||
|
||||
- Within a request: the request ID bound by django-structlog, so that audit entries
|
||||
can be matched against log entries (and the X-Request-ID header) for the request.
|
||||
Note: django-structlog uses the incoming 'X-Request-ID' header if provided
|
||||
(e.g. by a reverse proxy), otherwise generates a new UUID.
|
||||
can be matched against log entries (and the X-InvenTree-ReqId header) for the request.
|
||||
Note: django-structlog generates a new UUID for each request - an incoming
|
||||
'X-Request-ID' header is not trusted (TRUST_UPSTREAM_REQUEST_ID is not set),
|
||||
so clients cannot spoof the correlation ID. The ID is returned to the client
|
||||
in the 'X-InvenTree-ReqId' response header.
|
||||
- Otherwise (e.g. background tasks, or if the structlog middleware is not installed):
|
||||
a server-generated ID (see audit_request / audit_source).
|
||||
"""
|
||||
|
||||
@@ -458,29 +458,38 @@ class AuditAPITest(AuditTestMixin, InvenTreeAPITestCase):
|
||||
self.assertEqual(data[0]['changes']['description'][1], 'Session edit')
|
||||
|
||||
def test_correlation_id(self):
|
||||
"""The correlation ID matches the request ID (as used by structlog)."""
|
||||
"""The correlation ID matches the request ID (as used by structlog).
|
||||
|
||||
The request ID is generated server-side - a client-supplied X-Request-ID
|
||||
header is not trusted (django-structlog TRUST_UPSTREAM_REQUEST_ID is not set),
|
||||
so a client cannot spoof the correlation ID recorded in the audit log.
|
||||
"""
|
||||
url = reverse('api-po-detail', kwargs={'pk': self.po.pk})
|
||||
|
||||
# Request ID provided by the client / reverse proxy
|
||||
self.patch(url, {'description': 'One'}, HTTP_X_REQUEST_ID='req-abc-123')
|
||||
|
||||
entry = self.entries_for(self.po, action=AuditLogEntry.Action.UPDATE).get()
|
||||
self.assertEqual(entry.cid, 'req-abc-123')
|
||||
|
||||
# Otherwise a unique ID is generated per request
|
||||
self.patch(url, {'description': 'Two'})
|
||||
self.patch(url, {'description': 'Three'})
|
||||
|
||||
cids = list(
|
||||
self
|
||||
.entries_for(self.po, action=AuditLogEntry.Action.UPDATE)
|
||||
.exclude(cid='req-abc-123')
|
||||
.values_list('cid', flat=True)
|
||||
# A client-supplied request ID is ignored
|
||||
response = self.patch(
|
||||
url, {'description': 'One'}, HTTP_X_REQUEST_ID='req-abc-123'
|
||||
)
|
||||
|
||||
self.assertEqual(len(cids), 2)
|
||||
self.assertTrue(all(cids))
|
||||
self.assertNotEqual(cids[0], cids[1])
|
||||
entry = self.entries_for(self.po, action=AuditLogEntry.Action.UPDATE).get()
|
||||
self.assertNotEqual(entry.cid, 'req-abc-123')
|
||||
self.assertEqual(entry.cid, response['X-InvenTree-ReqId'])
|
||||
|
||||
# A unique ID is generated per request, matching the response header
|
||||
req_ids = {entry.cid}
|
||||
|
||||
for description in ['Two', 'Three']:
|
||||
response = self.patch(url, {'description': description})
|
||||
req_ids.add(response['X-InvenTree-ReqId'])
|
||||
|
||||
cids = set(
|
||||
self.entries_for(self.po, action=AuditLogEntry.Action.UPDATE).values_list(
|
||||
'cid', flat=True
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(len(cids), 3)
|
||||
self.assertEqual(cids, req_ids)
|
||||
|
||||
@override_settings(BASIC_AUTH_ENABLED=True)
|
||||
def test_basic_auth_actor(self):
|
||||
|
||||
Reference in new issue
Block a user