#
# Copyright 2018-2025 Elyra Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Tests for elyra-metadata application"""
import json
import os
import shutil
from tempfile import mkdtemp
from tempfile import TemporaryDirectory
from typing import AnyStr
from typing import List
from typing import Optional

import pytest

from elyra.metadata.manager import MetadataManager
from elyra.metadata.metadata import Metadata
from elyra.metadata.schema import METADATA_TEST_SCHEMASPACE
from elyra.metadata.schema import METADATA_TEST_SCHEMASPACE_ID
from elyra.tests.metadata.test_utils import all_of_json
from elyra.tests.metadata.test_utils import another_metadata_json
from elyra.tests.metadata.test_utils import create_json_file
from elyra.tests.metadata.test_utils import invalid_metadata_json
from elyra.tests.metadata.test_utils import invalid_no_display_name_json
from elyra.tests.metadata.test_utils import invalid_schema_name_json
from elyra.tests.metadata.test_utils import one_of_json
from elyra.tests.metadata.test_utils import PropertyTester
from elyra.tests.metadata.test_utils import valid_display_name_json
from elyra.tests.metadata.test_utils import valid_metadata2_json
from elyra.tests.metadata.test_utils import valid_metadata_json

os.environ["METADATA_TESTING"] = "1"  # Enable metadata-tests schemaspace


@pytest.fixture()
def mock_data_dir():
    runtime_dir = mkdtemp(prefix="runtime_")
    orig_data_dir = os.environ.get("JUPYTER_DATA_DIR")
    os.environ["JUPYTER_DATA_DIR"] = runtime_dir
    yield runtime_dir  # provide the fixture value
    shutil.rmtree(runtime_dir)
    if orig_data_dir:
        os.environ["JUPYTER_DATA_DIR"] = orig_data_dir
    else:
        os.environ.pop("JUPYTER_DATA_DIR")


def get_file_contents(filename: str) -> AnyStr:
    contents: AnyStr
    with open(filename) as f:
        contents = f.read()
    return contents


def test_no_opts(script_runner):
    ret = script_runner.run("elyra-metadata")
    assert ret.success is False
    message = (
        "No subcommand specified.  One of: "
        "['list', 'create', 'update', 'remove', 'migrate', 'export', 'import'] "
        "must be specified."
    )
    assert message in ret.stdout


def test_bad_subcommand(script_runner):
    ret = script_runner.run("elyra-metadata", "bogus-subcommand")
    assert ret.success is False
    assert (
        "Subcommand 'bogus-subcommand' is invalid.  One of: "
        "['list', 'create', 'update', 'remove', 'migrate', 'export', 'import'] "
        "must be specified." in ret.stdout
    )


# ---------- begin of 'create' command tests


@pytest.mark.parametrize("option_style", ["equals", "sans-equals", "missing"])
def test_create_from_file(script_runner, mock_data_dir, option_style):
    content = create_json_file(mock_data_dir, "valid.json", valid_metadata_json)

    argv: List[str] = [
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
    ]

    if option_style == "equals":
        argv.append(f"--file={content}")
    elif option_style == "sans-equals":
        argv.append("--file")
        argv.append(f"{content}")
    else:  # missing
        argv.append("--file")

    ret = script_runner.run(*argv)

    if option_style == "missing":
        assert ret.success is False
        assert (
            "ERROR: Parameter '--file' requires a file with format JSON and no value was provided.  "
            "Try again with an appropriate value." in ret.stdout
        )
    else:  # success expected
        assert ret.success
        assert "Metadata instance 'valid' for schema 'metadata-test' has been written" in ret.stdout


@pytest.mark.parametrize("option_style", ["equals", "sans-equals", "missing"])
def test_create_from_json(script_runner, mock_data_dir, option_style):
    content = json.dumps(valid_metadata_json)

    argv: List[str] = [
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
    ]

    if option_style == "equals":
        argv.append(f"--json={content}")
    elif option_style == "sans-equals":
        argv.append("--json")
        argv.append(f"{content}")
    else:  # missing
        argv.append("--json")

    ret = script_runner.run(*argv)

    if option_style == "missing":
        assert ret.success is False
        assert (
            "ERROR: Parameter '--json' requires a value with format JSON and no value was provided.  "
            "Try again with an appropriate value." in ret.stdout
        )
    else:  # success expected
        assert ret.success
        assert "Metadata instance 'valid_metadata_instance' for schema 'metadata-test' has been written" in ret.stdout


def test_create_bad_argument(script_runner):
    ret = script_runner.run("elyra-metadata", "create", "--bogus-argument")
    assert ret.success is False
    assert "Subcommand '--bogus-argument' is invalid." in ret.stdout
    assert f"Create a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_create_bad_schemaspace(script_runner):
    ret = script_runner.run("elyra-metadata", "create", "bogus-schemaspace")
    assert ret.success is False
    assert "Subcommand 'bogus-schemaspace' is invalid." in ret.stdout
    assert f"Create a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_create_help(script_runner):
    ret = script_runner.run("elyra-metadata", "create", METADATA_TEST_SCHEMASPACE, "--help")
    assert ret.success is False
    assert f"Create a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_create_no_schema_single(script_runner, mock_data_dir):
    # Use the runtime-images schemaspace since that is most likely to always be a single-schema schemaspace.
    # Note: this test will break if it ever supports multiple.
    ret = script_runner.run("elyra-metadata", "create", "runtime-images")
    assert ret.success is False
    assert "ERROR: '--display_name' is a required parameter." in ret.stdout


def test_create_no_schema_multiple(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "create", METADATA_TEST_SCHEMASPACE)
    assert ret.success is False
    # Since order in dictionaries, where the one-of list is derived, can be random, just check up to the
    # first known difference in the schema names.
    assert (
        "ERROR: '--schema_name' is a required parameter and must be one of the "
        "following values: ['metadata-test" in ret.stdout
    )


def test_create_bad_schema_multiple(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "create", METADATA_TEST_SCHEMASPACE, "--schema_name=metadata-foo")
    assert ret.success is False
    assert "ERROR: Parameter '--schema_name' requires one of the " "following values: ['metadata-test" in ret.stdout


def test_create_no_name(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "create", METADATA_TEST_SCHEMASPACE, "--schema_name=metadata-test")
    assert ret.success is False
    assert "ERROR: '--display_name' is a required parameter." in ret.stdout


def test_create_complex_usage(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "create", METADATA_TEST_SCHEMASPACE, "--schema_name=metadata-test")
    assert ret.success is False
    assert "Note: The following properties in this schema contain JSON keywords that are not supported" in ret.stdout
    assert "*** References unsupported keywords: {'oneOf'}" in ret.stdout
    assert "*** References unsupported keywords: {'allOf'}" in ret.stdout
    assert "*** References unsupported keywords: {'$ref'}" in ret.stdout


def test_create_only_display_name(script_runner, mock_data_dir):
    metadata_display_name = "1 teste 'rápido'"
    metadata_name = "a_1_teste_rpido"

    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        f"--display_name={metadata_display_name}",
        "--required_test=required_value",
    )
    assert ret.success is True
    assert f"Metadata instance '{metadata_name}' for schema 'metadata-test' has been written to:" in ret.stdout

    # Ensure it can be fetched by name...
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE_ID)
    resource = metadata_manager.get(metadata_name)
    assert resource.display_name == metadata_display_name


def test_create_invalid_name(script_runner, mock_data_dir):
    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=UPPER_CASE_NOT_ALLOWED",
        "--display_name=display_name",
        "--required_test=required_value",
    )
    assert ret.success is False
    assert "The following exception occurred saving metadata instance for schema 'metadata-test'" in ret.stdout
    assert "Name of metadata must be lowercase alphanumeric" in ret.stdout


def test_create_simple(script_runner, mock_data_dir):
    expected_file = os.path.join(
        mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE, "test-metadata_42_valid-name.json"
    )
    # Cleanup from any potential previous failures
    if os.path.exists(expected_file):
        os.remove(expected_file)

    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=required_value",
    )
    assert ret.success
    assert "Metadata instance 'test-metadata_42_valid-name' for schema 'metadata-test' has been written" in ret.stdout

    assert os.path.isdir(os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE))
    assert os.path.isfile(expected_file)

    with open(expected_file, "r") as fd:
        instance_json = json.load(fd)
        assert instance_json["schema_name"] == "metadata-test"
        assert instance_json["display_name"] == "display_name"
        assert instance_json["metadata"]["required_test"] == "required_value"
        assert instance_json["metadata"]["number_default_test"] == 42  # defaults will always persist


def test_create_existing(script_runner, mock_data_dir):
    expected_file = os.path.join(
        mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE, "test-metadata_42_valid-name.json"
    )
    # Cleanup from any potential previous failures
    if os.path.exists(expected_file):
        os.remove(expected_file)

    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=required_value",
        "--number_default_test=24",
    )
    assert ret.success
    assert "Metadata instance 'test-metadata_42_valid-name' for schema 'metadata-test' has been written" in ret.stdout
    assert expected_file in ret.stdout
    assert os.path.isdir(os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE))
    assert os.path.isfile(expected_file)

    with open(expected_file, "r") as fd:
        instance_json = json.load(fd)
        assert instance_json["metadata"]["number_default_test"] == 24  # ensure CLI value is used over default

    # Re-attempt create - failure expected
    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=required_value",
    )

    assert ret.success is False
    assert (
        "An instance named 'test-metadata_42_valid-name' already exists in the metadata-tests "
        "schemaspace" in ret.stderr
    )


def test_create_complex(script_runner, mock_data_dir):
    complex_keyword = "defs"
    name: str = f"test-complex-{complex_keyword}".lower()

    option = "--json"
    value = '{ "defs_test": 42 }'

    expected_file = os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE, f"{name}.json")
    # Cleanup from any potential previous failures (should be rare)
    if os.path.exists(expected_file):
        os.remove(expected_file)

    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        f"--name={name}",
        f"--display_name=Test Complex {complex_keyword}",
        "--required_test=required_value",
        f"{option}",  # don't use '=' between option and value
        f"{value}",
    )
    assert ret.success
    assert f"Metadata instance '{name}' for schema 'metadata-test' has been written" in ret.stdout
    assert expected_file in ret.stdout
    assert os.path.exists(expected_file)

    with open(expected_file) as fd:
        json_results = json.load(fd)

    # Verify common stuff
    assert json_results["display_name"] == f"Test Complex {complex_keyword}"
    assert json_results["metadata"]["required_test"] == "required_value"
    assert json_results["metadata"]["defs_test"] == 42


# ---------- end of 'create' command tests
#
# ---------- begin of 'update' command tests


def test_update_bad_argument(script_runner):
    ret = script_runner.run("elyra-metadata", "update", "--bogus-argument")
    assert ret.success is False
    assert "Subcommand '--bogus-argument' is invalid." in ret.stdout
    assert f"Update a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_update_bad_schemaspace(script_runner):
    ret = script_runner.run("elyra-metadata", "update", "bogus-schemaspace")
    assert ret.success is False
    assert "Subcommand 'bogus-schemaspace' is invalid." in ret.stdout
    assert f"Update a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_update_help(script_runner):
    ret = script_runner.run("elyra-metadata", "update", METADATA_TEST_SCHEMASPACE, "--help")
    assert ret.success is False
    assert f"Update a metadata instance in schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_update_no_schema_single(script_runner, mock_data_dir):
    # Use the runtime-images schemaspace since that is most likely to always be a single-schema schemaspace.
    # Note: this test will break if it ever supports multiple.
    ret = script_runner.run("elyra-metadata", "update", "runtime-images")
    assert ret.success is False
    assert (
        "The following exception occurred saving metadata instance for schema 'runtime-image': "
        "The 'name' parameter requires a value." in ret.stdout
    )


def test_update_no_schema_multiple(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "update", METADATA_TEST_SCHEMASPACE)
    assert ret.success is False
    # Since order in dictionaries, where the one-of list is derived, can be random, just check up to the
    # first known difference in the schema names.
    assert (
        "ERROR: '--schema_name' is a required parameter and must be one of the "
        "following values: ['metadata-test" in ret.stdout
    )


def test_update_bad_schema_multiple(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "update", METADATA_TEST_SCHEMASPACE, "--schema_name=metadata-foo")
    assert ret.success is False
    assert "ERROR: Parameter '--schema_name' requires one of the " "following values: ['metadata-test" in ret.stdout


def test_update_no_name(script_runner, mock_data_dir):
    ret = script_runner.run("elyra-metadata", "update", METADATA_TEST_SCHEMASPACE, "--schema_name=metadata-test")
    assert ret.success is False
    assert (
        "The following exception occurred saving metadata instance for schema 'metadata-test': "
        "The 'name' parameter requires a value." in ret.stdout
    )


def test_update_no_instance(script_runner, mock_data_dir):
    """Attempt replace before instance exists"""
    ret = script_runner.run(
        "elyra-metadata",
        "update",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=required_value",
    )
    assert ret.success is False
    assert (
        "No such instance named 'test-metadata_42_valid-name' was found in the metadata-tests schemaspace."
        in ret.stdout
    )


def test_update_simple(script_runner, mock_data_dir):
    expected_file = os.path.join(
        mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE, "test-metadata_42_valid-name.json"
    )
    # Cleanup from any potential previous failures
    if os.path.exists(expected_file):
        os.remove(expected_file)

    # create an instance
    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=required_value",
    )
    assert ret.success
    assert "Metadata instance 'test-metadata_42_valid-name' for schema 'metadata-test' has been written" in ret.stdout

    # update instance
    ret = script_runner.run(
        "elyra-metadata",
        "update",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        "--name=test-metadata_42_valid-name",
        "--display_name=display_name",
        "--required_test=updated_required_value",
    )
    assert ret.success
    assert "Metadata instance 'test-metadata_42_valid-name' for schema 'metadata-test' has been written" in ret.stdout

    assert os.path.isdir(os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE))
    assert os.path.isfile(expected_file)

    with open(expected_file, "r") as fd:
        instance_json = json.load(fd)
        assert instance_json["schema_name"] == "metadata-test"
        assert instance_json["display_name"] == "display_name"
        assert instance_json["metadata"]["required_test"] == "updated_required_value"
        assert instance_json["metadata"]["number_default_test"] == 42  # defaults will always persist


@pytest.mark.parametrize("complex_keyword", ["defs", "oneOf", "allOf"])
def test_update_complex(script_runner, mock_data_dir, complex_keyword):
    # create and use deep copies of the global one_of_json and one_of_json
    # to avoid side effects
    one_of_json_cp = json.loads(json.dumps(one_of_json))
    all_of_json_cp = json.loads(json.dumps(all_of_json))

    test_file: Optional[str] = None
    name: str = f"test-complex-{complex_keyword}".lower()

    if complex_keyword == "defs":
        option = "--json"
        value = '{ "defs_test": 42 }'
    elif complex_keyword == "oneOf":
        option = "--file"
        # Build the file...
        test_file = os.path.join(mock_data_dir, f"{complex_keyword}.json")
        with open(test_file, mode="w") as one_of_fd:
            json.dump(one_of_json_cp, one_of_fd)
        value = test_file
    else:  # allOf
        option = "--allOf_test"  # Use "ovp-from-file" approach
        # Build the file...
        test_file = os.path.join(mock_data_dir, f"{complex_keyword}.json")
        with open(test_file, mode="w") as all_of_fd:
            json.dump(all_of_json_cp, all_of_fd)
        value = test_file

    expected_file = os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE, f"{name}.json")
    # Cleanup from any potential previous failures (should be rare)
    if os.path.exists(expected_file):
        os.remove(expected_file)

    # create instance
    ret = script_runner.run(
        "elyra-metadata",
        "create",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        f"--name={name}",
        f"--display_name=Test Complex {complex_keyword}",
        "--required_test=required_value",
        f"{option}={value}",
    )
    assert ret.success
    assert f"Metadata instance '{name}' for schema 'metadata-test' has been written" in ret.stdout
    assert expected_file in ret.stdout
    assert os.path.exists(expected_file)

    with open(expected_file) as fd:
        json_results = json.load(fd)

    # Verify common stuff
    assert json_results["display_name"] == f"Test Complex {complex_keyword}"
    assert json_results["metadata"]["required_test"] == "required_value"

    # Verify result and prepare for replace...
    if complex_keyword == "defs":
        assert json_results["metadata"]["defs_test"] == 42
        value = '{ "defs_test": 24 }'
    elif complex_keyword == "oneOf":
        assert json_results["metadata"]["oneOf_test"]["obj_switch"] == "obj2"
        assert json_results["metadata"]["oneOf_test"]["obj2_prop1"] == 42, f"--> {json_results}"
        one_of_json_cp["metadata"]["oneOf_test"]["obj2_prop1"] = 24
        with open(test_file, mode="w+") as one_of_fd:
            json.dump(one_of_json_cp, one_of_fd)
    elif complex_keyword == "allOf":
        assert len(json_results["metadata"]["allOf_test"]) == 9
        assert json_results["metadata"]["allOf_test"]["obj1_switch"] == "obj1"
        assert json_results["metadata"]["allOf_test"]["obj1_prop1"] == "allOf-test-val1"
        assert json_results["metadata"]["allOf_test"]["obj1_prop2"] == "allOf-test-val2"
        all_of_json_cp["obj1_prop1"] = "allOf-test-val1-replace"
        assert json_results["metadata"]["allOf_test"]["obj2_switch"] == "obj2"
        assert json_results["metadata"]["allOf_test"]["obj2_prop1"] == 42
        assert json_results["metadata"]["allOf_test"]["obj2_prop2"] == 24
        all_of_json_cp["obj2_prop1"] = 24
        assert json_results["metadata"]["allOf_test"]["obj3_switch"] == "obj3"
        assert json_results["metadata"]["allOf_test"]["obj3_prop1"] == 42.7
        assert json_results["metadata"]["allOf_test"]["obj3_prop2"] is True
        all_of_json_cp["obj3_prop1"] = 7.24

        with open(test_file, mode="w+") as all_of_fd:
            json.dump(all_of_json_cp, all_of_fd)

    # Replace the previously created instance
    ret = script_runner.run(
        "elyra-metadata",
        "update",
        METADATA_TEST_SCHEMASPACE,
        "--schema_name=metadata-test",
        f"--name={name}",
        f"--display_name=Test Complex {complex_keyword}2",
        "--required_test=required_value",
        f"{option}={value}",
    )
    assert ret.success
    assert f"Metadata instance '{name}' for schema 'metadata-test' has been written" in ret.stdout
    assert expected_file in ret.stdout
    assert os.path.exists(expected_file)

    with open(expected_file) as fd:
        json_results = json.load(fd)

    # Verify common stuff
    assert json_results["display_name"] == f"Test Complex {complex_keyword}2"
    assert json_results["metadata"]["required_test"] == "required_value"

    # Verify result following replace...
    if complex_keyword == "defs":
        assert json_results["metadata"]["defs_test"] == 24
    elif complex_keyword == "oneOf":
        assert json_results["metadata"]["oneOf_test"]["obj_switch"] == "obj2"
        assert json_results["metadata"]["oneOf_test"]["obj2_prop1"] == 24
        assert json_results["metadata"]["oneOf_test"]["obj2_prop2"] == 24
    elif complex_keyword == "allOf":
        assert len(json_results["metadata"]["allOf_test"]) == 9
        assert json_results["metadata"]["allOf_test"]["obj1_prop1"] == "allOf-test-val1-replace"
        assert json_results["metadata"]["allOf_test"]["obj1_prop2"] == "allOf-test-val2"
        assert json_results["metadata"]["allOf_test"]["obj2_prop1"] == 24
        assert json_results["metadata"]["allOf_test"]["obj2_prop2"] == 24
        assert json_results["metadata"]["allOf_test"]["obj3_prop1"] == 7.24
        assert json_results["metadata"]["allOf_test"]["obj3_prop2"] is True


# ---------- end of 'update' command tests


def test_list_help(script_runner):
    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--help")
    assert ret.success is False
    assert f"List installed metadata for {METADATA_TEST_SCHEMASPACE}." in ret.stdout


def test_list_bad_argument(script_runner):
    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--bogus-argument")
    assert ret.success is False
    assert "ERROR: The following arguments were unexpected: ['--bogus-argument']" in ret.stdout


def test_list_instances(script_runner, mock_data_dir):
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE)

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE)
    assert ret.success
    lines = ret.stdout.split("\n")
    assert len(lines) == 2  # always 2 more than the actual runtime count
    assert f"No metadata instances found for {METADATA_TEST_SCHEMASPACE}" in lines[0]

    valid = Metadata(**valid_metadata_json)
    resource = metadata_manager.create("valid", valid)
    assert resource is not None
    resource = metadata_manager.create("valid2", valid)
    assert resource is not None
    another = Metadata(**another_metadata_json)
    resource = metadata_manager.create("another", another)
    assert resource is not None
    resource = metadata_manager.create("another2", another)
    assert resource is not None

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--include-invalid")
    assert ret.success
    lines = ret.stdout.split("\n")
    assert len(lines) == 9  # always 5 more than the actual runtime count
    assert lines[0] == f"Available metadata instances for {METADATA_TEST_SCHEMASPACE} (includes invalid):"
    line_elements = [line.split() for line in lines[4:8]]
    assert line_elements[0][0] == "metadata-test"
    assert line_elements[0][1] == "another"
    assert line_elements[1][0] == "metadata-test"
    assert line_elements[1][1] == "another2"
    assert line_elements[2][0] == "metadata-test"
    assert line_elements[2][1] == "valid"
    assert line_elements[3][0] == "metadata-test"
    assert line_elements[3][1] == "valid2"

    # Remove the '2' runtimes and reconfirm smaller set
    metadata_manager.remove("valid2")
    metadata_manager.remove("another2")
    # Include two additional invalid files as well - one for uri failure, another missing display_name
    metadata_dir = os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE)
    create_json_file(metadata_dir, "invalid.json", invalid_metadata_json)
    create_json_file(metadata_dir, "no_display_name.json", invalid_no_display_name_json)
    create_json_file(metadata_dir, "invalid_schema_name.json", invalid_schema_name_json)

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--include-invalid")
    assert ret.success
    lines = ret.stdout.split("\n")
    assert len(lines) == 10  # always 5 more than the actual runtime count
    assert lines[0] == f"Available metadata instances for {METADATA_TEST_SCHEMASPACE} (includes invalid):"
    line_elements = [line.split() for line in lines[4:9]]
    assert line_elements[0][1] == "another"
    assert line_elements[1][1] == "invalid"
    assert line_elements[1][3] == "**INVALID**"
    assert line_elements[1][4] == "(ValidationError)"
    assert line_elements[2][3] == "**INVALID**"
    assert line_elements[2][4] == "(ValidationError)"
    assert line_elements[3][1] == "valid"
    assert line_elements[4][3] == "**INVALID**"
    assert line_elements[4][4] == "(SchemaNotFoundError)"

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE)
    assert ret.success
    lines = ret.stdout.split("\n")
    assert len(lines) == 7  # always 5 more than the actual runtime count
    assert lines[0] == f"Available metadata instances for {METADATA_TEST_SCHEMASPACE}:"
    line_elements = [line.split() for line in lines[4:6]]
    assert line_elements[0][1] == "another"
    assert line_elements[1][1] == "valid"


def test_list_json_instances(script_runner, mock_data_dir):
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE)

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--json")
    assert ret.success
    lines = ret.stdout.split("\n")
    assert len(lines) == 2
    assert lines[0] == "[]"

    valid = Metadata(**valid_metadata_json)
    resource = metadata_manager.create("valid", valid)
    assert resource is not None
    resource = metadata_manager.create("valid2", valid)
    assert resource is not None
    another = Metadata(**another_metadata_json)
    resource = metadata_manager.create("another", another)
    assert resource is not None
    resource = metadata_manager.create("another2", another)
    assert resource is not None

    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--json", "--include-invalid")
    assert ret.success
    # Consume results
    results = json.loads(ret.stdout)
    assert len(results) == 4

    # Remove the '2' runtimes and reconfirm smaller set
    metadata_manager.remove("valid2")
    metadata_manager.remove("another2")

    # Include two additional invalid files as well - one for uri failure, another missing display_name
    metadata_dir = os.path.join(mock_data_dir, "metadata", METADATA_TEST_SCHEMASPACE)
    create_json_file(metadata_dir, "invalid.json", invalid_metadata_json)
    create_json_file(metadata_dir, "no_display_name.json", invalid_no_display_name_json)

    # Ensure invalids are NOT listed
    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--json")
    assert ret.success
    results = json.loads(ret.stdout)
    assert len(results) == 2

    # Ensure invalids ARE listed
    ret = script_runner.run("elyra-metadata", "list", METADATA_TEST_SCHEMASPACE, "--json", "--include-invalid")
    assert ret.success
    results = json.loads(ret.stdout)
    assert len(results) == 4


def test_remove_help(script_runner):
    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE, "--help")
    assert ret.success is False
    assert f"Remove a metadata instance from schemaspace '{METADATA_TEST_SCHEMASPACE}'." in ret.stdout


def test_remove_no_name(script_runner):
    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE)
    assert ret.success is False
    assert "ERROR: '--name' is a required parameter." in ret.stdout


def test_remove_with_no_equals(script_runner, mock_data_dir):
    # Attempt removal w/o the '=' between parameter and value
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE)
    valid = Metadata(**valid_metadata_json)
    metadata_manager.create("valid", valid)

    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE, "--name", "valid")
    assert ret.success is True


def test_remove_missing(script_runner, mock_data_dir):
    # Create an instance so that the schemaspace exists.
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE)
    valid = Metadata(**valid_metadata_json)
    metadata_manager.create("valid", valid)

    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE, "--name=missing")
    assert ret.success is False
    assert "No such instance named 'missing' was found in the metadata-tests schemaspace." in ret.stdout

    # Now cleanup original instance.
    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE, "--name=valid")
    assert ret.success


def test_remove_instance(script_runner, mock_data_dir):
    metadata_manager = MetadataManager(schemaspace=METADATA_TEST_SCHEMASPACE)

    valid = Metadata(**valid_metadata_json)
    resource = metadata_manager.create("valid", valid)
    assert resource is not None
    resource = metadata_manager.create("valid2", valid)
    assert resource is not None
    another = Metadata(**another_metadata_json)
    resource = metadata_manager.create("another", another)
    assert resource is not None
    resource = metadata_manager.create("another2", another)
    assert resource is not None

    ret = script_runner.run("elyra-metadata", "remove", METADATA_TEST_SCHEMASPACE, "--name=valid")
    assert ret.success

