Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ COPY . /backend
COPY ./utils/docker/entrypoint.sh /entrypoint.sh
WORKDIR /backend

RUN poetry install --no-interaction --only main
ARG INSTALL_DEV_DEPS=false
RUN if [ "$INSTALL_DEV_DEPS" = "true" ]; then \
poetry install --no-interaction; \
else \
poetry install --no-interaction --only main; \
fi

ENV DJANGO_APP="kernelCI"

Expand Down
21 changes: 21 additions & 0 deletions backend/conftest.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import tomllib
from pytest import Item
import os
import django
from django.conf import settings


def pytest_addoption(parser):
parser.addoption(
"--run-all", action="store_true", default=False, help="run all test cases"
)
parser.addoption(
"--use-local-db",
action="store_true",
default=False,
help="use local test database instead of remote",
)


def pytest_configure(config):
"""Configure Django settings for tests."""
if not settings.configured:
use_local_db = config.getoption("--use-local-db")

if use_local_db:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kernelCI.test_settings")
else:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kernelCI.settings")

django.setup()


def get_test_markers() -> list[str]:
Expand Down
93 changes: 93 additions & 0 deletions backend/kernelCI/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Test-specific Django settings for integration tests with local database.
"""

# Override database configuration for tests
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "kcidb_test",
"USER": "test_user",
"PASSWORD": "test_password",
"HOST": "test_db",
"PORT": "5432",
"OPTIONS": {
"connect_timeout": 5,
},
},
"dashboard_db": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "dashboard_test",
"USER": "test_user",
"PASSWORD": "test_password",
"HOST": "test_db",
"PORT": "5432",
"OPTIONS": {
"connect_timeout": 5,
},
},
"cache": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
"notifications": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
},
}


# Disable migrations for faster tests
class DisableMigrations:
def __contains__(self, item):
return True

def __getitem__(self, item):
return None


MIGRATION_MODULES = DisableMigrations()

# Disable debug for tests
DEBUG = False
DEBUG_SQL_QUERY = False

# Use in-memory cache for tests
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "test-cache",
}
}

# Disable CORS for tests
CORS_ALLOW_ALL_ORIGINS = True

# Shorter cache timeout for tests
CACHE_TIMEOUT = 60

# Disable security features for tests
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
SECURE_SSL_REDIRECT = False
SECURE_HSTS_SECONDS = 0

# Disable cron jobs for tests
CRONJOBS = []

# Use test email backend
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"

# Disable logging for tests
LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"handlers": {
"null": {
"class": "logging.NullHandler",
},
},
"root": {
"handlers": ["null"],
},
}
221 changes: 221 additions & 0 deletions backend/kernelCI_app/management/commands/seed_test_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
"""
Management command to seed test database with realistic data.
"""

from django.core.management.base import BaseCommand
from django.db import transaction
from kernelCI_app.tests.factories import (
CheckoutFactory,
BuildFactory,
TestFactory,
IssueFactory,
IncidentFactory,
)
from kernelCI_app.tests.factories.test_data_mapping import (
ISSUE_TEST_DATA,
EXPECTED_BUILD_IDS,
)


class Command(BaseCommand):
help = "Seed test database with realistic data for integration tests"

def add_arguments(self, parser):
parser.add_argument(
"--checkouts",
type=int,
default=50,
help="Number of checkouts to create (default: 50)",
)
parser.add_argument(
"--issues",
type=int,
default=20,
help="Number of issues to create (default: 20)",
)
parser.add_argument(
"--clear", action="store_true", help="Clear existing data before seeding"
)

def handle(self, *args, **options):
if options["clear"]:
self.stdout.write("Clearing existing data...")
self.clear_data()

self.stdout.write("Seeding test database with realistic data...")

with transaction.atomic():
checkouts = self.create_checkouts(options["checkouts"])

builds = self.create_builds(checkouts)

tests = self.create_tests_and_boots(builds)

issues = self.create_issues(options["issues"])

self.create_incidents(issues, builds, tests)

self.stdout.write(
self.style.SUCCESS(
f"Successfully seeded database with:\n"
f"- {len(checkouts)} checkouts\n"
f"- {len(builds)} builds\n"
f"- {len(tests)} tests\n"
f"- {len(issues)} issues\n"
)
)

def clear_data(self):
"""Clear existing test data."""
from kernelCI_app.models import (
Issues,
Tests,
Builds,
Checkouts,
Incidents,
)

Incidents.objects.all().delete()
Issues.objects.all().delete()
Tests.objects.all().delete()
Builds.objects.all().delete()
Checkouts.objects.all().delete()

def create_checkouts(self, count):
"""Create checkouts with realistic data including specific test data."""
self.stdout.write(f"Creating {count} checkouts...")

checkouts = []

for _ in range(count):
checkout = CheckoutFactory.create()
checkouts.append(checkout)

self.stdout.write(
f"Created checkouts: {', '.join(checkout.id for checkout in checkouts)}"
)

return checkouts

def create_builds(self, checkouts):
"""Create builds for checkouts."""
self.stdout.write("Creating builds...")

builds = []
for checkout in checkouts:
build_ids = [
build_id
for build_id, data in EXPECTED_BUILD_IDS.items()
if data.get("checkout_id") == checkout.id
]

for specific_build_id in build_ids:
build_data = EXPECTED_BUILD_IDS[specific_build_id]
build = BuildFactory.create(
checkout=checkout,
origin=build_data.get("origin", checkout.origin),
architecture=build_data.get("architecture", None),
config_name=build_data.get("config_name", "defconfig"),
status=build_data.get("status", None),
id=specific_build_id,
)
builds.append(build)

return builds

def create_tests_and_boots(self, builds):
"""Create tests and boots for builds."""
self.stdout.write("Creating tests...")
tests = []
test_ids = [
"maestro:67b898cdf7707533c0067a02",
"maestro:67bd70e6323b35c54a8824a0",
"test_issue_test",
]
checkout = CheckoutFactory.create()
build = BuildFactory.create(
id="failed_tests_build",
origin="maestro",
config_name="defconfig",
architecture="x86_64",
status="FAIL",
checkout=checkout,
)

for test_id in test_ids:
test = TestFactory.create(
id=test_id,
origin=build.origin,
build=build,
)
tests.append(test)

for build in builds:
if build.id == "amlogic_build_valid":
test = TestFactory.create(
build=build,
origin=build.origin,
path="boot.boot_test",
)
tests.append(test)
test = TestFactory.create(
build=build,
origin=build.origin,
path="test.test_test",
)
tests.append(test)
else:
test = TestFactory.create(
build=build,
origin=build.origin,
)
tests.append(test)

return tests

def create_issues(self, count):
"""Create issues including specific test data."""
self.stdout.write(f"Creating {count} issues...")

issues = []

for _ in range(count):
issue = IssueFactory.create()
issues.append(issue)

return issues

def create_incidents(self, issues, builds, tests):
"""Create incidents - only essential ones for tests."""
self.stdout.write("Creating only essential incidents for tests...")
for issue in issues:
if issue.id in ISSUE_TEST_DATA:
issue_data = ISSUE_TEST_DATA[issue.id]
if "build_ids" in issue_data and issue_data["build_ids"]:
for build_id in issue_data["build_ids"]:
build = next((b for b in builds if b.id == build_id), None)
if build:
IncidentFactory.create(
issue=issue,
build=build,
origin=issue.origin,
issue_version=issue.version,
)
self.stdout.write(
f"Created build incident for: {build.id} with issue: {issue.id}"
)

if "test_ids" in issue_data and issue_data["test_ids"]:
for test_id in issue_data["test_ids"]:
test = next((t for t in tests if t.id == test_id), None)

if test:
IncidentFactory.create(
issue=issue,
test=test,
origin=issue.origin,
issue_version=issue.version,
)
self.stdout.write(
f"Created test incident for: {test.id} with issue: {issue.id}"
)
17 changes: 17 additions & 0 deletions backend/kernelCI_app/tests/factories/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Factories for generating test data using factory-boy.
"""

from .checkout_factory import CheckoutFactory
from .build_factory import BuildFactory
from .test_factory import TestFactory
from .issue_factory import IssueFactory
from .incident_factory import IncidentFactory

__all__ = [
"CheckoutFactory",
"BuildFactory",
"TestFactory",
"IssueFactory",
"IncidentFactory",
]
Loading