Repository: packit-service/packit
Pull request #788 (2020-04-08 15:46:59)

Job metadata config & schema
- Allows us to validate job metadata in `.packit.yaml`
- Renames job metadata key `dist-git-branch` to `dist_git_branch` (#782).

packit-as-a-service[bot]: Congratulations! One of the builds has completed. :champagne:

You can install the built RPMs by following these steps:

* `sudo yum install -y dnf-plugins-core` on RHEL 8
* `sudo dnf install -y dnf-plugins-core` on Fedora
* `dnf copr enable packit/packit-service-packit-788`
* And now you can install the packages.

Please note that the RPMs should be used only in a testing environment.
softwarefactory-project-zuul[bot]: Build failed.

- [packit-pre-commit ](https://softwarefactory-project.io/zuul/t/local/build/3038884ee7b2485680c0797e210d8600) : SUCCESS in 2m 21s
- [packit-tests-rpm ](https://softwarefactory-project.io/zuul/t/local/build/55e5ee029b094a38bb393c0d15329994) : SUCCESS in 7m 52s
- [packit-tests-pip-deps ](https://softwarefactory-project.io/zuul/t/local/build/0dbbcbc1aa93486ebe997d377230a02a) : SUCCESS in 8m 03s
- [packit-tests-git-master ](https://softwarefactory-project.io/zuul/t/local/build/f206dd71759d489e8cd2f5ca114d6e4b) : SUCCESS in 8m 29s
- [packit-tests-rpm-sess-rec ](https://softwarefactory-project.io/zuul/t/local/build/7f37170f91c84828b4740f9be9312d2e) : SUCCESS in 2m 12s
- [packit-tests-pip-deps-sess-rec ](https://softwarefactory-project.io/zuul/t/local/build/ef40d6c30af34cecb155d7ac075f3d47) : SUCCESS in 2m 22s
- [packit-tests-git-master-sess-rec ](https://softwarefactory-project.io/zuul/t/local/build/029fd0fcd4c84b60a20f885335a93f1f) : SUCCESS in 2m 32s
- [reverse-dep-packit-service-tests ](https://softwarefactory-project.io/zuul/t/local/build/833848051c6241f3aa5e96d7fe826e71) : FAILURE in 3m 45s


Patch:
diff --git a/.packit.yaml b/.packit.yaml
index 0b0a66e2..68df8a78 100644
--- a/.packit.yaml
+++ b/.packit.yaml
@@ -17,7 +17,7 @@ jobs:
   - job: propose_downstream
     trigger: release
     metadata:
-      dist-git-branch: fedora-all
+      dist_git_branch: fedora-all
   - job: sync_from_downstream
     trigger: commit
   - job: copr_build
diff --git a/packit/config/job_config.py b/packit/config/job_config.py
index fbfef107..be36254f 100644
--- a/packit/config/job_config.py
+++ b/packit/config/job_config.py
@@ -22,6 +22,7 @@
 
 import logging
 from enum import Enum
+from typing import List
 
 from packit.exceptions import PackitConfigException
 
@@ -51,11 +52,61 @@ class JobConfigTriggerType(Enum):
     commit = "commit"
 
 
+class JobMetadataConfig:
+    def __init__(
+        self,
+        targets: List[str] = None,
+        timeout: int = 7200,
+        owner: str = None,
+        project: str = None,
+        dist_git_branch: str = None,
+    ):
+        """
+        :param targets: copr_build job, mock chroots where to build
+        :param timeout: copr_build, give up watching a build after timeout, defaults to 7200s
+        :param owner: copr_build, a namespace in COPR where the build should happen
+        :param project: copr_build, a name of the copr project
+        :param dist_git_branch: propose_downstream, a branch in dist-git where packit should work
+        """
+        self.targets = targets or []
+        self.timeout: int = timeout
+        self.owner: str = owner
+        self.project: str = project
+        self.dist_git_branch: str = dist_git_branch
+
+    def __repr__(self):
+        return (
+            f"JobMetadataConfig(targets={self.targets}, "
+            f"timeout={self.timeout}, "
+            f"owner={self.owner}, "
+            f"project={self.project}, "
+            f"dist_git_branch={self.dist_git_branch})"
+        )
+
+    def __eq__(self, other: object):
+        if not isinstance(other, JobMetadataConfig):
+            raise PackitConfigException(
+                "Provided object is not a JobMetadataConfig instance."
+            )
+        return (
+            self.targets == other.targets
+            and self.timeout == other.timeout
+            and self.owner == other.owner
+            and self.project == other.project
+            and self.dist_git_branch == other.dist_git_branch
+        )
+
+
 class JobConfig:
-    def __init__(self, type: JobType, trigger: JobConfigTriggerType, metadata: dict):
-        self.type = type
+    def __init__(
+        self,
+        type: JobType,
+        trigger: JobConfigTriggerType,
+        metadata: JobMetadataConfig = None,
+    ):
+        self.type: JobType = type
         self.trigger: JobConfigTriggerType = trigger
-        self.metadata: dict = metadata
+        self.metadata: JobMetadataConfig = metadata or JobMetadataConfig()
 
     def __repr__(self):
         return (
@@ -89,11 +140,11 @@ default_jobs = [
     JobConfig(
         type=JobType.tests,
         trigger=JobConfigTriggerType.pull_request,
-        metadata={"targets": ["fedora-stable"]},
+        metadata=JobMetadataConfig(targets=["fedora-stable"]),
     ),
     JobConfig(
         type=JobType.propose_downstream,
         trigger=JobConfigTriggerType.release,
-        metadata={"dist-git-branch": ["fedora-all"]},
+        metadata=JobMetadataConfig(dist_git_branch="fedora-all"),
     ),
 ]
diff --git a/packit/config/package_config.py b/packit/config/package_config.py
index cbbd8e65..3a8a9e6e 100644
--- a/packit/config/package_config.py
+++ b/packit/config/package_config.py
@@ -214,9 +214,9 @@ class PackageConfig:
 
     def get_copr_build_project_value(self) -> Optional[str]:
         projects_list = [
-            job.metadata.get("project")
+            job.metadata.project
             for job in self.jobs
-            if job.type == JobType.copr_build and job.metadata.get("project")
+            if job.type == JobType.copr_build and job.metadata.project
         ]
         if not projects_list:
             return None
diff --git a/packit/schema.py b/packit/schema.py
index 733075c8..cff7116c 100644
--- a/packit/schema.py
+++ b/packit/schema.py
@@ -46,6 +46,7 @@ from packit.config.job_config import (
     JobConfig,
     default_jobs,
     JobConfigTriggerType,
+    JobMetadataConfig,
 )
 from packit.config.package_config import (
     NotificationsConfig,
@@ -197,6 +198,33 @@ class SyncFilesConfigSchema(Schema):
             return data
 
 
+class JobMetadataSchema(Schema):
+    """ Jobs metadata. """
+
+    targets = fields.List(fields.String())
+    timeout = fields.Integer()
+    owner = fields.String()
+    project = fields.String()
+    dist_git_branch = fields.String()
+
+    @pre_load
+    def ordered_preprocess(self, data, **_):
+        if "dist-git-branch" in data:
+            logger.warning(
+                f"Job metadata key 'dist-git-branch' has been renamed to 'dist_git_branch', "
+                f"please update your configuration file."
+            )
+            data["dist_git_branch"] = data.pop("dist-git-branch")
+        if isinstance(data.get("targets"), str):
+            # allow "targets" being specified as string
+            data["targets"] = [data.pop("targets")]
+        return data
+
+    @post_load
+    def make_instance(self, data, **_):
+        return JobMetadataConfig(**data)
+
+
 # TODO: inherit from MM23Schema
 class JobConfigSchema(Schema):
     """
@@ -205,7 +233,7 @@ class JobConfigSchema(Schema):
 
     job = EnumField(JobType, required=True)
     trigger = EnumField(JobConfigTriggerType, required=True)
-    metadata = fields.Dict(missing={})
+    metadata = fields.Nested(JobMetadataSchema)
 
     @post_load
     def make_instance(self, data, **_):


Test patch:
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
index 64fe1e2e..3c647981 100644
--- a/tests/unit/test_config.py
+++ b/tests/unit/test_config.py
@@ -24,77 +24,68 @@ import os
 from pathlib import Path
 
 import pytest
+from flexmock import flexmock
 from marshmallow import ValidationError
 
-from flexmock import flexmock
 from ogr import GithubService, PagureService
-from ogr.abstract import GitProject, GitService
-from packit.actions import ActionName
 from packit.config import (
     Config,
     JobConfig,
     JobType,
-    PackageConfig,
-    get_package_config_from_repo,
-    SyncFilesConfig,
-    SyncFilesItem,
     JobConfigTriggerType,
 )
-from packit.config.package_config import get_local_specfile_path
-from tests.spellbook import UP_OSBUILD, SYNC_FILES
+from packit.config.job_config import JobMetadataConfig
 
 
 def get_job_config_dict_simple():
     return {"job": "build", "trigger": "release"}
 
 
+def get_job_config_simple():
+    return JobConfig(type=JobType.build, trigger=JobConfigTriggerType.release,)
+
+
+@pytest.fixture()
+def job_config_simple():
+    return get_job_config_simple()
+
+
 def get_job_config_dict_full():
     return {
         "job": "propose_downstream",
         "trigger": "pull_request",
-        "metadata": {"a": "b"},
+        "metadata": {"dist-git-branch": "master"},
     }
 
 
-def get_job_config_simple():
-    return JobConfig(
-        type=JobType.build, trigger=JobConfigTriggerType.release, metadata={}
-    )
-
-
 def get_job_config_full():
     return JobConfig(
         type=JobType.propose_downstream,
         trigger=JobConfigTriggerType.pull_request,
-        metadata={"a": "b"},
+        metadata=JobMetadataConfig(dist_git_branch="master"),
     )
 
 
+@pytest.fixture()
+def job_config_full():
+    return get_job_config_full()
+
+
 def get_default_job_config():
     return [
         JobConfig(
             type=JobType.tests,
             trigger=JobConfigTriggerType.pull_request,
-            metadata={"targets": ["fedora-stable"]},
+            metadata=JobMetadataConfig(targets=["fedora-stable"]),
         ),
         JobConfig(
             type=JobType.propose_downstream,
             trigger=JobConfigTriggerType.release,
-            metadata={"dist-git-branch": ["fedora-all"]},
+            metadata=JobMetadataConfig(dist_git_branch="fedora-all"),
         ),
     ]
 
 
-@pytest.fixture()
-def job_config_simple():
-    return get_job_config_simple()
-
-
-@pytest.fixture()
-def job_config_full():
-    return get_job_config_full()
-
-
 def test_job_config_equal(job_config_simple):
     assert job_config_simple == job_config_simple
 
@@ -142,489 +133,6 @@ def test_job_config_parse(raw, expected_config):
     assert job_config == expected_config
 
 
-def test_package_config_equal(job_config_simple):
-    assert PackageConfig(
-        specfile_path="fedora/package.spec",
-        synced_files=SyncFilesConfig(
-            files_to_sync=[SyncFilesItem(src="packit.yaml", dest="packit.yaml")]
-        ),
-        jobs=[job_config_simple],
-        downstream_package_name="package",
-        create_pr=True,
-    ) == PackageConfig(
-        specfile_path="fedora/package.spec",
-        synced_files=SyncFilesConfig(
-            files_to_sync=[SyncFilesItem(src="packit.yaml", dest="packit.yaml")]
-        ),
-        jobs=[job_config_simple],
-        downstream_package_name="package",
-        create_pr=True,
-    )
-
-
-@pytest.mark.parametrize(
-    "not_equal_package_config",
-    [
-        PackageConfig(
-            specfile_path="fedora/other-package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="a", dest="a"),
-                    SyncFilesItem(src="b", dest="b"),
-                ]
-            ),
-            jobs=[get_job_config_simple()],
-        ),
-        PackageConfig(
-            specfile_path="fedora/package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[SyncFilesItem(src="c", dest="c")]
-            ),
-            jobs=[get_job_config_simple()],
-        ),
-        PackageConfig(
-            specfile_path="fedora/package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="a", dest="a"),
-                    SyncFilesItem(src="b", dest="b"),
-                ]
-            ),
-            jobs=[get_job_config_full()],
-        ),
-        PackageConfig(
-            specfile_path="fedora/package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="c", dest="c"),
-                    SyncFilesItem(src="d", dest="d"),
-                ]
-            ),
-            jobs=[get_job_config_full()],
-        ),
-        PackageConfig(
-            specfile_path="fedora/package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="c", dest="c"),
-                    SyncFilesItem(src="d", dest="d"),
-                ]
-            ),
-            jobs=[get_job_config_full()],
-            create_pr=False,
-        ),
-    ],
-)
-def test_package_config_not_equal(not_equal_package_config):
-    j = get_job_config_full()
-    j.metadata["b"] = "c"
-    assert (
-        not PackageConfig(
-            specfile_path="fedora/package.spec",
-            synced_files=SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="c", dest="c"),
-                    SyncFilesItem(src="d", dest="d"),
-                ]
-            ),
-            jobs=[j],
-            create_pr=True,
-        )
-        == not_equal_package_config
-    )
-
-
-@pytest.mark.parametrize(
-    "raw,is_valid",
-    [
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": "fedora/foobar.spec",
-            },
-            False,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/foobar.spec"],
-            },
-            True,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/foobar.spec", "somefile", "somedirectory"],
-                "jobs": [],
-            },
-            True,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/foobar.spec"],
-                "actions": {
-                    "pre-sync": "some/pre-sync/command --option",
-                    "get-current-version": "get-me-version",
-                },
-            },
-            True,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/foobar.spec"],
-                "actions": {
-                    "pre-sync": "some/pre-sync/command --option",
-                    "unknown-action": "nothing",
-                },
-            },
-            False,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "actions": ["actions" "has", "to", "be", "key", "value"],
-                "jobs": [{"job": "asd", "trigger": "qwe"}],
-            },
-            False,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "notifications": {"pull_request": {"successful_build": False}},
-            },
-            True,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "notifications": {"pull_request": {"successful_build": "nie"}},
-            },
-            False,
-        ),
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "notifications": {"pull_request": False},
-            },
-            False,
-        ),
-    ],
-)
-def test_package_config_validate(raw, is_valid):
-    if not is_valid:
-        with pytest.raises((ValidationError, ValueError)):
-            PackageConfig.get_from_dict(raw)
-    else:
-        PackageConfig.get_from_dict(raw)
-
-
-@pytest.mark.parametrize(
-    "raw",
-    [
-        # {"specfile_path": "test/spec/file/path", "something": "different"},
-        {
-            "specfile_path": "test/spec/file/path",
-            "jobs": [{"trigger": "release", "release_to": ["f28"]}],
-        }
-    ],
-)
-def test_package_config_parse_error(raw):
-    with pytest.raises(Exception):
-        PackageConfig.get_from_dict(raw_dict=raw)
-
-
-@pytest.mark.parametrize(
-    "raw,expected",
-    [
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec"],
-                "jobs": [get_job_config_dict_full()],
-                "downstream_package_name": "package",
-                "create_pr": False,
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        )
-                    ]
-                ),
-                jobs=[get_job_config_full()],
-                downstream_package_name="package",
-                create_pr=False,
-            ),
-            id="specfile_path+synced_files+job_config_full+downstream_package_name+create_pr",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": [
-                    "fedora/package.spec",
-                    "somefile",
-                    "other",
-                    "directory/files",
-                ],
-                "jobs": [get_job_config_dict_simple()],
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        ),
-                        SyncFilesItem(src="somefile", dest="somefile"),
-                        SyncFilesItem(src="other", dest="other"),
-                        SyncFilesItem(src="directory/files", dest="directory/files"),
-                    ]
-                ),
-                jobs=[get_job_config_simple()],
-                downstream_package_name="package",
-            ),
-            id="specfile_path+synced_files+job_config_dict_simple+downstream_package_name",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec"],
-                "jobs": [get_job_config_dict_full()],
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        )
-                    ]
-                ),
-                jobs=[get_job_config_full()],
-                downstream_package_name="package",
-            ),
-            id="specfile_path+synced_files(spec_only)+job_config_full+downstream_package_name",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec", "somefile"],
-                "jobs": [get_job_config_dict_full()],
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        ),
-                        SyncFilesItem(src="somefile", dest="somefile"),
-                    ]
-                ),
-                jobs=[get_job_config_full()],
-                downstream_package_name="package",
-            ),
-            id="specfile_path+synced_files+job_config_full+downstream_package_name",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec"],
-                "jobs": [get_job_config_dict_full()],
-                "upstream_project_url": "https://github.com/asd/qwe",
-                "upstream_package_name": "qwe",
-                "dist_git_base_url": "https://something.wicked",
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        )
-                    ]
-                ),
-                jobs=[get_job_config_full()],
-                upstream_project_url="https://github.com/asd/qwe",
-                upstream_package_name="qwe",
-                dist_git_base_url="https://something.wicked",
-                downstream_package_name="package",
-            ),
-            id="specfile_path+synced_files+job_config_dict_full+upstream_project_url"
-            "+upstream_package_name+dist_git_base_url+downstream_package_name",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "actions": {
-                    "pre-sync": "some/pre-sync/command --option",
-                    "get-current-version": "get-me-version",
-                },
-                "jobs": [],
-                "upstream_project_url": "https://github.com/asd/qwe",
-                "upstream_package_name": "qwe",
-                "dist_git_base_url": "https://something.wicked",
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                actions={
-                    ActionName.pre_sync: "some/pre-sync/command --option",
-                    ActionName.get_current_version: "get-me-version",
-                },
-                jobs=[],
-                upstream_project_url="https://github.com/asd/qwe",
-                upstream_package_name="qwe",
-                dist_git_base_url="https://something.wicked",
-                downstream_package_name="package",
-            ),
-            id="specfile_path+actions+empty_jobs+upstream_project_url"
-            "+upstream_package_name+dist_git_base_url+downstream_package_name",
-        ),
-        pytest.param(
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec"],
-                "downstream_package_name": "package",
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        )
-                    ]
-                ),
-                jobs=get_default_job_config(),
-                downstream_package_name="package",
-            ),
-            id="specfile_path+synced_files+downstream_package_name",
-        ),
-    ],
-)
-def test_package_config_parse(raw, expected):
-    package_config = PackageConfig.get_from_dict(raw_dict=raw)
-    assert package_config
-    # tests for https://github.com/packit-service/packit-service/pull/342
-    if expected.jobs:
-        for j in package_config.jobs:
-            assert j.type
-    assert package_config == expected
-
-
-@pytest.mark.parametrize(
-    "raw,expected",
-    [
-        (
-            {
-                "specfile_path": "fedora/package.spec",
-                "synced_files": ["fedora/package.spec"],
-                "jobs": [],
-            },
-            PackageConfig(
-                specfile_path="fedora/package.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[
-                        SyncFilesItem(
-                            src="fedora/package.spec", dest="fedora/package.spec"
-                        )
-                    ]
-                ),
-                downstream_package_name="package",
-                upstream_package_name="package",
-            ),
-        )
-    ],
-)
-def test_package_config_upstream_and_downstream_package_names(raw, expected):
-    package_config = PackageConfig.get_from_dict(raw_dict=raw, repo_name="package")
-    assert package_config
-    assert package_config == expected
-
-
-def test_dist_git_package_url():
-    di = {
-        "dist_git_base_url": "https://packit.dev/",
-        "downstream_package_name": "packit",
-        "dist_git_namespace": "awesome",
-        "synced_files": ["fedora/foobar.spec"],
-        "specfile_path": "fedora/package.spec",
-        "create_pr": False,
-    }
-    new_pc = PackageConfig.get_from_dict(di)
-    pc = PackageConfig(
-        dist_git_base_url="https://packit.dev/",
-        downstream_package_name="packit",
-        dist_git_namespace="awesome",
-        synced_files=SyncFilesConfig(
-            files_to_sync=[
-                SyncFilesItem(src="fedora/foobar.spec", dest="fedora/foobar.spec")
-            ]
-        ),
-        specfile_path="fedora/package.spec",
-        create_pr=False,
-        jobs=get_default_job_config(),
-    )
-    assert new_pc.specfile_path.endswith("fedora/package.spec")
-    assert pc.specfile_path.endswith("fedora/package.spec")
-    assert pc == new_pc
-    assert pc.dist_git_package_url == "https://packit.dev/awesome/packit.git"
-    assert new_pc.dist_git_package_url == "https://packit.dev/awesome/packit.git"
-    assert not pc.create_pr
-
-
-@pytest.mark.parametrize(
-    "content",
-    [
-        "---\nspecfile_path: packit.spec\n"
-        "synced_files:\n"
-        "  - packit.spec\n"
-        "  - src: .packit.yaml\n"
-        "    dest: .packit2.yaml",
-        '{"specfile_path": "packit.spec", "synced_files": ["packit.spec", '
-        '{"src": ".packit.yaml", "dest": ".packit2.yaml"}]}',
-    ],
-)
-def test_get_package_config_from_repo(content):
-    gp = flexmock(GitProject)
-    gp.should_receive("full_repo_name").and_return("a/b")
-    gp.should_receive("get_file_content").and_return(content)
-    gp.should_receive("get_files").and_return(["packit.spec"])
-    git_project = GitProject(repo="", service=GitService(), namespace="")
-    config = get_package_config_from_repo(sourcegit_project=git_project, ref="")
-    assert isinstance(config, PackageConfig)
-    assert Path(config.specfile_path).name == "packit.spec"
-    assert config.synced_files == SyncFilesConfig(
-        files_to_sync=[
-            SyncFilesItem(src="packit.spec", dest="packit.spec"),
-            SyncFilesItem(src=".packit.yaml", dest=".packit2.yaml"),
-        ]
-    )
-    assert config.create_pr
-
-
-@pytest.mark.parametrize("content", ["{}"])
-def test_get_package_config_from_repo_spec_file_not_defined(content):
-    gp = flexmock(GitProject)
-    gp.should_receive("full_repo_name").and_return("a/b")
-    gp.should_receive("get_file_content").and_return(content)
-    gp.should_receive("get_files").and_return(["packit.spec"])
-    git_project = GitProject(repo="", service=GitService(), namespace="")
-    config = get_package_config_from_repo(sourcegit_project=git_project, ref="")
-    assert isinstance(config, PackageConfig)
-    assert Path(config.specfile_path).name == "packit.spec"
-    assert config.create_pr
-
-
 def test_get_user_config(tmpdir):
     user_config_file_path = Path(tmpdir) / ".packit.yaml"
     user_config_file_path.write_text(
@@ -687,66 +195,3 @@ def test_user_config_fork_token(tmpdir, recwarn):
     Config.get_user_config()
     w = recwarn.pop(UserWarning)
     assert "pagure_fork_token" in str(w.message)
-
-
-@pytest.mark.parametrize(
-    "package_config, all_synced_files",
-    [
-        (
-            PackageConfig(
-                config_file_path="packit.yaml",
-                specfile_path="file.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[SyncFilesItem(src="file.spec", dest="file.spec")]
-                ),
-            ),
-            SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="file.spec", dest="file.spec"),
-                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
-                ]
-            ),
-        ),
-        (
-            PackageConfig(
-                config_file_path="packit.yaml",
-                specfile_path="file.spec",
-                synced_files=SyncFilesConfig(
-                    files_to_sync=[SyncFilesItem(src="file.txt", dest="file.txt")]
-                ),
-            ),
-            SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="file.txt", dest="file.txt"),
-                    SyncFilesItem(src="file.spec", dest="file.spec"),
-                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
-                ]
-            ),
-        ),
-        (
-            PackageConfig(
-                config_file_path="packit.yaml",
-                specfile_path="file.spec",
-                synced_files=SyncFilesConfig([]),
-            ),
-            SyncFilesConfig(
-                files_to_sync=[
-                    SyncFilesItem(src="file.spec", dest="file.spec"),
-                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
-                ]
-            ),
-        ),
-    ],
-)
-def test_get_all_files_to_sync(package_config, all_synced_files):
-    assert package_config.get_all_files_to_sync() == all_synced_files
-
-
-def test_get_local_specfile_path():
-    assert str(get_local_specfile_path(UP_OSBUILD)) == "osbuild.spec"
-    assert not get_local_specfile_path(SYNC_FILES)
-
-
-def test_notifications_section():
-    pc = PackageConfig.get_from_dict({"specfile_path": "package.spec"})
-    assert pc.notifications.pull_request.successful_build
diff --git a/tests/unit/test_package_config.py b/tests/unit/test_package_config.py
index 7ac85b6b..d7737f32 100644
--- a/tests/unit/test_package_config.py
+++ b/tests/unit/test_package_config.py
@@ -1,9 +1,39 @@
+from pathlib import Path
+
 import pytest
 from flexmock import flexmock
-from packit.config import JobType, JobConfigTriggerType, JobConfig
+from marshmallow import ValidationError
+from tests.spellbook import UP_OSBUILD, SYNC_FILES
+from tests.unit.test_config import (
+    get_job_config_dict_full,
+    get_job_config_dict_simple,
+    get_job_config_simple,
+    get_job_config_full,
+    get_default_job_config,
+)
 
 from ogr.abstract import GitProject, GitService
-from packit.config.package_config import get_specfile_path_from_repo, PackageConfig
+from packit.actions import ActionName
+from packit.config import (
+    JobType,
+    JobConfigTriggerType,
+    JobConfig,
+    get_package_config_from_repo,
+    SyncFilesConfig,
+)
+from packit.config.job_config import JobMetadataConfig
+from packit.config.package_config import (
+    get_specfile_path_from_repo,
+    PackageConfig,
+    get_local_specfile_path,
+)
+from packit.schema import MM3
+from packit.sync import SyncFilesItem
+
+
+@pytest.fixture()
+def job_config_simple():
+    return get_job_config_simple()
 
 
 @pytest.mark.parametrize(
@@ -27,7 +57,6 @@ def test_get_specfile_path_from_repo(files, expected):
                     JobConfig(
                         type=JobType.copr_build,
                         trigger=JobConfigTriggerType.pull_request,
-                        metadata={},
                     )
                 ],
             ),
@@ -40,7 +69,7 @@ def test_get_specfile_path_from_repo(files, expected):
                     JobConfig(
                         type=JobType.copr_build,
                         trigger=JobConfigTriggerType.pull_request,
-                        metadata={"project": "example"},
+                        metadata=JobMetadataConfig(project="example"),
                     )
                 ],
             ),
@@ -53,12 +82,12 @@ def test_get_specfile_path_from_repo(files, expected):
                     JobConfig(
                         type=JobType.copr_build,
                         trigger=JobConfigTriggerType.release,
-                        metadata={"project": "example1"},
+                        metadata=JobMetadataConfig(project="example1"),
                     ),
                     JobConfig(
                         type=JobType.copr_build,
                         trigger=JobConfigTriggerType.pull_request,
-                        metadata={"project": "example2"},
+                        metadata=JobMetadataConfig(project="example2"),
                     ),
                 ],
             ),
@@ -69,3 +98,619 @@ def test_get_specfile_path_from_repo(files, expected):
 def test_project_from_copr_build_job(package_config, project):
     config_project_value = package_config.get_copr_build_project_value()
     assert config_project_value == project
+
+
+def test_package_config_equal(job_config_simple):
+    assert PackageConfig(
+        specfile_path="fedora/package.spec",
+        synced_files=SyncFilesConfig(
+            files_to_sync=[SyncFilesItem(src="packit.yaml", dest="packit.yaml")]
+        ),
+        jobs=[job_config_simple],
+        downstream_package_name="package",
+        create_pr=True,
+    ) == PackageConfig(
+        specfile_path="fedora/package.spec",
+        synced_files=SyncFilesConfig(
+            files_to_sync=[SyncFilesItem(src="packit.yaml", dest="packit.yaml")]
+        ),
+        jobs=[job_config_simple],
+        downstream_package_name="package",
+        create_pr=True,
+    )
+
+
+@pytest.mark.parametrize(
+    "not_equal_package_config",
+    [
+        PackageConfig(
+            specfile_path="fedora/other-package.spec",
+            synced_files=SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="a", dest="a"),
+                    SyncFilesItem(src="b", dest="b"),
+                ]
+            ),
+            jobs=[get_job_config_simple()],
+        ),
+        PackageConfig(
+            specfile_path="fedora/package.spec",
+            synced_files=SyncFilesConfig(
+                files_to_sync=[SyncFilesItem(src="c", dest="c")]
+            ),
+            jobs=[get_job_config_simple()],
+        ),
+        PackageConfig(
+            specfile_path="fedora/package.spec",
+            synced_files=SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="a", dest="a"),
+                    SyncFilesItem(src="b", dest="b"),
+                ]
+            ),
+            jobs=[get_job_config_full()],
+        ),
+        PackageConfig(
+            specfile_path="fedora/package.spec",
+            synced_files=SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="c", dest="c"),
+                    SyncFilesItem(src="d", dest="d"),
+                ]
+            ),
+            jobs=[get_job_config_full()],
+            create_pr=False,
+        ),
+    ],
+)
+def test_package_config_not_equal(not_equal_package_config):
+    config = PackageConfig(
+        specfile_path="fedora/package.spec",
+        synced_files=SyncFilesConfig(
+            files_to_sync=[
+                SyncFilesItem(src="c", dest="c"),
+                SyncFilesItem(src="d", dest="d"),
+            ]
+        ),
+        jobs=[get_job_config_full()],
+        create_pr=True,
+    )
+    assert config != not_equal_package_config
+
+
+@pytest.mark.parametrize(
+    "raw,is_valid",
+    [
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": "fedora/foobar.spec",
+            },
+            False,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/foobar.spec"],
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/foobar.spec", "somefile", "somedirectory"],
+                "jobs": [],
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "jobs": [
+                    {
+                        "job": "propose_downstream",
+                        "trigger": "release",
+                        "metadata": {"dist-git-branch": "fedora-all"},
+                    }
+                ],
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "jobs": [
+                    {
+                        "job": "propose_downstream",
+                        "trigger": "release",
+                        "metadata": {"targets": "fedora-stable"},
+                    }
+                ],
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "jobs": [
+                    {
+                        "job": "propose_downstream",
+                        "trigger": "release",
+                        "metadata": {
+                            "targets": ["f31", "f32"],
+                            "timeout": 123,
+                            "owner": "santa",
+                            "project": "gifts",
+                        },
+                    }
+                ],
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/foobar.spec"],
+                "actions": {
+                    "pre-sync": "some/pre-sync/command --option",
+                    "get-current-version": "get-me-version",
+                },
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/foobar.spec"],
+                "actions": {
+                    "pre-sync": "some/pre-sync/command --option",
+                    "unknown-action": "nothing",
+                },
+            },
+            False,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "actions": ["actions" "has", "to", "be", "key", "value"],
+                "jobs": [{"job": "asd", "trigger": "qwe"}],
+            },
+            False,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "notifications": {"pull_request": {"successful_build": False}},
+            },
+            True,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "notifications": {"pull_request": {"successful_build": "nie"}},
+            },
+            False,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "notifications": {"pull_request": False},
+            },
+            False,
+        ),
+    ],
+)
+def test_package_config_validate(raw, is_valid):
+    if not is_valid:
+        with pytest.raises((ValidationError, ValueError)):
+            PackageConfig.get_from_dict(raw)
+    else:
+        PackageConfig.get_from_dict(raw)
+
+
+@pytest.mark.xfail(not MM3, reason="Marshmallow v2 doesn't raise when unknown field")
+@pytest.mark.parametrize(
+    "raw,is_valid",
+    [
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "jobs": [
+                    {
+                        "job": "propose_downstream",
+                        "trigger": "release",
+                        "unknown": "key",
+                    }
+                ],
+            },
+            False,
+        ),
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "jobs": [
+                    {
+                        "job": "propose_downstream",
+                        "trigger": "release",
+                        "metadata": {"unknown": "key"},
+                    }
+                ],
+            },
+            False,
+        ),
+    ],
+)
+def test_package_config_validate_unknown_key(raw, is_valid):
+    if not is_valid:
+        with pytest.raises((ValidationError, ValueError)):
+            PackageConfig.get_from_dict(raw)
+    else:
+        PackageConfig.get_from_dict(raw)
+
+
+@pytest.mark.parametrize(
+    "raw",
+    [
+        # {"specfile_path": "test/spec/file/path", "something": "different"},
+        {
+            "specfile_path": "test/spec/file/path",
+            "jobs": [{"trigger": "release", "release_to": ["f28"]}],
+        }
+    ],
+)
+def test_package_config_parse_error(raw):
+    with pytest.raises(Exception):
+        PackageConfig.get_from_dict(raw_dict=raw)
+
+
+@pytest.mark.parametrize(
+    "raw,expected",
+    [
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec"],
+                "jobs": [get_job_config_dict_full()],
+                "downstream_package_name": "package",
+                "create_pr": False,
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        )
+                    ]
+                ),
+                jobs=[get_job_config_full()],
+                downstream_package_name="package",
+                create_pr=False,
+            ),
+            id="specfile_path+synced_files+job_config_full+downstream_package_name+create_pr",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": [
+                    "fedora/package.spec",
+                    "somefile",
+                    "other",
+                    "directory/files",
+                ],
+                "jobs": [get_job_config_dict_simple()],
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        ),
+                        SyncFilesItem(src="somefile", dest="somefile"),
+                        SyncFilesItem(src="other", dest="other"),
+                        SyncFilesItem(src="directory/files", dest="directory/files"),
+                    ]
+                ),
+                jobs=[get_job_config_simple()],
+                downstream_package_name="package",
+            ),
+            id="specfile_path+synced_files+job_config_dict_simple+downstream_package_name",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec"],
+                "jobs": [get_job_config_dict_full()],
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        )
+                    ]
+                ),
+                jobs=[get_job_config_full()],
+                downstream_package_name="package",
+            ),
+            id="specfile_path+synced_files(spec_only)+job_config_full+downstream_package_name",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec", "somefile"],
+                "jobs": [get_job_config_dict_full()],
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        ),
+                        SyncFilesItem(src="somefile", dest="somefile"),
+                    ]
+                ),
+                jobs=[get_job_config_full()],
+                downstream_package_name="package",
+            ),
+            id="specfile_path+synced_files+job_config_full+downstream_package_name",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec"],
+                "jobs": [get_job_config_dict_full()],
+                "upstream_project_url": "https://github.com/asd/qwe",
+                "upstream_package_name": "qwe",
+                "dist_git_base_url": "https://something.wicked",
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        )
+                    ]
+                ),
+                jobs=[get_job_config_full()],
+                upstream_project_url="https://github.com/asd/qwe",
+                upstream_package_name="qwe",
+                dist_git_base_url="https://something.wicked",
+                downstream_package_name="package",
+            ),
+            id="specfile_path+synced_files+job_config_dict_full+upstream_project_url"
+            "+upstream_package_name+dist_git_base_url+downstream_package_name",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "actions": {
+                    "pre-sync": "some/pre-sync/command --option",
+                    "get-current-version": "get-me-version",
+                },
+                "jobs": [],
+                "upstream_project_url": "https://github.com/asd/qwe",
+                "upstream_package_name": "qwe",
+                "dist_git_base_url": "https://something.wicked",
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                actions={
+                    ActionName.pre_sync: "some/pre-sync/command --option",
+                    ActionName.get_current_version: "get-me-version",
+                },
+                jobs=[],
+                upstream_project_url="https://github.com/asd/qwe",
+                upstream_package_name="qwe",
+                dist_git_base_url="https://something.wicked",
+                downstream_package_name="package",
+            ),
+            id="specfile_path+actions+empty_jobs+upstream_project_url"
+            "+upstream_package_name+dist_git_base_url+downstream_package_name",
+        ),
+        pytest.param(
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec"],
+                "downstream_package_name": "package",
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        )
+                    ]
+                ),
+                jobs=get_default_job_config(),
+                downstream_package_name="package",
+            ),
+            id="specfile_path+synced_files+downstream_package_name",
+        ),
+    ],
+)
+def test_package_config_parse(raw, expected):
+    package_config = PackageConfig.get_from_dict(raw_dict=raw)
+    assert package_config
+    # tests for https://github.com/packit-service/packit-service/pull/342
+    if expected.jobs:
+        for j in package_config.jobs:
+            assert j.type
+    assert package_config == expected
+
+
+@pytest.mark.parametrize(
+    "raw,expected",
+    [
+        (
+            {
+                "specfile_path": "fedora/package.spec",
+                "synced_files": ["fedora/package.spec"],
+                "jobs": [],
+            },
+            PackageConfig(
+                specfile_path="fedora/package.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[
+                        SyncFilesItem(
+                            src="fedora/package.spec", dest="fedora/package.spec"
+                        )
+                    ]
+                ),
+                downstream_package_name="package",
+                upstream_package_name="package",
+            ),
+        )
+    ],
+)
+def test_package_config_upstream_and_downstream_package_names(raw, expected):
+    package_config = PackageConfig.get_from_dict(raw_dict=raw, repo_name="package")
+    assert package_config
+    assert package_config == expected
+
+
+def test_dist_git_package_url():
+    di = {
+        "dist_git_base_url": "https://packit.dev/",
+        "downstream_package_name": "packit",
+        "dist_git_namespace": "awesome",
+        "synced_files": ["fedora/foobar.spec"],
+        "specfile_path": "fedora/package.spec",
+        "create_pr": False,
+    }
+    new_pc = PackageConfig.get_from_dict(di)
+    pc = PackageConfig(
+        dist_git_base_url="https://packit.dev/",
+        downstream_package_name="packit",
+        dist_git_namespace="awesome",
+        synced_files=SyncFilesConfig(
+            files_to_sync=[
+                SyncFilesItem(src="fedora/foobar.spec", dest="fedora/foobar.spec")
+            ]
+        ),
+        specfile_path="fedora/package.spec",
+        create_pr=False,
+        jobs=get_default_job_config(),
+    )
+    assert new_pc.specfile_path.endswith("fedora/package.spec")
+    assert pc.specfile_path.endswith("fedora/package.spec")
+    assert pc == new_pc
+    assert pc.dist_git_package_url == "https://packit.dev/awesome/packit.git"
+    assert new_pc.dist_git_package_url == "https://packit.dev/awesome/packit.git"
+    assert not pc.create_pr
+
+
+@pytest.mark.parametrize(
+    "content",
+    [
+        "---\nspecfile_path: packit.spec\n"
+        "synced_files:\n"
+        "  - packit.spec\n"
+        "  - src: .packit.yaml\n"
+        "    dest: .packit2.yaml",
+        '{"specfile_path": "packit.spec", "synced_files": ["packit.spec", '
+        '{"src": ".packit.yaml", "dest": ".packit2.yaml"}]}',
+    ],
+)
+def test_get_package_config_from_repo(content):
+    gp = flexmock(GitProject)
+    gp.should_receive("full_repo_name").and_return("a/b")
+    gp.should_receive("get_file_content").and_return(content)
+    gp.should_receive("get_files").and_return(["packit.spec"])
+    git_project = GitProject(repo="", service=GitService(), namespace="")
+    config = get_package_config_from_repo(sourcegit_project=git_project, ref="")
+    assert isinstance(config, PackageConfig)
+    assert Path(config.specfile_path).name == "packit.spec"
+    assert config.synced_files == SyncFilesConfig(
+        files_to_sync=[
+            SyncFilesItem(src="packit.spec", dest="packit.spec"),
+            SyncFilesItem(src=".packit.yaml", dest=".packit2.yaml"),
+        ]
+    )
+    assert config.create_pr
+
+
+@pytest.mark.parametrize("content", ["{}"])
+def test_get_package_config_from_repo_spec_file_not_defined(content):
+    gp = flexmock(GitProject)
+    gp.should_receive("full_repo_name").and_return("a/b")
+    gp.should_receive("get_file_content").and_return(content)
+    gp.should_receive("get_files").and_return(["packit.spec"])
+    git_project = GitProject(repo="", service=GitService(), namespace="")
+    config = get_package_config_from_repo(sourcegit_project=git_project, ref="")
+    assert isinstance(config, PackageConfig)
+    assert Path(config.specfile_path).name == "packit.spec"
+    assert config.create_pr
+
+
+@pytest.mark.parametrize(
+    "package_config, all_synced_files",
+    [
+        (
+            PackageConfig(
+                config_file_path="packit.yaml",
+                specfile_path="file.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[SyncFilesItem(src="file.spec", dest="file.spec")]
+                ),
+            ),
+            SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="file.spec", dest="file.spec"),
+                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
+                ]
+            ),
+        ),
+        (
+            PackageConfig(
+                config_file_path="packit.yaml",
+                specfile_path="file.spec",
+                synced_files=SyncFilesConfig(
+                    files_to_sync=[SyncFilesItem(src="file.txt", dest="file.txt")]
+                ),
+            ),
+            SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="file.txt", dest="file.txt"),
+                    SyncFilesItem(src="file.spec", dest="file.spec"),
+                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
+                ]
+            ),
+        ),
+        (
+            PackageConfig(
+                config_file_path="packit.yaml",
+                specfile_path="file.spec",
+                synced_files=SyncFilesConfig([]),
+            ),
+            SyncFilesConfig(
+                files_to_sync=[
+                    SyncFilesItem(src="file.spec", dest="file.spec"),
+                    SyncFilesItem(src="packit.yaml", dest="packit.yaml"),
+                ]
+            ),
+        ),
+    ],
+)
+def test_get_all_files_to_sync(package_config, all_synced_files):
+    assert package_config.get_all_files_to_sync() == all_synced_files
+
+
+def test_notifications_section():
+    pc = PackageConfig.get_from_dict({"specfile_path": "package.spec"})
+    assert pc.notifications.pull_request.successful_build
+
+
+def test_get_local_specfile_path():
+    assert str(get_local_specfile_path(UP_OSBUILD)) == "osbuild.spec"
+    assert not get_local_specfile_path(SYNC_FILES)