Skip to content

Commit 598fe97

Browse files
committed
Add more test cases
Signed-off-by: Federico Torres <[email protected]>
1 parent e62a619 commit 598fe97

File tree

2 files changed

+208
-8
lines changed

2 files changed

+208
-8
lines changed

src/encoding.rs

+148-8
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ fn escape_name(name: &str, scheme: &EscapingScheme) -> String {
891891
} else if is_valid_legacy_char(b, i) {
892892
escaped.push(b);
893893
} else {
894-
escaped.push('_');
894+
escaped.push_str("__");
895895
}
896896
}
897897
}
@@ -901,14 +901,12 @@ fn escape_name(name: &str, scheme: &EscapingScheme) -> String {
901901
}
902902
escaped.push_str("U__");
903903
for (i, b) in name.chars().enumerate() {
904-
if is_valid_legacy_char(b, i) {
904+
if b == '_' {
905+
escaped.push_str("__");
906+
} else if is_valid_legacy_char(b, i) {
905907
escaped.push(b);
906-
} else if !b.is_ascii() {
907-
escaped.push_str("_FFFD_");
908-
} else if b as u32 <= 0xFF {
909-
write!(escaped, "_{:02X}_", b as u32).unwrap();
910-
} else if b as u32 <= 0xFFFF {
911-
write!(escaped, "_{:04X}_", b as u32).unwrap();
908+
} else {
909+
write!(escaped, "_{:x}_", b as i64).unwrap();
912910
}
913911
}
914912
}
@@ -935,3 +933,145 @@ pub fn negotiate_escaping_scheme(
935933
}
936934
default_escaping_scheme
937935
}
936+
937+
#[cfg(test)]
938+
mod tests {
939+
use super::*;
940+
941+
#[test]
942+
fn metric_name_is_legacy_valid() {
943+
struct Scenario {
944+
input: &'static str,
945+
expected: bool,
946+
}
947+
948+
let scenarios = vec![
949+
Scenario {
950+
input: "Avalid_23name",
951+
expected: true,
952+
},
953+
Scenario {
954+
input: "_Avalid_23name",
955+
expected: true,
956+
},
957+
Scenario {
958+
input: "1valid_23name",
959+
expected: false,
960+
},
961+
Scenario {
962+
input: "avalid_23name",
963+
expected: true,
964+
},
965+
Scenario {
966+
input: "Ava:lid_23name",
967+
expected: true,
968+
},
969+
Scenario {
970+
input: "a lid_23name",
971+
expected: false,
972+
},
973+
Scenario {
974+
input: ":leading_colon",
975+
expected: true,
976+
},
977+
Scenario {
978+
input: "colon:in:the:middle",
979+
expected: true,
980+
},
981+
Scenario {
982+
input: "",
983+
expected: false,
984+
},
985+
Scenario {
986+
input: "aÅz",
987+
expected: false,
988+
},
989+
];
990+
991+
for scenario in scenarios {
992+
let result = is_valid_legacy_metric_name(scenario.input);
993+
assert_eq!(result, scenario.expected);
994+
}
995+
}
996+
997+
#[test]
998+
fn test_escape_name() {
999+
struct Scenario {
1000+
name: &'static str,
1001+
input: &'static str,
1002+
expected_underscores: &'static str,
1003+
expected_dots: &'static str,
1004+
expected_value: &'static str,
1005+
}
1006+
1007+
let scenarios = vec![
1008+
Scenario {
1009+
name: "empty string",
1010+
input: "",
1011+
expected_underscores: "",
1012+
expected_dots: "",
1013+
expected_value: "",
1014+
},
1015+
Scenario {
1016+
name: "legacy valid name",
1017+
input: "no:escaping_required",
1018+
expected_underscores: "no:escaping_required",
1019+
expected_dots: "no:escaping__required",
1020+
expected_value: "no:escaping_required",
1021+
},
1022+
Scenario {
1023+
name: "name with dots",
1024+
input: "mysystem.prod.west.cpu.load",
1025+
expected_underscores: "mysystem_prod_west_cpu_load",
1026+
expected_dots: "mysystem_dot_prod_dot_west_dot_cpu_dot_load",
1027+
expected_value: "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load",
1028+
},
1029+
Scenario {
1030+
name: "name with dots and underscore",
1031+
input: "mysystem.prod.west.cpu.load_total",
1032+
expected_underscores: "mysystem_prod_west_cpu_load_total",
1033+
expected_dots: "mysystem_dot_prod_dot_west_dot_cpu_dot_load__total",
1034+
expected_value: "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load__total",
1035+
},
1036+
Scenario {
1037+
name: "name with dots and colon",
1038+
input: "http.status:sum",
1039+
expected_underscores: "http_status:sum",
1040+
expected_dots: "http_dot_status:sum",
1041+
expected_value: "U__http_2e_status:sum",
1042+
},
1043+
Scenario {
1044+
name: "name with spaces and emoji",
1045+
input: "label with 😱",
1046+
expected_underscores: "label_with__",
1047+
expected_dots: "label__with____",
1048+
expected_value: "U__label_20_with_20__1f631_",
1049+
},
1050+
Scenario {
1051+
name: "name with unicode characters > 0x100",
1052+
input: "花火",
1053+
expected_underscores: "__",
1054+
expected_dots: "____",
1055+
expected_value: "U___82b1__706b_",
1056+
},
1057+
Scenario {
1058+
name: "name with spaces and edge-case value",
1059+
input: "label with \u{0100}",
1060+
expected_underscores: "label_with__",
1061+
expected_dots: "label__with____",
1062+
expected_value: "U__label_20_with_20__100_",
1063+
},
1064+
];
1065+
1066+
for scenario in scenarios {
1067+
let result = escape_name(scenario.input, &EscapingScheme::UnderscoreEscaping);
1068+
assert_eq!(result, scenario.expected_underscores, "{}", scenario.name);
1069+
1070+
let result = escape_name(scenario.input, &EscapingScheme::DotsEscaping);
1071+
assert_eq!(result, scenario.expected_dots, "{}", scenario.name);
1072+
1073+
let result = escape_name(scenario.input, &EscapingScheme::ValueEncodingEscaping);
1074+
assert_eq!(result, scenario.expected_value, "{}", scenario.name);
1075+
}
1076+
}
1077+
}

src/encoding/text.rs

+60
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,66 @@ mod tests {
15821582
assert_eq!(&response[response.len() - 20..], "ogins_total 0\n# EOF\n");
15831583
}
15841584

1585+
#[test]
1586+
fn encode_labels_no_labels() {
1587+
let mut buffer = String::new();
1588+
let mut encoder = MetricEncoder {
1589+
writer: &mut buffer,
1590+
prefix: None,
1591+
name: "name",
1592+
unit: None,
1593+
const_labels: &[],
1594+
family_labels: None,
1595+
name_validation_scheme: &UTF8Validation,
1596+
escaping_scheme: &NoEscaping,
1597+
};
1598+
1599+
encoder.encode_labels::<NoLabelSet>(None).unwrap();
1600+
assert_eq!(buffer, "");
1601+
}
1602+
1603+
#[test]
1604+
fn encode_labels_with_all_labels() {
1605+
let mut buffer = String::new();
1606+
let const_labels = vec![(Cow::Borrowed("t1"), Cow::Borrowed("t1"))];
1607+
let additional_labels = vec![(Cow::Borrowed("t2"), Cow::Borrowed("t2"))];
1608+
let family_labels = vec![(Cow::Borrowed("t3"), Cow::Borrowed("t3"))];
1609+
let mut encoder = MetricEncoder {
1610+
writer: &mut buffer,
1611+
prefix: None,
1612+
name: "name",
1613+
unit: None,
1614+
const_labels: &const_labels,
1615+
family_labels: Some(&family_labels),
1616+
name_validation_scheme: &UTF8Validation,
1617+
escaping_scheme: &NoEscaping,
1618+
};
1619+
1620+
encoder.encode_labels(Some(&additional_labels)).unwrap();
1621+
assert_eq!(buffer, "{t1=\"t1\",t2=\"t2\",t3=\"t3\"}");
1622+
}
1623+
1624+
#[test]
1625+
fn encode_labels_with_quoted_label_names() {
1626+
let mut buffer = String::new();
1627+
let const_labels = vec![(Cow::Borrowed("service.name"), Cow::Borrowed("t1"))];
1628+
let additional_labels = vec![(Cow::Borrowed("whatever\\whatever"), Cow::Borrowed("t2"))];
1629+
let family_labels = vec![(Cow::Borrowed("t*3"), Cow::Borrowed("t3"))];
1630+
let mut encoder = MetricEncoder {
1631+
writer: &mut buffer,
1632+
prefix: None,
1633+
name: "name",
1634+
unit: None,
1635+
const_labels: &const_labels,
1636+
family_labels: Some(&family_labels),
1637+
name_validation_scheme: &UTF8Validation,
1638+
escaping_scheme: &NoEscaping,
1639+
};
1640+
1641+
encoder.encode_labels(Some(&additional_labels)).unwrap();
1642+
assert_eq!(buffer, "{\"service.name\"=\"t1\",\"whatever\\whatever\"=\"t2\",\"t*3\"=\"t3\"}");
1643+
}
1644+
15851645
fn parse_with_python_client(input: String) {
15861646
pyo3::prepare_freethreaded_python();
15871647

0 commit comments

Comments
 (0)