Skip to content
Open
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
25 changes: 25 additions & 0 deletions CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,31 @@ cfengine build --hub myhub --non-interactive
cfengine deploy --hub myhub
```

## Testing policy with `cfengine test`

Runs `test_*.cf` files (bundles that contain their own `assert:` section) in disposable Docker containers and reports a pytest-style pass/fail summary.

```bash
# Auto-discover and run tests/test_*.cf
cfengine test

# Run specific files or directories instead
cfengine test tests/test_foo.cf
cfengine test tests/

# Inside a cfbs project: builds first, then runs tests in that project's built
# masterfiles (custom promise types, services/init.cf), against a minimal
# generated policy, not the project's real bundlesequence; see --full-policy below
cfengine test

# Test against a custom base image (e.g. a different distro), bypassing the build cache
cfengine test --dockerfile ./my-ubuntu.Dockerfile --rebuild


# Inside a cfbs project: run the tests after a normal policy run
cfengine test --full-policy
```

## Running the CFEngine agent

```bash
Expand Down
54 changes: 48 additions & 6 deletions src/cfengine_cli/cfengine_wrapper/cfengine_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
)

from cfengine_cli.utils import UserError
from cfengine_cli.container import run_in_container
from cfengine_cli.container import (
run_in_container,
run_files_in_container,
discover_test_files,
discover_module_files,
)
from cfengine_cli.cfengine_wrapper.cfengine_objects import (
Executable,
ensure_default_agent_flags,
Expand Down Expand Up @@ -287,12 +292,49 @@ def deploy(
return error


def test() -> int:
rc = build_command()
if rc != 0:
return rc
def test(
files: list[str] | None = None,
dockerfile: str | None = None,
rebuild: bool = False,
full_policy: bool = False,
) -> int:
is_cfbs = is_cfbs_repo()
if is_cfbs:
# Build first so any custom promise types / modules this
# project uses are shipped
rc = build_command()
if rc != 0:
return rc

if files:
return run_files_in_container(
files,
masterfiles_dir="out/masterfiles" if is_cfbs else None,
dockerfile=dockerfile,
rebuild=rebuild,
full_policy=full_policy,
)

discovered = discover_test_files()
if discovered:
return run_files_in_container(
discover_module_files() + discovered,
masterfiles_dir="out/masterfiles" if is_cfbs else None,
dockerfile=dockerfile,
rebuild=rebuild,
full_policy=full_policy,
)

if is_cfbs:
return run_in_container(
"out/masterfiles", dockerfile=dockerfile, rebuild=rebuild
)

return run_in_container("out/masterfiles")
raise UserError(
"Nothing to test here -- no cfbs.json project and no test_*.cf files "
"found. Add a test_*.cf file, or run `cfengine init` to start a cfbs "
"project."
)


def show(target: list[str] | None = None) -> int:
Expand Down
12 changes: 9 additions & 3 deletions src/cfengine_cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cfengine_cli.utils import UserError
from cfengine_cli.up import validate_config, up_do, resolve_templates
from cfengine_cli.initialize_project import init_policy_module, init_promise_type
from cfengine_cli.container import assert_module_declaration_path
from cf_remote.paths import cf_remote_dir
from cfbs.commands import init_command
from cfengine_cli.cfengine_wrapper.cfengine_commands import test as _cfengine_test
Expand Down Expand Up @@ -48,15 +49,20 @@ def lint(files, strict, syntax_path) -> int:
return errors


def test(files, strict) -> int:
errors = _lint(files, strict, None)
def test(files, strict, dockerfile=None, rebuild=False, full_policy=False) -> int:
# Adds the assert module so linting passes
lint_targets = (list(files) if files else ["."]) + [
assert_module_declaration_path()
]

errors = _lint(lint_targets, strict, None)
if errors != 0:
plural = "error" if errors == 1 else "errors"
print(f"Lint failed, {errors} {plural} in total. Skipping build/deploy/run.")
return errors

print("Lint passed, no errors found.")
return _cfengine_test()
return _cfengine_test(files, dockerfile, rebuild, full_policy)


def dev(subcommand, args) -> int:
Expand Down
Loading