mirror of
https://github.com/martinvonz/jj.git
synced 2025-05-16 20:54:27 +00:00
We haven't had any reports of problems from people who opted in. Since it's early in the release cycle now, let's now test it on everyone who builds from head, so we get almost a month of testing from those people before it's enabled by default in a released version. This impacts lots of test cases because the change-id header is added to the Git commit. Most are uninteresting. `test_git_fetch` now sees some divergent changes where it used to see only divergent bookmarks, which makes sense.
1560 lines
45 KiB
Rust
1560 lines
45 KiB
Rust
// Copyright 2022 The Jujutsu Authors
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// https://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
use std::env::join_paths;
|
||
use std::path::PathBuf;
|
||
|
||
use indoc::indoc;
|
||
use regex::Regex;
|
||
|
||
use crate::common::fake_editor_path;
|
||
use crate::common::force_interactive;
|
||
use crate::common::to_toml_value;
|
||
use crate::common::TestEnvironment;
|
||
|
||
#[test]
|
||
fn test_config_list_single() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
[test-table]
|
||
somekey = "some value"
|
||
"#,
|
||
);
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-table.somekey"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-table.somekey = "some value"
|
||
[EOF]
|
||
"#);
|
||
|
||
let output = test_env.run_jj_in(
|
||
".",
|
||
["config", "list", r#"-Tname ++ "\n""#, "test-table.somekey"],
|
||
);
|
||
insta::assert_snapshot!(output, @r"
|
||
test-table.somekey
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_nonexistent() {
|
||
let test_env = TestEnvironment::default();
|
||
let output = test_env.run_jj_in(".", ["config", "list", "nonexistent-test-key"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Warning: No matching config key for nonexistent-test-key
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_table() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
[test-table]
|
||
x = true
|
||
y.foo = "abc"
|
||
y.bar = 123
|
||
"z"."with space"."function()" = 5
|
||
"#,
|
||
);
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-table"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-table.x = true
|
||
test-table.y.foo = "abc"
|
||
test-table.y.bar = 123
|
||
test-table.z."with space"."function()" = 5
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_inline_table() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
test-table = { x = true, y = 1 }
|
||
"#,
|
||
);
|
||
// Inline tables are expanded
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-table"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
test-table.x = true
|
||
test-table.y = 1
|
||
[EOF]
|
||
");
|
||
// Inner value can also be addressed by a dotted name path
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-table.x"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
test-table.x = true
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_array() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
test-array = [1, "b", 3.4]
|
||
"#,
|
||
);
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-array"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-array = [1, "b", 3.4]
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_array_of_tables() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
[[test-table]]
|
||
x = 1
|
||
[[test-table]]
|
||
y = ["z"]
|
||
z."key=with whitespace" = []
|
||
"#,
|
||
);
|
||
// Array is a value, so is array of tables
|
||
let output = test_env.run_jj_in(".", ["config", "list", "test-table"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-table = [{ x = 1 }, { y = ["z"], z = { "key=with whitespace" = [] } }]
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_all() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
test-val = [1, 2, 3]
|
||
[test-table]
|
||
x = true
|
||
y.foo = "abc"
|
||
y.bar = 123
|
||
"#,
|
||
);
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "list"]);
|
||
insta::assert_snapshot!(
|
||
output.normalize_stdout_with(|s| find_stdout_lines(r"(test-val|test-table\b[^=]*)", &s)), @r#"
|
||
test-val = [1, 2, 3]
|
||
test-table.x = true
|
||
test-table.y.foo = "abc"
|
||
test-table.y.bar = 123
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_multiline_string() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
multiline = '''
|
||
foo
|
||
bar
|
||
'''
|
||
"#,
|
||
);
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "list", "multiline"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
multiline = '''
|
||
foo
|
||
bar
|
||
'''
|
||
[EOF]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(
|
||
".",
|
||
[
|
||
"config",
|
||
"list",
|
||
"multiline",
|
||
"--include-overridden",
|
||
"--config=multiline='single'",
|
||
],
|
||
);
|
||
insta::assert_snapshot!(output, @r"
|
||
# multiline = '''
|
||
# foo
|
||
# bar
|
||
# '''
|
||
multiline = 'single'
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_layer() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
// User
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-key", "test-val"])
|
||
.success();
|
||
|
||
work_dir
|
||
.run_jj([
|
||
"config",
|
||
"set",
|
||
"--user",
|
||
"test-layered-key",
|
||
"test-original-val",
|
||
])
|
||
.success();
|
||
|
||
let output = work_dir.run_jj(["config", "list", "--user"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
test-key = "test-val"
|
||
test-layered-key = "test-original-val"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Repo
|
||
work_dir
|
||
.run_jj([
|
||
"config",
|
||
"set",
|
||
"--repo",
|
||
"test-layered-key",
|
||
"test-layered-val",
|
||
])
|
||
.success();
|
||
|
||
let output = work_dir.run_jj(["config", "list", "--user"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-key = "test-val"
|
||
[EOF]
|
||
"#);
|
||
|
||
let output = work_dir.run_jj(["config", "list", "--repo"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
test-layered-key = "test-layered-val"
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_list_origin() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
// User
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-key", "test-val"])
|
||
.success();
|
||
|
||
work_dir
|
||
.run_jj([
|
||
"config",
|
||
"set",
|
||
"--user",
|
||
"test-layered-key",
|
||
"test-original-val",
|
||
])
|
||
.success();
|
||
|
||
// Repo
|
||
work_dir
|
||
.run_jj([
|
||
"config",
|
||
"set",
|
||
"--repo",
|
||
"test-layered-key",
|
||
"test-layered-val",
|
||
])
|
||
.success();
|
||
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
"-Tbuiltin_config_list_detailed",
|
||
"--config",
|
||
"test-cli-key=test-cli-val",
|
||
]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
test-key = "test-val" # user $TEST_ENV/config/config.toml
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json" # repo $TEST_ENV/repo/.jj/repo/config.toml
|
||
test-layered-key = "test-layered-val" # repo $TEST_ENV/repo/.jj/repo/config.toml
|
||
user.name = "Test User" # env
|
||
user.email = "test.user@example.com" # env
|
||
debug.commit-timestamp = "2001-02-03T04:05:11+07:00" # env
|
||
debug.randomness-seed = 5 # env
|
||
debug.operation-timestamp = "2001-02-03T04:05:11+07:00" # env
|
||
operation.hostname = "host.example.com" # env
|
||
operation.username = "test-username" # env
|
||
test-cli-key = "test-cli-val" # cli
|
||
[EOF]
|
||
"#);
|
||
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
"-Tbuiltin_config_list_detailed",
|
||
"--color=debug",
|
||
"--include-defaults",
|
||
"--include-overridden",
|
||
"--config=test-key=test-cli-val",
|
||
"test-key",
|
||
]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
[38;5;8m<<config_list overridden name::# test-key>><<config_list overridden:: = >><<config_list overridden value::"test-val">><<config_list overridden:: # >><<config_list overridden source::user>><<config_list overridden:: >><<config_list overridden path::$TEST_ENV/config/config.toml>><<config_list overridden::>>[39m
|
||
[38;5;2m<<config_list name::test-key>>[39m<<config_list:: = >>[38;5;3m<<config_list value::"test-cli-val">>[39m<<config_list:: # >>[38;5;4m<<config_list source::cli>>[39m<<config_list::>>
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_layer_override_default() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
let config_key = "merge-tools.vimdiff.program";
|
||
|
||
// Default
|
||
let output = work_dir.run_jj(["config", "list", config_key, "--include-defaults"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
merge-tools.vimdiff.program = "vim"
|
||
[EOF]
|
||
"#);
|
||
|
||
// User
|
||
test_env.add_config(format!(
|
||
"{config_key} = {value}\n",
|
||
value = to_toml_value("user")
|
||
));
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
merge-tools.vimdiff.program = "user"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Repo
|
||
work_dir.write_file(
|
||
".jj/repo/config.toml",
|
||
format!("{config_key} = {value}\n", value = to_toml_value("repo")),
|
||
);
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
merge-tools.vimdiff.program = "repo"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Command argument
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
config_key,
|
||
"--config",
|
||
&format!("{config_key}={value}", value = to_toml_value("command-arg")),
|
||
]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
merge-tools.vimdiff.program = "command-arg"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Allow printing overridden values
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
config_key,
|
||
"--include-overridden",
|
||
"--config",
|
||
&format!("{config_key}={value}", value = to_toml_value("command-arg")),
|
||
]);
|
||
insta::assert_snapshot!(output, @r##"
|
||
# merge-tools.vimdiff.program = "user"
|
||
# merge-tools.vimdiff.program = "repo"
|
||
merge-tools.vimdiff.program = "command-arg"
|
||
[EOF]
|
||
"##);
|
||
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
"--color=always",
|
||
config_key,
|
||
"--include-overridden",
|
||
]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
[38;5;8m# merge-tools.vimdiff.program = "user"[39m
|
||
[38;5;2mmerge-tools.vimdiff.program[39m = [38;5;3m"repo"[39m
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_layer_override_env() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let config_key = "ui.editor";
|
||
|
||
// Environment base
|
||
test_env.add_env_var("EDITOR", "env-base");
|
||
let work_dir = test_env.work_dir("repo");
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "env-base"
|
||
[EOF]
|
||
"#);
|
||
|
||
// User
|
||
test_env.add_config(format!(
|
||
"{config_key} = {value}\n",
|
||
value = to_toml_value("user")
|
||
));
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "user"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Repo
|
||
work_dir.write_file(
|
||
".jj/repo/config.toml",
|
||
format!("{config_key} = {value}\n", value = to_toml_value("repo")),
|
||
);
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "repo"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Environment override
|
||
test_env.add_env_var("JJ_EDITOR", "env-override");
|
||
let work_dir = test_env.work_dir("repo");
|
||
let output = work_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "env-override"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Command argument
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
config_key,
|
||
"--config",
|
||
&format!("{config_key}={value}", value = to_toml_value("command-arg")),
|
||
]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "command-arg"
|
||
[EOF]
|
||
"#);
|
||
|
||
// Allow printing overridden values
|
||
let output = work_dir.run_jj([
|
||
"config",
|
||
"list",
|
||
config_key,
|
||
"--include-overridden",
|
||
"--config",
|
||
&format!("{config_key}={value}", value = to_toml_value("command-arg")),
|
||
]);
|
||
insta::assert_snapshot!(output, @r##"
|
||
# ui.editor = "env-base"
|
||
# ui.editor = "user"
|
||
# ui.editor = "repo"
|
||
# ui.editor = "env-override"
|
||
ui.editor = "command-arg"
|
||
[EOF]
|
||
"##);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_layer_workspace() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "main"]).success();
|
||
let main_dir = test_env.work_dir("main");
|
||
let secondary_dir = test_env.work_dir("secondary");
|
||
let config_key = "ui.editor";
|
||
|
||
main_dir.write_file("file", "contents");
|
||
main_dir.run_jj(["new"]).success();
|
||
main_dir
|
||
.run_jj(["workspace", "add", "--name", "second", "../secondary"])
|
||
.success();
|
||
|
||
// Repo
|
||
main_dir.write_file(
|
||
".jj/repo/config.toml",
|
||
format!(
|
||
"{config_key} = {value}\n",
|
||
value = to_toml_value("main-repo")
|
||
),
|
||
);
|
||
let output = main_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "main-repo"
|
||
[EOF]
|
||
"#);
|
||
let output = secondary_dir.run_jj(["config", "list", config_key]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
ui.editor = "main-repo"
|
||
[EOF]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_bad_opts() {
|
||
let test_env = TestEnvironment::default();
|
||
let output = test_env.run_jj_in(".", ["config", "set"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: the following required arguments were not provided:
|
||
<--user|--repo>
|
||
<NAME>
|
||
<VALUE>
|
||
|
||
Usage: jj config set <--user|--repo> <NAME> <VALUE>
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "set", "--user", "", "x"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: invalid value '' for '<NAME>': TOML parse error at line 1, column 1
|
||
|
|
||
1 |
|
||
| ^
|
||
invalid key
|
||
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "set", "--user", "x", "['typo'}"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: invalid value '['typo'}' for '<VALUE>': TOML parse error at line 1, column 8
|
||
|
|
||
1 | ['typo'}
|
||
| ^
|
||
invalid array
|
||
expected `]`
|
||
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_for_user() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-key", "test-val"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.foo", "true"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.'bar()'", "0"])
|
||
.success();
|
||
|
||
// Ensure test-key successfully written to user config.
|
||
let user_config_toml = std::fs::read_to_string(&user_config_path)
|
||
.unwrap_or_else(|_| panic!("Failed to read file {}", user_config_path.display()));
|
||
insta::assert_snapshot!(user_config_toml, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
test-key = "test-val"
|
||
|
||
[test-table]
|
||
foo = true
|
||
'bar()' = 0
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_for_user_directory() {
|
||
let test_env = TestEnvironment::default();
|
||
|
||
test_env
|
||
.run_jj_in(".", ["config", "set", "--user", "test-key", "test-val"])
|
||
.success();
|
||
insta::assert_snapshot!(
|
||
std::fs::read_to_string(test_env.last_config_file_path()).unwrap(),
|
||
@r#"
|
||
test-key = "test-val"
|
||
|
||
[template-aliases]
|
||
'format_time_range(time_range)' = 'time_range.start() ++ " - " ++ time_range.end()'
|
||
"#);
|
||
|
||
// Add one more config file to the directory
|
||
test_env.add_config("");
|
||
let output = test_env.run_jj_in(
|
||
".",
|
||
["config", "set", "--user", "test-key", "test-other-val"],
|
||
);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
1: $TEST_ENV/config/config0001.toml
|
||
2: $TEST_ENV/config/config0002.toml
|
||
Choose a config file (default 1): 1
|
||
[EOF]
|
||
");
|
||
|
||
insta::assert_snapshot!(
|
||
std::fs::read_to_string(test_env.first_config_file_path()).unwrap(),
|
||
@r#"
|
||
test-key = "test-other-val"
|
||
|
||
[template-aliases]
|
||
'format_time_range(time_range)' = 'time_range.start() ++ " - " ++ time_range.end()'
|
||
"#);
|
||
|
||
insta::assert_snapshot!(
|
||
std::fs::read_to_string(test_env.last_config_file_path()).unwrap(),
|
||
@"");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_for_repo() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
work_dir
|
||
.run_jj(["config", "set", "--repo", "test-key", "test-val"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--repo", "test-table.foo", "true"])
|
||
.success();
|
||
// Ensure test-key successfully written to user config.
|
||
let repo_config_toml = work_dir.read_file(".jj/repo/config.toml");
|
||
insta::assert_snapshot!(repo_config_toml, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
test-key = "test-val"
|
||
|
||
[test-table]
|
||
foo = true
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_toml_types() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
let set_value = |key, value| {
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", key, value])
|
||
.success();
|
||
};
|
||
set_value("test-table.integer", "42");
|
||
set_value("test-table.float", "3.14");
|
||
set_value("test-table.array", r#"["one", "two"]"#);
|
||
set_value("test-table.boolean", "true");
|
||
set_value("test-table.string", r#""foo""#);
|
||
set_value("test-table.invalid", r"a + b");
|
||
insta::assert_snapshot!(std::fs::read_to_string(&user_config_path).unwrap(), @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
|
||
[test-table]
|
||
integer = 42
|
||
float = 3.14
|
||
array = ["one", "two"]
|
||
boolean = true
|
||
string = "foo"
|
||
invalid = "a + b"
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_type_mismatch() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.foo", "test-val"])
|
||
.success();
|
||
let output = work_dir.run_jj(["config", "set", "--user", "test-table", "not-a-table"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Error: Failed to set test-table
|
||
Caused by: Would overwrite entire table test-table
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
// But it's fine to overwrite arrays and inline tables
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.array", "[1,2,3]"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.array", "[4,5,6]"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.inline", "{ x = 42}"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-table.inline", "42"])
|
||
.success();
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_set_nontable_parent() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "test-nontable", "test-val"])
|
||
.success();
|
||
let output = work_dir.run_jj(["config", "set", "--user", "test-nontable.foo", "test-val"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Error: Failed to set test-nontable.foo
|
||
Caused by: Would overwrite non-table value with parent table test-nontable
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_unset_non_existent_key() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
let output = work_dir.run_jj(["config", "unset", "--user", "nonexistent"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
------- stderr -------
|
||
Error: "nonexistent" doesn't exist
|
||
[EOF]
|
||
[exit status: 1]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_unset_inline_table_key() {
|
||
let mut test_env = TestEnvironment::default();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "inline-table", "{ foo = true }"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "unset", "--user", "inline-table.foo"])
|
||
.success();
|
||
let user_config_toml = std::fs::read_to_string(&user_config_path).unwrap();
|
||
insta::assert_snapshot!(user_config_toml, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
inline-table = {}
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_unset_table_like() {
|
||
let mut test_env = TestEnvironment::default();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
|
||
std::fs::write(
|
||
&user_config_path,
|
||
indoc! {b"
|
||
inline-table = { foo = true }
|
||
[non-inline-table]
|
||
foo = true
|
||
"},
|
||
)
|
||
.unwrap();
|
||
|
||
// Inline table is syntactically a "value", so it can be deleted.
|
||
test_env
|
||
.run_jj_in(".", ["config", "unset", "--user", "inline-table"])
|
||
.success();
|
||
// Non-inline table cannot be deleted.
|
||
let output = test_env.run_jj_in(".", ["config", "unset", "--user", "non-inline-table"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Error: Failed to unset non-inline-table
|
||
Caused by: Would delete entire table non-inline-table
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
let user_config_toml = std::fs::read_to_string(&user_config_path).unwrap();
|
||
insta::assert_snapshot!(user_config_toml, @r"
|
||
[non-inline-table]
|
||
foo = true
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_unset_for_user() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.config_path().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "foo", "true"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "unset", "--user", "foo"])
|
||
.success();
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "table.foo", "true"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "unset", "--user", "table.foo"])
|
||
.success();
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "table.inline", "{ foo = true }"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "unset", "--user", "table.inline"])
|
||
.success();
|
||
|
||
let user_config_toml = std::fs::read_to_string(&user_config_path).unwrap();
|
||
insta::assert_snapshot!(user_config_toml, @r#"
|
||
"$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
|
||
|
||
[table]
|
||
"#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_unset_for_repo() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--repo", "test-key", "test-val"])
|
||
.success();
|
||
work_dir
|
||
.run_jj(["config", "unset", "--repo", "test-key"])
|
||
.success();
|
||
|
||
let repo_config_toml = work_dir.read_file(".jj/repo/config.toml");
|
||
insta::assert_snapshot!(repo_config_toml, @r#""$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json""#);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_missing_opt() {
|
||
let test_env = TestEnvironment::default();
|
||
let output = test_env.run_jj_in(".", ["config", "edit"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: the following required arguments were not provided:
|
||
<--user|--repo>
|
||
|
||
Usage: jj config edit <--user|--repo>
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_user() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Remove one of the config file to disambiguate
|
||
std::fs::remove_file(test_env.last_config_file_path()).unwrap();
|
||
let edit_script = test_env.set_up_fake_editor();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
std::fs::write(edit_script, "dump-path path").unwrap();
|
||
work_dir.run_jj(["config", "edit", "--user"]).success();
|
||
|
||
let edited_path =
|
||
PathBuf::from(std::fs::read_to_string(test_env.env_root().join("path")).unwrap());
|
||
assert_eq!(
|
||
edited_path,
|
||
dunce::simplified(&test_env.last_config_file_path())
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_user_new_file() {
|
||
let mut test_env = TestEnvironment::default();
|
||
let user_config_path = test_env.config_path().join("config").join("file.toml");
|
||
test_env.set_up_fake_editor(); // set $EDIT_SCRIPT, but added configuration is ignored
|
||
test_env.add_env_var("EDITOR", fake_editor_path());
|
||
test_env.set_config_path(&user_config_path);
|
||
assert!(!user_config_path.exists());
|
||
|
||
test_env
|
||
.run_jj_in(".", ["config", "edit", "--user"])
|
||
.success();
|
||
assert!(
|
||
user_config_path.exists(),
|
||
"new file and directory should be created"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_repo() {
|
||
let mut test_env = TestEnvironment::default();
|
||
let edit_script = test_env.set_up_fake_editor();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
let repo_config_path = work_dir
|
||
.root()
|
||
.join(PathBuf::from_iter([".jj", "repo", "config.toml"]));
|
||
assert!(!repo_config_path.exists());
|
||
|
||
std::fs::write(edit_script, "dump-path path").unwrap();
|
||
work_dir.run_jj(["config", "edit", "--repo"]).success();
|
||
|
||
let edited_path =
|
||
PathBuf::from(std::fs::read_to_string(test_env.env_root().join("path")).unwrap());
|
||
assert_eq!(edited_path, dunce::simplified(&repo_config_path));
|
||
assert!(repo_config_path.exists(), "new file should be created");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_invalid_config() {
|
||
let mut test_env = TestEnvironment::default();
|
||
let edit_script = test_env.set_up_fake_editor();
|
||
|
||
// Test re-edit
|
||
std::fs::write(
|
||
&edit_script,
|
||
"write\ninvalid config here\0next invocation\n\0write\ntest=\"success\"",
|
||
)
|
||
.unwrap();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
let output = work_dir.run_jj_with(|cmd| {
|
||
force_interactive(cmd)
|
||
.args(["config", "edit", "--repo"])
|
||
.write_stdin("Y\n")
|
||
});
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Warning: An error has been found inside the config:
|
||
Caused by:
|
||
1: Configuration cannot be parsed as TOML document
|
||
2: TOML parse error at line 1, column 9
|
||
|
|
||
1 | invalid config here
|
||
| ^
|
||
expected `.`, `=`
|
||
|
||
Do you want to keep editing the file? If not, previous config will be restored. (Yn): [EOF]
|
||
");
|
||
|
||
let output = work_dir.run_jj(["config", "get", "test"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
success
|
||
[EOF]"
|
||
);
|
||
|
||
// Test the restore previous config
|
||
std::fs::write(&edit_script, "write\ninvalid config here").unwrap();
|
||
let work_dir = test_env.work_dir("repo");
|
||
let output = work_dir.run_jj_with(|cmd| {
|
||
force_interactive(cmd)
|
||
.args(["config", "edit", "--repo"])
|
||
.write_stdin("n\n")
|
||
});
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Warning: An error has been found inside the config:
|
||
Caused by:
|
||
1: Configuration cannot be parsed as TOML document
|
||
2: TOML parse error at line 1, column 9
|
||
|
|
||
1 | invalid config here
|
||
| ^
|
||
expected `.`, `=`
|
||
|
||
Do you want to keep editing the file? If not, previous config will be restored. (Yn): [EOF]
|
||
");
|
||
|
||
let output = work_dir.run_jj(["config", "get", "test"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
success
|
||
[EOF]"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_path() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
let user_config_path = test_env.env_root().join("config.toml");
|
||
let repo_config_path = work_dir
|
||
.root()
|
||
.join(PathBuf::from_iter([".jj", "repo", "config.toml"]));
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
insta::assert_snapshot!(work_dir.run_jj(["config", "path", "--user"]), @r"
|
||
$TEST_ENV/config.toml
|
||
[EOF]
|
||
");
|
||
assert!(
|
||
!user_config_path.exists(),
|
||
"jj config path shouldn't create new file"
|
||
);
|
||
|
||
insta::assert_snapshot!(work_dir.run_jj(["config", "path", "--repo"]), @r"
|
||
$TEST_ENV/repo/.jj/repo/config.toml
|
||
[EOF]
|
||
");
|
||
assert!(
|
||
!repo_config_path.exists(),
|
||
"jj config path shouldn't create new file"
|
||
);
|
||
|
||
insta::assert_snapshot!(test_env.run_jj_in(".", ["config", "path", "--repo"]), @r"
|
||
------- stderr -------
|
||
Error: No repo config path found
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_path_multiple() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let config_path = test_env.config_path().join("config.toml");
|
||
let work_config_path = test_env.config_path().join("conf.d");
|
||
let user_config_path = join_paths([config_path, work_config_path]).unwrap();
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
insta::assert_snapshot!(work_dir.run_jj(["config", "path", "--user"]), @r"
|
||
$TEST_ENV/config/config.toml
|
||
$TEST_ENV/config/conf.d
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_only_loads_toml_files() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.set_up_fake_editor();
|
||
std::fs::File::create(test_env.config_path().join("is-not.loaded")).unwrap();
|
||
insta::assert_snapshot!(test_env.run_jj_in(".", ["config", "edit", "--user"]), @r"
|
||
------- stderr -------
|
||
1: $TEST_ENV/config/config0001.toml
|
||
2: $TEST_ENV/config/config0002.toml
|
||
Choose a config file (default 1): 1
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_edit_repo_outside_repo() {
|
||
let test_env = TestEnvironment::default();
|
||
let output = test_env.run_jj_in(".", ["config", "edit", "--repo"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Error: No repo config path found to edit
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_get() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
[table]
|
||
string = "some value 1"
|
||
int = 123
|
||
list = ["list", "value"]
|
||
overridden = "foo"
|
||
"#,
|
||
);
|
||
test_env.add_config(
|
||
r#"
|
||
[table]
|
||
overridden = "bar"
|
||
"#,
|
||
);
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "nonexistent"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Config error: Value not found for nonexistent
|
||
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "table.string"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
some value 1
|
||
[EOF]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "table.int"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
123
|
||
[EOF]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "table.list"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Config error: Invalid type or value for table.list
|
||
Caused by: Expected a value convertible to a string, but is an array
|
||
Hint: Check the config file: $TEST_ENV/config/config0002.toml
|
||
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "table"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Config error: Invalid type or value for table
|
||
Caused by: Expected a value convertible to a string, but is a table
|
||
Hint: Check the config file: $TEST_ENV/config/config0003.toml
|
||
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "table.overridden"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
bar
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_path_syntax() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.add_config(
|
||
r#"
|
||
a.'b()' = 0
|
||
'b c'.d = 1
|
||
'b c'.e.'f[]' = 2
|
||
- = 3
|
||
_ = 4
|
||
'.' = 5
|
||
"#,
|
||
);
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "list", "a.'b()'"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
a.'b()' = 0
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "list", "'b c'"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
'b c'.d = 1
|
||
'b c'.e."f[]" = 2
|
||
[EOF]
|
||
"#);
|
||
let output = test_env.run_jj_in(".", ["config", "list", "'b c'.d"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
'b c'.d = 1
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "list", "'b c'.e.'f[]'"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
'b c'.e.'f[]' = 2
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "get", "'b c'.e.'f[]'"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
2
|
||
[EOF]
|
||
");
|
||
|
||
// Not a table
|
||
let output = test_env.run_jj_in(".", ["config", "list", "a.'b()'.x"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Warning: No matching config key for a.'b()'.x
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "get", "a.'b()'.x"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Config error: Value not found for a.'b()'.x
|
||
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
// "-" and "_" are valid TOML keys
|
||
let output = test_env.run_jj_in(".", ["config", "list", "-"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
- = 3
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "list", "_"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
_ = 4
|
||
[EOF]
|
||
");
|
||
|
||
// "." requires quoting
|
||
let output = test_env.run_jj_in(".", ["config", "list", "'.'"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
'.' = 5
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "get", "'.'"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
5
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "get", "."]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: invalid value '.' for '<NAME>': TOML parse error at line 1, column 1
|
||
|
|
||
1 | .
|
||
| ^
|
||
invalid key
|
||
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
|
||
// Invalid TOML keys
|
||
let output = test_env.run_jj_in(".", ["config", "list", "b c"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: invalid value 'b c' for '[NAME]': TOML parse error at line 1, column 3
|
||
|
|
||
1 | b c
|
||
| ^
|
||
|
||
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "list", ""]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
error: invalid value '' for '[NAME]': TOML parse error at line 1, column 1
|
||
|
|
||
1 |
|
||
| ^
|
||
invalid key
|
||
|
||
|
||
For more information, try '--help'.
|
||
[EOF]
|
||
[exit status: 2]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
#[cfg_attr(windows, ignore = "dirs::home_dir() can't be overridden by $HOME")] // TODO
|
||
fn test_config_conditional() {
|
||
let mut test_env = TestEnvironment::default();
|
||
let home_dir = test_env.work_dir(test_env.home_dir());
|
||
home_dir.run_jj(["git", "init", "repo1"]).success();
|
||
home_dir.run_jj(["git", "init", "repo2"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.env_root().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
std::fs::write(
|
||
&user_config_path,
|
||
indoc! {"
|
||
foo = 'global'
|
||
baz = 'global'
|
||
qux = 'global'
|
||
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo1']
|
||
foo = 'repo1'
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo2']
|
||
foo = 'repo2'
|
||
|
||
[[--scope]]
|
||
--when.commands = ['config']
|
||
baz = 'config'
|
||
[[--scope]]
|
||
--when.commands = ['config get']
|
||
qux = 'get'
|
||
[[--scope]]
|
||
--when.commands = ['config list']
|
||
qux = 'list'
|
||
"},
|
||
)
|
||
.unwrap();
|
||
let home_dir = test_env.work_dir(test_env.home_dir());
|
||
let work_dir1 = home_dir.dir("repo1");
|
||
let work_dir2 = home_dir.dir("repo2");
|
||
|
||
// get and list should refer to the resolved config
|
||
let output = test_env.run_jj_in(".", ["config", "get", "foo"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
global
|
||
[EOF]
|
||
");
|
||
let output = work_dir1.run_jj(["config", "get", "foo"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
repo1
|
||
[EOF]
|
||
");
|
||
// baz should be the same for `jj config get` and `jj config list`
|
||
// qux should be different
|
||
let output = work_dir1.run_jj(["config", "get", "baz"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
config
|
||
[EOF]
|
||
");
|
||
let output = work_dir1.run_jj(["config", "get", "qux"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
get
|
||
[EOF]
|
||
");
|
||
let output = test_env.run_jj_in(".", ["config", "list", "--user"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
foo = 'global'
|
||
baz = 'config'
|
||
qux = 'list'
|
||
[EOF]
|
||
");
|
||
let output = work_dir1.run_jj(["config", "list", "--user"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
foo = 'repo1'
|
||
baz = 'config'
|
||
qux = 'list'
|
||
[EOF]
|
||
");
|
||
let output = work_dir2.run_jj(["config", "list", "--user"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
foo = 'repo2'
|
||
baz = 'config'
|
||
qux = 'list'
|
||
[EOF]
|
||
");
|
||
|
||
// relative workspace path
|
||
let output = work_dir2.run_jj(["config", "list", "--user", "-R../repo1"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
foo = 'repo1'
|
||
baz = 'config'
|
||
qux = 'list'
|
||
[EOF]
|
||
");
|
||
|
||
// set and unset should refer to the source config
|
||
// (there's no option to update scoped table right now.)
|
||
let output = test_env.run_jj_in(".", ["config", "set", "--user", "bar", "new value"]);
|
||
insta::assert_snapshot!(output, @"");
|
||
insta::assert_snapshot!(std::fs::read_to_string(&user_config_path).unwrap(), @r#"
|
||
foo = 'global'
|
||
baz = 'global'
|
||
qux = 'global'
|
||
bar = "new value"
|
||
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo1']
|
||
foo = 'repo1'
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo2']
|
||
foo = 'repo2'
|
||
|
||
[[--scope]]
|
||
--when.commands = ['config']
|
||
baz = 'config'
|
||
[[--scope]]
|
||
--when.commands = ['config get']
|
||
qux = 'get'
|
||
[[--scope]]
|
||
--when.commands = ['config list']
|
||
qux = 'list'
|
||
"#);
|
||
let output = work_dir1.run_jj(["config", "unset", "--user", "foo"]);
|
||
insta::assert_snapshot!(output, @"");
|
||
insta::assert_snapshot!(std::fs::read_to_string(&user_config_path).unwrap(), @r#"
|
||
baz = 'global'
|
||
qux = 'global'
|
||
bar = "new value"
|
||
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo1']
|
||
foo = 'repo1'
|
||
[[--scope]]
|
||
--when.repositories = ['~/repo2']
|
||
foo = 'repo2'
|
||
|
||
[[--scope]]
|
||
--when.commands = ['config']
|
||
baz = 'config'
|
||
[[--scope]]
|
||
--when.commands = ['config get']
|
||
qux = 'get'
|
||
[[--scope]]
|
||
--when.commands = ['config list']
|
||
qux = 'list'
|
||
"#);
|
||
}
|
||
|
||
// Minimal test for Windows where the home directory can't be switched.
|
||
// (Can be removed if test_config_conditional() is enabled on Windows.)
|
||
#[test]
|
||
fn test_config_conditional_without_home_dir() {
|
||
let mut test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
// Test with fresh new config file
|
||
let user_config_path = test_env.env_root().join("config.toml");
|
||
test_env.set_config_path(&user_config_path);
|
||
let work_dir = test_env.work_dir("repo");
|
||
std::fs::write(
|
||
&user_config_path,
|
||
format!(
|
||
indoc! {"
|
||
foo = 'global'
|
||
[[--scope]]
|
||
--when.repositories = [{repo_path}]
|
||
foo = 'repo'
|
||
"},
|
||
// "\\?\" paths shouldn't be required on Windows
|
||
repo_path = to_toml_value(dunce::simplified(work_dir.root()).to_str().unwrap())
|
||
),
|
||
)
|
||
.unwrap();
|
||
|
||
let output = test_env.run_jj_in(".", ["config", "get", "foo"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
global
|
||
[EOF]
|
||
");
|
||
let output = work_dir.run_jj(["config", "get", "foo"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
repo
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_show_paths() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
|
||
work_dir
|
||
.run_jj(["config", "set", "--user", "ui.paginate", ":builtin"])
|
||
.success();
|
||
let output = test_env.run_jj_in(".", ["st"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
------- stderr -------
|
||
Config error: Invalid type or value for ui.paginate
|
||
Caused by: unknown variant `:builtin`, expected `never` or `auto`
|
||
|
||
Hint: Check the config file: $TEST_ENV/config/config0001.toml
|
||
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_author_change_warning() {
|
||
let test_env = TestEnvironment::default();
|
||
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
|
||
let work_dir = test_env.work_dir("repo");
|
||
let output = work_dir.run_jj(["config", "set", "--repo", "user.email", "'Foo'"]);
|
||
insta::assert_snapshot!(output, @r#"
|
||
------- stderr -------
|
||
Warning: This setting will only impact future commits.
|
||
The author of the working copy will stay "Test User <test.user@example.com>".
|
||
To change the working copy author, use "jj describe --reset-author --no-edit"
|
||
[EOF]
|
||
"#);
|
||
|
||
// test_env.run_jj*() resets state for every invocation
|
||
// for this test, the state (user.email) is needed
|
||
work_dir
|
||
.run_jj_with(|cmd| {
|
||
cmd.args(["describe", "--reset-author", "--no-edit"])
|
||
.env_remove("JJ_EMAIL")
|
||
})
|
||
.success();
|
||
|
||
let output = work_dir.run_jj(["log"]);
|
||
insta::assert_snapshot!(output, @r"
|
||
@ qpvuntsm Foo 2001-02-03 08:05:09 f64cf908
|
||
│ (empty) (no description set)
|
||
◆ zzzzzzzz root() 00000000
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_config_author_change_warning_root_env() {
|
||
let test_env = TestEnvironment::default();
|
||
let output = test_env.run_jj_in(".", ["config", "set", "--user", "user.email", "'Foo'"]);
|
||
insta::assert_snapshot!(output, @"");
|
||
}
|
||
|
||
fn find_stdout_lines(keyname_pattern: &str, stdout: &str) -> String {
|
||
let key_line_re = Regex::new(&format!(r"(?m)^{keyname_pattern} = .*\n")).unwrap();
|
||
key_line_re.find_iter(stdout).map(|m| m.as_str()).collect()
|
||
}
|