// Copyright 2018-2026 the Deno authors. MIT license.
#![allow(
  clippy::useless_vec,
  reason = "svec! macro creates Vec<String> for test inputs"
)]
// Ported from cli/args/flags.rs `mod tests` (the clap parity contract) to run
// against the zero-cost parser via `convert::flags_from_vec`. A few tests that
// need CLI-side state (cwd resolution, env vars, the bare-run fast path) are
// skipped inline and stay in cli/args/flags.rs.

use std::net::SocketAddr;
use std::num::NonZeroU8;
use std::num::NonZeroU32;
use std::num::NonZeroUsize;

use deno_semver::package::PackageReq;
use pretty_assertions::assert_eq;

use crate::CliErrorKind;
use crate::convert::escape_and_split_commas;
use crate::convert::flags_from_vec;
use crate::convert::set_test_node_options;
use crate::flags::*;

/// Creates a Vec<String> from string literals.
macro_rules! svec {
  ($($x:expr),* $(,)?) => {
    vec![$($x.to_string()),*]
  };
}

#[test]
fn global_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "--log-level", "debug", "--quiet", "run", "script.ts"]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string()
      )),
      log_level: Some(Level::Error),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  #[rustfmt::skip]
    let r2 = flags_from_vec(svec!["deno", "run", "--log-level", "debug", "--quiet", "script.ts"]);
  let flags2 = r2.unwrap();
  assert_eq!(flags2, flags);
}

#[test]
fn crlf_shebang_arg() {
  // A script saved with CRLF line endings whose first line is
  // `#!/usr/bin/env -S deno run --allow-net` is invoked by the kernel as
  // roughly `deno run --allow-net\r script.ts`. The stray `\r` on the last
  // shebang token must not break flag parsing.
  let r = flags_from_vec(svec!["deno", "run", "--allow-net\r", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string()
      )),
      permissions: PermissionFlags {
        allow_net: Some(vec![]),
        ..PermissionFlags::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--dry-run", "--force"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: true,
        dry_run: true,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: None,
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_with_output_flag() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--output", "example.txt"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: Some(String::from("example.txt")),
        version_or_hash_or_channel: None,
        checksum: None,
        pr: None,
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn version() {
  let r = flags_from_vec(svec!["deno", "--version"]);
  assert_eq!(r.unwrap_err().kind, CliErrorKind::DisplayVersion);
  let r = flags_from_vec(svec!["deno", "-V"]);
  assert_eq!(r.unwrap_err().kind, CliErrorKind::DisplayVersion);
}

#[test]
fn run_reload() {
  let r = flags_from_vec(svec!["deno", "run", "-r", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string()
      )),
      reload: true,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_watch() {
  let r = flags_from_vec(svec!["deno", "run", "--watch", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(svec!["deno", "--watch", "--no-clear-screen", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--watch-hmr",
    "--no-clear-screen",
    "script.ts"
  ]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![],
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--unstable-hmr",
    "--no-clear-screen",
    "script.ts"
  ]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![],
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--watch-hmr=foo.txt",
    "--no-clear-screen",
    "script.ts"
  ]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![String::from("foo.txt")],
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "--hmr", "--watch", "script.ts"]);
  assert!(r.is_err());
}

#[test]
fn watch_subcommand() {
  // `deno watch script.ts` is an alias for `deno run --watch-hmr script.ts`.
  let r = flags_from_vec(svec!["deno", "watch", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  // Additional watched paths and watch options are still respected.
  let r = flags_from_vec(svec![
    "deno",
    "watch",
    "--watch-hmr=foo.txt",
    "--no-clear-screen",
    "--watch-exclude=bar.txt",
    "script.ts"
  ]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![String::from("foo.txt")],
        no_clear_screen: true,
        exclude: vec![String::from("bar.txt")],
      }),
      ..Flags::default()
    }
  );

  // `--watch-exclude` is honored even without an explicit `--watch-hmr` flag.
  let r = flags_from_vec(svec![
    "deno",
    "watch",
    "--watch-exclude=bar.txt",
    "script.ts"
  ]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: true,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![String::from("bar.txt")],
      }),
      ..Flags::default()
    }
  );

  // Reading from stdin while watching is not supported.
  let r = flags_from_vec(svec!["deno", "watch", "-"]);
  assert!(r.is_err());
}

#[test]
fn run_watch_with_external() {
  let r = flags_from_vec(svec!["deno", "--watch=file1,file2", "script.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("file1"), String::from("file2")],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_watch_with_no_clear_screen() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--watch",
    "--no-clear-screen",
    "script.ts"
  ]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_watch_with_excluded_paths() {
  let r = flags_from_vec(svec!(
    "deno",
    "--watch",
    "--watch-exclude=foo",
    "script.ts"
  ));

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![String::from("foo")],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!(
    "deno",
    "run",
    "--watch=foo",
    "--watch-exclude=bar",
    "script.ts"
  ));
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo")],
        no_clear_screen: false,
        exclude: vec![String::from("bar")],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--watch",
    "--watch-exclude=foo,bar",
    "script.ts"
  ]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![String::from("foo"), String::from("bar")],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "--watch=foo,bar",
    "--watch-exclude=baz,qux",
    "script.ts"
  ]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo"), String::from("bar")],
        no_clear_screen: false,
        exclude: vec![String::from("baz"), String::from("qux"),],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_watch_with_stdin_is_error() {
  // `--watch` and `--watch-hmr` cannot be combined with reading from stdin,
  // previously `--watch-hmr -` panicked and `--watch -` was silently ignored.
  for watch_flag in ["--watch", "--watch-hmr", "--unstable-hmr"] {
    let r = flags_from_vec(svec!["deno", "run", watch_flag, "-"]);
    assert!(r.is_err(), "expected conflict for {watch_flag}");
  }

  // a regular script with the same flags must still parse fine
  let r = flags_from_vec(svec!["deno", "run", "--watch-hmr", "script.ts"]);
  assert!(r.is_ok());
}

#[test]
fn run_reload_allow_write() {
  let r =
    flags_from_vec(svec!["deno", "run", "-r", "--allow-write", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      reload: true,
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string()
      )),
      permissions: PermissionFlags {
        allow_write: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_coverage() {
  let r = flags_from_vec(svec!["deno", "run", "--coverage=foo", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: false,
        coverage_dir: Some("foo".to_string()),
        print_task_list: false,
      }),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_v8_flags() {
  let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--help"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default("_".to_string())),
      v8_flags: svec!["--help"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--v8-flags=--expose-gc,--gc-stats=1",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      v8_flags: svec!["--expose-gc", "--gc-stats=1"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--expose-gc"]);
  assert!(r.is_ok());
}

#[test]
fn serve_flags() {
  let r = flags_from_vec(svec!["deno", "serve", "main.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
        "main.ts".to_string(),
        8000,
        "0.0.0.0"
      )),
      permissions: PermissionFlags {
        allow_net: None,
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  let r = flags_from_vec(svec!["deno", "serve", "--port", "5000", "main.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
        "main.ts".to_string(),
        5000,
        "0.0.0.0"
      )),
      permissions: PermissionFlags {
        allow_net: None,
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  let r = flags_from_vec(svec![
    "deno",
    "serve",
    "--port",
    "5000",
    "--allow-net=example.com",
    "main.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
        "main.ts".to_string(),
        5000,
        "0.0.0.0"
      )),
      permissions: PermissionFlags {
        allow_net: Some(vec!["example.com".to_string(),]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  let r = flags_from_vec(svec![
    "deno",
    "serve",
    "--port",
    "5000",
    "--allow-net",
    "main.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
        "main.ts".to_string(),
        5000,
        "0.0.0.0"
      )),
      permissions: PermissionFlags {
        allow_net: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn has_permission() {
  let r = flags_from_vec(svec!["deno", "--allow-read", "x.ts"]);
  assert_eq!(r.unwrap().has_permission(), true);

  let r = flags_from_vec(svec!["deno", "run", "--deny-read", "x.ts"]);
  assert_eq!(r.unwrap().has_permission(), true);

  let r = flags_from_vec(svec!["deno", "run", "x.ts"]);
  assert_eq!(r.unwrap().has_permission(), false);
}

#[test]
fn has_permission_in_argv() {
  let r = flags_from_vec(svec!["deno", "run", "x.ts", "--allow-read"]);
  assert_eq!(r.unwrap().has_permission_in_argv(), true);

  let r = flags_from_vec(svec!["deno", "x.ts", "--deny-read"]);
  assert_eq!(r.unwrap().has_permission_in_argv(), true);

  let r = flags_from_vec(svec!["deno", "run", "x.ts"]);
  assert_eq!(r.unwrap().has_permission_in_argv(), false);
}

#[test]
fn script_args() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-net",
    "gist.ts",
    "--title",
    "X"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "gist.ts".to_string()
      )),
      argv: svec!["--title", "X"],
      permissions: PermissionFlags {
        allow_net: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_all() {
  let r = flags_from_vec(svec!["deno", "run", "--allow-all", "gist.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "gist.ts".to_string()
      )),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_read() {
  let r = flags_from_vec(svec!["deno", "run", "--allow-read", "gist.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "gist.ts".to_string()
      )),
      permissions: PermissionFlags {
        allow_read: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn short_permission_flags() {
  let r = flags_from_vec(svec!["deno", "run", "-RNESWI", "gist.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "gist.ts".to_string()
      )),
      permissions: PermissionFlags {
        allow_read: Some(vec![]),
        allow_write: Some(vec![]),
        allow_env: Some(vec![]),
        allow_import: Some(vec![]),
        allow_net: Some(vec![]),
        allow_sys: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_read() {
  let r = flags_from_vec(svec!["deno", "--deny-read", "gist.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "gist.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      permissions: PermissionFlags {
        deny_read: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn double_hyphen() {
  // notice that flags passed after double dash will not
  // be parsed to Flags but instead forwarded to
  // script args as Deno.args
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-write",
    "script.ts",
    "--",
    "-D",
    "--allow-net"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      argv: svec!["--", "-D", "--allow-net"],
      permissions: PermissionFlags {
        allow_write: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn fmt() {
  let r = flags_from_vec(svec!["deno", "fmt", "script_1.ts", "script_2.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "fmt", "--permit-no-files", "--check"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: true,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: true,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "fmt"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "fmt", "--watch"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "fmt",
    "--watch",
    "--no-clear-screen",
    "--unstable-css",
    "--unstable-html",
    "--unstable-component",
    "--unstable-yaml",
    "--unstable-sql"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: true,
        unstable_sql: true,
      }),
      watch: Some(WatchFlagsWithPaths {
        paths: vec![],
        hmr: false,
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "fmt",
    "--check",
    "--watch",
    "foo.ts",
    "--ignore=bar.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: true,
        fail_fast: false,
        files: FileFlags {
          include: vec!["foo.ts".to_string()],
          ignore: vec!["bar.js".to_string()],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "fmt", "--config", "deno.jsonc"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "fmt",
    "--config",
    "deno.jsonc",
    "--watch",
    "foo.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec!["foo.ts".to_string()],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "fmt",
    "--use-tabs",
    "--line-width",
    "60",
    "--indent-width",
    "4",
    "--single-quote",
    "--prose-wrap",
    "never",
    "--no-semicolons",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: Some(true),
        line_width: Some(NonZeroU32::new(60).unwrap()),
        indent_width: Some(NonZeroU8::new(4).unwrap()),
        single_quote: Some(true),
        prose_wrap: Some("never".to_string()),
        no_semicolons: Some(true),
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  // try providing =false to the booleans
  let r = flags_from_vec(svec![
    "deno",
    "fmt",
    "--use-tabs=false",
    "--single-quote=false",
    "--no-semicolons=false",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: Some(false),
        line_width: None,
        indent_width: None,
        single_quote: Some(false),
        prose_wrap: None,
        no_semicolons: Some(false),
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  // --no-editorconfig opts out of reading .editorconfig
  let r = flags_from_vec(svec!["deno", "fmt", "--no-editorconfig"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: true,
        unstable_component: false,
        unstable_sql: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html", "./**"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Fmt(FmtFlags {
        check: false,
        fail_fast: false,
        files: FileFlags {
          include: vec!["./**".to_string()],
          ignore: vec![],
        },
        permit_no_files: false,
        use_tabs: None,
        line_width: None,
        indent_width: None,
        single_quote: None,
        prose_wrap: None,
        no_semicolons: None,
        no_editorconfig: false,
        unstable_component: false,
        unstable_sql: false,
      }),
      ext: Some("html".to_string()),
      ..Flags::default()
    }
  );
}

#[test]
fn lint() {
  let r = flags_from_vec(svec!["deno", "lint", "script_1.ts", "script_2.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string(), "script_2.ts".to_string(),],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--permit-no-files",
    "--allow-import",
    "--watch",
    "script_1.ts",
    "script_2.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: true,
        json: false,
        compact: false,
      }),
      permissions: PermissionFlags {
        allow_import: Some(vec![]),
        ..Default::default()
      },
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--watch",
    "--no-clear-screen",
    "script_1.ts",
    "script_2.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      watch: Some(WatchFlagsWithPaths {
        paths: vec![],
        hmr: false,
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--fix",
    "--ignore=script_1.ts,script_2.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec![],
          ignore: vec!["script_1.ts".to_string(), "script_2.ts".to_string()],
        },
        fix: true,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "lint", "--rules"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        fix: false,
        rules: true,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--rules",
    "--rules-tags=recommended"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        fix: false,
        rules: true,
        maybe_rules_tags: Some(svec!["recommended"]),
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--rules-tags=",
    "--rules-include=ban-untagged-todo,no-undef",
    "--rules-exclude=no-const-assign"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: Some(svec![""]),
        maybe_rules_include: Some(svec!["ban-untagged-todo", "no-undef"]),
        maybe_rules_exclude: Some(svec!["no-const-assign"]),
        permit_no_files: false,
        json: false,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "lint", "--json", "script_1.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string()],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: true,
        compact: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--config",
    "Deno.jsonc",
    "--json",
    "script_1.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string()],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: true,
        compact: false,
      }),
      config_flag: ConfigFlag::Path("Deno.jsonc".to_string()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "lint",
    "--config",
    "Deno.jsonc",
    "--compact",
    "script_1.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Lint(LintFlags {
        files: FileFlags {
          include: vec!["script_1.ts".to_string()],
          ignore: vec![],
        },
        fix: false,
        rules: false,
        maybe_rules_tags: None,
        maybe_rules_include: None,
        maybe_rules_exclude: None,
        permit_no_files: false,
        json: false,
        compact: true,
      }),
      config_flag: ConfigFlag::Path("Deno.jsonc".to_string()),
      ..Flags::default()
    }
  );
}

#[test]
fn types() {
  let r = flags_from_vec(svec!["deno", "types"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Types,
      ..Flags::default()
    }
  );
}

#[test]
fn cache() {
  let r = flags_from_vec(svec!["deno", "cache", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Cache(CacheFlags {
        files: svec!["script.ts"],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "cache", "--env-file", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Cache(CacheFlags {
        files: svec!["script.ts"],
      }),
      env_file: Some(svec![".env"]),
      ..Flags::default()
    }
  );
}

#[test]
fn check() {
  let r = flags_from_vec(svec!["deno", "check", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["script.ts"],
        doc: false,
        doc_only: false,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "check"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["."],
        doc: false,
        doc_only: false,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "check", "--doc", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["script.ts"],
        doc: true,
        doc_only: false,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "check", "--doc-only", "markdown.md"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["markdown.md"],
        doc: false,
        doc_only: true,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  // `--doc` and `--doc-only` are mutually exclusive
  let r =
    flags_from_vec(svec!["deno", "check", "--doc", "--doc-only", "script.ts"]);
  assert!(r.is_err());

  for all_flag in ["--remote", "--all"] {
    let r = flags_from_vec(svec!["deno", "check", all_flag, "script.ts"]);
    assert_eq!(
      r.unwrap(),
      Flags {
        subcommand: DenoSubcommand::Check(CheckFlags {
          files: svec!["script.ts"],
          doc: false,
          doc_only: false,
          check_js: false,
        }),
        type_check_mode: TypeCheckMode::All,
        code_cache_enabled: true,
        ..Flags::default()
      }
    );

    let r = flags_from_vec(svec![
      "deno",
      "check",
      all_flag,
      "--no-remote",
      "script.ts"
    ]);
    assert!(r.is_err());
  }

  let r = flags_from_vec(svec!["deno", "check", "--check-js", "script.js"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["script.js"],
        doc: false,
        doc_only: false,
        check_js: true,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "check", "--desktop", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["script.ts"],
        doc: false,
        doc_only: false,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      internal: InternalFlags {
        is_desktop: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn info() {
  let r = flags_from_vec(svec!["deno", "info", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: false,
        file: Some("script.ts".to_string()),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "info", "--reload", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: false,
        file: Some("script.ts".to_string()),
      }),
      reload: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "info", "--json", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: true,
        file: Some("script.ts".to_string()),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "info"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: false,
        file: None
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "info", "--json"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: true,
        file: None
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "info",
    "--no-npm",
    "--no-remote",
    "--config",
    "tsconfig.json"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: false,
        file: None
      }),
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      no_npm: true,
      no_remote: true,
      ..Flags::default()
    }
  );
}

#[test]
fn tsconfig() {
  let r =
    flags_from_vec(svec!["deno", "run", "-c", "tsconfig.json", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn eval() {
  let r = flags_from_vec(svec!["deno", "eval", "'console.log(\"hello\")'"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Eval(EvalFlags {
        print: false,
        code: "'console.log(\"hello\")'".to_string(),
      }),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn eval_p() {
  let r = flags_from_vec(svec!["deno", "eval", "-p", "1+2"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Eval(EvalFlags {
        print: true,
        code: "1+2".to_string(),
      }),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn eval_typescript() {
  let r = flags_from_vec(svec![
    "deno",
    "eval",
    "--ext=ts",
    "'console.log(\"hello\")'"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Eval(EvalFlags {
        print: false,
        code: "'console.log(\"hello\")'".to_string(),
      }),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ext: Some("ts".to_string()),
      ..Flags::default()
    }
  );
}

#[test]
fn eval_with_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "eval", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--env=.example.env", "42"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Eval(EvalFlags {
        print: false,
        code: "42".to_string(),
      }),
      import_map_path: Some("import_map.json".to_string()),
      no_remote: true,
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      type_check_mode: TypeCheckMode::None,
      reload: true,
      lock: Some(String::from("lock.json")),
      ca_data: Some(CaData::File("example.crt".to_string())),
      cached_only: true,
      location: Some(Url::parse("https://foo/").unwrap()),
      v8_flags: svec!["--help", "--random-seed=1"],
      seed: Some(1),
      inspect: Some("127.0.0.1:9229".parse().unwrap()),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      env_file: Some(vec![".example.env".to_owned()]),
      ..Flags::default()
    }
  );
}

#[test]
fn eval_args() {
  let r = flags_from_vec(svec![
    "deno",
    "eval",
    "console.log(Deno.args)",
    "arg1",
    "arg2"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Eval(EvalFlags {
        print: false,
        code: "console.log(Deno.args)".to_string(),
      }),
      argv: svec!["arg1", "arg2"],
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn repl() {
  let r = flags_from_vec(svec!["deno"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: None,
        is_default_command: true,
        json: false,
      }),
      unsafely_ignore_certificate_errors: None,
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn repl_trace_ops() {
  // Lightly test this undocumented flag
  let r = flags_from_vec(svec!["deno", "repl", "--trace-ops"]);
  assert_eq!(r.unwrap().trace_ops, Some(vec![]));
  let r = flags_from_vec(svec!["deno", "repl", "--trace-ops=http,websocket"]);
  assert_eq!(
    r.unwrap().trace_ops,
    Some(vec!["http".to_string(), "websocket".to_string()])
  );
}

#[test]
fn repl_with_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: None,
        is_default_command: false,
        json: false,
      }),
      import_map_path: Some("import_map.json".to_string()),
      no_remote: true,
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      type_check_mode: TypeCheckMode::None,
      reload: true,
      lock: Some(String::from("lock.json")),
      ca_data: Some(CaData::File("example.crt".to_string())),
      cached_only: true,
      location: Some(Url::parse("https://foo/").unwrap()),
      v8_flags: svec!["--help", "--random-seed=1"],
      seed: Some(1),
      inspect: Some("127.0.0.1:9229".parse().unwrap()),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      env_file: Some(vec![".example.env".to_owned()]),
      unsafely_ignore_certificate_errors: Some(vec![]),
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_eval_flag() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "repl", "--allow-write", "--eval", "console.log('hello');"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: Some("console.log('hello');".to_string()),
        is_default_command: false,
        json: false,
      }),
      permissions: PermissionFlags {
        allow_write: Some(vec![]),
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_eval_file_flag() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "repl", "--eval-file=./a.js,./b.ts,https://docs.deno.com/hello_world.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: Some(vec![
          "./a.js".to_string(),
          "./b.ts".to_string(),
          "https://docs.deno.com/hello_world.ts".to_string()
        ]),
        eval: None,
        is_default_command: false,
        json: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_eval_file_flag_no_equals() {
  // Test without equals sign (for hashbang usage)
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "repl", "--eval-file", "./script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: Some(vec!["./script.ts".to_string()]),
        eval: None,
        is_default_command: false,
        json: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_eval_file_flag_multiple() {
  // Test multiple --eval-file flags
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "repl", "--eval-file", "./a.ts", "--eval-file", "./b.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: Some(vec!["./a.ts".to_string(), "./b.ts".to_string()]),
        eval: None,
        is_default_command: false,
        json: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn allow_read_allowlist() {
  #[allow(
    clippy::disallowed_methods,
    reason = "only building an argv string; no fs access"
  )]
  let temp_dir = std::env::temp_dir().to_string_lossy().into_owned();

  let r = flags_from_vec(svec![
    "deno",
    "run",
    format!("--allow-read=.,{}", temp_dir),
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      permissions: PermissionFlags {
        allow_read: Some(vec![String::from("."), temp_dir]),
        ..Default::default()
      },
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_read_denylist() {
  #[allow(
    clippy::disallowed_methods,
    reason = "only building an argv string; no fs access"
  )]
  let temp_dir = std::env::temp_dir().to_string_lossy().into_owned();

  let r = flags_from_vec(svec![
    "deno",
    "run",
    format!("--deny-read=.,{}", temp_dir),
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      permissions: PermissionFlags {
        deny_read: Some(vec![String::from("."), temp_dir]),
        ..Default::default()
      },
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn ignore_read_ignorelist() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--ignore-read=something.txt",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        ignore_read: Some(svec!["something.txt"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn ignore_read_ignorelist_multiple() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--ignore-read=something.txt",
    "--ignore-read=something2.txt",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        ignore_read: Some(svec!["something.txt", "something2.txt"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn ignore_read_ignorelist_comma_separated() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--ignore-read=something.txt,something2.txt",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        ignore_read: Some(svec!["something.txt", "something2.txt"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_write_allowlist() {
  #[allow(
    clippy::disallowed_methods,
    reason = "only building an argv string; no fs access"
  )]
  let temp_dir = std::env::temp_dir().to_string_lossy().into_owned();

  let r = flags_from_vec(svec![
    "deno",
    "run",
    format!("--allow-write=.,{}", temp_dir),
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      permissions: PermissionFlags {
        allow_write: Some(vec![String::from("."), temp_dir]),
        ..Default::default()
      },
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_write_denylist() {
  #[allow(
    clippy::disallowed_methods,
    reason = "only building an argv string; no fs access"
  )]
  let temp_dir = std::env::temp_dir().to_string_lossy().into_owned();

  let r = flags_from_vec(svec![
    "deno",
    "run",
    format!("--deny-write=.,{}", temp_dir),
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      permissions: PermissionFlags {
        deny_write: Some(vec![String::from("."), temp_dir]),
        ..Default::default()
      },
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_net_allowlist() {
  let r =
    flags_from_vec(svec!["deno", "run", "--allow-net=127.0.0.1", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_net: Some(svec!["127.0.0.1"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_net_denylist() {
  let r = flags_from_vec(svec!["deno", "--deny-net=127.0.0.1", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      permissions: PermissionFlags {
        deny_net: Some(svec!["127.0.0.1"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_env_allowlist() {
  let r = flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_env: Some(svec!["HOME"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_env_denylist() {
  let r = flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_env: Some(svec!["HOME"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn ignore_env_ignorelist() {
  let r =
    flags_from_vec(svec!["deno", "run", "--ignore-env=HOME", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        ignore_env: Some(svec!["HOME"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_env_allowlist_multiple() {
  let r =
    flags_from_vec(svec!["deno", "run", "--allow-env=HOME,PATH", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_env: Some(svec!["HOME", "PATH"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_env_denylist_multiple() {
  let r =
    flags_from_vec(svec!["deno", "run", "--deny-env=HOME,PATH", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_env: Some(svec!["HOME", "PATH"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_env_ignorelist_multiple() {
  let r =
    flags_from_vec(svec!["deno", "run", "--ignore-env=HOME,PATH", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        ignore_env: Some(svec!["HOME", "PATH"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_env_allowlist_validator() {
  let r = flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec!["deno", "--allow-env=H=ME", "script.ts"]);
  assert!(r.is_err());
  let r =
    flags_from_vec(svec!["deno", "run", "--allow-env=H\0ME", "script.ts"]);
  assert!(r.is_err());
}

#[test]
fn deny_env_denylist_validator() {
  let r = flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec!["deno", "run", "--deny-env=H=ME", "script.ts"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec!["deno", "--deny-env=H\0ME", "script.ts"]);
  assert!(r.is_err());
}

#[test]
fn allow_sys() {
  let r = flags_from_vec(svec!["deno", "run", "--allow-sys", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_sys: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_sys() {
  let r = flags_from_vec(svec!["deno", "run", "--deny-sys", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_sys: Some(vec![]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_sys_allowlist() {
  let r =
    flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_sys: Some(svec!["hostname"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_sys_denylist() {
  let r = flags_from_vec(svec!["deno", "--deny-sys=hostname", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      permissions: PermissionFlags {
        deny_sys: Some(svec!["hostname"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_sys_allowlist_multiple() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-sys=hostname,osRelease",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_sys: Some(svec!["hostname", "osRelease"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_sys_denylist_multiple() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-sys=hostname,osRelease",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_sys: Some(svec!["hostname", "osRelease"]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_sys_allowlist_validator() {
  let r =
    flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-sys=hostname,osRelease",
    "script.ts"
  ]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec!["deno", "run", "--allow-sys=foo", "script.ts"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-sys=hostname,foo",
    "script.ts"
  ]);
  assert!(r.is_err());
}

#[test]
fn deny_sys_denylist_validator() {
  let r =
    flags_from_vec(svec!["deno", "run", "--deny-sys=hostname", "script.ts"]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-sys=hostname,osRelease",
    "script.ts"
  ]);
  assert!(r.is_ok());
  let r = flags_from_vec(svec!["deno", "run", "--deny-sys=foo", "script.ts"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-sys=hostname,foo",
    "script.ts"
  ]);
  assert!(r.is_err());
}

#[test]
fn reload_validator() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--reload=http://deno.land/",
    "script.ts"
  ]);
  assert!(r.is_ok(), "should accept valid urls");

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--reload=http://deno.land/a,http://deno.land/b",
    "script.ts"
  ]);
  assert!(r.is_ok(), "should accept accept multiple valid urls");

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--reload=./relativeurl/",
    "script.ts"
  ]);
  assert!(r.is_err(), "Should reject relative urls that start with ./");

  let r =
    flags_from_vec(svec!["deno", "run", "--reload=relativeurl/", "script.ts"]);
  assert!(r.is_err(), "Should reject relative urls");

  let r =
    flags_from_vec(svec!["deno", "run", "--reload=/absolute", "script.ts"]);
  assert!(r.is_err(), "Should reject absolute urls");

  let r = flags_from_vec(svec!["deno", "--reload=/", "script.ts"]);
  assert!(r.is_err(), "Should reject absolute root url");

  let r = flags_from_vec(svec!["deno", "run", "--reload=", "script.ts"]);
  assert!(r.is_err(), "Should reject when nothing is provided");

  let r = flags_from_vec(svec!["deno", "run", "--reload=,", "script.ts"]);
  assert!(r.is_err(), "Should reject when a single comma is provided");

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--reload=,http://deno.land/a",
    "script.ts"
  ]);
  assert!(r.is_err(), "Should reject a leading comma");

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--reload=http://deno.land/a,",
    "script.ts"
  ]);
  assert!(r.is_err(), "Should reject a trailing comma");
}

#[test]
fn run_import_map() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--import-map=import_map.json",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      import_map_path: Some("import_map.json".to_owned()),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn info_import_map() {
  let r = flags_from_vec(svec![
    "deno",
    "info",
    "--import-map=import_map.json",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        file: Some("script.ts".to_string()),
        json: false,
      }),
      import_map_path: Some("import_map.json".to_owned()),
      ..Flags::default()
    }
  );
}

#[test]
fn cache_import_map() {
  let r = flags_from_vec(svec![
    "deno",
    "cache",
    "--import-map=import_map.json",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Cache(CacheFlags {
        files: svec!["script.ts"],
      }),
      import_map_path: Some("import_map.json".to_owned()),
      ..Flags::default()
    }
  );
}

#[test]
fn doc_import_map() {
  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--import-map=import_map.json",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        source_files: DocSourceFileFlag::Paths(vec!["script.ts".to_owned()]),
        private: false,
        json: false,
        html: None,
        lint: false,
        filter: None,
      }),
      import_map_path: Some("import_map.json".to_owned()),
      ..Flags::default()
    }
  );
}

#[test]
fn run_env_default() {
  let r = flags_from_vec(svec!["deno", "run", "--env", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      env_file: Some(vec![".env".to_owned()]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_env_file_default() {
  let r = flags_from_vec(svec!["deno", "run", "--env-file", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      env_file: Some(vec![".env".to_owned()]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_no_code_cache() {
  let r = flags_from_vec(svec!["deno", "--no-code-cache", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_env_defined() {
  let r =
    flags_from_vec(svec!["deno", "run", "--env=.another_env", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      env_file: Some(vec![".another_env".to_owned()]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_env_file_defined() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--env-file=.another_env",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      env_file: Some(vec![".another_env".to_owned()]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_multiple_env_file_defined() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--env-file",
    "--env-file=.two_env",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      env_file: Some(vec![".env".to_owned(), ".two_env".to_owned()]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

// The dependency and registry subcommands also accept --env-file so that
// registry credentials kept in a .env file can be loaded before the command
// resolves a dependency graph or talks to a registry.
#[test]
fn dep_and_registry_subcommands_env_file() {
  // (args, expected env_file) pairs, one per supported subcommand.
  let cases: Vec<(Vec<String>, Vec<String>)> = vec![
    (
      svec!["deno", "add", "--env-file", "@david/which"],
      svec![".env"],
    ),
    (svec!["deno", "audit", "--env-file"], svec![".env"]),
    (svec!["deno", "why", "--env-file", "express"], svec![".env"]),
    (svec!["deno", "outdated", "--env-file"], svec![".env"]),
    (svec!["deno", "update", "--env-file"], svec![".env"]),
    (
      svec!["deno", "bundle", "--env-file", "main.ts"],
      svec![".env"],
    ),
    (
      svec!["deno", "check", "--env-file", "main.ts"],
      svec![".env"],
    ),
    (svec!["deno", "doc", "--env-file", "main.ts"], svec![".env"]),
    (
      svec!["deno", "info", "--env-file", "main.ts"],
      svec![".env"],
    ),
    (svec!["deno", "ci", "--env-file"], svec![".env"]),
    (svec!["deno", "publish", "--env-file"], svec![".env"]),
    (svec!["deno", "pack", "--env-file"], svec![".env"]),
  ];
  for (args, expected) in cases {
    let flags = flags_from_vec(args.clone())
      .unwrap_or_else(|e| panic!("failed to parse {args:?}: {e}"));
    assert_eq!(
      flags.env_file,
      Some(expected),
      "unexpected env_file for {args:?}",
    );
  }
}

#[test]
fn dep_and_registry_subcommands_env_file_explicit_and_multiple() {
  // An explicit path is honored.
  let flags =
    flags_from_vec(svec!["deno", "check", "--env-file=.prod.env", "main.ts"])
      .unwrap();
  assert_eq!(flags.env_file, Some(svec![".prod.env"]));

  // The --env alias works the same as --env-file.
  let flags =
    flags_from_vec(svec!["deno", "outdated", "--env=.prod.env"]).unwrap();
  assert_eq!(flags.env_file, Some(svec![".prod.env"]));

  // Multiple --env-file flags accumulate in order.
  let flags = flags_from_vec(svec![
    "deno",
    "publish",
    "--env-file",
    "--env-file=.prod.env"
  ])
  .unwrap();
  assert_eq!(
    flags.env_file,
    Some(svec![".env".to_owned(), ".prod.env".to_owned()])
  );
}

#[test]
fn cache_multiple() {
  let r = flags_from_vec(svec!["deno", "cache", "script.ts", "script_two.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Cache(CacheFlags {
        files: svec!["script.ts", "script_two.ts"],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_seed() {
  let r = flags_from_vec(svec!["deno", "run", "--seed", "250", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      seed: Some(250_u64),
      v8_flags: svec!["--random-seed=250"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_seed_with_v8_flags() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--seed",
    "250",
    "--v8-flags=--expose-gc",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      seed: Some(250_u64),
      v8_flags: svec!["--expose-gc", "--random-seed=250"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn install() {
  let r = flags_from_vec(svec![
    "deno",
    "install",
    "-g",
    "jsr:@std/http/file-server",
    "npm:chalk",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Global(
        InstallFlagsGlobal {
          name: None,
          module_urls: svec!["jsr:@std/http/file-server", "npm:chalk"],
          args: vec![],
          root: None,
          force: false,
          compile: false,
        }
      ),),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(svec!["deno", "install", "-g", "jsr:@std/http/file-server"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Global(
        InstallFlagsGlobal {
          name: None,
          module_urls: svec!["jsr:@std/http/file-server"],
          args: vec![],
          root: None,
          force: false,
          compile: false,
        }
      ),),
      ..Flags::default()
    }
  );
}

#[test]
fn install_with_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "install", "--global", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "--env=.example.env", "jsr:@std/http/file-server", "--", "foo", "bar"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Global(
        InstallFlagsGlobal {
          name: Some("file_server".to_string()),
          module_urls: svec!["jsr:@std/http/file-server"],
          args: svec!["foo", "bar"],
          root: Some("/foo".to_string()),
          force: true,
          compile: false,
        }
      ),),
      import_map_path: Some("import_map.json".to_string()),
      no_remote: true,
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      type_check_mode: TypeCheckMode::None,
      reload: true,
      lock: Some(String::from("lock.json")),
      ca_data: Some(CaData::File("example.crt".to_string())),
      cached_only: true,
      v8_flags: svec!["--help", "--random-seed=1"],
      seed: Some(1),
      inspect: Some("127.0.0.1:9229".parse().unwrap()),
      unsafely_ignore_certificate_errors: Some(vec![]),
      permissions: PermissionFlags {
        allow_net: Some(vec![]),
        allow_read: Some(vec![]),
        ..Default::default()
      },
      env_file: Some(vec![".example.env".to_owned()]),
      ..Flags::default()
    }
  );
}

#[test]
fn uninstall() {
  let r = flags_from_vec(svec!["deno", "uninstall"]);
  assert!(r.is_err(),);

  let r = flags_from_vec(svec![
    "deno",
    "uninstall",
    "--frozen",
    "--lockfile-only",
    "@std/load"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Local(RemoveFlags {
          packages: vec!["@std/load".to_string()],
          lockfile_only: true,
          package_json: false,
        }),
      }),
      frozen_lockfile: Some(true),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(svec!["deno", "uninstall", "file_server", "@std/load"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Local(RemoveFlags {
          packages: vec!["file_server".to_string(), "@std/load".to_string()],
          lockfile_only: false,
          package_json: false,
        }),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "uninstall", "-g", "file_server"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Global(UninstallFlagsGlobal {
          packages: svec!["file_server"],
          root: None,
        }),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "uninstall",
    "-g",
    "--root",
    "/user/foo/bar",
    "file_server"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Global(UninstallFlagsGlobal {
          packages: svec!["file_server"],
          root: Some("/user/foo/bar".to_string()),
        }),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "uninstall",
    "-g",
    "--root",
    "/user/foo/bar",
    "cowsay",
    "file_server"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Global(UninstallFlagsGlobal {
          packages: svec!["cowsay", "file_server"],
          root: Some("/user/foo/bar".to_string()),
        }),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn uninstall_with_help_flag() {
  let r = flags_from_vec(svec!["deno", "uninstall", "--help"]);
  assert!(r.is_ok());
}

#[test]
fn log_level() {
  let r =
    flags_from_vec(svec!["deno", "run", "--log-level=debug", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      log_level: Some(Level::Debug),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn quiet() {
  let r = flags_from_vec(svec!["deno", "-q", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      log_level: Some(Level::Error),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn completions() {
  let r = flags_from_vec(svec!["deno", "completions", "zsh"]).unwrap();

  match r.subcommand {
    DenoSubcommand::Completions(CompletionsFlags::Static(buf)) => {
      assert!(!buf.is_empty())
    }
    _ => unreachable!(),
  }
}

#[test]
fn run_with_args() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "script.ts",
    "--allow-read",
    "--allow-net"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      argv: svec!["--allow-read", "--allow-net"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--location",
    "https:foo",
    "--allow-read",
    "script.ts",
    "--allow-net",
    "-r",
    "--help",
    "--foo",
    "bar"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      location: Some(Url::parse("https://foo/").unwrap()),
      permissions: PermissionFlags {
        allow_read: Some(vec![]),
        ..Default::default()
      },
      argv: svec!["--allow-net", "-r", "--help", "--foo", "bar"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "script.ts", "foo", "bar"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      argv: svec!["foo", "bar"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
  let r = flags_from_vec(svec!["deno", "run", "script.ts", "-"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      argv: svec!["-"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "script.ts", "-", "foo", "bar"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      argv: svec!["-", "foo", "bar"],
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn no_check() {
  let r = flags_from_vec(svec!["deno", "--no-check", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      type_check_mode: TypeCheckMode::None,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn no_check_remote() {
  let r =
    flags_from_vec(svec!["deno", "run", "--no-check=remote", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_unsafely_ignore_certificate_errors() {
  let r = flags_from_vec(svec![
    "deno",
    "repl",
    "--eval",
    "console.log('hello');",
    "--unsafely-ignore-certificate-errors"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: Some("console.log('hello');".to_string()),
        is_default_command: false,
        json: false,
      }),
      unsafely_ignore_certificate_errors: Some(vec![]),
      type_check_mode: TypeCheckMode::None,
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_unsafely_ignore_certificate_errors() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--unsafely-ignore-certificate-errors",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      unsafely_ignore_certificate_errors: Some(vec![]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_unsafely_treat_insecure_origin_as_secure_with_ipv6_address() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      unsafely_ignore_certificate_errors: Some(svec![
        "deno.land",
        "localhost",
        "[::]",
        "127.0.0.1",
        "[::1]",
        "1.2.3.4"
      ]),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn repl_with_unsafely_treat_insecure_origin_as_secure_with_ipv6_address() {
  let r = flags_from_vec(svec![
    "deno",
    "repl",
    "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: None,
        is_default_command: false,
        json: false,
      }),
      unsafely_ignore_certificate_errors: Some(svec![
        "deno.land",
        "localhost",
        "[::]",
        "127.0.0.1",
        "[::1]",
        "1.2.3.4"
      ]),
      type_check_mode: TypeCheckMode::None,
      ..Flags::default()
    }
  );
}

#[test]
fn no_remote() {
  let r = flags_from_vec(svec!["deno", "run", "--no-remote", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      no_remote: true,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn no_npm() {
  let r = flags_from_vec(svec!["deno", "run", "--no-npm", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      no_npm: true,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn local_npm() {
  let r = flags_from_vec(svec!["deno", "--node-modules-dir", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      node_modules_dir: Some(NodeModulesDirMode::Auto),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn vendor_flag() {
  let r = flags_from_vec(svec!["deno", "run", "--vendor", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      vendor: Some(true),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "--vendor=false", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      vendor: Some(false),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn cached_only() {
  let r = flags_from_vec(svec!["deno", "run", "--cached-only", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      cached_only: true,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_net_allowlist_with_ports() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-net=deno.land,:8000,:4545",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_net: Some(svec![
          "deno.land",
          "0.0.0.0:8000",
          "127.0.0.1:8000",
          "localhost:8000",
          "0.0.0.0:4545",
          "127.0.0.1:4545",
          "localhost:4545"
        ]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_net_denylist_with_ports() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-net=deno.land,:8000,:4545",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_net: Some(svec![
          "deno.land",
          "0.0.0.0:8000",
          "127.0.0.1:8000",
          "localhost:8000",
          "0.0.0.0:4545",
          "127.0.0.1:4545",
          "localhost:4545"
        ]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn allow_net_allowlist_with_ipv6_address() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        allow_net: Some(svec![
          "deno.land",
          "deno.land:80",
          "[::]",
          "127.0.0.1",
          "[::1]",
          "1.2.3.4:5678",
          "0.0.0.0:5678",
          "127.0.0.1:5678",
          "localhost:5678",
          "[::1]:8080"
        ]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn deny_net_denylist_with_ipv6_address() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      permissions: PermissionFlags {
        deny_net: Some(svec![
          "deno.land",
          "deno.land:80",
          "[::]",
          "127.0.0.1",
          "[::1]",
          "1.2.3.4:5678",
          "0.0.0.0:5678",
          "127.0.0.1:5678",
          "localhost:5678",
          "[::1]:8080"
        ]),
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

// PORT-SKIP: no_colon_in_value_name walks the whole CommandDef tree; kept in cli/args/flags.rs

#[test]
fn test_with_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "test", "--no-npm", "--no-remote", "--trace-leaks", "--no-run", "--filter", "- foo", "--coverage=cov", "--clean", "--location", "https:foo", "--allow-net", "--permit-no-files", "dir1/", "dir2/", "--", "arg1", "arg2"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: true,
        doc: false,
        fail_fast: None,
        filter: Some("- foo".to_string()),
        permit_no_files: true,
        files: FileFlags {
          include: vec!["dir1/".to_string(), "dir2/".to_string()],
          ignore: vec![],
        },
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        parallel: false,
        trace_leaks: true,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: Some("cov".to_string()),
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: true,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      no_npm: true,
      no_remote: true,
      location: Some(Url::parse("https://foo/").unwrap()),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        allow_net: Some(vec![]),
        ..Default::default()
      },
      argv: svec!["arg1", "arg2"],
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_cafile() {
  let r =
    flags_from_vec(svec!["deno", "run", "--cert", "example.crt", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      ca_data: Some(CaData::File("example.crt".to_owned())),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_base64_cafile() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--cert",
    "base64:bWVvdw==",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      ca_data: Some(CaData::Bytes(b"meow".into())),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_enable_testing_features() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--enable-testing-features-do-not-use",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      enable_testing_features: true,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn test_with_fail_fast() {
  let r = flags_from_vec(svec!["deno", "test", "--fail-fast=3"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: Some(NonZeroUsize::new(3).unwrap()),
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--fail-fast=0"]);
  assert!(r.is_err());
}

#[test]
fn test_with_enable_testing_features() {
  let r = flags_from_vec(svec![
    "deno",
    "test",
    "--enable-testing-features-do-not-use"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      enable_testing_features: true,
      ..Flags::default()
    }
  );
}

#[test]
fn test_changed() {
  let r = flags_from_vec(svec!["deno", "test", "--changed"]);
  assert_eq!(
    r.unwrap().subcommand,
    DenoSubcommand::Test(TestFlags {
      changed: Some(None),
      ..Default::default()
    })
  );

  let r = flags_from_vec(svec!["deno", "test", "--changed=origin/main"]);
  assert_eq!(
    r.unwrap().subcommand,
    DenoSubcommand::Test(TestFlags {
      changed: Some(Some("origin/main".to_string())),
      ..Default::default()
    })
  );

  // space-separated value is not allowed (would be ambiguous with file args)
  let r = flags_from_vec(svec!["deno", "test", "--changed", "HEAD~1"]);
  assert_eq!(
    r.unwrap().subcommand,
    DenoSubcommand::Test(TestFlags {
      changed: Some(None),
      files: FileFlags {
        include: vec!["HEAD~1".to_string()],
        ignore: vec![],
      },
      ..Default::default()
    })
  );
}

#[test]
fn test_related() {
  let r = flags_from_vec(svec!["deno", "test", "--related=src/a.ts,src/b.ts"]);
  assert_eq!(
    r.unwrap().subcommand,
    DenoSubcommand::Test(TestFlags {
      related: svec!["src/a.ts", "src/b.ts"],
      ..Default::default()
    })
  );
}

#[test]
fn test_changed_conflicts_with_watch() {
  let r = flags_from_vec(svec!["deno", "test", "--changed", "--watch"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec!["deno", "test", "--related=a.ts", "--watch"]);
  assert!(r.is_err());
}

#[test]
fn test_reporter() {
  let r = flags_from_vec(svec!["deno", "test", "--reporter=pretty"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        reporter: TestReporterConfig::Pretty,
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--reporter=dot"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        reporter: TestReporterConfig::Dot,
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      log_level: Some(Level::Error),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--reporter=junit"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        reporter: TestReporterConfig::Junit,
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--reporter=tap"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        reporter: TestReporterConfig::Tap,
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      log_level: Some(Level::Error),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "test",
    "--reporter=dot",
    "--junit-path=report.xml"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        reporter: TestReporterConfig::Dot,
        junit_path: Some("report.xml".to_string()),
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      log_level: Some(Level::Error),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--junit-path"]);
  assert!(r.is_err());
}

#[test]
fn test_shuffle() {
  let r = flags_from_vec(svec!["deno", "test", "--shuffle=1"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: Some(1),
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );
}

#[test]
fn test_retry_and_repeats() {
  let r = flags_from_vec(svec!["deno", "test", "--retry=3", "--repeats=2"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 3,
        repeats: 2,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );
}

#[test]
fn test_shard() {
  let r = flags_from_vec(svec!["deno", "test", "--shard=2/3"]);
  let flags = r.unwrap();
  assert!(matches!(
    flags.subcommand,
    DenoSubcommand::Test(TestFlags {
      shard: Some((2, 3)),
      ..
    })
  ));

  // Invalid shard values are rejected at parse time.
  assert!(flags_from_vec(svec!["deno", "test", "--shard=3/2"]).is_err());
  assert!(flags_from_vec(svec!["deno", "test", "--shard=0/2"]).is_err());
  assert!(flags_from_vec(svec!["deno", "test", "--shard=1/0"]).is_err());
  assert!(flags_from_vec(svec!["deno", "test", "--shard=foo"]).is_err());
}

#[test]
fn test_watch() {
  let r = flags_from_vec(svec!["deno", "test", "--watch"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );
}
#[test]
fn test_watch_explicit_cwd() {
  let r = flags_from_vec(svec!["deno", "test", "--watch", "./"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec!["./".to_string()],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );
}

#[test]
fn test_watch_with_no_clear_screen() {
  let r = flags_from_vec(svec!["deno", "test", "--watch", "--no-clear-screen"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        no_run: false,
        doc: false,
        fail_fast: None,
        filter: None,
        permit_no_files: false,
        shuffle: None,
        retry: 0,
        repeats: 0,
        shard: None,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        parallel: false,
        trace_leaks: false,
        sanitize_ops: false,
        sanitize_resources: false,
        coverage_dir: None,
        coverage_raw_data_only: false,
        coverage_threshold: None,
        clean: false,
        reporter: Default::default(),
        junit_path: None,
        hide_stacktraces: false,
        changed: None,
        related: vec![],
        update_snapshots: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        no_clear_screen: true,
        exclude: vec![],
        paths: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn test_watch_with_paths() {
  let r = flags_from_vec(svec!("deno", "test", "--watch=foo"));

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo")],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "test", "--watch=foo,bar"]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo"), String::from("bar")],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn test_watch_with_excluded_paths() {
  let r =
    flags_from_vec(svec!("deno", "test", "--watch", "--watch-exclude=foo",));

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![String::from("foo")],
      }),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(
      svec!("deno", "test", "--watch=foo", "--watch-exclude=bar",),
    );
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo")],
        no_clear_screen: false,
        exclude: vec![String::from("bar")],
      }),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(
      svec!["deno", "test", "--watch", "--watch-exclude=foo,bar",],
    );

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![],
        no_clear_screen: false,
        exclude: vec![String::from("foo"), String::from("bar")],
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "test",
    "--watch=foo,bar",
    "--watch-exclude=baz,qux",
  ]);

  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo"), String::from("bar")],
        no_clear_screen: false,
        exclude: vec![String::from("baz"), String::from("qux"),],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn test_coverage_default_dir() {
  let r = flags_from_vec(svec!["deno", "test", "--coverage"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        coverage_dir: Some("coverage".to_string()),
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn test_hide_stacktraces() {
  let r = flags_from_vec(svec!["deno", "test", "--hide-stacktraces"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags {
        hide_stacktraces: true,
        update_snapshots: false,
        ..TestFlags::default()
      }),
      type_check_mode: TypeCheckMode::Local,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_with_ca_file() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--cert", "example.crt"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: None,
        branch: None,
      }),
      ca_data: Some(CaData::File("example.crt".to_owned())),
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_release_candidate() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--rc"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: false,
        release_candidate: true,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: None,
        branch: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "upgrade", "--rc", "--canary"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "upgrade", "--rc", "--version"]);
  assert!(r.is_err());
}

#[test]
fn upgrade_pr() {
  let r = flags_from_vec(svec!["deno", "upgrade", "pr", "12345"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: Some(12345),
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_pr_with_hash_prefix() {
  let r = flags_from_vec(svec!["deno", "upgrade", "pr", "#6789"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: Some(6789),
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_pr_with_flags() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--dry-run", "pr", "33250"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: true,
        canary: false,
        no_delta: false,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: Some(33250),
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn upgrade_pr_missing_number() {
  let r = flags_from_vec(svec!["deno", "upgrade", "pr"]);
  assert!(r.is_err());
}

#[test]
fn upgrade_pr_invalid_number() {
  let r = flags_from_vec(svec!["deno", "upgrade", "pr", "abc"]);
  assert!(r.is_err());
}

#[test]
fn cache_with_cafile() {
  let r = flags_from_vec(svec![
    "deno",
    "cache",
    "--cert",
    "example.crt",
    "script.ts",
    "script_two.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Cache(CacheFlags {
        files: svec!["script.ts", "script_two.ts"],
      }),
      ca_data: Some(CaData::File("example.crt".to_owned())),
      ..Flags::default()
    }
  );
}

#[test]
fn info_with_cafile() {
  let r = flags_from_vec(svec![
    "deno",
    "info",
    "--cert",
    "example.crt",
    "https://example.com"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Info(InfoFlags {
        json: false,
        file: Some("https://example.com".to_string()),
      }),
      ca_data: Some(CaData::File("example.crt".to_owned())),
      ..Flags::default()
    }
  );
}

#[test]
fn doc() {
  let r = flags_from_vec(svec!["deno", "doc", "--json", "path/to/module.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: true,
        html: None,
        lint: false,
        source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "doc", "--html", "path/to/module.ts"]);
  assert!(r.is_ok());

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--html",
    "--name=My library",
    "path/to/module.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: false,
        lint: false,
        html: Some(DocHtmlFlag {
          name: Some("My library".to_string()),
          category_docs_path: None,
          symbol_redirect_map_path: None,
          default_symbol_map_path: None,
          strip_trailing_html: false,
          output: String::from("./docs/"),
        }),
        source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--html",
    "--name=My library",
    "--lint",
    "--output=./foo",
    "path/to/module.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: false,
        html: Some(DocHtmlFlag {
          name: Some("My library".to_string()),
          category_docs_path: None,
          symbol_redirect_map_path: None,
          default_symbol_map_path: None,
          strip_trailing_html: false,
          output: String::from("./foo"),
        }),
        lint: true,
        source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "doc", "--html", "--name=My library",]);
  assert!(r.is_err());

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--filter",
    "SomeClass.someField",
    "path/to/module.ts",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: false,
        html: None,
        lint: false,
        source_files: DocSourceFileFlag::Paths(vec![
          "path/to/module.ts".to_string()
        ]),
        filter: Some("SomeClass.someField".to_string()),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "doc"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: false,
        html: None,
        lint: false,
        source_files: Default::default(),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--filter",
    "Deno.Listener",
    "--builtin"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        lint: false,
        json: false,
        html: None,
        source_files: DocSourceFileFlag::Builtin,
        filter: Some("Deno.Listener".to_string()),
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--no-npm",
    "--no-remote",
    "--private",
    "path/to/module.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: true,
        lint: false,
        json: false,
        html: None,
        source_files: DocSourceFileFlag::Paths(svec!["path/to/module.js"]),
        filter: None,
      }),
      no_npm: true,
      no_remote: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "path/to/module.js",
    "path/to/module2.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        lint: false,
        json: false,
        html: None,
        source_files: DocSourceFileFlag::Paths(vec![
          "path/to/module.js".to_string(),
          "path/to/module2.js".to_string()
        ]),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "path/to/module.js",
    "--builtin",
    "path/to/module2.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        json: false,
        html: None,
        lint: false,
        source_files: DocSourceFileFlag::Paths(vec![
          "path/to/module.js".to_string(),
          "path/to/module2.js".to_string()
        ]),
        filter: None,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "doc", "--lint",]);
  assert!(r.is_err());

  let r = flags_from_vec(svec![
    "deno",
    "doc",
    "--lint",
    "path/to/module.js",
    "path/to/module2.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Doc(DocFlags {
        private: false,
        lint: true,
        json: false,
        html: None,
        source_files: DocSourceFileFlag::Paths(vec![
          "path/to/module.js".to_string(),
          "path/to/module2.js".to_string()
        ]),
        filter: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn inspect_default_host() {
  let r = flags_from_vec(svec!["deno", "run", "--inspect", "foo.js"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "foo.js".to_string(),
      )),
      inspect: Some("127.0.0.1:9229".parse().unwrap()),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn inspect_wait() {
  let r = flags_from_vec(svec!["deno", "--inspect-wait", "foo.js"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "foo.js".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      inspect_wait: Some("127.0.0.1:9229".parse().unwrap()),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect-wait=127.0.0.1:3567",
    "foo.js"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "foo.js".to_string(),
      )),
      inspect_wait: Some("127.0.0.1:3567".parse().unwrap()),
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn compile() {
  let r = flags_from_vec(svec![
    "deno",
    "compile",
    "https://examples.deno.land/color-logging.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Compile(CompileFlags {
        source_file: "https://examples.deno.land/color-logging.ts".to_string(),
        output: None,
        args: vec![],
        target: None,
        no_terminal: false,
        icon: None,
        include: Default::default(),
        exclude: Default::default(),
        eszip: false,
        self_extracting: false,
        bundle: false,
        app_name: None,
        minify: false,
        exclude_unused_npm: false,
        engine: Default::default(),
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn compile_and_desktop_engine() {
  let flags =
    flags_from_vec(svec!["deno", "compile", "--engine", "quickjs", "main.ts"])
      .unwrap();
  let DenoSubcommand::Compile(compile) = flags.subcommand else {
    panic!("expected compile subcommand");
  };
  assert_eq!(compile.engine, JavaScriptEngine::QuickJs);

  let flags =
    flags_from_vec(svec!["deno", "desktop", "--engine", "quickjs", "main.tsx"])
      .unwrap();
  let DenoSubcommand::Desktop(desktop) = flags.subcommand else {
    panic!("expected desktop subcommand");
  };
  assert_eq!(desktop.engine, JavaScriptEngine::QuickJs);
}

#[test]
fn compile_target_aarch64_windows() {
  // The denort runtime for Windows arm64 is built and published by CI, so
  // `--target aarch64-pc-windows-msvc` must be accepted. This is the
  // regression guard for the clap -> deno_cli_parser cutover: the new parser
  // validates `--target` against its own SUPPORTED_OS list, so the triple has
  // to be present here too (not just in clap's list).
  let r = flags_from_vec(svec![
    "deno",
    "compile",
    "--target=aarch64-pc-windows-msvc",
    "main.ts"
  ]);
  let DenoSubcommand::Compile(c) = r.unwrap().subcommand else {
    panic!("expected compile subcommand");
  };
  assert_eq!(c.target, Some("aarch64-pc-windows-msvc".to_string()));

  // An unsupported triple is still rejected, proving the validator is active
  // (and this test isn't passing just because validation was dropped).
  assert!(
    flags_from_vec(svec![
      "deno",
      "compile",
      "--target=riscv64gc-unknown-linux-gnu",
      "main.ts"
    ])
    .is_err()
  );
}

#[test]
fn desktop_backend_default() {
  let r = flags_from_vec(svec!["deno", "desktop", "main.tsx"]);
  let flags = r.unwrap();
  let DenoSubcommand::Desktop(desktop) = flags.subcommand else {
    panic!("expected desktop subcommand");
  };
  assert_eq!(desktop.source_file, "main.tsx");
  // No --backend flag leaves the field unset so `desktop.backend` in
  // deno.json (or the webview default) can take effect during config merge.
  assert_eq!(desktop.backend.as_deref(), None);
}

#[test]
fn desktop_backend_explicit() {
  let r =
    flags_from_vec(svec!["deno", "desktop", "--backend", "cef", "main.tsx"]);
  let flags = r.unwrap();
  let DenoSubcommand::Desktop(desktop) = flags.subcommand else {
    panic!("expected desktop subcommand");
  };
  assert_eq!(desktop.backend.as_deref(), Some("cef"));
}

#[test]
fn desktop_exclude_unused_npm() {
  let r = flags_from_vec(svec!["deno", "desktop", "main.tsx"]);
  let DenoSubcommand::Desktop(desktop) = r.unwrap().subcommand else {
    panic!("expected desktop subcommand");
  };
  // Off by default: the full managed npm snapshot keeps
  // non-statically-analyzable dynamic imports working.
  assert!(!desktop.exclude_unused_npm);

  let r = flags_from_vec(svec![
    "deno",
    "desktop",
    "--exclude-unused-npm",
    "main.tsx"
  ]);
  let DenoSubcommand::Desktop(desktop) = r.unwrap().subcommand else {
    panic!("expected desktop subcommand");
  };
  assert!(desktop.exclude_unused_npm);
}

#[test]
fn compile_watch_with_no_clear_screen() {
  let r = flags_from_vec(svec![
    "deno",
    "compile",
    "--watch",
    "--no-clear-screen",
    "main.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Compile(CompileFlags {
        source_file: "main.ts".to_string(),
        output: None,
        args: vec![],
        target: None,
        no_terminal: false,
        icon: None,
        include: Default::default(),
        exclude: Default::default(),
        eszip: false,
        self_extracting: false,
        bundle: false,
        app_name: None,
        minify: false,
        exclude_unused_npm: false,
        engine: Default::default(),
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      watch: Some(WatchFlagsWithPaths {
        paths: vec![],
        hmr: false,
        no_clear_screen: true,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn compile_with_flags() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "compile", "--include", "include.txt", "--exclude", "exclude.txt", "--import-map", "import_map.json", "--no-code-cache", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Compile(CompileFlags {
        source_file: "https://examples.deno.land/color-logging.ts".to_string(),
        output: Some(String::from("colors")),
        args: svec!["foo", "bar", "-p", "8080"],
        target: None,
        no_terminal: true,
        icon: Some(String::from("favicon.ico")),
        include: vec!["include.txt".to_string()],
        exclude: vec!["exclude.txt".to_string()],
        eszip: false,
        self_extracting: false,
        bundle: false,
        app_name: None,
        minify: false,
        exclude_unused_npm: false,
        engine: Default::default(),
      }),
      import_map_path: Some("import_map.json".to_string()),
      no_remote: true,
      code_cache_enabled: false,
      config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
      type_check_mode: TypeCheckMode::None,
      reload: true,
      lock: Some(String::from("lock.json")),
      ca_data: Some(CaData::File("example.crt".to_string())),
      cached_only: true,
      location: Some(Url::parse("https://foo/").unwrap()),
      permissions: PermissionFlags {
        allow_read: Some(vec![]),
        allow_net: Some(vec![]),
        ..Default::default()
      },
      unsafely_ignore_certificate_errors: Some(vec![]),
      v8_flags: svec!["--help", "--random-seed=1"],
      seed: Some(1),
      env_file: Some(vec![".example.env".to_owned()]),
      ..Flags::default()
    }
  );
}

#[test]
fn coverage() {
  let r = flags_from_vec(svec!["deno", "coverage", "foo.json"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Coverage(CoverageFlags {
        files: FileFlags {
          include: vec!["foo.json".to_string()],
          ignore: vec![],
        },
        include: vec![r"^file:".to_string()],
        exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()],
        ..CoverageFlags::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn coverage_with_threshold() {
  let r =
    flags_from_vec(svec!["deno", "coverage", "--threshold=80", "foo.json"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Coverage(CoverageFlags {
        files: FileFlags {
          include: vec!["foo.json".to_string()],
          ignore: vec![],
        },
        include: vec![r"^file:".to_string()],
        exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()],
        threshold: Some(80),
        ..CoverageFlags::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn coverage_threshold_out_of_range() {
  // Percentages above 100 are rejected by the value parser.
  let r =
    flags_from_vec(svec!["deno", "coverage", "--threshold=150", "foo.json"]);
  assert!(r.is_err());
}

#[test]
fn coverage_with_lcov_and_out_file() {
  let r = flags_from_vec(svec![
    "deno",
    "coverage",
    "--lcov",
    "--output=foo.lcov",
    "foo.json"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Coverage(CoverageFlags {
        files: FileFlags {
          include: vec!["foo.json".to_string()],
          ignore: vec![],
        },
        include: vec![r"^file:".to_string()],
        exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()],
        r#type: CoverageType::Lcov,
        threshold: None,
        output: Some(String::from("foo.lcov")),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn coverage_with_default_files() {
  let r = flags_from_vec(svec!["deno", "coverage",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Coverage(CoverageFlags {
        files: FileFlags {
          include: vec!["coverage".to_string()],
          ignore: vec![],
        },
        include: vec![r"^file:".to_string()],
        exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()],
        ..CoverageFlags::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn location_with_bad_scheme() {
  #[rustfmt::skip]
    let r = flags_from_vec(svec!["deno", "run", "--location", "foo:", "mod.ts"]);
  assert!(r.is_err());
  assert!(
    r.unwrap_err()
      .to_string()
      .contains("Expected protocol \"http\" or \"https\"")
  );
}

// PORT-SKIP: test_config_path_args needs CLI-side cwd resolution; kept in cli/args/flags.rs

#[test]
fn test_no_clear_watch_flag_without_watch_flag() {
  let r = flags_from_vec(svec!["deno", "run", "--no-clear-screen", "foo.js"]);
  assert!(r.is_err());
  let error_message = r.unwrap_err().to_string();
  assert!(
    &error_message
      .contains("error: the following required arguments were not provided:")
  );
  // PORT-NOTE: clap renders the missing arg as `--watch[=<FILES>...]`; the
  // new parser reports the plain flag name.
  assert!(&error_message.contains("--watch"));
}

#[test]
fn task_subcommand() {
  let r = flags_from_vec(svec!["deno", "task", "build", "hello", "world",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["hello", "world"],
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--cwd", "foo", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: Some("foo".to_string()),
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--filter", "*", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: Some("*".to_string()),
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--recursive", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: true,
        members: false,
        filter: Some("*".to_string()),
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "-r", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: true,
        members: false,
        filter: Some("*".to_string()),
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--members", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: true,
        filter: Some("*".to_string()),
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--members", "-r", "build"]);
  assert!(r.is_err());

  let r =
    flags_from_vec(svec!["deno", "task", "--members", "-f", "*", "build"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "task", "--eval", "echo 1"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("echo 1".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: true,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "task", "--eval"]);
  assert!(r.is_err());
}

#[test]
fn task_subcommand_jobs() {
  // `--jobs`, its `--concurrency` alias, and the `-j` short form all parse
  // to the same value.
  for args in [
    svec!["deno", "task", "--jobs", "1", "build"],
    svec!["deno", "task", "--concurrency", "1", "build"],
    svec!["deno", "task", "-j", "1", "build"],
  ] {
    let r = flags_from_vec(args.clone());
    assert_eq!(
      r.unwrap(),
      Flags {
        subcommand: DenoSubcommand::Task(TaskFlags {
          cwd: None,
          task: Some("build".to_string()),
          is_run: false,
          recursive: false,
          members: false,
          filter: None,
          eval: false,
          no_prefix: false,
          concurrency: Some(NonZeroUsize::new(1).unwrap()),
          if_present: false,
        }),
        ..Flags::default()
      },
      "unexpected parse for {args:?}"
    );
  }

  // Reject zero, negative, and non-numeric values.
  for invalid in ["0", "-1", "abc"] {
    let r = flags_from_vec(svec!["deno", "task", "--jobs", invalid, "build"]);
    assert!(r.is_err(), "expected error for value {invalid:?}");
  }
}

#[test]
fn task_subcommand_double_hyphen() {
  let r = flags_from_vec(svec![
    "deno",
    "task",
    "-c",
    "deno.json",
    "build",
    "--",
    "hello",
    "world",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["--", "hello", "world"],
      config_flag: ConfigFlag::Path("deno.json".to_owned()),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno", "task", "--cwd", "foo", "build", "--", "hello", "world"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: Some("foo".to_string()),
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["--", "hello", "world"],
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_double_hyphen_only() {
  // edge case, but it should forward
  let r = flags_from_vec(svec!["deno", "task", "build", "--"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["--"],
      ..Flags::default()
    }
  );
}

#[test]
fn task_following_arg() {
  let r = flags_from_vec(svec!["deno", "task", "build", "-1", "--test"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["-1", "--test"],
      ..Flags::default()
    }
  );
}

#[test]
fn task_following_double_hyphen_arg() {
  let r = flags_from_vec(svec!["deno", "task", "build", "--test"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      argv: svec!["--test"],
      ..Flags::default()
    }
  );
}

#[test]
fn task_with_global_flags() {
  // can fail if the custom parser in task_parse() starts at the wrong index
  let r = flags_from_vec(svec!["deno", "--quiet", "task", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      log_level: Some(log::Level::Error),
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_empty() {
  let r = flags_from_vec(svec!["deno", "task"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: None,
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_config() {
  let r = flags_from_vec(svec!["deno", "task", "--config", "deno.jsonc"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: None,
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_config_short() {
  let r = flags_from_vec(svec!["deno", "task", "-c", "deno.jsonc"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: None,
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_noconfig_invalid() {
  let r = flags_from_vec(svec!["deno", "task", "--no-config"]);
  assert!(r.is_err());
}

#[test]
fn task_subcommand_env_file() {
  let r = flags_from_vec(svec!["deno", "task", "--env-file", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      env_file: Some(vec![".env".to_owned()]),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "task",
    "--env-file=.env.dev",
    "--env-file=.env.local",
    "build"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: false,
      }),
      env_file: Some(vec![".env.dev".to_owned(), ".env.local".to_owned()]),
      ..Flags::default()
    }
  );
}

#[test]
fn task_subcommand_if_present() {
  let r = flags_from_vec(svec!["deno", "task", "--if-present", "build"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Task(TaskFlags {
        cwd: None,
        task: Some("build".to_string()),
        is_run: false,
        recursive: false,
        members: false,
        filter: None,
        eval: false,
        no_prefix: false,
        concurrency: None,
        if_present: true,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bench_with_flags() {
  let r = flags_from_vec(svec![
    "deno",
    "bench",
    "--json",
    "--no-npm",
    "--no-remote",
    "--no-run",
    "--filter",
    "- foo",
    "--location",
    "https:foo",
    "--allow-net",
    "dir1/",
    "dir2/",
    "--",
    "arg1",
    "arg2"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Bench(BenchFlags {
        filter: Some("- foo".to_string()),
        json: true,
        no_run: true,
        files: FileFlags {
          include: vec!["dir1/".to_string(), "dir2/".to_string()],
          ignore: vec![],
        },
        permit_no_files: false,
      }),
      no_npm: true,
      no_remote: true,
      type_check_mode: TypeCheckMode::Local,
      location: Some(Url::parse("https://foo/").unwrap()),
      permissions: PermissionFlags {
        allow_net: Some(vec![]),
        no_prompt: true,
        ..Default::default()
      },
      argv: svec!["arg1", "arg2"],
      ..Flags::default()
    }
  );
}

#[test]
fn bench_watch() {
  let r = flags_from_vec(svec!["deno", "bench", "--watch"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Bench(BenchFlags {
        filter: None,
        json: false,
        no_run: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: false
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      watch: Some(Default::default()),
      ..Flags::default()
    }
  );
}

#[test]
fn bench_watch_with_paths() {
  let r = flags_from_vec(svec!["deno", "bench", "--watch=foo,bar"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Bench(BenchFlags {
        ..Default::default()
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      watch: Some(WatchFlagsWithPaths {
        hmr: false,
        paths: vec![String::from("foo"), String::from("bar")],
        no_clear_screen: false,
        exclude: vec![],
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn minimum_dependency_age_alias() {
  for flag in ["--minimum-dependency-age=0", "--min-dep-age=0"] {
    let flags =
      flags_from_vec(svec!["deno", "run", flag, "script.ts"]).unwrap();
    assert_eq!(
      flags.minimum_dependency_age,
      Some(NewestDependencyDate::Disabled)
    );
  }
}

#[test]
fn bench_no_files() {
  let r = flags_from_vec(svec!["deno", "bench", "--permit-no-files"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Bench(BenchFlags {
        filter: None,
        json: false,
        no_run: false,
        files: FileFlags {
          include: vec![],
          ignore: vec![],
        },
        permit_no_files: true
      }),
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_check() {
  let r = flags_from_vec(svec!["deno", "run", "--check", "script.ts",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "run", "--check=all", "script.ts",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      type_check_mode: TypeCheckMode::All,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "--check=foo", "script.ts",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      type_check_mode: TypeCheckMode::None,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(svec!["deno", "run", "--no-check", "--check", "script.ts",]);
  assert!(r.is_err());
}

#[test]
fn no_config() {
  let r = flags_from_vec(svec!["deno", "run", "--no-config", "script.ts",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags::new_default(
        "script.ts".to_string(),
      )),
      config_flag: ConfigFlag::Disabled,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--config",
    "deno.json",
    "--no-config",
    "script.ts",
  ]);
  assert!(r.is_err());
}

#[test]
fn init() {
  let r = flags_from_vec(svec!["deno", "init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "foo"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: Some(String::from("foo")),
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--quiet"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      log_level: Some(Level::Error),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--lib"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: None,
        lib: true,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--serve"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: None,
        lib: false,
        serve: true,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "foo", "--lib"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: None,
        package_args: vec![],
        dir: Some(String::from("foo")),
        lib: true,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--lib", "--npm", "vite"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "init", "--serve", "--npm", "vite"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--lib"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: svec!["--lib"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--serve"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: svec!["--serve"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "new_dir"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: svec!["new_dir"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "init", "--npm", "--yes", "npm:vite"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: true,
      }),
      ..Flags::default()
    }
  );

  // --jsr basic
  let r = flags_from_vec(svec!["deno", "init", "--jsr", "@denotest/create"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@denotest/create".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  // --jsr with jsr: prefix already present
  let r = flags_from_vec(svec!["deno", "init", "--jsr", "jsr:@fresh/init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  // --jsr with --yes
  let r =
    flags_from_vec(svec!["deno", "init", "--jsr", "--yes", "@denotest/create"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@denotest/create".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: true,
      }),
      ..Flags::default()
    }
  );

  // --jsr with extra args
  let r = flags_from_vec(svec![
    "deno",
    "init",
    "--jsr",
    "@denotest/create",
    "my-project"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@denotest/create".to_string()),
        package_args: svec!["my-project"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  // --jsr conflicts with --npm, --lib, --serve, --empty
  let r = flags_from_vec(svec!["deno", "init", "--jsr", "--npm", "@foo/bar"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "init", "--jsr", "--lib", "@foo/bar"]);
  assert!(r.is_err());

  // --jsr without package name
  let r = flags_from_vec(svec!["deno", "init", "--jsr"]);
  assert!(r.is_err());
}

#[test]
fn create() {
  let r = flags_from_vec(svec!["deno", "create"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "create", "vite"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "create", "npm:vite", "my-project"]);
  assert!(r.is_err());

  let r =
    flags_from_vec(svec!["deno", "create", "npm:vite", "--", "my-project"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: svec!["my-project"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "create", "--npm", "vite"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r =
    flags_from_vec(svec!["deno", "create", "--npm", "vite", "my-project"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "create", "--yes", "npm:vite"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("npm:vite".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: true,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "create", "jsr:@std/http/file-server"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@std/http/file-server".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "create", "jsr:@fresh/init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "create", "--yes", "jsr:@fresh/init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: true,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "create",
    "jsr:@fresh/init",
    "--",
    "my-project"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: svec!["my-project"],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  // empty jsr: prefix
  let r = flags_from_vec(svec!["deno", "create", "jsr:"]);
  assert!(r.is_err());

  // --jsr flag
  let r = flags_from_vec(svec!["deno", "create", "--jsr", "@fresh/init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: false,
      }),
      ..Flags::default()
    }
  );

  // --jsr with --yes
  let r =
    flags_from_vec(svec!["deno", "create", "--jsr", "--yes", "@fresh/init"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Init(InitFlags {
        package: Some("jsr:@fresh/init".to_string()),
        package_args: vec![],
        dir: None,
        lib: false,
        serve: false,
        empty: false,
        yes: true,
      }),
      ..Flags::default()
    }
  );

  // --jsr with npm: specifier is contradictory
  let r = flags_from_vec(svec!["deno", "create", "--jsr", "npm:vite"]);
  assert!(r.is_err());

  // --jsr and --npm conflict
  let r = flags_from_vec(svec!["deno", "create", "--jsr", "--npm", "@foo"]);
  assert!(r.is_err());

  let r = flags_from_vec(svec!["deno", "create", "npm:"]);
  assert!(r.is_err());

  // --npm with jsr: is contradictory
  let r = flags_from_vec(svec!["deno", "create", "--npm", "jsr:@std/http"]);
  assert!(r.is_err());
}

#[test]
fn jupyter() {
  let r = flags_from_vec(svec!["deno", "jupyter"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: false,
        kernel: false,
        conn_file: None,
        name: None,
        display: None,
        force: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "jupyter", "--install"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: true,
        kernel: false,
        conn_file: None,
        name: None,
        display: None,
        force: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--force"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: true,
        kernel: false,
        conn_file: None,
        name: None,
        display: None,
        force: true,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "jupyter",
    "--install",
    "--name",
    "debugdeno",
    "--display",
    "Deno (debug)"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: true,
        kernel: false,
        conn_file: None,
        name: Some("debugdeno".to_string()),
        display: Some("Deno (debug)".to_string()),
        force: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec!["deno", "jupyter", "-n", "debugdeno",]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: false,
        kernel: false,
        conn_file: None,
        name: Some("debugdeno".to_string()),
        display: None,
        force: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "jupyter",
    "--kernel",
    "--conn",
    "path/to/conn/file"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: false,
        kernel: true,
        conn_file: Some(String::from("path/to/conn/file")),
        name: None,
        display: None,
        force: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "jupyter",
    "--install",
    "--conn",
    "path/to/conn/file"
  ]);
  r.unwrap_err();
  let r = flags_from_vec(svec!["deno", "jupyter", "--kernel",]);
  r.unwrap_err();
  let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]);
  r.unwrap_err();
  let r = flags_from_vec(svec!["deno", "jupyter", "--display", "deno"]);
  r.unwrap_err();
  let r = flags_from_vec(svec!["deno", "jupyter", "--kernel", "--display"]);
  r.unwrap_err();
  let r = flags_from_vec(svec!["deno", "jupyter", "--force"]);
  r.unwrap_err();
}

#[test]
fn publish_args() {
  let r = flags_from_vec(svec![
    "deno",
    "publish",
    "--no-provenance",
    "--dry-run",
    "--allow-slow-types",
    "--allow-dirty",
    "--token=asdf",
    "--set-version=1.0.1",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Publish(PublishFlags {
        token: Some("asdf".to_string()),
        dry_run: true,
        allow_slow_types: true,
        allow_dirty: true,
        no_provenance: true,
        set_version: Some("1.0.1".to_string()),
      }),
      type_check_mode: TypeCheckMode::Local,
      ..Flags::default()
    }
  );
}

#[test]
fn add_or_install_subcommand() {
  let r = flags_from_vec(svec!["deno", "add"]);
  r.unwrap_err();
  for cmd in ["add", "install"] {
    let mk_flags = |flags: AddFlags| -> Flags {
      match cmd {
        "add" => Flags {
          subcommand: DenoSubcommand::Add(flags),
          ..Flags::default()
        },
        "install" => Flags {
          subcommand: DenoSubcommand::Install(InstallFlags::Local(
            InstallFlagsLocal::Add(flags),
            Default::default(),
          )),
          ..Flags::default()
        },
        _ => unreachable!(),
      }
    };

    {
      let r = flags_from_vec(svec!["deno", cmd, "@david/which"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["@david/which"],
          dev: false, // default is false
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        })
      );
    }
    {
      let r = flags_from_vec(svec![
        "deno",
        cmd,
        "--frozen",
        "--lockfile-only",
        "@david/which",
        "@luca/hello"
      ]);
      let mut expected_flags = mk_flags(AddFlags {
        packages: svec!["@david/which", "@luca/hello"],
        dev: false,
        optional: false,
        no_save: false,
        default_registry: Some(DefaultRegistry::Npm),
        lockfile_only: true,
        save_exact: false,
        package_json: false,
        unscoped: false,
      });
      expected_flags.frozen_lockfile = Some(true);
      assert_eq!(r.unwrap(), expected_flags);
    }
    {
      let r = flags_from_vec(svec!["deno", cmd, "--dev", "npm:chalk"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["npm:chalk"],
          dev: true,
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
      );
    }
    {
      let r = flags_from_vec(svec!["deno", cmd, "--npm", "chalk"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["chalk"],
          dev: false,
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
      );
    }
    {
      let r = flags_from_vec(svec!["deno", cmd, "--jsr", "@std/fs"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["@std/fs"],
          dev: false,
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Jsr),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
      );
    }
    for arg in ["--save-optional", "-O"] {
      let r = flags_from_vec(svec!["deno", cmd, arg, "npm:chalk"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["npm:chalk"],
          dev: false,
          optional: true,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
      );
    }
    {
      let r = flags_from_vec(svec!["deno", cmd, "--no-save", "npm:chalk"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["npm:chalk"],
          dev: false,
          optional: false,
          no_save: true,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
      );
    }
    {
      let r =
        flags_from_vec(svec!["deno", cmd, "--unscoped", "jsr:@david/which"]);
      assert_eq!(
        r.unwrap(),
        mk_flags(AddFlags {
          packages: svec!["jsr:@david/which"],
          dev: false,
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: true,
        }),
      );
    }
    // --save-optional conflicts with --dev
    assert!(
      flags_from_vec(svec![
        "deno",
        cmd,
        "--save-optional",
        "--dev",
        "npm:chalk"
      ])
      .is_err()
    );
    // --no-save conflicts with --dev and --save-optional
    assert!(
      flags_from_vec(svec!["deno", cmd, "--no-save", "--dev", "npm:chalk"])
        .is_err()
    );
    assert!(
      flags_from_vec(svec![
        "deno",
        cmd,
        "--no-save",
        "--save-optional",
        "npm:chalk"
      ])
      .is_err()
    );
  }

  {
    let r = flags_from_vec(svec![
      "deno",
      "add",
      "--allow-import=example.com",
      "@david/which"
    ]);
    assert_eq!(
      r.unwrap(),
      Flags {
        subcommand: DenoSubcommand::Add(AddFlags {
          packages: svec!["@david/which"],
          dev: false,
          optional: false,
          no_save: false,
          default_registry: Some(DefaultRegistry::Npm),
          lockfile_only: false,
          save_exact: false,
          package_json: false,
          unscoped: false,
        }),
        permissions: PermissionFlags {
          allow_import: Some(svec!["example.com"]),
          ..Default::default()
        },
        ..Flags::default()
      }
    );
  }
}

#[test]
fn remove_subcommand() {
  let r = flags_from_vec(svec!["deno", "remove"]);
  r.unwrap_err();

  let r = flags_from_vec(svec!["deno", "remove", "@david/which"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Remove(RemoveFlags {
        packages: svec!["@david/which"],
        lockfile_only: false,
        package_json: false,
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "remove",
    "--frozen",
    "--lockfile-only",
    "@david/which",
    "@luca/hello"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Remove(RemoveFlags {
        packages: svec!["@david/which", "@luca/hello"],
        lockfile_only: true,
        package_json: false,
      }),
      frozen_lockfile: Some(true),
      ..Flags::default()
    }
  );
}

#[test]
fn remove_global_alias_for_uninstall() {
  // `deno remove --global <name>` is an alias for `deno uninstall --global
  // <name>` and produces the exact same subcommand.
  for global_flag in ["--global", "-g"] {
    let r = flags_from_vec(svec!["deno", "remove", global_flag, "file_server"]);
    assert_eq!(
      r.unwrap(),
      Flags {
        subcommand: DenoSubcommand::Uninstall(UninstallFlags {
          kind: UninstallKind::Global(UninstallFlagsGlobal {
            packages: svec!["file_server"],
            root: None,
          }),
        }),
        ..Flags::default()
      }
    );
  }

  // `--root` is honored, just like `deno uninstall --global --root`.
  let r = flags_from_vec(svec![
    "deno",
    "remove",
    "-g",
    "--root",
    "/user/foo/bar",
    "file_server"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Global(UninstallFlagsGlobal {
          packages: svec!["file_server"],
          root: Some("/user/foo/bar".to_string()),
        }),
      }),
      ..Flags::default()
    }
  );

  // A global removal accepts multiple executables, like `deno uninstall
  // --global`.
  let r = flags_from_vec(svec!["deno", "remove", "-g", "file_server", "chalk"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Uninstall(UninstallFlags {
        kind: UninstallKind::Global(UninstallFlagsGlobal {
          packages: svec!["file_server", "chalk"],
          root: None,
        }),
      }),
      ..Flags::default()
    }
  );

  // `--root` requires `--global`.
  let r =
    flags_from_vec(svec!["deno", "remove", "--root", "/tmp", "@std/path"]);
  assert!(r.is_err());

  // Without `--global`, removal stays a config-file dependency removal.
  let r = flags_from_vec(svec!["deno", "remove", "@david/which"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Remove(RemoveFlags {
        packages: svec!["@david/which"],
        lockfile_only: false,
        package_json: false,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn run_with_frozen_lockfile() {
  let cases = [
    (Some("--frozen"), Some(true)),
    (Some("--frozen=true"), Some(true)),
    (Some("--frozen=false"), Some(false)),
    (None, None),
  ];
  for (flag, frozen) in cases {
    let mut args = svec!["deno", "run"];
    if let Some(f) = flag {
      args.push(f.into());
    }
    args.push("script.ts".into());
    let r = flags_from_vec(args);
    assert_eq!(
      r.unwrap(),
      Flags {
        subcommand: DenoSubcommand::Run(RunFlags::new_default(
          "script.ts".to_string(),
        )),
        frozen_lockfile: frozen,
        code_cache_enabled: true,
        ..Flags::default()
      }
    );
  }
}

#[test]
fn allow_scripts() {
  let cases = [
    (Some("--allow-scripts"), Ok(PackagesAllowedScripts::All)),
    (None, Ok(PackagesAllowedScripts::None)),
    (
      Some("--allow-scripts=npm:foo"),
      Ok(PackagesAllowedScripts::Some(vec![
        PackageReq::from_str("foo").unwrap(),
      ])),
    ),
    (
      Some("--allow-scripts=npm:foo,npm:bar@2"),
      Ok(PackagesAllowedScripts::Some(vec![
        PackageReq::from_str("foo").unwrap(),
        PackageReq::from_str("bar@2").unwrap(),
      ])),
    ),
    (Some("--allow-scripts=foo"), Err("Invalid package")),
    (
      Some("--allow-scripts=npm:foo@next"),
      Err("Tags are not supported in --allow-scripts: npm:foo@next"),
    ),
    (
      Some("--allow-scripts=jsr:@foo/bar"),
      Err("An 'npm:' specifier is required"),
    ),
  ];
  for (flag, value) in cases {
    let mut args = svec!["deno", "cache"];
    if let Some(flag) = flag {
      args.push(flag.into());
    }
    args.push("script.ts".into());
    let r = flags_from_vec(args);
    match value {
      Ok(value) => {
        assert_eq!(
          r.unwrap(),
          Flags {
            subcommand: DenoSubcommand::Cache(CacheFlags {
              files: svec!["script.ts"],
            }),
            allow_scripts: value,
            ..Flags::default()
          }
        );
      }
      Err(e) => {
        let err = r.unwrap_err();
        assert!(
          err.to_string().contains(e),
          "expected to contain '{e}' got '{err}'"
        );
      }
    }
  }
}

#[test]
fn x_ignore_scripts() {
  let flags = flags_from_vec(svec![
    "deno",
    "x",
    "--ignore-scripts",
    "-y",
    "npm:foo",
    "--bar"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::X(XFlags {
        kind: XFlagsKind::Command(XCommandFlags {
          yes: true,
          command: "npm:foo".to_string(),
          ignore_scripts: PackagesAllowedScripts::All,
          package: None,
        }),
      }),
      argv: svec!["--bar"],
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );

  let err = flags_from_vec(svec![
    "deno",
    "x",
    "--ignore-scripts",
    "--allow-scripts",
    "npm:foo"
  ])
  .unwrap_err();
  assert!(err.to_string().contains("cannot be used with"));

  let flags = flags_from_vec(svec![
    "deno",
    "x",
    "--ignore-scripts=foo,npm:bar@2",
    "npm:foo"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::X(XFlags {
        kind: XFlagsKind::Command(XCommandFlags {
          yes: false,
          command: "npm:foo".to_string(),
          ignore_scripts: PackagesAllowedScripts::Some(vec![
            PackageReq::from_str("foo").unwrap(),
            PackageReq::from_str("bar@2").unwrap(),
          ]),
          package: None,
        }),
      }),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn bare_run() {
  let r = flags_from_vec(svec!["deno", "--no-config", "script.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        bare: true,
        coverage_dir: None,
        print_task_list: false,
      }),
      config_flag: ConfigFlag::Disabled,
      code_cache_enabled: true,
      ..Flags::default()
    }
  );
}

#[test]
fn bare_global() {
  let r = flags_from_vec(svec!["deno", "--log-level=debug"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: None,
        is_default_command: true,
        json: false,
      }),
      log_level: Some(Level::Debug),
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn repl_user_args() {
  let r = flags_from_vec(svec!["deno", "repl", "foo"]);
  assert!(r.is_err());
  let r = flags_from_vec(svec!["deno", "repl", "--", "foo"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Repl(ReplFlags {
        eval_files: None,
        eval: None,
        is_default_command: false,
        json: false,
      }),
      argv: svec!["foo"],
      ..Flags::default()
    }
  );
}

#[test]
fn bare_with_flag_no_file() {
  let r = flags_from_vec(svec!["deno", "--no-config"]);

  let err = r.unwrap_err();
  assert!(err.to_string().contains("error: [SCRIPT_ARG] may only be omitted with --v8-flags=--help, else to use the repl with arguments, please use the `deno repl` subcommand"));
  assert!(
    err
      .to_string()
      .contains("Usage: deno [OPTIONS] [COMMAND] [SCRIPT_ARG]...")
  );
}

// PORT-SKIP: subcommands_recognized_by_node_shim depends on the node_shim crate; kept in cli/args/flags.rs

// PORT-SKIP: equal_help_output walks the whole CommandDef tree; kept in cli/args/flags.rs

#[test]
fn ci_subcommand_defaults() {
  let r = flags_from_vec(svec!["deno", "ci"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Ci(CiFlags::default()),
      frozen_lockfile: Some(true),
      ..Flags::default()
    }
  );
}

#[test]
fn ci_subcommand_prod_skip_types() {
  let r = flags_from_vec(svec!["deno", "ci", "--prod", "--skip-types"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Ci(CiFlags {
        production: true,
        skip_types: true,
      }),
      frozen_lockfile: Some(true),
      ..Flags::default()
    }
  );
}

#[test]
fn install_permissions_non_global() {
  let r =
    flags_from_vec(svec!["deno", "install", "--allow-net", "jsr:@std/fs"]);

  assert!(
    r.unwrap_err()
      .to_string()
      .contains("Note: Permission flags can only be used in a global setting")
  );
}

// PORT-SKIP: install_os_arch_flags asserts DenoSubcommand::npm_system_info (cli-only); kept in cli/args/flags.rs

// PORT-SKIP: install_os_only_flag asserts DenoSubcommand::npm_system_info (cli-only); kept in cli/args/flags.rs

#[test]
fn install_os_arch_conflicts_with_global() {
  let r =
    flags_from_vec(svec!["deno", "install", "-g", "--os", "linux", "mod.ts"]);
  assert!(r.is_err());
}

#[test]
fn install_production() {
  let r = flags_from_vec(svec!["deno", "install", "--prod"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Local(
        InstallFlagsLocal::TopLevel(InstallTopLevelFlags {
          lockfile_only: false,
          production: true,
          skip_types: false,
        }),
        NpmInstallTargetFlags::default(),
      )),
      ..Flags::default()
    }
  );
}

#[test]
fn install_production_with_entrypoint() {
  let r = flags_from_vec(svec![
    "deno",
    "install",
    "--prod",
    "--entrypoint",
    "main.ts"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Local(
        InstallFlagsLocal::Entrypoints(InstallEntrypointsFlags {
          entrypoints: svec!["main.ts"],
          lockfile_only: false,
          production: true,
          skip_types: false,
        }),
        NpmInstallTargetFlags::default(),
      )),
      ..Flags::default()
    }
  );
}

#[test]
fn install_production_with_skip_types() {
  let r = flags_from_vec(svec!["deno", "install", "--prod", "--skip-types"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Install(InstallFlags::Local(
        InstallFlagsLocal::TopLevel(InstallTopLevelFlags {
          lockfile_only: false,
          production: true,
          skip_types: true,
        }),
        NpmInstallTargetFlags::default(),
      )),
      ..Flags::default()
    }
  );
}

#[test]
fn install_skip_types_requires_prod() {
  let r = flags_from_vec(svec!["deno", "install", "--skip-types"]);
  assert!(r.is_err());
}

#[test]
fn install_production_conflicts_with_global() {
  let r = flags_from_vec(svec![
    "deno",
    "install",
    "--prod",
    "--global",
    "jsr:@std/http/file-server"
  ]);
  assert!(r.is_err());
}

#[test]
fn install_production_conflicts_with_dev() {
  let r =
    flags_from_vec(svec!["deno", "install", "--prod", "--dev", "npm:chalk"]);
  assert!(r.is_err());
}

#[test]
fn jupyter_unstable_flags() {
  let r = flags_from_vec(svec![
    "deno",
    "jupyter",
    "--unstable-ffi",
    "--unstable-bare-node-builtins",
    "--unstable-worker-options"
  ]);

  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Jupyter(JupyterFlags {
        install: false,
        kernel: false,
        conn_file: None,
        name: None,
        display: None,
        force: false,
      }),
      unstable_config: UnstableConfig {
        sloppy_imports: false,
        features: svec!["bare-node-builtins", "ffi", "worker-options"],
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn serve_with_allow_all() {
  let r = flags_from_vec(svec!["deno", "serve", "--allow-all", "./main.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    &flags,
    &Flags {
      subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
        "./main.ts".into(),
        8000,
        "0.0.0.0"
      )),
      permissions: PermissionFlags {
        allow_all: true,
        allow_net: None,
        ..Default::default()
      },
      code_cache_enabled: true,
      ..Default::default()
    }
  );
}

#[test]
fn escape_and_split_commas_test() {
  assert_eq!(escape_and_split_commas("foo".to_string()).unwrap(), ["foo"]);
  assert!(escape_and_split_commas("foo,".to_string()).is_err());
  assert_eq!(
    escape_and_split_commas("foo,,".to_string()).unwrap(),
    ["foo,"]
  );
  assert!(escape_and_split_commas("foo,,,".to_string()).is_err());
  assert_eq!(
    escape_and_split_commas("foo,,,,".to_string()).unwrap(),
    ["foo,,"]
  );
  assert_eq!(
    escape_and_split_commas("foo,bar".to_string()).unwrap(),
    ["foo", "bar"]
  );
  assert_eq!(
    escape_and_split_commas("foo,,bar".to_string()).unwrap(),
    ["foo,bar"]
  );
  assert_eq!(
    escape_and_split_commas("foo,,,bar".to_string()).unwrap(),
    ["foo,", "bar"]
  );
}

#[test]
fn net_flag_with_url() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-net=https://example.com",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap_err().to_string(),
    "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs"
  );
}

#[test]
fn node_modules_dir_default() {
  let r =
    flags_from_vec(svec!["deno", "run", "--node-modules-dir", "./foo.ts"]);
  let flags = r.unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "./foo.ts".into(),
        ..Default::default()
      }),
      node_modules_dir: Some(NodeModulesDirMode::Auto),
      code_cache_enabled: true,
      ..Default::default()
    }
  )
}

#[test]
fn flag_before_subcommand() {
  // PORT-NOTE: clap appends a `Usage: deno repl [OPTIONS] [-- [ARGS]...]`
  // trailer; the new parser reports only the error and the tip.
  let r = flags_from_vec(svec!["deno", "--allow-net", "repl"]);
  assert_eq!(
    r.unwrap_err().to_string(),
    "error: unexpected argument '--allow-net' found

  tip: 'repl --allow-net' exists"
  )
}

#[test]
fn flag_before_subcommand_not_supported() {
  // `lint` does not accept `--allow-all`, so the error must not claim that
  // `lint --allow-all` exists (see issue #27336).
  // PORT-NOTE: clap appends a `Usage: deno lint [OPTIONS] [files]...`
  // trailer; the new parser reports only the error.
  let r = flags_from_vec(svec!["deno", "--allow-all", "lint"]);
  assert_eq!(
    r.unwrap_err().to_string(),
    "error: unexpected argument '--allow-all' found"
  )
}

#[test]
fn allow_all_conflicts_allow_perms() {
  let flags = [
    "--allow-read",
    "--allow-write",
    "--allow-net",
    "--allow-env",
    "--allow-run",
    "--allow-sys",
    "--allow-ffi",
    "--allow-import",
  ];
  for flag in flags {
    let r = flags_from_vec(svec!["deno", "run", "--allow-all", flag, "foo.ts"]);
    assert!(r.is_err());
  }
}

#[test]
fn allow_import_with_url() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-import=https://example.com",
    "script.ts"
  ]);
  assert_eq!(
    r.unwrap_err().to_string(),
    "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs"
  );
}

#[test]
fn deny_import_with_url() {
  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--deny-import=https://example.com",
    "script.ts",
  ]);
  assert_eq!(
    r.unwrap_err().to_string(),
    "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs"
  );
}

#[test]
fn outdated_subcommand() {
  let cases = [
    (
      svec![],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::PrintOutdated { compatible: false },
        recursive: false,
      },
    ),
    (
      svec!["--recursive"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::PrintOutdated { compatible: false },
        recursive: true,
      },
    ),
    (
      svec!["--recursive", "--compatible"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::PrintOutdated { compatible: true },
        recursive: true,
      },
    ),
    (
      svec!["--update"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--update", "--latest"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: true,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--update", "--recursive"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: true,
      },
    ),
    (
      svec!["--update", "--lockfile-only"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: true,
        },
        recursive: false,
      },
    ),
    (
      svec!["--update", "@foo/bar"],
      OutdatedFlags {
        filters: svec!["@foo/bar"],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--latest"],
      OutdatedFlags {
        filters: svec![],
        kind: OutdatedKind::PrintOutdated { compatible: false },
        recursive: false,
      },
    ),
    (
      svec!["--update", "--latest", "--interactive"],
      OutdatedFlags {
        filters: svec![],
        kind: OutdatedKind::Update {
          latest: true,
          interactive: true,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
  ];
  for (input, expected) in cases {
    let mut args = svec!["deno", "outdated"];
    args.extend(input);
    let r = flags_from_vec(args.clone()).unwrap();
    assert_eq!(
      r.subcommand,
      DenoSubcommand::Outdated(expected),
      "incorrect result for args: {:?}",
      args
    );
  }
}

#[test]
fn list_subcommand() {
  let cases = [
    (svec![], ListFlags::default()),
    (
      svec!["--recursive"],
      ListFlags {
        recursive: true,
        ..Default::default()
      },
    ),
    (
      svec!["--depth", "3"],
      ListFlags {
        depth: 3,
        ..Default::default()
      },
    ),
    (
      svec!["--prod"],
      ListFlags {
        prod: true,
        ..Default::default()
      },
    ),
    (
      svec!["--dev", "@foo/bar", "react*"],
      ListFlags {
        dev: true,
        filters: svec!["@foo/bar", "react*"],
        ..Default::default()
      },
    ),
  ];
  for (input, expected) in cases {
    let mut args = svec!["deno", "list"];
    args.extend(input);
    let r = flags_from_vec(args.clone()).unwrap();
    assert_eq!(
      r.subcommand,
      DenoSubcommand::List(expected),
      "incorrect result for args: {:?}",
      args
    );
  }

  // --prod and --dev are mutually exclusive
  assert!(flags_from_vec(svec!["deno", "list", "--prod", "--dev"]).is_err());
}

#[test]
fn update_subcommand() {
  let cases = [
    (
      svec![],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--latest"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: true,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--recursive"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: true,
      },
    ),
    (
      svec!["--lockfile-only"],
      OutdatedFlags {
        filters: vec![],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: true,
        },
        recursive: false,
      },
    ),
    (
      svec!["@foo/bar"],
      OutdatedFlags {
        filters: svec!["@foo/bar"],
        kind: OutdatedKind::Update {
          latest: false,
          interactive: false,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
    (
      svec!["--latest", "--interactive"],
      OutdatedFlags {
        filters: svec![],
        kind: OutdatedKind::Update {
          latest: true,
          interactive: true,
          lockfile_only: false,
        },
        recursive: false,
      },
    ),
  ];
  for (input, expected) in cases {
    let mut args = svec!["deno", "update"];
    args.extend(input);
    let r = flags_from_vec(args.clone()).unwrap();
    assert_eq!(
      r.subcommand,
      DenoSubcommand::Outdated(expected),
      "incorrect result for args: {:?}",
      args
    );
  }
}

#[test]
fn update_subcommand_frozen_flag() {
  let r = flags_from_vec(svec!["deno", "update", "--frozen=false"]).unwrap();
  assert_eq!(r.frozen_lockfile, Some(false));

  let r = flags_from_vec(svec!["deno", "update", "--frozen"]).unwrap();
  assert_eq!(r.frozen_lockfile, Some(true));
}

#[test]
fn outdated_subcommand_frozen_flag() {
  let r = flags_from_vec(svec!["deno", "outdated", "--frozen=false"]).unwrap();
  assert_eq!(r.frozen_lockfile, Some(false));
}

#[test]
fn approve_scripts_subcommand() {
  let cases = [
    (
      svec![],
      ApproveScriptsFlags {
        packages: vec![],
        lockfile_only: false,
      },
    ),
    (
      svec!["npm:pkg@1.0.0"],
      ApproveScriptsFlags {
        packages: vec!["npm:pkg@1.0.0".to_string()],
        lockfile_only: false,
      },
    ),
    (
      svec!["npm:pkg1@1.0.0", "npm:pkg2@2.0.0"],
      ApproveScriptsFlags {
        packages: vec![
          "npm:pkg1@1.0.0".to_string(),
          "npm:pkg2@2.0.0".to_string(),
        ],
        lockfile_only: false,
      },
    ),
    (
      svec!["npm:pkg1@1.0.0,npm:pkg2@2.0.0"],
      ApproveScriptsFlags {
        packages: vec![
          "npm:pkg1@1.0.0".to_string(),
          "npm:pkg2@2.0.0".to_string(),
        ],
        lockfile_only: false,
      },
    ),
    (
      svec!["--lockfile-only"],
      ApproveScriptsFlags {
        packages: vec![],
        lockfile_only: true,
      },
    ),
    (
      svec!["--lockfile-only", "npm:pkg@1.0.0"],
      ApproveScriptsFlags {
        packages: vec!["npm:pkg@1.0.0".to_string()],
        lockfile_only: true,
      },
    ),
    (
      svec!["npm:pkg@1.0.0", "--lockfile-only"],
      ApproveScriptsFlags {
        packages: vec!["npm:pkg@1.0.0".to_string()],
        lockfile_only: true,
      },
    ),
    (
      svec!["npm:pkg1@1.0.0", "npm:pkg2@2.0.0", "--lockfile-only"],
      ApproveScriptsFlags {
        packages: vec![
          "npm:pkg1@1.0.0".to_string(),
          "npm:pkg2@2.0.0".to_string(),
        ],
        lockfile_only: true,
      },
    ),
  ];
  for (input, expected) in cases {
    let mut args = svec!["deno", "approve-scripts"];
    args.extend(input);
    let r = flags_from_vec(args.clone()).unwrap();
    assert_eq!(
      r.subcommand,
      DenoSubcommand::ApproveScripts(expected),
      "incorrect result for args: {:?}",
      args
    );
  }
}

#[test]
fn clean_subcommand() {
  let cases = [
    (
      svec![],
      CleanFlags {
        except_paths: vec![],
        dry_run: false,
      },
    ),
    (
      svec!["--except", "path1"],
      CleanFlags {
        except_paths: vec!["path1".to_string()],
        dry_run: false,
      },
    ),
    (
      svec!["--except", "path1", "path2"],
      CleanFlags {
        except_paths: vec!["path1".to_string(), "path2".to_string()],
        dry_run: false,
      },
    ),
    (
      svec!["--except", "path1", "--dry-run"],
      CleanFlags {
        except_paths: vec!["path1".to_string()],
        dry_run: true,
      },
    ),
    (
      svec!["--dry-run"],
      CleanFlags {
        except_paths: vec![],
        dry_run: true,
      },
    ),
  ];
  for (input, expected) in cases {
    // `--except` builds a module graph of the retained paths, so it parses
    // with `--cached-only`; other invocations (e.g. `--dry-run` alone) don't.
    let cached_only = input.iter().any(|arg| arg == "--except");
    let mut args = svec!["deno", "clean"];
    args.extend(input);
    let r = flags_from_vec(args.clone())
      .inspect_err(|e| {
        #[allow(clippy::print_stderr, reason = "actually want to output")]
        {
          eprintln!("error: {:?} on input: {:?}", e, args);
        }
      })
      .unwrap();
    assert_eq!(
      r,
      Flags {
        subcommand: DenoSubcommand::Clean(expected),
        cached_only,
        ..Flags::default()
      },
      "incorrect result for args: {:?}",
      args
    );
  }
}

#[test]
fn conditions_test() {
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--conditions",
    "development",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      node_conditions: svec!["development"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--conditions",
    "development,production",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      node_conditions: svec!["development", "production"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--conditions",
    "development",
    "--conditions",
    "production",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      node_conditions: svec!["development", "production"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );
}

#[test]
fn preload_flag_test() {
  let flags =
    flags_from_vec(svec!["deno", "run", "--preload", "preload.js", "main.ts"])
      .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      preload: svec!["preload.js"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags =
    flags_from_vec(svec!["deno", "run", "--preload", "data:,()", "main.ts"])
      .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      preload: svec!["data:,()"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "compile",
    "--preload",
    "p1.js",
    "--preload",
    "./p2.js",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Compile(CompileFlags {
        source_file: "main.ts".into(),
        output: None,
        args: vec![],
        target: None,
        no_terminal: false,
        icon: None,
        include: Default::default(),
        exclude: Default::default(),
        eszip: false,
        self_extracting: false,
        bundle: false,
        app_name: None,
        minify: false,
        exclude_unused_npm: false,
        engine: Default::default(),
      }),
      type_check_mode: TypeCheckMode::Local,
      preload: svec!["p1.js", "./p2.js"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "test",
    "--preload",
    "p1.js",
    "--import",
    "./p2.js",
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Test(TestFlags::default()),
      preload: svec!["p1.js", "./p2.js"],
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: false,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "bench",
    "--preload",
    "p1.js",
    "--import",
    "./p2.js",
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Bench(BenchFlags::default()),
      preload: svec!["p1.js", "./p2.js"],
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: false,
      permissions: PermissionFlags {
        no_prompt: true,
        ..Default::default()
      },
      ..Default::default()
    }
  );
}

#[test]
fn require_flag_test() {
  let flags =
    flags_from_vec(svec!["deno", "run", "--require", "require.js", "main.ts"])
      .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      require: svec!["require.js"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );

  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--require",
    "r1.js",
    "--require",
    "./r2.js",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "main.ts".into(),
        ..Default::default()
      }),
      require: svec!["r1.js", "./r2.js"],
      code_cache_enabled: true,
      ..Default::default()
    }
  );
}

#[test]
fn check_with_v8_flags() {
  let flags =
    flags_from_vec(svec!["deno", "check", "--v8-flags=--help", "script.ts",])
      .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Check(CheckFlags {
        files: svec!["script.ts"],
        doc: false,
        doc_only: false,
        check_js: false,
      }),
      type_check_mode: TypeCheckMode::Local,
      code_cache_enabled: true,
      v8_flags: svec!["--help"],
      ..Flags::default()
    }
  );
}

#[test]
fn multiple_allow_all() {
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--allow-all",
    "--inspect",
    "-A",
    "script.ts",
  ])
  .unwrap();
  assert_eq!(
    flags,
    Flags {
      subcommand: DenoSubcommand::Run(RunFlags {
        script: "script.ts".to_string(),
        ..Default::default()
      }),
      inspect: Some("127.0.0.1:9229".parse().unwrap()),
      code_cache_enabled: true,
      permissions: PermissionFlags {
        allow_all: true,
        ..Default::default()
      },
      ..Flags::default()
    }
  );
}

#[test]
fn inspect_flag_parsing() {
  use std::net::IpAddr;
  use std::net::Ipv4Addr;

  let cases = vec![
    (
      "127.0.0.1:9229",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9229),
    ),
    (
      "192.168.0.1",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), 9229),
    ),
    (
      "10000",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10000),
    ),
    (
      ":10000",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10000),
    ),
    (
      ":0",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0),
    ),
    (
      "0",
      SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0),
    ),
  ];

  for case in cases {
    let flags = flags_from_vec(svec![
      "deno",
      "run",
      &format!("--inspect={}", case.0),
      "script.ts",
    ])
    .unwrap();
    assert_eq!(
      flags,
      Flags {
        subcommand: DenoSubcommand::Run(RunFlags {
          script: "script.ts".to_string(),
          ..Default::default()
        }),
        inspect: Some(case.1),
        code_cache_enabled: true,
        ..Flags::default()
      }
    );
  }
}

/// Node accepts `--inspect=localhost:0` (and similar hostname forms); the
/// parser resolves them via DNS rather than rejecting or silently dropping
/// them. Ported from the clap-era `inspect_value_parser_resolves_hostnames`.
#[test]
fn inspect_resolves_hostnames() {
  let cases = [
    ("localhost:0", 0u16),
    ("localhost:1234", 1234),
    ("localhost", 9229),
  ];
  for (input, expected_port) in cases {
    let flags = flags_from_vec(svec![
      "deno",
      "run",
      &format!("--inspect={input}"),
      "foo.ts"
    ])
    .unwrap_or_else(|e| panic!("failed to parse {input:?}: {e}"));
    let addr = flags
      .inspect
      .unwrap_or_else(|| panic!("--inspect={input} did not set an address"));
    assert_eq!(addr.port(), expected_port, "port for {input:?}");
    assert!(
      addr.ip().is_loopback(),
      "expected loopback for {input:?}, got {}",
      addr.ip()
    );
  }
}

/// Non-hostname forms: bare port, bare `:port`, bare IP, and full `host:port`.
#[test]
fn inspect_address_forms() {
  let cases = [
    ("9222", "127.0.0.1:9222"),
    (":9222", "127.0.0.1:9222"),
    ("192.168.0.1", "192.168.0.1:9229"),
    ("192.168.0.1:8080", "192.168.0.1:8080"),
    ("[::1]:8080", "[::1]:8080"),
  ];
  for (input, expected) in cases {
    let flags = flags_from_vec(svec![
      "deno",
      "run",
      &format!("--inspect={input}"),
      "foo.ts"
    ])
    .unwrap_or_else(|e| panic!("failed to parse {input:?}: {e}"));
    assert_eq!(
      flags.inspect.map(|a| a.to_string()).as_deref(),
      Some(expected),
      "{input:?}"
    );
  }
}

/// An unresolvable host or out-of-range port is rejected outright rather than
/// silently dropping the flag.
#[test]
fn inspect_invalid_address_is_rejected() {
  for input in ["localhost:99999", ""] {
    let r = flags_from_vec(svec![
      "deno",
      "run",
      &format!("--inspect={input}"),
      "foo.ts"
    ]);
    assert!(r.is_err(), "expected --inspect={input:?} to be rejected");
  }
}

#[test]
fn inspect_publish_uid_flag_parsing() {
  // Test with both stderr and http
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect",
    "--inspect-publish-uid=stderr,http",
    "script.ts",
  ])
  .unwrap();
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: true,
      http: true,
    })
  );

  // Test with only stderr
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect",
    "--inspect-publish-uid=stderr",
    "script.ts",
  ])
  .unwrap();
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: true,
      http: false,
    })
  );

  // Test with only http
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect",
    "--inspect-publish-uid=http",
    "script.ts",
  ])
  .unwrap();
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: false,
      http: true,
    })
  );

  // Test without the flag (should be None)
  let flags =
    flags_from_vec(svec!["deno", "run", "--inspect", "script.ts",]).unwrap();
  assert_eq!(flags.inspect_publish_uid, None);
}

#[test]
fn node_options_require() {
  // Test NODE_OPTIONS --require when no CLI --require is passed
  set_test_node_options(Some("--require only.js"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.require, vec!["only.js"]);
}

#[test]
fn node_options_require_prepend_to_cli() {
  // Test NODE_OPTIONS --require is prepended to CLI --require values
  set_test_node_options(Some("--require foo.js --require bar.js"));
  let flags =
    flags_from_vec(svec!["deno", "run", "--require", "cli.js", "script.ts",])
      .unwrap();
  set_test_node_options(None);
  assert_eq!(flags.require, vec!["foo.js", "bar.js", "cli.js"]);
}

#[test]
fn node_options_inspect_publish_uid() {
  set_test_node_options(Some("--inspect-publish-uid=http"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: false,
      http: true,
    })
  );
}

#[test]
fn node_options_inspect_publish_uid_cli_precedence() {
  set_test_node_options(Some("--inspect-publish-uid=http"));
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect-publish-uid=stderr",
    "script.ts",
  ])
  .unwrap();
  set_test_node_options(None);
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: true,
      http: false,
    })
  );
}

#[test]
fn node_options_inspect() {
  set_test_node_options(Some("--inspect=127.0.0.1:9333"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.inspect, Some("127.0.0.1:9333".parse().unwrap()));
}

#[test]
fn node_options_inspect_default_address() {
  set_test_node_options(Some("--inspect"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.inspect, Some("127.0.0.1:9229".parse().unwrap()));
}

#[test]
fn node_options_inspect_brk_and_wait() {
  set_test_node_options(Some("--inspect-brk=127.0.0.1:9334"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.inspect_brk, Some("127.0.0.1:9334".parse().unwrap()));

  set_test_node_options(Some("--inspect-wait=127.0.0.1:9335"));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.inspect_wait, Some("127.0.0.1:9335".parse().unwrap()));
}

#[test]
fn node_options_inspect_cli_precedence() {
  // An explicit CLI inspector flag suppresses the entire NODE_OPTIONS
  // inspector family, since --inspect/-brk/-wait are mutually exclusive.
  set_test_node_options(Some("--inspect=127.0.0.1:9333"));
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--inspect-brk=127.0.0.1:9444",
    "script.ts",
  ])
  .unwrap();
  set_test_node_options(None);
  assert_eq!(flags.inspect, None);
  assert_eq!(flags.inspect_brk, Some("127.0.0.1:9444".parse().unwrap()));
}

#[test]
fn node_options_combined() {
  // Test NODE_OPTIONS with both --require and --inspect-publish-uid
  set_test_node_options(Some(
    "--require foo.js --inspect-publish-uid=stderr,http",
  ));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.require, vec!["foo.js"]);
  assert_eq!(
    flags.inspect_publish_uid,
    Some(InspectPublishUid {
      console: true,
      http: true,
    })
  );
}

#[test]
fn node_options_empty() {
  set_test_node_options(Some(""));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert!(flags.require.is_empty());
  assert_eq!(flags.inspect_publish_uid, None);
}

#[test]
fn node_options_ignores_unknown_flags() {
  set_test_node_options(Some(
    "--require known.js --unknown-flag --another-unknown",
  ));
  let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap();
  set_test_node_options(None);
  assert_eq!(flags.require, vec!["known.js"]);
}

#[test]
fn bump_version_patch() {
  let r = flags_from_vec(svec!["deno", "bump-version", "patch"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Patch),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_minor() {
  let r = flags_from_vec(svec!["deno", "bump-version", "minor"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Minor),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_major() {
  let r = flags_from_vec(svec!["deno", "bump-version", "major"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Major),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_prerelease() {
  let r = flags_from_vec(svec!["deno", "bump-version", "prerelease"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Prerelease),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_premajor() {
  let r = flags_from_vec(svec!["deno", "bump-version", "premajor"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Premajor),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_preminor() {
  let r = flags_from_vec(svec!["deno", "bump-version", "preminor"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Preminor),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_prepatch() {
  let r = flags_from_vec(svec!["deno", "bump-version", "prepatch"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Prepatch),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_no_args() {
  let r = flags_from_vec(svec!["deno", "bump-version"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags::default()),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_invalid_increment() {
  let r = flags_from_vec(svec!["deno", "bump-version", "invalid"]);
  assert!(r.is_err());
}

#[test]
fn bump_version_workspace_flags() {
  let r = flags_from_vec(svec![
    "deno",
    "bump-version",
    "--workspace",
    "--dry-run",
    "--start",
    "v1.0.0",
    "--base",
    "main",
    "--import-map",
    "import_map.json",
    "--release-notes",
    "Releases.md",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: None,
        workspace: Some(true),
        dry_run: true,
        start: Some("v1.0.0".to_string()),
        base: Some("main".to_string()),
        import_map: Some("import_map.json".to_string()),
        release_notes: Some("Releases.md".to_string()),
        config: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_config() {
  let r = flags_from_vec(svec![
    "deno",
    "bump-version",
    "patch",
    "--config",
    "package.json",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Patch),
        config: Some("package.json".to_string()),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );

  let r = flags_from_vec(svec![
    "deno",
    "bump-version",
    "-c",
    "package.json",
    "patch",
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Patch),
        config: Some("package.json".to_string()),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn bump_version_no_workspace() {
  let r =
    flags_from_vec(svec!["deno", "bump-version", "patch", "--no-workspace"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::BumpVersion(VersionFlags {
        increment: Some(VersionIncrement::Patch),
        workspace: Some(false),
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn why_package() {
  let r = flags_from_vec(svec!["deno", "why", "express"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Why(WhyFlags {
        package: "express".to_string(),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn why_package_with_version() {
  let r = flags_from_vec(svec!["deno", "why", "express@4.18.2"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Why(WhyFlags {
        package: "express@4.18.2".to_string(),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn why_scoped_package() {
  let r = flags_from_vec(svec!["deno", "why", "@scope/pkg"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Why(WhyFlags {
        package: "@scope/pkg".to_string(),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn why_scoped_package_with_version() {
  let r = flags_from_vec(svec!["deno", "why", "@scope/pkg@1.0.0"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Why(WhyFlags {
        package: "@scope/pkg@1.0.0".to_string(),
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn why_missing_package() {
  let r = flags_from_vec(svec!["deno", "why"]);
  assert!(r.is_err());
}

#[test]
fn audit_basic() {
  let r = flags_from_vec(svec!["deno", "audit"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Audit(AuditFlags {
        severity: "low".to_string(),
        dev: true,
        prod: true,
        optional: true,
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn audit_fix_flag() {
  let r = flags_from_vec(svec!["deno", "audit", "--fix"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Audit(AuditFlags {
        severity: "low".to_string(),
        dev: true,
        prod: true,
        optional: true,
        fix: true,
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn audit_fix_positional() {
  let r = flags_from_vec(svec!["deno", "audit", "fix"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Audit(AuditFlags {
        severity: "low".to_string(),
        dev: true,
        prod: true,
        optional: true,
        fix: true,
        ..Default::default()
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn audit_invalid_positional() {
  let r = flags_from_vec(svec!["deno", "audit", "bogus"]);
  assert!(r.is_err());
}

#[test]
fn transpile_single_file() {
  let r = flags_from_vec(svec!["deno", "transpile", "main.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: None,
        output_dir: None,
        declaration: false,
        source_map: SourceMapMode::None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_multiple_files() {
  let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "helpers.ts"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts", "helpers.ts"],
        output: None,
        output_dir: None,
        declaration: false,
        source_map: SourceMapMode::None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_with_output() {
  let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "-o", "out.js"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: Some("out.js".to_string()),
        output_dir: None,
        declaration: false,
        source_map: SourceMapMode::None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_with_outdir() {
  let r =
    flags_from_vec(svec!["deno", "transpile", "main.ts", "--outdir", "dist"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: None,
        output_dir: Some("dist".to_string()),
        declaration: false,
        source_map: SourceMapMode::None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_with_source_map_inline() {
  let r = flags_from_vec(svec![
    "deno",
    "transpile",
    "main.ts",
    "--source-map",
    "inline"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: None,
        output_dir: None,
        declaration: false,
        source_map: SourceMapMode::Inline,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_with_source_map_separate() {
  let r = flags_from_vec(svec![
    "deno",
    "transpile",
    "main.ts",
    "--source-map",
    "separate"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: None,
        output_dir: None,
        declaration: false,
        source_map: SourceMapMode::Separate,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_with_declaration() {
  let r =
    flags_from_vec(svec!["deno", "transpile", "main.ts", "--declaration"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: None,
        output_dir: None,
        declaration: true,
        source_map: SourceMapMode::None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn transpile_all_flags() {
  let r = flags_from_vec(svec![
    "deno",
    "transpile",
    "main.ts",
    "-o",
    "out.js",
    "--source-map",
    "separate",
    "--declaration"
  ]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Transpile(TranspileFlags {
        files: svec!["main.ts"],
        output: Some("out.js".to_string()),
        output_dir: None,
        declaration: true,
        source_map: SourceMapMode::Separate,
      }),
      ..Flags::default()
    }
  );
}

// ---------------------------------------------------------------------------
// Tier-1 functionality-parity tests: flags that existed in clap but were
// missing/unreachable in the new parser (see the parity audit).
// ---------------------------------------------------------------------------

#[test]
fn tier1_test_sanitize_and_snapshot_flags() {
  let flags = flags_from_vec(svec![
    "deno",
    "test",
    "--sanitize-ops",
    "--sanitize-resources",
    "--coverage=cov",
    "--coverage-threshold=80",
    "--update-snapshots"
  ])
  .unwrap();
  let DenoSubcommand::Test(t) = flags.subcommand else {
    unreachable!()
  };
  assert!(t.sanitize_ops);
  assert!(t.sanitize_resources);
  assert_eq!(t.coverage_threshold, Some(80));
  assert!(t.update_snapshots);

  // -u is short for --update-snapshots.
  let flags = flags_from_vec(svec!["deno", "test", "-u"]).unwrap();
  assert!(
    matches!(flags.subcommand, DenoSubcommand::Test(t) if t.update_snapshots)
  );

  // --coverage-threshold requires --coverage.
  assert!(
    flags_from_vec(svec!["deno", "test", "--coverage-threshold=80"]).is_err()
  );
}

#[test]
fn tier1_compile_bundle_flags() {
  let flags = flags_from_vec(svec![
    "deno",
    "compile",
    "--bundle",
    "--minify",
    "--app-name=MyApp",
    "--exclude-unused-npm",
    "main.ts"
  ])
  .unwrap();
  let DenoSubcommand::Compile(c) = flags.subcommand else {
    unreachable!()
  };
  assert!(c.bundle);
  assert!(c.minify);
  assert_eq!(c.app_name, Some("MyApp".to_string()));
  assert!(c.exclude_unused_npm);

  // --minify requires --bundle.
  assert!(
    flags_from_vec(svec!["deno", "compile", "--minify", "main.ts"]).is_err()
  );
}

#[test]
fn tier1_install_os_arch_package_json() {
  let flags =
    flags_from_vec(svec!["deno", "install", "--os", "linux", "--arch", "x64"])
      .unwrap();
  let DenoSubcommand::Install(InstallFlags::Local(_, npm_target)) =
    flags.subcommand
  else {
    unreachable!()
  };
  assert_eq!(npm_target.os, Some("linux".to_string()));
  assert_eq!(npm_target.arch, Some("x64".to_string()));

  let flags =
    flags_from_vec(svec!["deno", "install", "--package-json", "npm:foo"])
      .unwrap();
  assert!(matches!(
    flags.subcommand,
    DenoSubcommand::Install(InstallFlags::Local(
      InstallFlagsLocal::Add(a),
      _
    )) if a.package_json
  ));

  // --os / --arch conflict with --global.
  assert!(
    flags_from_vec(svec![
      "deno", "install", "--global", "--os", "linux", "foo"
    ])
    .is_err()
  );
}

#[test]
fn tier1_uninstall_remove_package_json() {
  let flags =
    flags_from_vec(svec!["deno", "uninstall", "--package-json", "foo"])
      .unwrap();
  assert!(matches!(flags.subcommand, DenoSubcommand::Uninstall(_)));
  let flags =
    flags_from_vec(svec!["deno", "remove", "--package-json", "foo"]).unwrap();
  assert!(matches!(flags.subcommand, DenoSubcommand::Remove(_)));
}

#[test]
fn tier1_check_flags() {
  let flags =
    flags_from_vec(svec!["deno", "check", "--no-code-cache", "main.ts"])
      .unwrap();
  assert!(!flags.code_cache_enabled);

  let flags =
    flags_from_vec(svec!["deno", "check", "--watch", "main.ts"]).unwrap();
  assert!(flags.watch.is_some());
}

#[test]
fn tier1_serve_open_tunnel_hmr() {
  let flags =
    flags_from_vec(svec!["deno", "serve", "--open", "main.ts"]).unwrap();
  assert!(matches!(&flags.subcommand, DenoSubcommand::Serve(s) if s.open_site));

  let flags =
    flags_from_vec(svec!["deno", "serve", "--tunnel", "main.ts"]).unwrap();
  assert!(flags.tunnel);

  // serve uses `--watch-hmr` (alias `--unstable-hmr`), not `--hmr`.
  for flag in ["--watch-hmr", "--unstable-hmr"] {
    let flags =
      flags_from_vec(svec!["deno", "serve", flag, "main.ts"]).unwrap();
    assert!(flags.watch.map(|w| w.hmr).unwrap_or(false));
  }
}

#[test]
fn tier1_bundle_check_and_declaration() {
  let flags =
    flags_from_vec(svec!["deno", "bundle", "--check=all", "main.ts"]).unwrap();
  assert_eq!(flags.type_check_mode, TypeCheckMode::All);

  let flags =
    flags_from_vec(svec!["deno", "bundle", "--declaration", "main.ts"])
      .unwrap();
  assert!(
    matches!(flags.subcommand, DenoSubcommand::Bundle(b) if b.declaration)
  );
}

#[test]
fn tier1_audit_why_lock_flags() {
  let flags =
    flags_from_vec(svec!["deno", "audit", "--lock=deno.lock", "--frozen"])
      .unwrap();
  assert!(matches!(&flags.subcommand, DenoSubcommand::Audit(_)));
  assert_eq!(flags.lock, Some("deno.lock".to_string()));
  assert_eq!(flags.frozen_lockfile, Some(true));

  let flags = flags_from_vec(svec!["deno", "why", "--no-lock", "foo"]).unwrap();
  assert!(matches!(&flags.subcommand, DenoSubcommand::Why(_)));
  assert!(flags.no_lock);
}

#[test]
fn tier1_node_modules_linker() {
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--node-modules-linker=isolated",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(
    flags.node_modules_linker,
    Some(NodeModulesLinkerMode::Isolated)
  );

  // `--linker` is an alias.
  let flags =
    flags_from_vec(svec!["deno", "run", "--linker=hoisted", "main.ts"])
      .unwrap();
  assert_eq!(
    flags.node_modules_linker,
    Some(NodeModulesLinkerMode::Hoisted)
  );
}

#[test]
fn tier1_upgrade_x_env_file() {
  let flags = flags_from_vec(svec![
    "deno",
    "upgrade",
    "--unsafely-ignore-certificate-errors=example.com"
  ])
  .unwrap();
  assert_eq!(
    flags.unsafely_ignore_certificate_errors,
    Some(svec!["example.com"])
  );

  let flags =
    flags_from_vec(svec!["deno", "x", "--package=npm:foo", "bar"]).unwrap();
  assert!(matches!(
    flags.subcommand,
    DenoSubcommand::X(XFlags {
      kind: XFlagsKind::Command(c),
    }) if c.package == Some("npm:foo".to_string()) && c.command == "bar"
  ));

  for sub in ["outdated", "update", "publish"] {
    let flags = flags_from_vec(svec!["deno", sub, "--env-file=.env"]).unwrap();
    assert_eq!(flags.env_file, Some(svec![".env"]));
  }
}

#[test]
fn tier1_desktop_flags() {
  let flags = flags_from_vec(svec![
    "deno",
    "desktop",
    "--output=MyApp.app",
    "--target=x86_64-unknown-linux-gnu",
    "--icon=icon.png",
    "--hmr",
    "--all-targets",
    "--backend=cef",
    "--include=extra.ts",
    "--exclude=skip.ts",
    "--compress=zstd",
    "--exclude-unused-npm",
    "--allow-read",
    "main.ts"
  ])
  .unwrap();
  assert_eq!(flags.type_check_mode, TypeCheckMode::Local);
  assert_eq!(flags.permissions.allow_read, Some(vec![]));
  let DenoSubcommand::Desktop(d) = flags.subcommand else {
    unreachable!()
  };
  assert_eq!(d.output, Some("MyApp.app".to_string()));
  assert_eq!(d.target, Some("x86_64-unknown-linux-gnu".to_string()));
  assert_eq!(d.icon, Some(IconConfig::Single("icon.png".to_string())));
  assert!(d.hmr);
  assert!(d.all_targets);
  assert_eq!(d.backend, Some("cef".to_string()));
  assert_eq!(d.compress, Some("zstd".to_string()));
  assert!(d.exclude_unused_npm);
  assert_eq!(d.include, svec!["extra.ts"]);
  assert_eq!(d.exclude, svec!["skip.ts"]);
  assert_eq!(d.source_file, "main.ts");

  // Bare `deno desktop` defaults the entrypoint to ".".
  let flags = flags_from_vec(svec!["deno", "desktop"]).unwrap();
  assert!(
    matches!(flags.subcommand, DenoSubcommand::Desktop(d) if d.source_file == ".")
  );
}

// ---------------------------------------------------------------------------
// Tier-2 value-validation parity: bad values now error at parse time instead
// of being silently accepted/coerced (mirrors clap's value_parser).
// ---------------------------------------------------------------------------

#[test]
fn tier2_choice_validation() {
  // Each of these passes an invalid value for a choice-restricted flag.
  let bad = [
    svec!["deno", "test", "--reporter=bogus"],
    svec!["deno", "fmt", "--prose-wrap=bogus", "x.md"],
    svec!["deno", "fmt", "--ext=bogus", "x"],
    svec!["deno", "run", "--ext=bogus", "x.ts"],
    svec!["deno", "compile", "--target=bogus", "x.ts"],
    svec!["deno", "desktop", "--backend=bogus", "x.ts"],
    svec!["deno", "desktop", "--compress=bogus", "x.ts"],
    svec!["deno", "transpile", "--source-map=bogus", "x.ts"],
    svec!["deno", "completions", "bogus"],
    svec!["deno", "install", "--os", "bogus"],
    svec!["deno", "install", "--arch", "bogus"],
    svec!["deno", "audit", "--level=bogus"],
    svec!["deno", "--log-level=bogus", "run", "x.ts"],
    svec!["deno", "run", "--node-modules-dir=bogus", "x.ts"],
    svec!["deno", "run", "--node-modules-linker=bogus", "x.ts"],
  ];
  for args in bad {
    assert!(
      flags_from_vec(args.clone()).is_err(),
      "expected error for {args:?}"
    );
  }
}

#[test]
fn tier2_numeric_validation() {
  let bad = [
    svec!["deno", "run", "--seed=abc", "x.ts"],
    svec!["deno", "serve", "--port=abc", "x.ts"],
    svec!["deno", "serve", "--port=99999", "x.ts"],
    svec!["deno", "list", "--depth=abc"],
    svec!["deno", "test", "--retry=abc"],
    svec!["deno", "test", "--shuffle=abc"],
    svec!["deno", "test", "--coverage=cov", "--coverage-threshold=101"],
    svec!["deno", "fmt", "--line-width=0", "x.ts"],
    svec!["deno", "fmt", "--indent-width=0", "x.ts"],
    svec!["deno", "test", "--fail-fast=0"],
  ];
  for args in bad {
    assert!(
      flags_from_vec(args.clone()).is_err(),
      "expected error for {args:?}"
    );
  }

  // Valid numeric values still parse.
  assert!(
    flags_from_vec(svec!["deno", "serve", "--port=8080", "x.ts"]).is_ok()
  );
  assert!(
    flags_from_vec(svec![
      "deno",
      "test",
      "--coverage=cov",
      "--coverage-threshold=100"
    ])
    .is_ok()
  );
}

#[test]
fn tier2_bool_validation() {
  let bad = [
    svec!["deno", "fmt", "--use-tabs=maybe", "x.ts"],
    svec!["deno", "fmt", "--single-quote=maybe", "x.ts"],
    svec!["deno", "run", "--vendor=maybe", "x.ts"],
    svec!["deno", "run", "--frozen=maybe", "x.ts"],
  ];
  for args in bad {
    assert!(
      flags_from_vec(args.clone()).is_err(),
      "expected error for {args:?}"
    );
  }
  // Bare boolean-ish flags (no value) remain valid.
  assert!(flags_from_vec(svec!["deno", "fmt", "--use-tabs", "x.ts"]).is_ok());
}

#[test]
fn tier2_location_strips_credentials() {
  let flags = flags_from_vec(svec![
    "deno",
    "run",
    "--location",
    "https://u:p@example.com/",
    "x.ts"
  ])
  .unwrap();
  let loc = flags.location.unwrap();
  assert_eq!(loc.username(), "");
  assert_eq!(loc.password(), None);
  assert_eq!(loc.host_str(), Some("example.com"));
}

// ---------------------------------------------------------------------------
// Tier-3 parity: conflicts, requires, and aliases that clap enforced/accepted.
// ---------------------------------------------------------------------------

#[test]
fn tier3_aliases() {
  // These previously-missing aliases/short flags now parse.
  let ok = [
    svec!["deno", "run", "--importmap=im.json", "x.ts"],
    svec!["deno", "run", "--unsafe-proto", "x.ts"],
    svec!["deno", "install", "--production", "pkg"],
    svec!["deno", "ci", "--production"],
    svec!["deno", "fmt", "--options-use-tabs", "x.ts"],
    svec!["deno", "jupyter", "-d", "deno", "--install"],
    svec!["deno", "pack", "-o", "out", "x.ts"],
  ];
  for args in ok {
    assert!(
      flags_from_vec(args.clone()).is_ok(),
      "expected ok for {args:?}"
    );
  }
}

#[test]
fn tier3_conflicts() {
  let bad = [
    svec!["deno", "doc", "--html", "--json", "x.ts"],
    svec!["deno", "doc", "--filter=x", "--json", "x.ts"],
    svec!["deno", "run", "--coverage=c", "--inspect", "x.ts"],
    svec!["deno", "test", "--watch", "--no-run"],
    svec!["deno", "lint", "--rules-include=x", "--rules"],
    svec!["deno", "outdated", "--latest", "--compatible"],
    svec!["deno", "transpile", "--output=o", "--outdir=d", "x.ts"],
    svec!["deno", "x", "--install-alias=dx", "script.ts"],
    svec!["deno", "install", "--npm", "--jsr", "pkg"],
    svec!["deno", "install", "--global", "--dev", "pkg"],
    svec!["deno", "bump-version", "-c", "cfg", "--workspace"],
  ];
  for args in bad {
    assert!(
      flags_from_vec(args.clone()).is_err(),
      "expected conflict error for {args:?}"
    );
  }
}

#[test]
fn tier3_requires() {
  let bad = [
    // fmt --fail-fast requires --check
    svec!["deno", "fmt", "--fail-fast", "x.ts"],
    // doc html-only decorations require --html
    svec!["deno", "doc", "--category-docs=c", "x.ts"],
    // outdated --interactive requires --update
    svec!["deno", "outdated", "--interactive"],
    // coverage --output requires --lcov
    svec!["deno", "coverage", "--output=o", "cov"],
    // install --name requires --global
    svec!["deno", "install", "--name", "foo", "pkg"],
  ];
  for args in bad {
    assert!(
      flags_from_vec(args.clone()).is_err(),
      "expected requires error for {args:?}"
    );
  }
  // The dependency satisfied → ok.
  assert!(
    flags_from_vec(svec!["deno", "fmt", "--fail-fast", "--check", "x.ts"])
      .is_ok()
  );
  assert!(
    flags_from_vec(svec![
      "deno", "install", "--global", "--name", "foo", "pkg"
    ])
    .is_ok()
  );
}

// ---------------------------------------------------------------------------
// PR5 cutover: link / unlink subcommands (newly wired into the parser).
// ---------------------------------------------------------------------------

#[test]
fn link_subcommand() {
  let flags =
    flags_from_vec(svec!["deno", "link", "../a", "../b", "--lockfile-only"])
      .unwrap();
  assert_eq!(
    flags.subcommand,
    DenoSubcommand::Link(LinkFlags {
      paths: svec!["../a", "../b"],
      lockfile_only: true,
    })
  );

  // lock args flow through to the top-level flags.
  let flags =
    flags_from_vec(svec!["deno", "link", "../a", "--frozen"]).unwrap();
  assert_eq!(flags.frozen_lockfile, Some(true));
  assert!(matches!(
    flags.subcommand,
    DenoSubcommand::Link(LinkFlags {
      lockfile_only: false,
      ..
    })
  ));

  // at least one path is required.
  assert!(flags_from_vec(svec!["deno", "link"]).is_err());
}

#[test]
fn unlink_subcommand() {
  let flags =
    flags_from_vec(svec!["deno", "unlink", "@scope/name", "../c"]).unwrap();
  assert_eq!(
    flags.subcommand,
    DenoSubcommand::Unlink(UnlinkFlags {
      names_or_paths: svec!["@scope/name", "../c"],
      lockfile_only: false,
    })
  );

  assert!(flags_from_vec(svec!["deno", "unlink"]).is_err());
}

#[test]
fn eval_double_dash_stripped() {
  // Unlike run/task, `deno eval` strips the `--` separator from argv
  // (clap plain trailing-var-arg, not `.last(true)`).
  let flags =
    flags_from_vec(svec!["deno", "eval", "console.log(1)", "--", "a&b"])
      .unwrap();
  assert_eq!(flags.argv, svec!["a&b"]);

  // run keeps it, for contrast.
  let flags =
    flags_from_vec(svec!["deno", "run", "x.ts", "--", "a&b"]).unwrap();
  assert_eq!(flags.argv, svec!["--", "a&b"]);
}

#[test]
fn sync_types_subcommand() {
  let flags = flags_from_vec(svec!["deno", "sync-types"]).unwrap();
  assert_eq!(
    flags.subcommand,
    DenoSubcommand::SyncTypes(SyncTypesFlags::default())
  );

  let flags =
    flags_from_vec(svec!["deno", "sync-types", "a.ts", "tools/"]).unwrap();
  assert_eq!(
    flags.subcommand,
    DenoSubcommand::SyncTypes(SyncTypesFlags {
      roots: svec!["a.ts", "tools/"],
    })
  );

  // --allow-import flows into permissions.
  let flags =
    flags_from_vec(svec!["deno", "sync-types", "--allow-import=example.com"])
      .unwrap();
  assert_eq!(flags.permissions.allow_import, Some(svec!["example.com"]));
}

#[test]
fn serve_compile_keep_double_dash() {
  // serve/compile use clap's trailing_var_arg (like run), so they keep `--`.
  let flags =
    flags_from_vec(svec!["deno", "serve", "x.ts", "--", "-a"]).unwrap();
  assert_eq!(flags.argv, svec!["--", "-a"]);

  let flags =
    flags_from_vec(svec!["deno", "compile", "x.ts", "--", "-a"]).unwrap();
  assert!(
    matches!(flags.subcommand, DenoSubcommand::Compile(c) if c.args == svec!["--", "-a"])
  );
}

#[test]
fn upgrade_no_delta() {
  let r = flags_from_vec(svec!["deno", "upgrade", "--no-delta"]);
  assert_eq!(
    r.unwrap(),
    Flags {
      subcommand: DenoSubcommand::Upgrade(UpgradeFlags {
        force: false,
        dry_run: false,
        canary: false,
        no_delta: true,
        release_candidate: false,
        version: None,
        output: None,
        version_or_hash_or_channel: None,
        checksum: None,
        pr: None,
        branch: None,
      }),
      ..Flags::default()
    }
  );
}

#[test]
fn hrtime_flags_are_accepted_as_no_ops() {
  // Removed in Deno 2, but still parsed so we can warn instead of erroring.
  for flag in ["--allow-hrtime", "--deny-hrtime"] {
    let r = flags_from_vec(svec!["deno", "run", flag, "script.ts"]);
    let flags = r.unwrap();
    assert_eq!(
      flags.subcommand,
      DenoSubcommand::Run(RunFlags::new_default("script.ts".to_string()))
    );
  }
}

#[test]
fn use_env_proxy_flags() {
  let r = flags_from_vec(svec!["deno", "run", "--use-env-proxy", "script.ts"]);
  assert!(r.is_ok());

  let r =
    flags_from_vec(svec!["deno", "run", "--no-use-env-proxy", "script.ts"]);
  assert!(r.is_ok());

  let r = flags_from_vec(svec![
    "deno",
    "run",
    "--use-env-proxy",
    "--no-use-env-proxy",
    "script.ts"
  ]);
  assert!(r.is_err());
}
