Skip to content

Commit cf134f2

Browse files
authored
chore(cli): add snapshot tests for reporters (#577)
i want to make sure the cli refactoring does not change functionality :D
1 parent c63ff0c commit cf134f2

18 files changed

+371
-115
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ squawk/
3030
# Auto generated treesitter files
3131
crates/pgt_treesitter_grammar/src/grammar.json
3232
crates/pgt_treesitter_grammar/src/node-types.json
33-
crates/pgt_treesitter_grammar/src/parser.c
33+
crates/pgt_treesitter_grammar/src/parser.c
34+
crates/pgt_treesitter_grammar/src/parser.c.codex-session-id
35+
.codex-session-id

Cargo.lock

Lines changed: 2 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pgt_cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ tikv-jemallocator = "0.6.0"
5353

5454
[dev-dependencies]
5555
assert_cmd = "2.0.16"
56-
predicates = "3.1.3"
56+
insta = { workspace = true, features = ["yaml"] }
5757

5858
[lib]
5959
doctest = false
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use assert_cmd::Command;
2+
use insta::assert_snapshot;
3+
use std::path::Path;
4+
use std::process::ExitStatus;
5+
6+
const BIN: &str = "postgres-language-server";
7+
const CONFIG_PATH: &str = "tests/fixtures/postgres-language-server.jsonc";
8+
9+
#[test]
10+
#[cfg_attr(
11+
target_os = "windows",
12+
ignore = "snapshot expectations only validated on unix-like platforms"
13+
)]
14+
fn check_default_reporter_snapshot() {
15+
assert_snapshot!(run_check(&["tests/fixtures/test.sql"]));
16+
}
17+
18+
#[test]
19+
#[cfg_attr(
20+
target_os = "windows",
21+
ignore = "snapshot expectations only validated on unix-like platforms"
22+
)]
23+
fn check_github_reporter_snapshot() {
24+
assert_snapshot!(run_check(&[
25+
"--reporter",
26+
"github",
27+
"tests/fixtures/test.sql"
28+
]));
29+
}
30+
31+
#[test]
32+
#[cfg_attr(
33+
target_os = "windows",
34+
ignore = "snapshot expectations only validated on unix-like platforms"
35+
)]
36+
fn check_gitlab_reporter_snapshot() {
37+
assert_snapshot!(run_check(&[
38+
"--reporter",
39+
"gitlab",
40+
"tests/fixtures/test.sql"
41+
]));
42+
}
43+
44+
#[test]
45+
#[cfg_attr(
46+
target_os = "windows",
47+
ignore = "snapshot expectations only validated on unix-like platforms"
48+
)]
49+
fn check_junit_reporter_snapshot() {
50+
assert_snapshot!(run_check(&[
51+
"--reporter",
52+
"junit",
53+
"tests/fixtures/test.sql"
54+
]));
55+
}
56+
57+
#[test]
58+
#[cfg_attr(
59+
target_os = "windows",
60+
ignore = "snapshot expectations only validated on unix-like platforms"
61+
)]
62+
fn check_stdin_snapshot() {
63+
assert_snapshot!(run_check_with(
64+
&[
65+
"--config-path",
66+
CONFIG_PATH,
67+
"--stdin-file-path",
68+
"virtual.sql"
69+
],
70+
Some("alter tqjable stdin drop column id;\n"),
71+
None
72+
));
73+
}
74+
75+
#[test]
76+
#[cfg_attr(
77+
target_os = "windows",
78+
ignore = "snapshot expectations only validated on unix-like platforms"
79+
)]
80+
fn check_directory_traversal_snapshot() {
81+
let project_dir = Path::new("tests/fixtures/traversal");
82+
assert_snapshot!(run_check_with(
83+
&["--diagnostic-level", "info", "."],
84+
None,
85+
Some(project_dir)
86+
));
87+
}
88+
89+
fn run_check(args: &[&str]) -> String {
90+
let mut full_args = vec!["--config-path", CONFIG_PATH];
91+
full_args.extend_from_slice(args);
92+
run_check_with(&full_args, None, None)
93+
}
94+
95+
fn run_check_with(args: &[&str], stdin: Option<&str>, cwd: Option<&Path>) -> String {
96+
let mut cmd = Command::cargo_bin(BIN).expect("binary not built");
97+
if let Some(dir) = cwd {
98+
cmd.current_dir(dir);
99+
}
100+
if let Some(input) = stdin {
101+
cmd.write_stdin(input);
102+
}
103+
104+
let mut full_args = vec!["check"];
105+
full_args.extend_from_slice(args);
106+
let output = cmd.args(full_args).output().expect("failed to run CLI");
107+
108+
normalize_output(
109+
output.status,
110+
&String::from_utf8_lossy(&output.stdout),
111+
&String::from_utf8_lossy(&output.stderr),
112+
)
113+
}
114+
115+
fn normalize_durations(input: &str) -> String {
116+
let mut content = input.to_owned();
117+
118+
let mut search_start = 0;
119+
while let Some(relative) = content[search_start..].find(" in ") {
120+
let start = search_start + relative + 4;
121+
if let Some(end_rel) = content[start..].find('.') {
122+
let end = start + end_rel;
123+
if content[start..end].chars().any(|c| c.is_ascii_digit()) {
124+
content.replace_range(start..end, "<duration>");
125+
search_start = start + "<duration>".len() + 1;
126+
continue;
127+
}
128+
search_start = end + 1;
129+
} else {
130+
break;
131+
}
132+
}
133+
134+
let mut time_search = 0;
135+
while let Some(relative) = content[time_search..].find("time=\"") {
136+
let start = time_search + relative + 6;
137+
if let Some(end_rel) = content[start..].find('"') {
138+
let end = start + end_rel;
139+
if content[start..end].chars().any(|c| c.is_ascii_digit()) {
140+
content.replace_range(start..end, "<duration>");
141+
}
142+
time_search = end + 1;
143+
} else {
144+
break;
145+
}
146+
}
147+
148+
content
149+
}
150+
151+
fn normalize_output(status: ExitStatus, stdout: &str, stderr: &str) -> String {
152+
let normalized_stdout = normalize_durations(stdout);
153+
let normalized_stderr = normalize_diagnostics(stderr);
154+
let status_label = if status.success() {
155+
"success"
156+
} else {
157+
"failure"
158+
};
159+
format!(
160+
"status: {status_label}\nstdout:\n{}\nstderr:\n{}\n",
161+
normalized_stdout.trim_end(),
162+
normalized_stderr.trim_end()
163+
)
164+
}
165+
166+
fn normalize_diagnostics(input: &str) -> String {
167+
let normalized = normalize_durations(input);
168+
let mut lines = normalized.lines().peekable();
169+
let mut diagnostic_sections: Vec<String> = Vec::new();
170+
let mut other_lines: Vec<String> = Vec::new();
171+
172+
while let Some(line) = lines.next() {
173+
if is_path_line(line) {
174+
let mut block = String::from(line);
175+
while let Some(&next) = lines.peek() {
176+
if is_path_line(next) || next.starts_with("check ") {
177+
break;
178+
}
179+
block.push('\n');
180+
block.push_str(next);
181+
lines.next();
182+
}
183+
diagnostic_sections.push(trim_trailing_newlines(block));
184+
} else {
185+
other_lines.push(line.to_string());
186+
}
187+
}
188+
189+
diagnostic_sections.sort();
190+
191+
let mut parts = Vec::new();
192+
if !diagnostic_sections.is_empty() {
193+
parts.push(diagnostic_sections.join("\n\n"));
194+
}
195+
196+
let rest = trim_trailing_newlines(other_lines.join("\n"));
197+
if rest.trim().is_empty() {
198+
parts.join("\n\n")
199+
} else if parts.is_empty() {
200+
rest
201+
} else {
202+
parts.push(rest);
203+
parts.join("\n\n")
204+
}
205+
}
206+
207+
fn is_path_line(line: &str) -> bool {
208+
let trimmed = line.trim_start();
209+
(trimmed.starts_with("./") || trimmed.starts_with("tests/"))
210+
&& trimmed.contains(':')
211+
&& trimmed.contains(" syntax")
212+
}
213+
214+
fn trim_trailing_newlines(mut value: String) -> String {
215+
while value.ends_with('\n') {
216+
value.pop();
217+
}
218+
value
219+
}

crates/pgt_cli/tests/assert_cmd.rs

Lines changed: 0 additions & 16 deletions
This file was deleted.

crates/pgt_cli/tests/commands/check.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.

crates/pgt_cli/tests/commands/mod.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "https://pg-language-server.com/schema/postgres-language-server.schema.json",
3+
"vcs": {
4+
"enabled": false,
5+
"clientKind": "git",
6+
"useIgnoreFile": false
7+
},
8+
"files": {
9+
"ignore": []
10+
},
11+
"linter": {
12+
"enabled": true,
13+
"rules": {
14+
"recommended": true
15+
}
16+
}
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
alter tqjable another drop column id;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
alter tqjable bad drop column id;

0 commit comments

Comments
 (0)