Skip to content

Commit

Permalink
refactor: breaks transformer into ToRuleTransformer and FromRuleTrans…
Browse files Browse the repository at this point in the history
…former

To align with upstream trestle, the transformers are broken down into single
responsibilties and importing the Transformer Base.

Signed-off-by: Jennifer Power <[email protected]>
  • Loading branch information
jpower432 committed Oct 4, 2023
1 parent 78df3d8 commit 56c5b52
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 120 deletions.
106 changes: 105 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,22 @@
import os
import pathlib
from tempfile import TemporaryDirectory
from typing import Generator, Tuple, TypeVar
from typing import Any, Dict, Generator, Tuple, TypeVar

import pytest
from git.repo import Repo
from trestle.common.err import TrestleError
from trestle.core.commands.init import InitCmd

from trestlebot import const
from trestlebot.transformers.trestle_rule import (
ComponentInfo,
Control,
Parameter,
Profile,
TrestleRule,
)


T = TypeVar("T")

Expand Down Expand Up @@ -74,3 +83,98 @@ def tmp_trestle_dir() -> YieldFixture[str]:
f"Initialization failed for temporary trestle directory: {e}."
)
yield tmpdir


@pytest.fixture(scope="function")
def valid_rule_data() -> Dict[str, Any]:
return {
const.RULE_INFO_TAG: {
const.NAME: "example_rule_1",
const.DESCRIPTION: "My rule description for example rule 1",
const.PROFILE: {
const.DESCRIPTION: "Simple NIST Profile",
const.HREF: "profiles/simplified_nist_profile/profile.json",
},
const.PARAMETER: {
const.NAME: "prm_1",
const.DESCRIPTION: "prm_1 description",
const.ALTERNATIVE_VALUES: {
"default": "5%",
"5pc": "5%",
"10pc": "10%",
"15pc": "15%",
"20pc": "20%",
},
const.DEFAULT_VALUE: "5%",
},
}
}


@pytest.fixture(scope="function")
def invalid_param_rule_data() -> Dict[str, Any]:
return {
const.RULE_INFO_TAG: {
const.NAME: "example_rule_1",
const.DESCRIPTION: "My rule description for example rule 1",
const.PROFILE: {
const.DESCRIPTION: "Simple NIST Profile",
const.HREF: "profiles/simplified_nist_profile/profile.json",
},
const.PARAMETER: {
const.NAME: "prm_1",
const.DESCRIPTION: "prm_1 description",
const.ALTERNATIVE_VALUES: {
"5pc": "5%",
"10pc": "10%",
"15pc": "15%",
"20pc": "20%",
},
const.DEFAULT_VALUE: "5%",
},
}
}


@pytest.fixture(scope="function")
def missing_key_rule_data() -> Dict[str, Any]:
return {
const.RULE_INFO_TAG: {
const.DESCRIPTION: "My rule description for example rule 1",
const.PROFILE: {
const.DESCRIPTION: "Simple NIST Profile",
const.HREF: "profiles/simplified_nist_profile/profile.json",
},
const.PARAMETER: {
const.NAME: "prm_1",
const.DESCRIPTION: "prm_1 description",
const.ALTERNATIVE_VALUES: {
"default": "5%",
"5pc": "5%",
"10pc": "10%",
"15pc": "15%",
"20pc": "20%",
},
const.DEFAULT_VALUE: "5%",
},
}
}


@pytest.fixture(scope="function")
def test_rule() -> TrestleRule:
test_trestle_rule: TrestleRule = TrestleRule(
name="test",
description="test",
component=ComponentInfo(name="test_comp", type="test", description="test"),
parameter=Parameter(
name="test",
description="test",
alternative_values={},
default_value="test",
),
profile=Profile(
description="test", href="test", include_controls=[Control(id="ac-1")]
),
)
return test_trestle_rule
17 changes: 17 additions & 0 deletions tests/data/yaml/test_rule_invalid_params.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
x-trestle-rule-info:
name: example_rule_1
description: My rule description for example rule 1
parameter:
name: prm_1
description: prm_1 description
alternative-values: {'5pc': '5%', '10pc': '10%', '15pc': '15%', '20pc': '20%'}
default-value: '5%'
profile:
description: Simple NIST Profile
href: profiles/simplified_nist_profile/profile.json
include-controls:
- id: ac-2
x-trestle-component-info:
name: Component 1
description: Component 1 description
type: service
10 changes: 5 additions & 5 deletions tests/trestlebot/tasks/test_rule_transform_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from tests.testutils import setup_rules_view
from trestlebot.tasks.base_task import TaskException
from trestlebot.tasks.rule_transform_task import RuleTransformTask
from trestlebot.transformers.yaml_transformer import RulesYAMLTransformer
from trestlebot.transformers.yaml_transformer import ToRulesYAMLTransformer


test_comp = "test_comp"
Expand All @@ -39,7 +39,7 @@ def test_rule_transform_task(tmp_trestle_dir: str) -> None:
"""Test rule transform task."""
trestle_root = pathlib.Path(tmp_trestle_dir)
setup_rules_view(trestle_root, test_comp, test_rules_dir)
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()
rule_transform_task = RuleTransformTask(
tmp_trestle_dir, test_rules_dir, transformer
)
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_rule_transform_task_with_no_rules(tmp_trestle_dir: str) -> None:
"""Test rule transform task with no rules."""
trestle_root = pathlib.Path(tmp_trestle_dir)
setup_rules_view(trestle_root, test_comp, test_rules_dir, skip_rules=True)
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()
rule_transform_task = RuleTransformTask(
tmp_trestle_dir, test_rules_dir, transformer
)
Expand All @@ -85,7 +85,7 @@ def test_rule_transform_task_with_invalid_rule(tmp_trestle_dir: str) -> None:
"""Test rule transform task with invalid rule."""
trestle_root = pathlib.Path(tmp_trestle_dir)
setup_rules_view(trestle_root, test_comp, test_rules_dir, incomplete_rule=True)
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()
rule_transform_task = RuleTransformTask(
tmp_trestle_dir, test_rules_dir, transformer
)
Expand All @@ -100,7 +100,7 @@ def test_rule_transform_task_with_skip(tmp_trestle_dir: str) -> None:
"""Test rule transform task with skip."""
trestle_root = pathlib.Path(tmp_trestle_dir)
setup_rules_view(trestle_root, test_comp, test_rules_dir)
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()
rule_transform_task = RuleTransformTask(
tmp_trestle_dir, test_rules_dir, transformer, skip_model_list=[test_comp]
)
Expand Down
14 changes: 8 additions & 6 deletions tests/trestlebot/transformers/test_csv_to_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
import pathlib

import pytest
import ruamel.yaml as yaml
from ruamel.yaml import YAML

from trestlebot.transformers.csv_to_yaml import YAMLBuilder
from trestlebot.transformers.yaml_transformer import RulesYAMLTransformer
from trestlebot.transformers.yaml_transformer import ToRulesYAMLTransformer


@pytest.fixture(scope="function")
Expand Down Expand Up @@ -70,9 +70,11 @@ def test_write_to_yaml(setup_yaml_builder: YAMLBuilder, tmp_trestle_dir: str) ->
write_sample_csv(csv_file)
setup_yaml_builder.read_from_csv(csv_file)
setup_yaml_builder.write_to_yaml(yaml_file)
yaml = YAML(typ="safe")
with open(yaml_file, "r") as f:
data = yaml.safe_load(f)
assert len(data) == 1
data = yaml.load(f)
# The file will contain a separate YAML document for each rule
assert len(data) == 2


def test_default_test_trestle_rule_keys(
Expand All @@ -83,8 +85,8 @@ def test_default_test_trestle_rule_keys(

# Check that the YAML file written is valid and integrates with the rule
# YAML transformer
transformer = RulesYAMLTransformer()
rule = transformer.transform_to_rule(yaml_file.read_text())
transformer = ToRulesYAMLTransformer()
rule = transformer.transform(yaml_file.read_text())

assert rule.name == "example rule"
assert rule.description == "example description"
Expand Down
50 changes: 14 additions & 36 deletions tests/trestlebot/transformers/test_csv_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,51 +23,29 @@
import pytest

from trestlebot.transformers.csv_transformer import CSVBuilder
from trestlebot.transformers.trestle_rule import (
ComponentInfo,
Control,
Parameter,
Profile,
TrestleRule,
)
from trestlebot.transformers.trestle_rule import TrestleRule


test_comp = "my comp"


def test_csv_builder(tmp_trestle_dir: str) -> None:
def test_csv_builder(test_rule: TrestleRule, tmp_trestle_dir: str) -> None:
"""Test CSV builder on a happy path"""
test_trestle_rule: TrestleRule = TrestleRule(
name="test",
description="test",
component=ComponentInfo(name=test_comp, type="test", description="test"),
parameter=Parameter(
name="test",
description="test",
alternative_values={},
default_value="test",
),
profile=Profile(
description="test", href="test", include_controls=[Control(id="ac-1")]
),
)

csv_builder = CSVBuilder()
csv_builder.add_row(test_trestle_rule)
csv_builder.add_row(test_rule)

assert len(csv_builder._rows) == 1
row = csv_builder._rows[0]
assert row["Rule_Id"] == test_trestle_rule.name
assert row["Rule_Description"] == test_trestle_rule.description
assert row["Component_Title"] == test_trestle_rule.component.name
assert row["Component_Type"] == test_trestle_rule.component.type
assert row["Component_Description"] == test_trestle_rule.component.description
assert row["Rule_Id"] == test_rule.name
assert row["Rule_Description"] == test_rule.description
assert row["Component_Title"] == test_rule.component.name
assert row["Component_Type"] == test_rule.component.type
assert row["Component_Description"] == test_rule.component.description
assert row["Control_Id_List"] == "ac-1"
assert row["Parameter_Id"] == test_trestle_rule.parameter.name # type: ignore
assert row["Parameter_Description"] == test_trestle_rule.parameter.description # type: ignore
assert row["Parameter_Id"] == test_rule.parameter.name # type: ignore
assert row["Parameter_Description"] == test_rule.parameter.description # type: ignore
assert row["Parameter_Value_Alternatives"] == "{}"
assert row["Parameter_Value_Default"] == test_trestle_rule.parameter.default_value # type: ignore
assert row["Profile_Description"] == test_trestle_rule.profile.description
assert row["Profile_Source"] == test_trestle_rule.profile.href
assert row["Parameter_Value_Default"] == test_rule.parameter.default_value # type: ignore
assert row["Profile_Description"] == test_rule.profile.description
assert row["Profile_Source"] == test_rule.profile.href

trestle_root = pathlib.Path(tmp_trestle_dir)
tmp_csv_path = trestle_root.joinpath("test.csv")
Expand Down
32 changes: 25 additions & 7 deletions tests/trestlebot/transformers/test_yaml_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@

from tests.testutils import YAML_TEST_DATA_PATH
from trestlebot.transformers.base_transformer import RulesTransformerException
from trestlebot.transformers.yaml_transformer import RulesYAMLTransformer
from trestlebot.transformers.trestle_rule import TrestleRule
from trestlebot.transformers.yaml_transformer import (
FromRulesYAMLTransformer,
ToRulesYAMLTransformer,
)


def test_rule_transformer() -> None:
Expand All @@ -32,8 +36,8 @@ def test_rule_transformer() -> None:
rule_file_info = rule_file.read()
rule_file.close()

transformer = RulesYAMLTransformer()
rule = transformer.transform_to_rule(rule_file_info)
transformer = ToRulesYAMLTransformer()
rule = transformer.transform(rule_file_info)

assert rule.name == "example_rule_1"
assert rule.description == "My rule description for example rule 1"
Expand All @@ -59,13 +63,13 @@ def test_rules_transform_with_incomplete_rule() -> None:
"""Test rules transform with incomplete rule."""
# Generate test json string
test_string = '{"test_json": "test"}'
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()

with pytest.raises(
RulesTransformerException,
match="Missing key in YAML file: 'x-trestle-rule-info'",
):
transformer.transform_to_rule(test_string)
transformer.transform(test_string)


def test_rules_transform_with_invalid_rule() -> None:
Expand All @@ -76,9 +80,23 @@ def test_rules_transform_with_invalid_rule() -> None:
rule_file = open(rule_path, "r")
rule_file_info = rule_file.read()
rule_file.close()
transformer = RulesYAMLTransformer()
transformer = ToRulesYAMLTransformer()

with pytest.raises(
RulesTransformerException, match="Invalid YAML file: 1 validation error .*"
):
transformer.transform_to_rule(rule_file_info)
transformer.transform(rule_file_info)


def test_read_write_integration(test_rule: TrestleRule) -> None:
"""Test read/write integration."""
from_rules_transformer = FromRulesYAMLTransformer()
to_rules_transformer = ToRulesYAMLTransformer()

yaml_data = from_rules_transformer.transform(test_rule)

blob = yaml_data.getvalue()

read_rule = to_rules_transformer.transform(blob)

assert read_rule == test_rule
3 changes: 3 additions & 0 deletions trestlebot/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
DESCRIPTION = "description"
PARAMETER = "parameter"
PROFILE = "profile"
HREF = "href"
ALTERNATIVE_VALUES = "alternative-values"
DEFAULT_VALUE = "default-value"

COMPONENT_YAML = "component.yaml"
COMPONENT_INFO_TAG = trestle_const.TRESTLE_TAG + "component-info"
Expand Down
Loading

0 comments on commit 56c5b52

Please sign in to comment.