Skip to content

Commit

Permalink
Refactor Recipe tests to parameterised tests (#896)
Browse files Browse the repository at this point in the history
* Revert "Upgrade jenkins script (#893)"

This reverts commit 78a7f4f.

* Converted recipe tests to parameterised

* Revert weird changes...?

* Linter appeasement
  • Loading branch information
ramo-j authored Jul 15, 2024
1 parent fb851cc commit ebb1e0e
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 40 deletions.
13 changes: 12 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ mkdocs = "^1.3.0"
graphviz = "^0.20"
pytype = "^2023.09.19"
ruff = "^0.3.4"
absl-py = "^2.1.0"

[tool.poetry.group.spannertelemetry]
optional = true
Expand Down
97 changes: 58 additions & 39 deletions tests/cli/main_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
# -*- coding: utf-8 -*-
"""Tests the main tool functionality."""

import unittest
import logging
import inspect

from absl.testing import absltest
from absl.testing import parameterized

from dftimewolf.cli import dftimewolf_recipes
from dftimewolf.lib import state as dftw_state
from dftimewolf.lib import resources, errors
Expand Down Expand Up @@ -50,17 +52,32 @@
resources.RecipeArgument(*arg) for arg in OPTIONAL_ARG_RECIPE['args']]


class MainToolTest(unittest.TestCase):
def _CreateToolObject():
"""Creates a DFTimewolfTool object instance."""
tool = dftimewolf_recipes.DFTimewolfTool()
tool.LoadConfiguration()
try:
tool.ReadRecipes()
except KeyError:
# Prevent conflicts from other tests where recipes are still registered.
pass
return tool


def _EnumerateRecipeNames():
"""Enumerate recipe names for the purposes of generatting parameterised tests.
"""
tool = _CreateToolObject()
# pylint: disable=protected-access
for recipe in tool._recipes_manager.GetRecipes():
yield (recipe.name, recipe.name)


class MainToolTest(parameterized.TestCase):
"""Tests for main tool functions."""

def setUp(self):
self.tool = dftimewolf_recipes.DFTimewolfTool()
self.tool.LoadConfiguration()
try:
self.tool.ReadRecipes()
except KeyError:
# Prevent conflicts from other tests where recipes are still registered.
pass
self.tool = _CreateToolObject()

def tearDown(self):
# pylint: disable=protected-access
Expand All @@ -85,44 +102,46 @@ def testToolWithArbitraryRecipe(self):
self.tool.ParseArguments(['upload_ts', '/tmp/test'])
self.tool.state.LogExecutionPlan()

def testRecipeSetupArgs(self):
@parameterized.named_parameters(_EnumerateRecipeNames())
def testRecipeSetupArgs(self, recipe_name):
"""Checks that all recipes pass the correct arguments to their modules."""
# We want to access the tool's state object to load recipes and go through
# modules.
# pylint: disable=protected-access
self.tool._state = dftw_state.DFTimewolfState(config.Config)

for recipe in self.tool._recipes_manager.GetRecipes():
self.tool._state.LoadRecipe(recipe.contents, dftimewolf_recipes.MODULES)
modules = recipe.contents['modules']
preflights = recipe.contents.get('preflights', [])
for module in modules + preflights:
runtime_name = module.get('runtime_name', module['name'])
if runtime_name in self.tool.state._module_pool:
setup_func = self.tool.state._module_pool[runtime_name].SetUp
expected_args = set(inspect.getfullargspec(setup_func).args)
expected_args.remove('self')
provided_args = set(module['args'])

self.assertEqual(
expected_args,
provided_args,
f'Error in {recipe.name}:{runtime_name}')

def testRecipeValidators(self):
recipe = self.tool._recipes_manager.Recipes()[recipe_name]

self.tool._state.LoadRecipe(recipe.contents, dftimewolf_recipes.MODULES)
modules = recipe.contents['modules']
preflights = recipe.contents.get('preflights', [])
for module in modules + preflights:
runtime_name = module.get('runtime_name', module['name'])
if runtime_name in self.tool.state._module_pool:
setup_func = self.tool.state._module_pool[runtime_name].SetUp
expected_args = set(inspect.getfullargspec(setup_func).args)
expected_args.remove('self')
provided_args = set(module['args'])

self.assertEqual(
expected_args,
provided_args,
f'Error in {recipe.name}:{runtime_name}')

@parameterized.named_parameters(_EnumerateRecipeNames())
def testRecipeValidators(self, recipe_name):
"""Tests that recipes do not specify invalid validators."""
# pylint: disable=protected-access
self.tool._state = dftw_state.DFTimewolfState(config.Config)
recipe = self.tool._recipes_manager.Recipes()[recipe_name]

for recipe in self.tool._recipes_manager.GetRecipes():
self.tool._state.LoadRecipe(recipe.contents, dftimewolf_recipes.MODULES)
for arg in recipe.args:
if arg.validation_params:
self.assertIn(
arg.validation_params['format'],
validators_manager.ValidatorsManager.ListValidators(),
f'Error in {recipe.name}:{arg.switch} - '
f'Invalid validator {arg.validation_params["format"]}.')
self.tool._state.LoadRecipe(recipe.contents, dftimewolf_recipes.MODULES)
for arg in recipe.args:
if arg.validation_params:
self.assertIn(
arg.validation_params['format'],
validators_manager.ValidatorsManager.ListValidators(),
f'Error in {recipe.name}:{arg.switch} - '
f'Invalid validator {arg.validation_params["format"]}.')

def testRecipeWithNestedArgs(self):
"""Tests that a recipe with args referenced in other args is populated."""
Expand Down Expand Up @@ -203,4 +222,4 @@ def testOptionalArguments(self):


if __name__ == '__main__':
unittest.main()
absltest.main()

0 comments on commit ebb1e0e

Please sign in to comment.