a textual notation to locate fields within atproto records (draft spec)
microcosm.tngl.io/RecordPath/
1use recordpath::{enumerate, is_vector, match_path, parse, PathType};
2use serde::Deserialize;
3use serde_json::Value;
4use std::collections::HashSet;
5
6const MATCH_JSON: &str = include_str!("../../interop-tests/match.json");
7const ENUMERATE_JSON: &str = include_str!("../../interop-tests/enumerate.json");
8const PARSE_JSON: &str = include_str!("../../interop-tests/parse.json");
9
10// -- Match tests --
11
12#[derive(Deserialize)]
13struct MatchFixture {
14 tests: Vec<MatchTest>,
15}
16
17#[derive(Deserialize)]
18struct MatchTest {
19 description: String,
20 record: Value,
21 path: String,
22 expected: Vec<Value>,
23}
24
25#[test]
26fn match_tests() {
27 let f: MatchFixture = serde_json::from_str(MATCH_JSON).unwrap();
28 for t in &f.tests {
29 let result = match_path(&t.record, &t.path);
30 assert_eq!(result, t.expected, "FAIL: {}", t.description);
31 }
32}
33
34// -- Enumerate tests --
35
36#[derive(Deserialize)]
37struct EnumFixture {
38 tests: Vec<EnumTest>,
39}
40
41#[derive(Deserialize)]
42struct EnumTest {
43 description: String,
44 record: Value,
45 expected: Vec<EnumExpected>,
46}
47
48#[derive(Deserialize)]
49struct EnumExpected {
50 path: String,
51 r#type: String,
52}
53
54#[test]
55fn enumerate_tests() {
56 let f: EnumFixture = serde_json::from_str(ENUMERATE_JSON).unwrap();
57 for t in &f.tests {
58 let result_set: HashSet<String> = enumerate(&t.record)
59 .map(|(p, _value)| {
60 let ty = match p.path_type {
61 PathType::Scalar => "scalar",
62 PathType::Vector => "vector",
63 };
64 format!("{}:{ty}", p.path)
65 })
66 .collect();
67 let expected_set: HashSet<String> = t
68 .expected
69 .iter()
70 .map(|p| format!("{}:{}", p.path, p.r#type))
71 .collect();
72 assert_eq!(result_set, expected_set, "FAIL: {}", t.description);
73 }
74}
75
76// -- Parse type classification tests --
77
78#[derive(Deserialize)]
79struct ParseFixture {
80 type_tests: Vec<TypeTest>,
81 invalid_tests: Vec<InvalidTest>,
82}
83
84#[derive(Deserialize)]
85struct TypeTest {
86 description: String,
87 path: String,
88 r#type: String,
89}
90
91#[derive(Deserialize)]
92struct InvalidTest {
93 description: String,
94 path: String,
95}
96
97#[test]
98fn type_classification_tests() {
99 let f: ParseFixture = serde_json::from_str(PARSE_JSON).unwrap();
100 for t in &f.type_tests {
101 let result = if is_vector(&t.path) {
102 "vector"
103 } else {
104 "scalar"
105 };
106 assert_eq!(result, t.r#type, "FAIL: {}", t.description);
107 }
108}
109
110#[test]
111fn invalid_parse_tests() {
112 let f: ParseFixture = serde_json::from_str(PARSE_JSON).unwrap();
113 for t in &f.invalid_tests {
114 assert!(
115 parse(&t.path).is_err(),
116 "FAIL: {} — expected parse('{}') to fail",
117 t.description,
118 t.path
119 );
120 }
121}