Skip to content

Commit

Permalink
feat: added additional testing to trigger file validator
Browse files Browse the repository at this point in the history
  • Loading branch information
clay-lake committed Oct 21, 2024
1 parent c4e3117 commit f5af210
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 16 deletions.
43 changes: 27 additions & 16 deletions src/image/utils/schema/triggers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pydantic

from datetime import datetime
from typing import Dict, List, Literal, Optional
from typing import Dict, List, Literal, Optional, Any
from typing_extensions import Annotated


LATEST_SCHEMA_VERSION = 1
Expand All @@ -20,41 +21,46 @@ class ImageUploadReleaseSchema(pydantic.BaseModel):
"""Schema of the release option for uploads in the image.yaml trigger"""

end_of_life: datetime = pydantic.Field(alias="end-of-life")
risks: List[Literal["edge", "beta", "candidate", "stable"]]
risks: List[Literal[*KNOWN_RISKS_ORDERED]]

model_config = pydantic.ConfigDict(extra="forbid")

@pydantic.field_validator("risks", mode="after")
def _ensure_non_empty_risks(cls, value):
if not value:
raise ImageTriggerValidationError(
"At least one upload risk must be present."
)
return value


class ImageUploadSchema(pydantic.BaseModel):
"""Schema of each upload within the image.yaml files."""

source: str
commit: str
directory: str
release: Optional[Dict[str, ImageUploadReleaseSchema]]
release: Optional[Dict[str, ImageUploadReleaseSchema]] = None

model_config = pydantic.ConfigDict(extra="forbid")



class ChannelsSchema(pydantic.BaseModel):
"""Schema of the 'release' tracks within the image.yaml file."""

end_of_life: datetime = pydantic.Field(alias="end-of-life")
stable: Optional[str]
candidate: Optional[str]
beta: Optional[str]
edge: Optional[str]
stable: str = None
candidate: str = None
beta: str = None
edge: str = None

model_config = pydantic.ConfigDict(extra="forbid")

# TODO: Do we need the pre keyword? Causes the validator to be
# called prior to other validation.
@pydantic.field_validator("stable", "candidate", "beta", "edge", mode="before")
def _check_risks(cls, values: List) -> List:
@pydantic.model_validator(mode="after")
def _check_risks(self, values: List) -> List:
"""There must be at least one risk specified."""
error = "At least one risk must be specified per track."
if not any(values):
if not any([self.stable, self.candidate, self.beta, self.edge]):
raise ImageTriggerValidationError(error)

return values
Expand All @@ -64,13 +70,18 @@ class ImageSchema(pydantic.BaseModel):
"""Validates the schema of the image.yaml files."""

version: str
upload: Optional[List[ImageUploadSchema]]
release: Optional[Dict[str, ChannelsSchema]]
upload: Optional[List[ImageUploadSchema]] = None
release: Optional[Dict[str, ChannelsSchema]] = None

model_config = pydantic.ConfigDict(extra="forbid")

@pydantic.field_validator("version", mode="before")
def _ensure_version_is_str(cls, value: Any):
"""Ensure that version is always cast to str."""
return str(value)

@pydantic.field_validator("upload")
def ensure_unique_triggers(
def _ensure_unique_triggers(
cls, v: Optional[List[ImageUploadSchema]]
) -> Optional[List[ImageUploadSchema]]:
"""Ensure that the triggers are unique."""
Expand Down
84 changes: 84 additions & 0 deletions tests/unit/test_image_trigger_file_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from pathlib import Path
import pytest

import src.image.prepare_single_image_build_matrix as prep_matrix
from src.image.utils.schema.triggers import ImageTriggerValidationError
from glob import glob
from pathlib import Path


def test_existing_image_trigger_files():

for oci_path in glob("oci/*"):
prep_matrix.load_trigger_yaml(Path(oci_path))


def test_image_trigger_validator_missing_channel_risks():

image_trigger = {
"version": 1,
"release": {
"latest": {
"end-of-life": "2030-05-01T00:00:00Z",
},
},
"upload": [],
}
with pytest.raises(ImageTriggerValidationError):
prep_matrix.validate_image_trigger(image_trigger)


def test_image_trigger_validator_missing_release_risks():

image_trigger = {
"version": 1,
"release": {
"latest": {
"end-of-life": "2030-05-01T00:00:00Z",
"candidate": "1.0-22.04_candidate",
},
},
"upload": [
{
"source": "canonical/rocks-toolbox",
"commit": "17916dd5de270e61a6a3fd3f4661a6413a50fd6f",
"directory": "mock_rock/1.2",
"release": {
"1.2-22.04": {
"end-of-life": "2030-05-01T00:00:00Z",
"risks": [],
}
},
},
],
}
with pytest.raises(ImageTriggerValidationError):
prep_matrix.validate_image_trigger(image_trigger)


def test_image_trigger_validator_minimal_input():

image_trigger = {
"version": 1,
"release": {
"latest": {
"end-of-life": "2030-05-01T00:00:00Z",
"candidate": "1.0-22.04_candidate",
},
},
"upload": [
{
"source": "canonical/rocks-toolbox",
"commit": "17916dd5de270e61a6a3fd3f4661a6413a50fd6f",
"directory": "mock_rock/1.2",
"release": {
"1.2-22.04": {
"end-of-life": "2030-05-01T00:00:00Z",
"risks": ["beta"],
}
},
},
],
}

prep_matrix.validate_image_trigger(image_trigger)
4 changes: 4 additions & 0 deletions tests/unit/test_prepare_single_image_build_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import pytest

import src.image.prepare_single_image_build_matrix as prep_matrix
from src.image.utils.schema.triggers import ImageTriggerValidationError
import yaml
from glob import glob
from pathlib import Path


def test_is_track_eol():
Expand Down

0 comments on commit f5af210

Please sign in to comment.