| 884 | 884 | | if let (Some(owner), Some(name)) = (param("owner"), param("name")) { |
| 885 | 885 | | input.insert("repo".to_owned(), Value::String(format!("{owner}/{name}"))); |
| 886 | 886 | | } |
| 887 | | − | for key in ["plan", "id", "workspace", "delivery", "workflow", "job", "setting", "username", "team", "basehead"] { |
| 888 | | − | if let Some(value) = param(key) { |
| 889 | | − | input.insert(key.to_owned(), Value::String(value.to_owned())); |
| 887 | + | // Every other name the path gives, under that name; the ones below that |
| 888 | + | // need more (a repository, a number, an encoded name) are set after. |
| 889 | + | for (key, value) in ¶ms { |
| 890 | + | if !matches!(*key, "owner" | "name") { |
| 891 | + | input.insert((*key).to_owned(), Value::String(percent_decoded(value))); |
| 890 | 892 | | } |
| 891 | 893 | | } |
| 892 | 894 | | // A repository of a team's workspace, named by itself. |
| ⋯ |
| 939 | 941 | | |
| 940 | 942 | | use super::*; |
| 941 | 943 | | |
| 944 | + | /// Every name a route's path gives reaches the operation: a name left |
| 945 | + | /// off the lists above is dropped, and the operation answers that it |
| 946 | + | /// was not given (the project routes were, until this test). |
| 947 | + | #[test] |
| 948 | + | fn every_path_parameter_reaches_the_input() { |
| 949 | + | for route in ROUTES.iter() { |
| 950 | + | let names: Vec<&str> = route.path.split('/').filter_map(|part| part.strip_prefix(':')).collect(); |
| 951 | + | if names.is_empty() { |
| 952 | + | continue; |
| 953 | + | } |
| 954 | + | let path: String = route |
| 955 | + | .path |
| 956 | + | .split('/') |
| 957 | + | .map(|part| match part.strip_prefix(':') { |
| 958 | + | Some("number" | "milestone") => "7".to_owned(), |
| 959 | + | Some(name) => format!("{name}-x"), |
| 960 | + | None => part.to_owned(), |
| 961 | + | }) |
| 962 | + | .collect::<Vec<_>>() |
| 963 | + | .join("/"); |
| 964 | + | let (found, input) = resolve(route.method, &path, &[], json!({})).unwrap(); |
| 965 | + | // A path two routes could take is checked under the first. |
| 966 | + | if found.path != route.path { |
| 967 | + | continue; |
| 968 | + | } |
| 969 | + | for name in names { |
| 970 | + | let key = match name { |
| 971 | + | "owner" | "name" => "repo", |
| 972 | + | "repo" if names_has_workspace(route.path) => "repo", |
| 973 | + | other => other, |
| 974 | + | }; |
| 975 | + | assert!( |
| 976 | + | input.get(key).is_some_and(|value| !value.is_null()), |
| 977 | + | "{} {}: :{name} does not reach the input", |
| 978 | + | route.method, |
| 979 | + | route.path |
| 980 | + | ); |
| 981 | + | } |
| 982 | + | } |
| 983 | + | } |
| 984 | + | |
| 985 | + | fn names_has_workspace(path: &str) -> bool { |
| 986 | + | path.split('/').any(|part| part == ":workspace") |
| 987 | + | } |
| 988 | + | |
| 942 | 989 | | #[test] |
| 943 | 990 | | fn a_path_resolves_to_its_operation_and_input() { |
| 944 | 991 | | let (route, input) = resolve( |