/*
 * Copyright 2020 Bloomberg Finance LP
 *
 * 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.
 */

#include <buildboxcasd_cmdlinespec.h>
#include <buildboxcasd_daemon.h>

#include <buildboxcommon_commandline.h>
#include <buildboxcommon_connectionoptions.h>
#include <buildboxcommon_digestgenerator.h>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

using namespace testing;

// clang-format off
const char *argvTest[] = {
    "/some/path/to/some_program.tsk",
    "--proxy-instance=dev",
    // Begin ConnectionOptions parameters
    "--cas-remote=http://127.0.0.1:50011",
    "--cas-instance=cas-dev",
    "--cas-server-cert=cas-server-cert",
    "--cas-client-key=cas-client-key",
    "--cas-client-cert=cas-client-cert",
    "--cas-access-token=cas-access-token",
    "--cas-googleapi-auth=true",
    "--cas-retry-limit=10",
    "--cas-retry-delay=500",
    "--ra-remote=http://127.0.0.1:30011",
    "--ra-instance=ra-dev",
    "--ra-server-cert=ra-server-cert",
    "--ra-client-key=ra-client-key",
    "--ra-client-cert=ra-client-cert",
    "--ra-access-token=ra-access-token",
    "--ra-googleapi-auth=false",
    "--ra-retry-limit=20",
    "--ra-retry-delay=1000",
    "--ac-remote=http://127.0.0.1:40011",
    "--ac-instance=ac-dev",
    "--ac-server-cert=ac-server-cert",
    "--ac-client-key=ac-client-key",
    "--ac-client-cert=ac-client-cert",
    "--ac-access-token=ac-access-token",
    "--ac-googleapi-auth=true",
    "--ac-retry-limit=15",
    "--ac-retry-delay=750",
    "--exec-remote=http://127.0.0.1:60011",
    "--exec-instance=exec-dev",
    "--exec-server-cert=exec-server-cert",
    "--exec-client-key=exec-client-key",
    "--exec-client-cert=exec-client-cert",
    "--exec-access-token=exec-access-token",
    "--exec-googleapi-auth=true",
    "--exec-retry-limit=15",
    "--exec-retry-delay=750",
    // End ConnectionOptions parameters
    "--bind=127.0.0.1:50011",
    "--quota-high=64G",
    "--quota-low=48G",
    "--reserved=3G",
    "--protect-session-blobs=true",
    "--store-writable-objects",
    "--findmissingblobs-cache-ttl=30",
    "--ac-validation-cache-ttl=60",
    "--log-level=info",
    "/path/to/cache"
};

const char *argvTest2[] = {
    "/some/path/to/some_program.tsk",
    "--proxy-instance=dev",
    "--read-only-remote",
    // Begin ConnectionOptions parameters
    "--cas-remote=http://127.0.0.1:50011",
    "--cas-instance=cas-dev",
    "--cas-server-cert=cas-server-cert",
    "--cas-client-key=cas-client-key",
    "--cas-client-cert=cas-client-cert",
    "--cas-access-token=cas-access-token",
    "--cas-googleapi-auth=true",
    "--cas-retry-limit=10",
    "--cas-retry-delay=500",
    "--ra-remote=http://127.0.0.1:30011",
    "--ra-instance=ra-dev",
    "--ra-server-cert=ra-server-cert",
    "--ra-client-key=ra-client-key",
    "--ra-client-cert=ra-client-cert",
    "--ra-access-token=ra-access-token",
    "--ra-googleapi-auth=false",
    "--ra-retry-limit=20",
    "--ra-retry-delay=1000",
    // End ConnectionOptions parameters
    "--bind=127.0.0.1:50011",
    "--quota-high=64G",
    "--reserved=3G",
    "--protect-session-blobs=true",
    "--findmissingblobs-cache-ttl=30",
    "--ac-validation-cache-ttl=60",
    "--log-level=info",
    "/path/to/cache"
};

const char *argvTest3[] = {
    "/some/path/to/some_program.tsk",
    "--proxy-instance=dev",
    "--read-only-remote=1",
    // Begin ConnectionOptions parameters
    "--cas-remote=http://127.0.0.1:50011",
    "--cas-instance=cas-dev",
    "--cas-server-cert=cas-server-cert",
    "--cas-client-key=cas-client-key",
    "--cas-client-cert=cas-client-cert",
    "--cas-access-token=cas-access-token",
    "--cas-googleapi-auth=true",
    "--cas-retry-limit=10",
    "--cas-retry-delay=500",
    "--ra-remote=http://127.0.0.1:30011",
    "--ra-instance=ra-dev",
    "--ra-server-cert=ra-server-cert",
    "--ra-client-key=ra-client-key",
    "--ra-client-cert=ra-client-cert",
    "--ra-access-token=ra-access-token",
    "--ra-googleapi-auth=false",
    "--ra-retry-limit=20",
    "--ra-retry-delay=1000",
    "--ac-remote=http://127.0.0.1:40011",
    "--ac-instance=ac-dev",
    "--ac-server-cert=ac-server-cert",
    "--ac-client-key=ac-client-key",
    "--ac-client-cert=ac-client-cert",
    "--ac-access-token=ac-access-token",
    "--ac-googleapi-auth=true",
    "--ac-retry-limit=15",
    "--ac-retry-delay=750",
    // End ConnectionOptions parameters
    "--bind=127.0.0.1:50011",
    "--quota-high=64G",
    "--quota-low=80%",
    "--reserved=3G",
    "--protect-session-blobs=true",
    "--findmissingblobs-cache-ttl=30",
    "--ac-validation-cache-ttl=60",
    "--log-level=info",
    "/path/to/cache"
};

const char *argvTestBackward[] = {
    "/some/path/to/some_program.tsk",
    "--cas-remote=http://127.0.0.1:12345",
    "--instance=dev",
    "--server-instance=srv",
    "/path/to/cache",
};

const char *argvTestBackwardDisabled[] = {
    "/some/path/to/some_program.tsk",
    "--remote=http://127.0.0.1:12345",
    "--instance=dev",
    "--local-server-instance=srv",
    "/path/to/cache",
};

const char *argvTestProxyInstance[] = {
    "/some/path/to/some_program.tsk",
    "--remote=http://127.0.0.1:12345",
    "--instance=dev",
    "--proxy-instance=prx",
    "/path/to/cache",
};
// clang-format on

void parse(buildboxcasd::Daemon &daemon, size_t argc, const char *argv[])
{
    buildboxcommon::DigestGenerator::resetState();
    buildboxcasd::CmdLineSpec spec(
        buildboxcommon::ConnectionOptionsCommandLine("", ""),
        buildboxcommon::ConnectionOptionsCommandLine("CAS", "cas-"),
        buildboxcommon::ConnectionOptionsCommandLine("Remote Asset", "ra-"),
        buildboxcommon::ConnectionOptionsCommandLine("Action Cache", "ac-"),
        buildboxcommon::ConnectionOptionsCommandLine("Execution", "exec-"));
    buildboxcommon::CommandLine commandLine(spec.d_spec);

    ASSERT_TRUE(commandLine.parse(argc, argv));

    // Configure the daemon and test the configuration
    ASSERT_TRUE(daemon.configure(commandLine, spec.d_cachePath));
}

TEST(CmdLineSpecTest, BasicTest)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTest) / sizeof(const char *), argvTest);

    EXPECT_EQ("http://127.0.0.1:50011", daemon.d_cas_server.d_url);
    EXPECT_EQ("cas-dev", daemon.d_cas_server.d_instanceName);
    EXPECT_EQ("cas-server-cert", daemon.d_cas_server.d_serverCertPath);
    EXPECT_EQ("cas-client-key", daemon.d_cas_server.d_clientKeyPath);
    EXPECT_EQ("cas-client-cert", daemon.d_cas_server.d_clientCertPath);
    EXPECT_EQ("cas-access-token", daemon.d_cas_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_cas_server.d_useGoogleApiAuth);
    EXPECT_EQ("10", daemon.d_cas_server.d_retryLimit);
    EXPECT_EQ("500", daemon.d_cas_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:30011", daemon.d_ra_server.d_url);
    EXPECT_EQ("ra-dev", daemon.d_ra_server.d_instanceName);
    EXPECT_EQ("ra-server-cert", daemon.d_ra_server.d_serverCertPath);
    EXPECT_EQ("ra-client-key", daemon.d_ra_server.d_clientKeyPath);
    EXPECT_EQ("ra-client-cert", daemon.d_ra_server.d_clientCertPath);
    EXPECT_EQ("ra-access-token", daemon.d_ra_server.d_accessTokenPath);
    EXPECT_FALSE(daemon.d_ra_server.d_useGoogleApiAuth);
    EXPECT_EQ("20", daemon.d_ra_server.d_retryLimit);
    EXPECT_EQ("1000", daemon.d_ra_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:40011", daemon.d_ac_server.d_url);
    EXPECT_EQ("ac-dev", daemon.d_ac_server.d_instanceName);
    EXPECT_EQ("ac-server-cert", daemon.d_ac_server.d_serverCertPath);
    EXPECT_EQ("ac-client-key", daemon.d_ac_server.d_clientKeyPath);
    EXPECT_EQ("ac-client-cert", daemon.d_ac_server.d_clientCertPath);
    EXPECT_EQ("ac-access-token", daemon.d_ac_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_ac_server.d_useGoogleApiAuth);
    EXPECT_EQ("15", daemon.d_ac_server.d_retryLimit);
    EXPECT_EQ("750", daemon.d_ac_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:60011", daemon.d_exec_server.d_url);
    EXPECT_EQ("exec-dev", daemon.d_exec_server.d_instanceName);
    EXPECT_EQ("exec-server-cert", daemon.d_exec_server.d_serverCertPath);
    EXPECT_EQ("exec-client-key", daemon.d_exec_server.d_clientKeyPath);
    EXPECT_EQ("exec-client-cert", daemon.d_exec_server.d_clientCertPath);
    EXPECT_EQ("exec-access-token", daemon.d_exec_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_exec_server.d_useGoogleApiAuth);
    EXPECT_EQ("15", daemon.d_exec_server.d_retryLimit);
    EXPECT_EQ("750", daemon.d_exec_server.d_retryDelay);

    EXPECT_EQ("127.0.0.1:50011", daemon.d_bind_addresses.front());
    EXPECT_EQ((double)48 / 64, daemon.d_quotaLowRatio);
    EXPECT_EQ(64000000000, daemon.d_quotaHigh);
    EXPECT_EQ(3000000000, daemon.d_reserved_space);
    EXPECT_TRUE(daemon.d_protect_session_blobs);
    EXPECT_EQ(30, daemon.d_proxy_findmissingblobs_cache_ttl_seconds);
    EXPECT_EQ(60, daemon.d_proxyAcValidationCacheTtlSeconds);
    EXPECT_EQ(buildboxcommon::LogLevel::INFO, daemon.d_log_level);
    EXPECT_EQ("/path/to/cache", daemon.d_local_cache_path);
    EXPECT_EQ(false, daemon.d_read_only_remote);
    EXPECT_TRUE(daemon.d_storeWritableObjects);
}

TEST(CmdLineSpecTest, NoActionCacheProxy)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTest2) / sizeof(const char *), argvTest2);

    EXPECT_EQ("http://127.0.0.1:50011", daemon.d_cas_server.d_url);
    EXPECT_EQ("cas-dev", daemon.d_cas_server.d_instanceName);
    EXPECT_EQ("cas-server-cert", daemon.d_cas_server.d_serverCertPath);
    EXPECT_EQ("cas-client-key", daemon.d_cas_server.d_clientKeyPath);
    EXPECT_EQ("cas-client-cert", daemon.d_cas_server.d_clientCertPath);
    EXPECT_EQ("cas-access-token", daemon.d_cas_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_cas_server.d_useGoogleApiAuth);
    EXPECT_EQ("10", daemon.d_cas_server.d_retryLimit);
    EXPECT_EQ("500", daemon.d_cas_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:30011", daemon.d_ra_server.d_url);
    EXPECT_EQ("ra-dev", daemon.d_ra_server.d_instanceName);
    EXPECT_EQ("ra-server-cert", daemon.d_ra_server.d_serverCertPath);
    EXPECT_EQ("ra-client-key", daemon.d_ra_server.d_clientKeyPath);
    EXPECT_EQ("ra-client-cert", daemon.d_ra_server.d_clientCertPath);
    EXPECT_EQ("ra-access-token", daemon.d_ra_server.d_accessTokenPath);
    EXPECT_FALSE(daemon.d_ra_server.d_useGoogleApiAuth);
    EXPECT_EQ("20", daemon.d_ra_server.d_retryLimit);
    EXPECT_EQ("1000", daemon.d_ra_server.d_retryDelay);

    EXPECT_EQ("", daemon.d_ac_server.d_url);
    EXPECT_EQ("", daemon.d_ac_server.d_instanceName);
    EXPECT_EQ("", daemon.d_ac_server.d_serverCertPath);
    EXPECT_EQ("", daemon.d_ac_server.d_clientKeyPath);
    EXPECT_EQ("", daemon.d_ac_server.d_clientCertPath);
    EXPECT_EQ("", daemon.d_ac_server.d_accessTokenPath);
    EXPECT_FALSE(daemon.d_ac_server.d_useGoogleApiAuth);
    EXPECT_EQ("4", daemon.d_ac_server.d_retryLimit);
    EXPECT_EQ("1000", daemon.d_ac_server.d_retryDelay);

    EXPECT_EQ("127.0.0.1:50011", daemon.d_bind_addresses.front());
    EXPECT_EQ(0.5, daemon.d_quotaLowRatio);
    EXPECT_EQ(64000000000, daemon.d_quotaHigh);
    EXPECT_EQ(3000000000, daemon.d_reserved_space);
    EXPECT_TRUE(daemon.d_protect_session_blobs);
    EXPECT_EQ(30, daemon.d_proxy_findmissingblobs_cache_ttl_seconds);
    EXPECT_EQ(60, daemon.d_proxyAcValidationCacheTtlSeconds);
    EXPECT_EQ(buildboxcommon::LogLevel::INFO, daemon.d_log_level);
    EXPECT_EQ("/path/to/cache", daemon.d_local_cache_path);
    EXPECT_EQ(true, daemon.d_read_only_remote);
}

TEST(CmdLineSpecTest, ReadOnlyArg)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTest3) / sizeof(const char *), argvTest3);

    EXPECT_EQ("http://127.0.0.1:50011", daemon.d_cas_server.d_url);
    EXPECT_EQ("cas-dev", daemon.d_cas_server.d_instanceName);
    EXPECT_EQ("cas-server-cert", daemon.d_cas_server.d_serverCertPath);
    EXPECT_EQ("cas-client-key", daemon.d_cas_server.d_clientKeyPath);
    EXPECT_EQ("cas-client-cert", daemon.d_cas_server.d_clientCertPath);
    EXPECT_EQ("cas-access-token", daemon.d_cas_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_cas_server.d_useGoogleApiAuth);
    EXPECT_EQ("10", daemon.d_cas_server.d_retryLimit);
    EXPECT_EQ("500", daemon.d_cas_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:30011", daemon.d_ra_server.d_url);
    EXPECT_EQ("ra-dev", daemon.d_ra_server.d_instanceName);
    EXPECT_EQ("ra-server-cert", daemon.d_ra_server.d_serverCertPath);
    EXPECT_EQ("ra-client-key", daemon.d_ra_server.d_clientKeyPath);
    EXPECT_EQ("ra-client-cert", daemon.d_ra_server.d_clientCertPath);
    EXPECT_EQ("ra-access-token", daemon.d_ra_server.d_accessTokenPath);
    EXPECT_FALSE(daemon.d_ra_server.d_useGoogleApiAuth);
    EXPECT_EQ("20", daemon.d_ra_server.d_retryLimit);
    EXPECT_EQ("1000", daemon.d_ra_server.d_retryDelay);

    EXPECT_EQ("http://127.0.0.1:40011", daemon.d_ac_server.d_url);
    EXPECT_EQ("ac-dev", daemon.d_ac_server.d_instanceName);
    EXPECT_EQ("ac-server-cert", daemon.d_ac_server.d_serverCertPath);
    EXPECT_EQ("ac-client-key", daemon.d_ac_server.d_clientKeyPath);
    EXPECT_EQ("ac-client-cert", daemon.d_ac_server.d_clientCertPath);
    EXPECT_EQ("ac-access-token", daemon.d_ac_server.d_accessTokenPath);
    EXPECT_TRUE(daemon.d_ac_server.d_useGoogleApiAuth);
    EXPECT_EQ("15", daemon.d_ac_server.d_retryLimit);
    EXPECT_EQ("750", daemon.d_ac_server.d_retryDelay);

    EXPECT_EQ("127.0.0.1:50011", daemon.d_bind_addresses.front());
    EXPECT_EQ(0.8, daemon.d_quotaLowRatio);
    EXPECT_EQ(64000000000, daemon.d_quotaHigh);
    EXPECT_EQ(3000000000, daemon.d_reserved_space);
    EXPECT_TRUE(daemon.d_protect_session_blobs);
    EXPECT_EQ(30, daemon.d_proxy_findmissingblobs_cache_ttl_seconds);
    EXPECT_EQ(60, daemon.d_proxyAcValidationCacheTtlSeconds);
    EXPECT_EQ(buildboxcommon::LogLevel::INFO, daemon.d_log_level);
    EXPECT_EQ("/path/to/cache", daemon.d_local_cache_path);
    EXPECT_EQ(true, daemon.d_read_only_remote);
    EXPECT_FALSE(daemon.d_storeWritableObjects);
}

TEST(CmdLineSpecTest, BackwardCompatibleTest)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTestBackward) / sizeof(const char *),
          argvTestBackward);

    // --instance does not propagate to CAS in backwards-compatible mode
    EXPECT_EQ("", daemon.d_cas_server.d_instanceName);
    // --instance is proxy instance
    EXPECT_EQ("dev", daemon.d_proxyInstanceNames[0]);
    // --server-instance is allowed
    EXPECT_EQ("srv", daemon.d_localServerInstanceNames[0]);
}

TEST(CmdLineSpecTest, BackwardCompatibleDisabledTest)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTestBackwardDisabled) / sizeof(const char *),
          argvTestBackwardDisabled);

    // new options disable backward-compatible mode

    // --instance is a default instance for connection options
    EXPECT_EQ("dev", daemon.d_cas_server.d_instanceName);
    EXPECT_EQ("dev", daemon.d_ra_server.d_instanceName);
    // --local-server-instance is used for additional CAS instance
    ASSERT_FALSE(daemon.d_localServerInstanceNames.empty());
    EXPECT_EQ("srv", daemon.d_localServerInstanceNames[0]);
}

TEST(CmdLineSpecTest, ProxyInstanceOverrideTest)
{
    buildboxcasd::Daemon daemon;

    parse(daemon, sizeof(argvTestProxyInstance) / sizeof(const char *),
          argvTestProxyInstance);

    // --proxy-instance overrides --instance
    EXPECT_EQ("prx", daemon.d_proxyInstanceNames[0]);
}

TEST(CmdLineSpecTest, BindToUnixSocketTest)
{
    buildboxcommon::TemporaryDirectory tmpdir;
    std::vector<std::string> argTestUnixDomainSocket = {
        "/some/path/to/some_program.tsk",
        "--remote=http://127.0.0.1:12345",
        "--instance=dev",
        std::string("--bind=unix:") + tmpdir.name() + "/casd.sock",
        std::string("--unix-socket-group=") + std::to_string(getegid()),
        "--unix-socket-mode=0775",
        "/path/to/cache",
    };
    std::vector<const char *> rawArgs;
    rawArgs.reserve(argTestUnixDomainSocket.size());
    for (const auto &arg : argTestUnixDomainSocket) {
        rawArgs.push_back(arg.c_str());
    }

    buildboxcasd::Daemon daemon;

    parse(daemon, rawArgs.size(), rawArgs.data());

    EXPECT_EQ(std::string("unix:") + tmpdir.strname() + "/casd.sock",
              daemon.d_bind_addresses.front());
    EXPECT_EQ(std::optional<gid_t>(getegid()), daemon.d_socket_gid);
    EXPECT_EQ(std::optional<mode_t>(0775), daemon.d_socket_mode);
}

TEST(CmdLineSpecTest, MultipleLocalOnlyInstances)
{
    std::vector<const char *> args = {
        "/some/path/to/some_program.tsk",
        "--local-server-instance=dev",
        "--local-server-instance=prod",
        "/path/to/cache",
    };

    buildboxcasd::Daemon daemon;
    parse(daemon, args.size(), args.data());

    EXPECT_EQ(2, daemon.d_localServerInstanceNames.size());
    EXPECT_EQ("dev", daemon.d_localServerInstanceNames[0]);
    EXPECT_EQ("prod", daemon.d_localServerInstanceNames[1]);
}

TEST(CmdLineSpecTest, MultipleProxyInstances)
{
    std::vector<const char *> args = {
        "/some/path/to/some_program.tsk",
        "--cas-remote=http://remote-address",
        "--instance=default",
        "--proxy-instance=default",
        "--proxy-instance=dev:default",
        "--proxy-instance=prod:prod",
        "/path/to/cache",
    };

    buildboxcasd::Daemon daemon;
    parse(daemon, args.size(), args.data());

    EXPECT_EQ(3, daemon.d_proxyInstanceNames.size());
    EXPECT_EQ("default", daemon.d_proxyInstanceNames[0]);
    EXPECT_EQ("dev", daemon.d_proxyInstanceNames[1]);
    EXPECT_EQ("prod", daemon.d_proxyInstanceNames[2]);

    EXPECT_EQ(2, daemon.d_instanceMappings.size());
    EXPECT_EQ("default", daemon.d_instanceMappings["dev"]);
    EXPECT_EQ("prod", daemon.d_instanceMappings["prod"]);
}

TEST(CmdLineSpecTest, MultipleBindAddresses)
{
    std::vector<const char *> args = {
        "/some/path/to/some_program.tsk",
        "--cas-remote=http://remote-address",
        "--bind=unix:/foo/bar/casd.sock",
        "--bind=localhost:50051",
        "/path/to/cache",
    };

    buildboxcasd::Daemon daemon;
    parse(daemon, args.size(), args.data());

    ASSERT_EQ(2, daemon.d_bind_addresses.size());
    EXPECT_EQ("unix:/foo/bar/casd.sock", daemon.d_bind_addresses[0]);
    EXPECT_EQ("localhost:50051", daemon.d_bind_addresses[1]);
}

TEST(CmdLineSpecTest, ThreadPools)
{
    std::vector<const char *> args = {
        "/some/path/to/some_program.tsk",
        "--cas-remote=http://remote-address",
        "--num-io-threads=64",
        "--num-digest-threads=32",
        "/path/to/cache",
    };

    buildboxcasd::Daemon daemon;
    parse(daemon, args.size(), args.data());

    EXPECT_EQ(daemon.d_numIOThreads, 64);
    EXPECT_EQ(daemon.d_numDigestThreads, 32);
}

TEST(CmdLineSpecTest, PreUnstageCommands)
{
    std::vector<const char *> args = {
        "/some/path/to/some_program.tsk",
        "--cas-remote=http://remote-address",
        "--allow-pre-unstage-command=/foo/bar",
        "--allow-pre-unstage-command=/bin/ls",
        "/path/to/cache",
    };

    buildboxcasd::Daemon daemon;
    parse(daemon, args.size(), args.data());

    ASSERT_TRUE(daemon.d_allowedPreUnstageCommands != nullptr);
    EXPECT_TRUE(daemon.d_allowedPreUnstageCommands->contains("/foo/bar"));
    EXPECT_TRUE(daemon.d_allowedPreUnstageCommands->contains("/bin/ls"));
}
