def test_skip_replacements(self, resolve_image_reference, tmp_path,
                               dry_run):
        eggs_image_reference = 'registry.example.com/eggs:9.8'
        spam_image_reference = 'registry.example.com/maps/spam-operator:1.2'

        manifest_dir = tmp_path / 'manifests'
        manifest_dir.mkdir()
        csv_path = manifest_dir / 'spam.yaml'
        original_csv_text = CSV_TEMPLATE_WITH_RELATED_IMAGES.format(
            eggs=eggs_image_reference, spam=spam_image_reference)
        csv_path.write_text(original_csv_text)

        output_extract_file = io.StringIO()
        output_replace_file = io.StringIO()
        pin_image_references(
            str(manifest_dir),
            output_extract=output_extract_file,
            output_replace=output_replace_file,
            dry_run=dry_run,
        )

        resolve_image_reference.assert_not_called()
        assert csv_path.read_text() == original_csv_text

        output_extract_file.seek(0)
        assert sorted(json.load(output_extract_file)) == [
            eggs_image_reference,
            spam_image_reference,
        ]

        output_replace_file.seek(0)
        assert output_replace_file.read() == ''
 def test_check_manifest_dir_exists(self, tmp_path, dry_run):
     with pytest.raises(
             ValueError,
             match=r'/manifests is not a directory or does not exist'):
         pin_image_references(
             str(tmp_path / 'manifests'),
             output_extract=io.StringIO(),
             output_replace=io.StringIO(),
             dry_run=dry_run,
         )
 def test_output_replace_is_seekable(self, tmp_path, dry_run):
     # Ideally, simply use sys.stdout as a non-seekable file object. However, pytest does
     # some special manipulation to sys.stdout that makes it seekable during unit tests.
     output_extract_file = io.IOBase()
     assert not output_extract_file.seekable()
     with pytest.raises(ValueError,
                        match=r'output_extract must be a seekable object'):
         pin_image_references(
             str(tmp_path / 'manifests'),
             output_extract=output_extract_file,
             output_replace=io.StringIO(),
             dry_run=dry_run,
         )
    def test_full_pinning(self, resolve_image_reference, tmp_path, dry_run):

        eggs_image_reference = 'registry.example.com/eggs:9.8'
        spam_image_reference = 'registry.example.com/maps/spam-operator:1.2'

        eggs_image_reference_resolved = 'registry.example.com/eggs@sha256:2'
        spam_image_reference_resolved = 'registry.example.com/maps/spam-operator@sha256:1'

        replacements = {
            spam_image_reference: spam_image_reference_resolved,
            eggs_image_reference: eggs_image_reference_resolved,
        }
        resolve_image_reference.side_effect = lambda image_ref, authfile: replacements[
            image_ref]

        manifest_dir = tmp_path / 'manifests'
        manifest_dir.mkdir()
        csv_path = manifest_dir / 'spam.yaml'
        original_csv_text = CSV_TEMPLATE.format(eggs=eggs_image_reference,
                                                spam=spam_image_reference)
        csv_path.write_text(original_csv_text)

        output_extract_file = io.StringIO()
        output_replace_file = io.StringIO()
        pin_image_references(
            str(manifest_dir),
            output_extract=output_extract_file,
            output_replace=output_replace_file,
            dry_run=dry_run,
        )

        if dry_run:
            assert csv_path.read_text() == original_csv_text
        else:
            assert csv_path.read_text() == CSV_RESOLVED_TEMPLATE.format(
                eggs=eggs_image_reference_resolved,
                spam=spam_image_reference_resolved)

        output_extract_file.seek(0)
        assert sorted(json.load(output_extract_file)) == [
            eggs_image_reference,
            spam_image_reference,
        ]

        output_replace_file.seek(0)
        assert (json.load(output_replace_file)) == {
            eggs_image_reference: eggs_image_reference_resolved,
            spam_image_reference: spam_image_reference_resolved,
        }