# Path: jacquard/cli.py
# def main(args=sys.argv[1:], config=None):
#     """
#     Run as if from the command line, with the given arguments.
# 
#     If the arguments in `args` are not given they default to using `sys.argv`.
# 
#     Note that this function is permitted to raise SystemExit; users who do not
#     want exciting exiting behaviour should be prepared to catch this.
# 
#     If `config` is given, it is used in place of loading a configuration file.
#     """
#     parser = argument_parser()
#     options = parser.parse_args(args)
# 
#     config = _configure_process_and_load_config_from_options(
#         options=options, override_config=config
#     )
# 
#     # Run subcommand
#     with contextlib.suppress(KeyboardInterrupt):
#         try:
#             options.func(config, options)
#         except CommandError as exc:
#             (message,) = exc.args
#             print(message, file=sys.stderr)
#             exit(1)
# 
# @functools.lru_cache()
# def _build_argument_parser(cwd=None):
#     parser = argparse.ArgumentParser(description="Split testing server")
#     parser.add_argument(
#         "-v",
#         "--verbose",
#         help="enable verbose output",
#         action="store_const",
#         dest="log_level",
#         const=logging.INFO,
#         default=logging.ERROR,
#     )
#     parser.add_argument(
#         "--debug",
#         help="enable debug output (implies -v)",
#         action="store_const",
#         dest="log_level",
#         const=logging.DEBUG,
#     )
#     parser.add_argument(
#         "-c",
#         "--config",
#         help="config file",
#         type=pathlib.Path,
#         default=DEFAULT_CONFIG_FILE_PATH,
#     )
#     parser.add_argument(
#         "-V",
#         "--version",
#         help="show version and exit",
#         action="version",
#         version=str(pkg_resources.working_set.by_key["jacquard-split"]),
#     )
#     parser.set_defaults(func=None)
# 
#     subparsers = parser.add_subparsers(metavar="command", title="subcommands")
# 
#     # Top-level plugins
#     _add_subparsers_from_plugins(subparsers=subparsers, plugin_group="commands")
# 
#     # Subcommand plugins
#     for subcommand, subcommand_help in SUBCOMMAND_GROUPS:
#         subcommand_parser = subparsers.add_parser(subcommand, help=subcommand_help)
#         subsubcommands = subcommand_parser.add_subparsers(
#             metavar="subject", title="subjects"
#         )
# 
#         _add_subparsers_from_plugins(
#             subparsers=subsubcommands,
#             plugin_group="commands.{subcommand}".format(subcommand=subcommand),
#         )
# 
#     _add_help_command(parser, subparsers)
# 
#     return parser
# 
# Path: jacquard/storage/dummy.py
# class DummyStore(StorageEngine):
#     """Dummy, in-memory storage engine."""
# 
#     def __init__(self, connection_string, *, data=None):
#         """
#         Make it so.
# 
#         The connection string is ignored. If the magic keyword argument `data`
#         is provided it is used for the initial data - very handy for unit
#         tests!
#         """
#         if data is not None:
#             self.data = {key: json.dumps(value) for key, value in data.items()}
#         else:
#             self.data = {}
#         self.lock = threading.Lock()
# 
#     def __getitem__(self, key):
#         """Direct item access. This is for test usage."""
#         return json.loads(self.data.get(key, "null"))
# 
#     def begin(self):
#         """Begin transaction."""
#         self.lock.acquire()
# 
#     def commit(self, changes, deletions):
#         """Commit transaction."""
#         self.data.update(changes)
#         for deletion in deletions:
#             del self.data[deletion]
#         self.lock.release()
# 
#     def rollback(self):
#         """Roll back transaction."""
#         self.lock.release()
# 
#     def keys(self):
#         """All keys."""
#         return self.data.keys()
# 
#     def get(self, key):
#         """Get value."""
#         return self.data.get(key)


import io
import copy
import datetime
import contextlib
import pytest
import dateutil.tz
from unittest.mock import Mock, patch
from jacquard.cli import main, _build_argument_parser
from jacquard.storage.dummy import DummyStore
def test_conclude_updates_defaults():
    config = Mock()
    config.storage = DummyStore("", data=DUMMY_DATA_POST_LAUNCH)

    main(("conclude", "foo", "bar"), config=config)

    assert "concluded" in config.storage["experiments/foo"]
    assert "foo" not in config.storage["active-experiments"]
    assert "foo" in config.storage["concluded-experiments"]
    assert config.storage["defaults"] == BRANCH_SETTINGS


def test_conclude_before_launch_not_allowed():
    config = Mock()
    config.storage = DummyStore("", data=DUMMY_DATA_PRE_LAUNCH)

    stderr = io.StringIO()
    with contextlib.redirect_stderr(stderr), pytest.raises(SystemExit):
        main(("conclude", "foo", "bar"), config=config)

    assert "concluded" not in config.storage["experiments/foo"]
    assert config.storage["active-experiments"] is None
    assert config.storage["concluded-experiments"] is None
    assert config.storage["defaults"] is None

    assert stderr.getvalue() == "Experiment 'foo' not launched!\n"


def test_conclude_twice_not_allowed():
    config = Mock()

# Path: jacquard/cli.py
# def main(args=sys.argv[1:], config=None):
#     """
#     Run as if from the command line, with the given arguments.
# 
#     If the arguments in `args` are not given they default to using `sys.argv`.
# 
#     Note that this function is permitted to raise SystemExit; users who do not
#     want exciting exiting behaviour should be prepared to catch this.
# 
#     If `config` is given, it is used in place of loading a configuration file.
#     """
#     parser = argument_parser()
#     options = parser.parse_args(args)
# 
#     config = _configure_process_and_load_config_from_options(
#         options=options, override_config=config
#     )
# 
#     # Run subcommand
#     with contextlib.suppress(KeyboardInterrupt):
#         try:
#             options.func(config, options)
#         except CommandError as exc:
#             (message,) = exc.args
#             print(message, file=sys.stderr)
#             exit(1)
# 
# @functools.lru_cache()
# def _build_argument_parser(cwd=None):
#     parser = argparse.ArgumentParser(description="Split testing server")
#     parser.add_argument(
#         "-v",
#         "--verbose",
#         help="enable verbose output",
#         action="store_const",
#         dest="log_level",
#         const=logging.INFO,
#         default=logging.ERROR,
#     )
#     parser.add_argument(
#         "--debug",
#         help="enable debug output (implies -v)",
#         action="store_const",
#         dest="log_level",
#         const=logging.DEBUG,
#     )
#     parser.add_argument(
#         "-c",
#         "--config",
#         help="config file",
#         type=pathlib.Path,
#         default=DEFAULT_CONFIG_FILE_PATH,
#     )
#     parser.add_argument(
#         "-V",
#         "--version",
#         help="show version and exit",
#         action="version",
#         version=str(pkg_resources.working_set.by_key["jacquard-split"]),
#     )
#     parser.set_defaults(func=None)
# 
#     subparsers = parser.add_subparsers(metavar="command", title="subcommands")
# 
#     # Top-level plugins
#     _add_subparsers_from_plugins(subparsers=subparsers, plugin_group="commands")
# 
#     # Subcommand plugins
#     for subcommand, subcommand_help in SUBCOMMAND_GROUPS:
#         subcommand_parser = subparsers.add_parser(subcommand, help=subcommand_help)
#         subsubcommands = subcommand_parser.add_subparsers(
#             metavar="subject", title="subjects"
#         )
# 
#         _add_subparsers_from_plugins(
#             subparsers=subsubcommands,
#             plugin_group="commands.{subcommand}".format(subcommand=subcommand),
#         )
# 
#     _add_help_command(parser, subparsers)
# 
#     return parser
# 
# Path: jacquard/storage/dummy.py
# class DummyStore(StorageEngine):
#     """Dummy, in-memory storage engine."""
# 
#     def __init__(self, connection_string, *, data=None):
#         """
#         Make it so.
# 
#         The connection string is ignored. If the magic keyword argument `data`
#         is provided it is used for the initial data - very handy for unit
#         tests!
#         """
#         if data is not None:
#             self.data = {key: json.dumps(value) for key, value in data.items()}
#         else:
#             self.data = {}
#         self.lock = threading.Lock()
# 
#     def __getitem__(self, key):
#         """Direct item access. This is for test usage."""
#         return json.loads(self.data.get(key, "null"))
# 
#     def begin(self):
#         """Begin transaction."""
#         self.lock.acquire()
# 
#     def commit(self, changes, deletions):
#         """Commit transaction."""
#         self.data.update(changes)
#         for deletion in deletions:
#             del self.data[deletion]
#         self.lock.release()
# 
#     def rollback(self):
#         """Roll back transaction."""
#         self.lock.release()
# 
#     def keys(self):
#         """All keys."""
#         return self.data.keys()
# 
#     def get(self, key):
#         """Get value."""
#         return self.data.get(key)


# Path: jacquard/experiments/tests/test_smoke.py
import io
import copy
import datetime
import contextlib
import pytest
import dateutil.tz
from unittest.mock import Mock, patch
from jacquard.cli import main, _build_argument_parser
from jacquard.storage.dummy import DummyStore

def test_conclude_updates_defaults():
    config = Mock()
    config.storage = DummyStore("", data=DUMMY_DATA_POST_LAUNCH)

    main(("conclude", "foo", "bar"), config=config)

    assert "concluded" in config.storage["experiments/foo"]
    assert "foo" not in config.storage["active-experiments"]
    assert "foo" in config.storage["concluded-experiments"]
    assert config.storage["defaults"] == BRANCH_SETTINGS


def test_conclude_before_launch_not_allowed():
    config = Mock()
    config.storage = DummyStore("", data=DUMMY_DATA_PRE_LAUNCH)

    stderr = io.StringIO()
    with contextlib.redirect_stderr(stderr), pytest.raises(SystemExit):
        main(("conclude", "foo", "bar"), config=config)

    assert "concluded" not in config.storage["experiments/foo"]
    assert config.storage["active-experiments"] is None
    assert config.storage["concluded-experiments"] is None
    assert config.storage["defaults"] is None

    assert stderr.getvalue() == "Experiment 'foo' not launched!\n"


def test_conclude_twice_not_allowed():
    config = Mock()

    config.storage = DummyStore("", data=DUMMY_DATA_POST_LAUNCH)