#generate python dagger sdk from within Dagger functions?

1 messages · Page 1 of 1 (latest)

wary prairie
#

Hi. Would this function receive an arbitrary module as input? Or is it just testing the Dagger functions next to it?

haughty goblet
#

It's just testing the Dagger functions next to it

wary prairie
#

Same module or a dedicated "test" sub-module?

haughty goblet
#

Same module

wary prairie
#

With a sub-module you'll be able to use your functions from dag like an external module would. Won't that be closer to what you want to test?

haughty goblet
#

Basically the code looks like this:

    @function
    def uv_container(self) -> dagger.Container:
        """Base container with uv installed for caching"""
        return (
            dag.container()
            .from_('python:3.12-slim')
            .with_exec(['apt-get', 'update'])
            .with_exec(['apt-get', 'install', '-y', 'curl'])
            .with_exec(['bash', '-c', 'curl -LsSf https://astral.sh/uv/install.sh | sh'])
            .with_env_variable('PATH', '/root/.local/bin:$PATH')
            .with_mounted_cache('/root/.cache/uv', dag.cache_volume('uv-cache'))
            .with_exec(['uv', 'tool', 'install', 'ruff'])
        )
    @function
    async def test_dagger(self, repo_root: dagger.Directory, dagger_sdk: dagger.Directory | None = None) -> str:
        """Run pytest for .dagger package"""
        return await (
            self.uv_container()
            .with_mounted_directory('/workspace', repo_root)
            .with_workdir('/workspace/.dagger')
            .with_exec(['uv', 'run', 'pytest'])
            .stdout()
        )
wary prairie
#

What do the pytest test functions look like?

haughty goblet
#

Usually they are just simple unit tests. So in theory I can move the logic to standalone library. But it'll add more complexity.

wary prairie
#

Just curious how your pytest functions are testing the dagger functions, and instantiating their parent objects.

haughty goblet
#

It would be fun to have a possibility to call Dagger from within Dagger so we can write integration tests.
But it's beginning to look scary

#

I don't intend to test Dagger objects with unit tests (but now I'm a little interested. Maybe I will try some mocks)

wary prairie
haughty goblet
#

I can show examples a little later.
I'm from my phone at the moment.

wary prairie
#

Yeah, that would be great. You can do unit tests and integration tests. Easiest way to do integration tests is by creating a sub Dagger module specifically for testing. That is described in general terms in #1403159511182872637 message.

#

No need to create a container and mount the repo and sdk since this works off of the generated client bindings that are generated already for a module's dependencies.

#

Similarly for unit tests in the main module, you can also just subprocess it since the module already runs in an ephemeral Python container with uv and your source code.

#

Note that the need for a sub-module to run integration tests will soon not be necessary because of https://github.com/dagger/dagger/pull/10584, which will allow calling functions from a Dagger module through dag, without having to go through a dependency. This feature will start out as experimental, but until it's production ready the sub-module route is the current best practice.

#

Note also that running "pytest" doesn't need to be through subprocess. You can call it programmatically with pytest.main() but you'll need a bit more boilerplate.

haughty goblet
#

Some simple unit tests:

import pytest

def test_original_repo_path(self, test_workdir):
    """Test original repository path method."""
    orig_path = test_workdir.original_repo_path()
    
    assert orig_path.name == "original-repo"
    assert orig_path.parent == test_workdir.base_path
    assert orig_path.exists()
    assert orig_path.is_dir()

Nothing crazy.

#

Integration test example:

"""Integration tests for main.py Dagger functions using real Dagger CLI."""

import pytest
from pathlib import Path
import tempfile
import shutil


class TestValidateWorkdirIntegration:
    """Integration tests for validate_workdir function via Dagger CLI."""

    def test_validate_valid_workdir(self, dagger_cli):
        """Test validation of a valid workdir via real Dagger CLI."""
        result = dagger_cli("call validate-workdir", directory="tests/data/test_workdir")
        
        assert result['success'], f"Command failed: {result['stderr']}"
        
        output = result['stdout']
        # Check that all required components are validated successfully
        assert "✅ workdir" in output
        assert "✅ config.yaml" in output
        assert "✅ Dockerfile" in output
        assert "✅ Valid migration workdir: True" in output

    def test_validate_workdir_missing_directory(self, dagger_cli):
        """Test validation when directory doesn't exist."""
        result = dagger_cli("call validate-workdir", directory="tests/data/nonexistent")
        
        # Should fail because directory doesn't exist
        assert not result['success']